In this blog post, we will discuss MongoDB 3.6 Retryable Writes, a new application-level feature.
From the beginning, MongoDB replica sets were designed to recover gracefully from many internal problems or events such as node crashes, network partitions/errors/interruptions, replica set member fail-overs, etc.
While these events eventually recover transparently to the overall replica set, in many instances these events return errors to the application. The most common example is a failover of the Primary during a write: this returns network errors to most MongoDB drivers. Another possible situation is a Primary receiving a write, but the acknowledgment response never makes it back to the driver. Here it is unclear to the application if the write really succeeded or not.
If an application is designed for writes to be idempotent, generally all the application needs to do in a problem scenario is send the same write operation again and again until it succeeds. This approach is extremely dangerous to data integrity, however, if the application was not designed for idempotent writes! Retrying writes relying on state can lead to incorrect results.
MongoDB 3.6 Retryable Writes
MongoDB 3.6 introduces the concept of Retryable Writes to address situations where simple retrying of idempotent operations is not possible or desired (often more code is required to perform retries). This feature is implemented transparently via the use of unique IDs for each write operation that both the MongoDB driver and server can consider when handling failures.
This feature allows the application driver to automatically retry a failed write behind-the-scenes, without throwing an exception/error to the application. Retryable Writes mitigates problems caused by short interruptions, not long-term problems. Therefore, the mechanism only retries a write operation exactly once. If the retry is unsuccessful, then the application receives an error/exception as normal.
If a healthy Primary cannot be found to retry the write, the MongoDB driver waits for a time period equal to the serverSelectionTimeoutMS server parameter before retrying the write, so that it can allow for a failover to occur.
MongoDB implemented this feature in both the MongoDB driver and server, and it has some requirements:
- MongoDB Version – every node in the cluster or replica set must run version 3.6 or greater. All nodes must also have featureCompatabilityVersion set to ‘3.6’.
- MongoDB Driver – this feature requires that your application use a MongoDB driver that supports it.
- Replication – The Retryable Writes feature requires that MongoDB Replication is enabled. You can use a single-node Replica Set to achieve this if you do not wish to deploy many nodes.
- Write Concern – A Write Concern of ‘1’ or greater is required for this feature to operate.
- Storage Engine – The use of MMAPv1 is not possible with this feature. WiredTiger or inMemory storage engines only!
With the exception of insert operations, this feature is limited to operations that change only a single document, meaning the following operations cannot use Retryable Writes:
- Multi-document Update (multi: true)
- Multi-document Delete
- Bulk Operations with Multi-document changes
The full list of operations available for use with this feature is here: https://docs.mongodb.com/manual/core/retryable-writes/#retryable-write-operations.
Using Retryable Writes
Enabling Retryable Writes doesn’t require major code changes!
Generally, you enable the use of Retryable Writes by adding the ‘retryWrites=’ flag to your MongoDB connection string that is passed to your MongoDB driver:
You enable the feature on the ‘mongo’ shell with the command-line flag ‘–retryWrites’:
That’s it! The rest is transparent to you!
The MongoDB 3.6 Retryable Writes feature continues a theme I’ve noticed in the last few major releases: improved data integrity and improved development experience.
The use of this great new feature should lead to simplified code and improved data integrity in applications using non-idempotent changes!