How Retryable Writes Works In MongoDB?

Rofl Facts
6 min readDec 7, 2023

--

When MongoDB drivers experience network failures or are unable to locate a healthy primary in the replica set or sharded cluster, they can automatically retry specific write operations one time thanks to retryable writes.

Prerequisites

Retryable writes must meet the following criteria:

Supported Deployment Topologies

Retryable writes do not support standalone instances and necessitate a replica set or sharded cluster.

Supported Storage Engine

Storage engines that provide document-level locking, such the WiredTiger or in-memory storage engines, are necessary for retryable writes.

3.6+ MongoDB Drivers

Updated MongoDB drivers are necessary for clients using MongoDB 3.6 or above.

MongoDB Version

All cluster nodes must have a MongoDB version of 3.6 or above, and all cluster nodes must have a featureCompatibilityVersion of 3.6 or higher. For further details on the featureCompatibilityVersion flag, see setFeatureCompatibilityVersion.

Write Acknowledgment

Retries are not allowed for write operations that have a Write Concern of 0.

Retryable Writes and Multi-Document Transactions

Retryable writing operations are the transaction commit and abort functions. Whether or not retryWrites is set to false, MongoDB drivers only attempt a single retry of an operation in the event that an error occurs during the commit or abort process.

Regardless of the value of retryWrites, the write operations inside the transaction cannot be separately retried.

See Transactions for additional details on transactions.

Enabling Retryable Writes

MongoDB Drivers

Retryable Writes are enabled by default in drivers that are compatible with MongoDB 4.2 and later. For older drivers, the retryWrites=true option is necessary. Applications that use drivers compatible with MongoDB 4.2 and higher can omit the retryWrites=true option.

Applications that use drivers compatible with MongoDB 4.2 and higher must add retryWrites=false in the connection string in order to disable retryable writings.

mongosh

In mongosh, retryable writes are enabled by default. Use the command line option — retryWrites=false to turn off retryable writes:

mongosh --retryWrites=false

Retryable Write Operations

When a write worry is raised, such as “Write Concern cannot be {w: 0},” the subsequent write operations are retryable.

Notes: There is no way to independently retry the write operations inside the transactions.

Methods - Descriptions

db.collection.insertOne(), db.collection.insertMany() Insert operations.

db.collection.updateOne(), db.collection.replaceOne() Single-document update operations.

db.collection.deleteOne(), db.collection.remove() Single document delete operations.

db.collection.findAndModify(), db.collection.findOneAndDelete(), db.collection.findOneAndReplace(), db.collection.findOneAndUpdate() findAndModify operations. All findAndModify operations are single document operations.

Note: Updates to Shard Key Values

As of MongoDB 4.2, single-document update/findAndModify operations can be performed as a retryable write or in a transaction to alter a document’s shard key value (unless the shard key field is the immutable _id field). See Modify a Document’s Shard Key Value for further information.

Note: When a duplicate key exception occurs, MongoDB 4.2 will retry a subset of single-document upserts (update with upsert: true and multi: false). For conditions, see Duplicate Key Errors on Upsert.Before MongoDB 4.2, when an upsert operation met a duplicate key error, MongoDB would not attempt the operation again.

Behavior

Persistent Network Errors

Retryable writes in MongoDB only attempt a single retry. Replica set elections and momentary network issues are assisted by this, but permanent network errors are not addressed.

Failover Period

Before retrying, the drivers wait serverSelectionTimeoutMS milliseconds to identify the new primary in the target replica set or sharded cluster shard if they are unable to locate a healthy primary there. In situations where the failover interval beyond serverSelectionTimeoutMS, retryable writes are not addressed.

Note: It is possible that the write operation will be retried and applied again when the client application responds (without restarting) if it is unresponsive for longer than the localLogicalSessionTimeoutMinutes following the issuance of the write operation.

Duplicate Key Errors on Upsert

Only if all of the following criteria are met will MongoDB 4.2 attempt to retry single-document upsert operations (i.e., upsert : true and multi : false) that fail because of a duplicate key error.

  • The duplicate key problem was brought on by the target collection’s unique index.
  • The requirement for an update match is either:

