Skip to content

Commit 4319c0f

Browse files
DOCSP-36301 Move duplicate key exception info to upsert page (#6332) (#6507)
* WIP * remove 4.2 info * formatting * formatting * first draft of review edits * edits * tweaks * edits * formatting * typo * edit * edits * review edits * formatting fix * remove extra unique index links
1 parent 816bfdf commit 4319c0f

File tree

3 files changed

+106
-203
lines changed

3 files changed

+106
-203
lines changed

source/core/retryable-writes.txt

Lines changed: 5 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@ Retryable Writes
1616

1717
Retryable writes allow MongoDB drivers to automatically retry certain
1818
write operations a single time if they encounter network errors, or if
19-
they cannot find a healthy :term:`primary` in the
20-
:ref:`replica sets <replication>` or :ref:`sharded cluster
21-
<sharding-introduction>`. [#duplicate-key-update]_
19+
they cannot find a healthy :term:`primary` in the :ref:`replica set
20+
<replication>` or :ref:`sharded cluster <sharding-introduction>`.
2221

2322
Prerequisites
2423
-------------
@@ -105,20 +104,15 @@ cannot be :writeconcern:`{w: 0} <\<number\>>`.
105104
* - | :method:`db.collection.insertOne()`
106105
| :method:`db.collection.insert()`
107106
| :method:`db.collection.insertMany()`
108-
109-
- Insert operations.
107+
- Insert operations
110108

111109
* - | :method:`db.collection.updateOne()`
112110
| :method:`db.collection.replaceOne()`
113-
| :method:`db.collection.save()`
114-
| :method:`db.collection.update()` where ``multi`` is ``false``
115-
116-
- Single-document update operations. [#duplicate-key-update]_
111+
- Single-document update operations
117112

118113
* - | :method:`db.collection.deleteOne()`
119114
| :method:`db.collection.remove()` where ``justOne`` is ``true``
120-
121-
- Single document delete operations.
115+
- Single document delete operations
122116

123117
* - | :method:`db.collection.findAndModify()`
124118
| :method:`db.collection.findOneAndDelete()`
@@ -165,16 +159,6 @@ cannot be :writeconcern:`{w: 0} <\<number\>>`.
165159
retryable write or in a :doc:`transaction </core/transactions>`. For
166160
details, see :ref:`update-shard-key`.
167161

168-
.. [#duplicate-key-update]
169-
170-
MongoDB 4.2 will retry certain single-document upserts
171-
(update with ``upsert: true`` and ``multi: false``) that encounter a
172-
duplicate key exception. See :ref:`retryable-update-upsert` for
173-
conditions.
174-
175-
Prior to MongoDB 4.2, MongoDB would not retry upsert operations
176-
that encountered a duplicate key error.
177-
178162
Behavior
179163
--------
180164

@@ -203,163 +187,6 @@ the failover period exceeds :urioption:`serverSelectionTimeoutMS`.
203187
applications starts responding (without a restart), the write
204188
operation may be retried and applied again.
205189

206-
.. _retryable-update-upsert:
207-
208-
Duplicate Key Errors on Upsert
209-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
210-
211-
MongoDB 4.2 will retry single-document upsert operations
212-
(i.e ``upsert : true`` and ``multi : false``) that
213-
fail due to a duplicate key error *only if* the operation meets
214-
*all* of the following conditions:
215-
216-
- The target collection has a unique index that caused the duplicate key
217-
error.
218-
219-
- The update match condition is either:
220-
221-
- A single equality predicate
222-
223-
``{ "fieldA" : "valueA" }``,
224-
225-
*or*
226-
227-
- a logical AND of equality predicates
228-
229-
``{ "fieldA" : "valueA", "fieldB" : "valueB" }``
230-
231-
- The set of fields in the unique index key pattern matches the set
232-
of fields in the update query predicate.
233-
234-
- The update operation does not modify any of the fields in the
235-
query predicate.
236-
237-
The following table contains examples of upsert operations that
238-
the server can or cannot retry on a duplicate key error:
239-
240-
.. list-table::
241-
:header-rows: 1
242-
:widths: 30 40 30
243-
244-
* - Unique Index Key Pattern
245-
- Update Operation
246-
- Retryable
247-
248-
* - .. code-block:: javascript
249-
:copyable: false
250-
251-
{ _id : 1 }
252-
- .. code-block:: javascript
253-
:copyable: false
254-
255-
db.collName.updateOne(
256-
{ _id : ObjectId("1aa1c1efb123f14aaa167aaa") },
257-
{ $set : { fieldA : 25 } },
258-
{ upsert : true }
259-
)
260-
- Yes
261-
262-
* - .. code-block:: javascript
263-
:copyable: false
264-
265-
{ fieldA : 1 }
266-
- .. code-block:: javascript
267-
:copyable: false
268-
269-
db.collName.updateOne(
270-
{ fieldA : { $in : [ 25 ] } },
271-
{ $set : { fieldB : "someValue" } },
272-
{ upsert : true }
273-
)
274-
- Yes
275-
276-
* - .. code-block:: javascript
277-
:copyable: false
278-
279-
{
280-
fieldA : 1,
281-
fieldB : 1
282-
}
283-
- .. code-block:: javascript
284-
:copyable: false
285-
286-
db.collName.updateOne(
287-
{ fieldA : 25, fieldB : "someValue" },
288-
{ $set : { fieldC : false } },
289-
{ upsert : true }
290-
)
291-
- Yes
292-
293-
* - .. code-block:: javascript
294-
:copyable: false
295-
296-
{ fieldA : 1 }
297-
- .. code-block:: javascript
298-
:copyable: false
299-
300-
db.collName.updateOne(
301-
{ fieldA : { $lte : 25 } },
302-
{ $set : { fieldC : true } },
303-
{ upsert : true }
304-
)
305-
- No
306-
307-
The query predicate on ``fieldA`` is not an equality
308-
309-
* - .. code-block:: javascript
310-
:copyable: false
311-
312-
{ fieldA : 1 }
313-
- .. code-block:: javascript
314-
:copyable: false
315-
316-
db.collName.updateOne(
317-
{ fieldA : { $in : [ 25 ] } },
318-
{ $set : { fieldA : 20 } },
319-
{ upsert : true }
320-
)
321-
- No
322-
323-
The update operation modifies fields specified in the
324-
query predicate.
325-
326-
* - .. code-block:: javascript
327-
:copyable: false
328-
329-
{ _id : 1 }
330-
- .. code-block:: javascript
331-
:copyable: false
332-
333-
db.collName.updateOne(
334-
{ fieldA : { $in : [ 25 ] } },
335-
{ $set : { fieldA : 20 } },
336-
{ upsert : true }
337-
)
338-
- No
339-
340-
The set of query predicate fields (``fieldA``) does not
341-
match the set of index key fields (``_id``).
342-
343-
* - .. code-block:: javascript
344-
:copyable: false
345-
346-
{ fieldA : 1 }
347-
- .. code-block:: javascript
348-
:copyable: false
349-
350-
db.collName.updateOne(
351-
{ fieldA : 25, fieldC : true },
352-
{ $set : { fieldD : false } },
353-
{ upsert : true }
354-
)
355-
- No
356-
357-
The set of query predicate fields (``fieldA``, ``fieldC``)
358-
does not match the set of index key fields (``fieldA``).
359-
360-
Prior to MongoDB 4.2, MongoDB retryable writes did not support
361-
retrying upserts which failed due to duplicate key errors.
362-
363190
Diagnostics
364191
~~~~~~~~~~~
365192

source/includes/extracts-upsert-unique-index.yaml

Lines changed: 97 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,114 @@
11
ref: _upsert-unique-index-base
22
content: |
33
4-
When using the {{upsert}} option with the {{command}}
5-
{{commandOrMethod}}, **and not** using a :ref:`unique index
6-
<index-type-unique>` on the query field(s), multiple
7-
instances of {{aOrAn}} {{command}} operation with similar query
8-
field(s) could result in duplicate documents being inserted in
9-
certain circumstances.
4+
Upserts can create duplicate documents, unless there is a
5+
:ref:`unique index <index-type-unique>` to prevent duplicates.
106
117
Consider an example where no document with the name ``Andy`` exists
128
and multiple clients issue the following command at roughly the same
139
time:
1410
1511
{{codeExample}}
1612
17-
If all {{command}} operations finish the query phase
18-
before any client successfully inserts data, **and** there is no
19-
:ref:`unique index <index-type-unique>` on the ``name`` field, each
20-
{{command}} operation may result in an insert, creating multiple
21-
documents with ``name: Andy``.
22-
23-
To ensure that only one such document is created, and the other
24-
{{command}} operations update this new document instead, create a
25-
:ref:`unique index <index-type-unique>` on the ``name`` field. This
26-
guarantees that only one document with ``name: Andy`` is permitted
27-
in the collection.
28-
29-
With this unique index in place, the multiple {{command}} operations
30-
now exhibit the following behavior:
13+
If all {{command}} operations finish the query phase before any
14+
client successfully inserts data, **and** there is no unique index on
15+
the ``name`` field, each {{command}} operation may result in an
16+
insert, creating multiple documents with ``name: Andy``.
17+
18+
A unique index on the ``name`` field ensures that only one document
19+
is created. With a unique index in place, the multiple {{command}}
20+
operations now exhibit the following behavior:
3121
3222
- Exactly one {{command}} operation will successfully insert a new
3323
document.
3424
35-
- All other {{command}} operations will update the newly-inserted
36-
document, incrementing the ``score`` value.
25+
- Other {{command}} operations either update the newly-inserted
26+
document or fail due to a unique key collision.
27+
28+
In order for other {{command}} operations to update the
29+
newly-inserted document, **all** of the following conditions must
30+
be met:
31+
32+
- The target collection has a unique index that would cause a
33+
duplicate key error.
34+
35+
- The update operation is not ``updateMany`` or ``multi`` is
36+
``false``.
37+
38+
- The update match condition is either:
39+
40+
- A single equality predicate. For example ``{ "fieldA" : "valueA" }``
41+
42+
- A logical AND of equality predicates. For example ``{ "fieldA" :
43+
"valueA", "fieldB" : "valueB" }``
44+
45+
- The fields in the equality predicate match the fields in the
46+
unique index key pattern.
47+
48+
- The update operation does not modify any fields in the
49+
unique index key pattern.
50+
51+
The following table shows examples of ``upsert`` operations that,
52+
when a key collision occurs, either result in an update or fail.
53+
54+
.. list-table::
55+
:header-rows: 1
56+
:widths: 30 40 30
57+
58+
* - Unique Index Key Pattern
59+
- Update Operation
60+
- Result
61+
62+
* - .. code-block:: javascript
63+
:copyable: false
64+
65+
{ name : 1 }
66+
67+
- .. code-block:: javascript
68+
:copyable: false
69+
70+
db.people.updateOne(
71+
{ name: "Andy" },
72+
{ $inc: { score: 1 } },
73+
{ upsert: true }
74+
)
75+
- The ``score`` field of the matched document is incremented by
76+
1.
77+
78+
* - .. code-block:: javascript
79+
:copyable: false
80+
81+
{ name : 1 }
82+
83+
- .. code-block:: javascript
84+
:copyable: false
85+
86+
db.people.updateOne(
87+
{ name: { $ne: "Joe" } },
88+
{ $set: { name: "Andy" } },
89+
{ upsert: true }
90+
)
91+
92+
- The operation fails because it modifies the field in the
93+
unique index key pattern (``name``).
94+
95+
* - .. code-block:: javascript
96+
:copyable: false
97+
98+
{ name : 1 }
99+
- .. code-block:: javascript
100+
:copyable: false
101+
102+
db.people.updateOne(
103+
{ name: "Andy", email: "[email protected]" },
104+
{ $set: { active: false } },
105+
{ upsert: true }
106+
)
107+
- The operation fails because the equality predicate fields
108+
(``name``, ``email``) do not match the index key field
109+
(``name``).
110+
111+
37112
38113
---
39114
ref: upsert-unique-index-findAndModify-command

source/reference/method/db.collection.update.txt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -987,9 +987,11 @@ with :method:`~db.collection.update()`.
987987
:method:`WriteResult()`
988988

989989
.. _update-with-unique-indexes:
990+
.. _retryable-update-upsert:
991+
.. _upsert-duplicate-key-error:
990992

991-
Upsert with Unique Index
992-
````````````````````````
993+
Upsert with Duplicate Values
994+
````````````````````````````
993995

994996
.. include:: /includes/extracts/upsert-unique-index-update-method.rst
995997

@@ -1488,4 +1490,3 @@ field:
14881490
.. seealso::
14891491

14901492
:method:`WriteResult.hasWriteError()`
1491-

0 commit comments

Comments
 (0)