ISSUE DESCRIPTION AND IMPACT
During an update with upsert:true option, two (or more) threads may attempt an upsert operation using the same query predicate and, upon not finding a match, the threads will attempt to insert a new document. Both inserts will (and should) succeed, unless the second causes a unique constraint violation. This MongoDB behavior is documented here.
It is expected that the client will take appropriate action upon detection of such constraint violation. Appropriate action may vary depending on individual application requirements.
IMPROVEMENT DESCRIPTION
As part of SERVER-37124, fixed in versions 4.1.6 and later, the server will automatically retry performing the upsert in cases where it is detectably safe to do so. It is safe for the server to retry when the following conditions hold:
- The operation has the upsert:true flag.
- The operation is not a multi-update.
- The upsert failed with the DuplicateKey error code due to a unique index with key pattern kp.
- The match condition is either a single equality predicate, or a logical AND of equality predicates.
- The set of fields over which there is an equality predicate equals the set of fields in the index key pattern kp.
- The fields in the match condition are not mutated by the update modifiers.
The following table contains examples of upsert operations that the server can automatically retry on DuplicateKey error, and examples where the server cannot automatically retry:
kp | update | server will retry |
---|---|---|
{_id: 1} | db.coll.update({_id:5}, {$set:{a:5}}, {upsert:true}) | yes |
{a:1} | db.coll.update({a:{$in:[5]}}, {$set:{b:5}}, {upsert:true}) | yes |
{a:1,b:1} | db.coll.update({a:5,b:7},{$set:{c:5}}, {upsert:true}) | yes |
{a:1,b:1} | db.coll.update({a:5,b:7},{$set:{a:7,c:5}},{upsert:true}) | no, since a is in kp and is mutated |
{a:1} | db.coll.update({_id:5},{$set:{b:6}}, {upsert:true}) | no, since the predicate does not contain a |
{_id:1} | db.coll.update({_id:5,v:6},{$set:{a:5,b:6}}, {upsert:true}) | no, since v is not in kp |
{a:1} | db.coll.update({a:{$lt:5}},{$set:{b:5}}, {upsert:true}) | no, since the predicate on a is not an equality |
This ticket will now be resolved as duplicate of SERVER-37124 since we don't believe there is further work that we can do on the server and the application must correctly determine whether the operation that failed can be safely retried again or not.
VERSIONS
This improvement in SERVER-37124 will be included in the 4.2 production release
Original description
It is possible that two updates come in with upsert:true, resulting in neither finding a document and both inserting new documents which conflict on unique index violations of the query predicate. In this case it is possible for the server to retry any failed updates.
Currently clients can retry the update to get the same behavior which is expected from the server.
This affects both updates and findAndModify. See SERVER-10350 for slightly more background.
- duplicates
-
SERVER-37124 Retry full upsert path when duplicate key exception matches exact query predicate
- Closed
- is duplicated by
-
SERVER-22607 Duplicate key error on replace with upsert
- Closed
-
SERVER-10350 Atomicity of Upserts on multi field unique index
- Closed
-
SERVER-16905 Concurrent upserts to the same non-existing document can result in duplicate key error
- Closed
-
SERVER-19600 Upsert results in E11000 duplicate key error
- Closed
-
SERVER-20022 Upsert errors with "duplicate key error"
- Closed
-
SERVER-20158 Errors during findAndModify runs on Mongo2.4.3 when both upsert and new are true
- Closed
-
SERVER-20380 Upsert is not atomic when inserting
- Closed
-
SERVER-21006 duplicate key error on upsert
- Closed
-
SERVER-22988 MongoDB Concurrency Lock Failed for Multithread Insert/Update
- Closed
-
SERVER-36763 refreshLogicalSessionCacheNow can error with DuplicateKey error in concurrency suite
- Closed
-
SERVER-18784 Errors during concurrent update/upsert on an existing document with a unique index
- Closed
-
SERVER-21948 DuplicateKeyError during upsert
- Closed
-
SERVER-16261 Make insert on update (upsert: true) atomic with query + insert
- Closed
- is related to
-
SERVER-19694 Distributed lock should retry on duplicate key error creating the lock entry
- Closed
-
SERVER-59758 findAndModify with upsert=true generates duplicate key errors
- Closed
-
SERVER-72028 E11000 duplicate key error collection: <col name> index: _id_ dup key: { _id: "xxxxxx 2022-12-10" }', details={}}
- Closed
-
SERVER-72127 Upsert behaving differently on clustered collections compared with regular
- Closed
-
SERVER-47212 Retry full upsert path when duplicate key exception matches exact query predicate in findAndModify
- Closed
- related to
-
SERVER-44980 wrong update result with upsert:true
- Closed
-
PYTHON-1510 db.collection.save() cause DuplicateKeyError
- Closed