A single equality predicate

{ "fieldA" : "valueA" },

or

a logical AND of equality predicates

{ "fieldA" : "valueA", "fieldB" : "valueB" }

  • The set of fields in the update query predicate corresponds to the set of fields in the unique index key pattern.
  • None of the fields in the query predicate are changed by the update operation.

Examples of upsert actions on duplicate key errors that the server can or cannot retry are shown in the following table:

#Unique Index Key Pattern
{ _id : 1 }


#Update Operation
db.collName.updateOne(
{ _id : ObjectId("1aa1c1efb123f14aaa167aaa") },
{ $set : { fieldA : 25 } },
{ upsert : true }
)

#Retryable
Yes
#Unique Index Key Pattern
{ fieldA : 1 }


#Update Operation
db.collName.updateOne(
{ fieldA : { $in : [ 25 ] } },
{ $set : { fieldB : "someValue" } },
{ upsert : true }
)

#Retryable
Yes
#Unique Index Key Pattern
{
fieldA : 1,
fieldB : 1
}


#Update Operation
db.collName.updateOne(
{ fieldA : 25, fieldB : "someValue" },
{ $set : { fieldC : false } },
{ upsert : true }
)

#Retryable
Yes
#Unique Index Key Pattern
{ fieldA : 1 }


#Update Operation
db.collName.updateOne(
{ fieldA : { $lte : 25 } },
{ $set : { fieldC : true } },
{ upsert : true }
)

#Retryable

No
The query predicate on fieldA is not an equality
#Unique Index Key Pattern
{ fieldA : 1 }


#Update Operation
db.collName.updateOne(
{ fieldA : { $in : [ 25 ] } },
{ $set : { fieldA : 20 } },
{ upsert : true }
)

#Retryable

No
The update operation modifies fields specified in the query predicate.
#Unique Index Key Pattern
{ _id : 1 }


#Update Operation
db.collName.updateOne(
{ fieldA : { $in : [ 25 ] } },
{ $set : { fieldA : 20 } },
{ upsert : true }
)

#Retryable

No
The set of query predicate fields (fieldA) does not match the set of index key fields (_id).
#Unique Index Key Pattern
{ fieldA : 1 }


#Update Operation
db.collName.updateOne(
{ fieldA : 25, fieldC : true },
{ $set : { fieldD : false } },
{ upsert : true }
)

#Retryable

No
The set of query predicate fields (fieldA, fieldC) does not match the set of index key fields (fieldA).

Up until MongoDB 4.2, retryable writes in MongoDB were not compatible with retrying upserts that failed because of duplicate key problems.

Diagnostics

In the transactions section, the serverStatus command and its mongosh shell helper db.serverStatus() provide data on retryable writes.

Retryable Writes Against local Database

Retryable writes are enabled by default in the official MongoDB 4.2-series drivers. Upgrading to 4.2-series drivers will result in write errors for applications that write to the local database unless retryable writes are specifically disabled.

You can set retryWrites=false in the MongoDB cluster’s connection string to prevent retryable writes.

Error Handling

Beginning with MongoDB 6.1, MongoDB sends an error with the NoWritesPerformed label if a retryable write fails on both its first and second attempts without a single write being completed.

The NoWritesPerformed label is used to distinguish between the outcomes of batch processes such as insertMany(). When doing an insertMany operation, one of the following results is possible:

Outcome — MongoDB Output

No documents are inserted. — Error returned with NoWritesPerformed label.

Partial work done. (At least one document is inserted, but not all.) — Error returned without NoWritesPerformed label.

All documents are inserted. — Success returned.

Applications can conclusively establish that no documents were inserted by using the NoWritesPerformed label. When handling retryable writes, this error reporting enables the programme to keep an accurate state of the database.

When a retryable write fails on both the first and second try, an error is returned in earlier versions of MongoDB. That being said, no distinction is made to suggest that no writing were done.

--

--