Skip to content

Commit 4ad26f1

Browse files
authored
DOCSP-36046 Transaction FindOneAndUpdate Doesn't Lock Document (#6100) (#6733)
* DOCSP-36046 Transaction FindOneAndUpdate Doesn't Lock Document * wording * IF feedback: add stale reads to glossary, add procedure steps * formatting * typo * * * IF suggestions * io code block * typo * hide default visibility * nit edit * AK feedback * wording * * * * * * * Asya feedback * Asya feedback 2/2 * final feedback
1 parent 699f5fc commit 4ad26f1

File tree

3 files changed

+85
-30
lines changed

3 files changed

+85
-30
lines changed

source/core/read-preference-use-cases.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ read preference modes:
5353

5454
Use :readmode:`primaryPreferred` if you want an application to
5555
read from the primary under normal circumstances, but to
56-
allow stale reads from secondaries when the primary is unavailable. This provides a
57-
"read-only mode" for your application during a failover.
56+
allow :term:`stale reads <stale read>` from secondaries when the primary is
57+
unavailable.
5858

5959
.. _read-preference-counter-indications:
6060

source/includes/extracts-transactions.yaml

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -207,37 +207,87 @@ content: |
207207
ref: transactions-stale-reads
208208
content: |
209209
210-
Read operations inside a transaction can return stale data. That is,
211-
read operations inside a transaction are not guaranteed to see
212-
writes performed by other committed transactions or
213-
non-transactional writes. For
214-
example, consider the following sequence: 1) a transaction is
215-
in-progress 2) a write outside the transaction deletes a document 3)
216-
a read operation inside the transaction is able to read the
217-
now-deleted document since the operation is using a snapshot from
218-
before the write.
210+
Read operations inside a transaction can return old data, which is known as a
211+
:term:`stale read`. Read operations inside a transaction are not guaranteed
212+
to see writes performed by other committed transactions or
213+
non-transactional writes. For example, consider the following sequence:
214+
215+
#. A transaction is in-progress.
216+
217+
#. A write outside the transaction deletes a document.
218+
219+
#. A read operation inside the transaction can read the now-deleted document
220+
since the operation uses a snapshot from before the write operation.
219221
220222
To avoid stale reads inside transactions for a single document, you
221-
can use the :method:`db.collection.findOneAndUpdate()` method. For
222-
example:
223-
224-
.. code-block:: javascript
225-
226-
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
227-
228-
employeesCollection = session.getDatabase("hr").employees;
229-
230-
employeeDoc = employeesCollection.findOneAndUpdate(
231-
{ _id: 1, employee: 1, status: "Active" },
232-
{ $set: { employee: 1 } },
233-
{ returnNewDocument: true }
234-
);
235-
236-
- If the employee document has changed outside the transaction, then
237-
the transaction aborts.
223+
can use the :method:`db.collection.findOneAndUpdate()` method. The following
224+
:binary:`~bin.mongosh` example demonstrates how you can use
225+
``db.collection.findOneAndUpdate()`` to take a :term:`write lock` and ensure
226+
that your reads are up to date:
227+
228+
.. procedure::
229+
:style: normal
230+
231+
.. step:: Insert a document into the ``employees`` collection
232+
233+
.. code-block:: javascript
234+
:copyable: true
235+
236+
db.getSiblingDB("hr").employees.insertOne(
237+
{ _id: 1, status: "Active" }
238+
)
239+
240+
.. step:: Start a session
241+
242+
.. code-block:: javascript
243+
:copyable: true
244+
245+
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } )
246+
247+
.. step:: Start a transaction
248+
249+
.. code-block:: javascript
250+
:copyable: true
251+
252+
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } )
253+
254+
employeesCollection = session.getDatabase("hr").employees
255+
256+
.. step:: Use ``db.collection.findOneAndUpdate()`` inside the transaction
257+
258+
.. code-block:: javascript
259+
:copyable: true
260+
261+
employeeDoc = employeesCollection.findOneAndUpdate(
262+
{ _id: 1, status: "Active" },
263+
{ $set: { lockId: ObjectId() } },
264+
{ returnNewDocument: true }
265+
)
266+
267+
Note that inside the transaction, the ``findOneAndUpdate`` operation
268+
sets a new ``lockId`` field. You can set ``lockId`` field to any
269+
value, as long as it modifies the document. By updating the
270+
document, the transaction acquires a lock.
271+
272+
If an operation outside of the transaction attempts to modify the
273+
document before you commit the transaction, MongoDB returns a write
274+
conflict error to the external operation.
275+
276+
.. step:: Commit the transaction
277+
278+
.. code-block:: javascript
279+
:copyable: true
280+
281+
session.commitTransaction()
282+
283+
After you commit the transaction, MongoDB releases the lock.
284+
285+
.. note::
286+
287+
If any operation in the transaction fails, the transaction
288+
aborts and all data changes made in the transaction are discarded
289+
without ever becoming visible in the collection.
238290
239-
- If the employee document has not changed, the transaction returns
240-
the document and locks the document.
241291
---
242292
ref: transactions-read-concern-majority
243293
content: |

source/reference/glossary.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1063,6 +1063,11 @@ Glossary
10631063
state electronics for persistence instead of rotating platters
10641064
and movable read/write heads used by mechanical hard drives.
10651065

1066+
stale read
1067+
A stale read refers to when a transaction reads old (stale) data that has
1068+
been modified by another transaction but not yet committed to the
1069+
database.
1070+
10661071
standalone
10671072
An instance of :binary:`~bin.mongod` that runs as a single server
10681073
and not as part of a :term:`replica set`. To convert it to a

0 commit comments

Comments
 (0)