Skip to content

Commit 73c392e

Browse files
committed
DOCS-2455: Split up the Isolate Sequence of Operations tutorial
1 parent 11eafac commit 73c392e

File tree

4 files changed

+151
-95
lines changed

4 files changed

+151
-95
lines changed

config/htaccess.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4826,6 +4826,13 @@ from: /core/multikey-index-bounds
48264826
to: /core/indexes/
48274827
code: 303
48284828
type: redirect
4829+
outputs:
4830+
- 'before-v2.4'
4831+
---
4832+
from: /tutorial/update-if-current
4833+
to: /tutorial/isolate-sequence-of-operations
4834+
code: 303
4835+
type: redirect
48294836
outputs:
48304837
- 'before-v2.4'
48314838
...

source/includes/toc-crud-tutorials.yaml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,14 @@ description: |
4141
---
4242
file: /tutorial/isolate-sequence-of-operations
4343
description: |
44-
Use the :operator:`<isolation> isolated` operator to *isolate* a
44+
Use the :update:`$isolated` operator to *isolate* a
4545
single write operation that affects multiple documents, preventing
4646
other operations from interrupting the sequence of write operations.
4747
---
48+
file: /tutorial/update-if-current
49+
description: |
50+
Update a document only if it has not changed since it was last read.
51+
---
4852
file: /tutorial/create-an-auto-incrementing-field
4953
description: |
5054
Describes how to create an incrementing sequence number for the

source/tutorial/isolate-sequence-of-operations.txt

Lines changed: 33 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -13,109 +13,48 @@ more than one collection.
1313

1414
When a single write operation modifies multiple documents, the
1515
operation as a whole is not atomic, and other operations may
16-
interleave. The modification of a single document, or record, is always
17-
atomic, even if the write operation modifies multiple sub-documents
18-
*within* the single record.
16+
interleave. The modification of a single document is always atomic, even if
17+
the write operation modifies multiple sub-documents *within* the single record.
1918

20-
No other operations are atomic; however, you can *isolate* a
19+
No other operations are atomic. However, you can *isolate* a
2120
single write operation that affects multiple documents using the
22-
:doc:`isolation operator </reference/operator/update/isolated>`.
21+
:doc:`isolation operator </reference/operator/update/isolated>`. Using this
22+
operator, you can update a set of documents without allowing other processes
23+
to update in between your writes.
2324

24-
This document describes one method of updating documents *only* if the
25-
local copy of the document reflects the current state of the document
26-
in the database. In addition the following methods provide a way to
27-
manage isolated sequences of operations:
25+
It is important to understand that this is *not* true atomicity, because a
26+
failed write will not roll back the preceding writes. Additionally, this
27+
operator does *not* work on sharded clusters.
2828

29-
- the :method:`~db.collection.findAndModify()`
30-
provides an isolated update and return operation.
29+
Approaches
30+
----------
3131

32-
- :doc:`/tutorial/perform-two-phase-commits`
32+
``findAndModify()``
33+
~~~~~~~~~~~~~~~~~~~
3334

34-
- Create a :ref:`unique index <index-type-unique>`, to ensure that a
35-
key doesn't exist when you insert it.
35+
The :method:`~db.collection.findAndModify()` method provides an isolated
36+
update-and-return operation. This allows you to update a document and get back
37+
the updated state, without having to worry about another process having mutated
38+
it.
3639

37-
.. _tutorial-atomic-update-if-current:
40+
Two Phase Commit
41+
~~~~~~~~~~~~~~~~
3842

39-
Update if Current
40-
-----------------
41-
42-
In this pattern, you will:
43-
44-
- query for a document,
45-
46-
- modify the fields in that document
47-
48-
- and update the fields of a document *only if* the fields have not
49-
changed in the collection since the query.
50-
51-
Consider the following example in JavaScript which attempts to update
52-
the ``qty`` field of a document in the ``products`` collection:
53-
54-
.. versionchanged:: 2.6
55-
The :method:`db.collection.update()` method now returns a
56-
:method:`WriteResult()` object that contains the status of
57-
the operation. Previous versions required an extra
58-
:method:`db.getLastErrorObj()` method call.
59-
60-
.. code-block:: javascript
61-
62-
var myCollection = db.products;
63-
var myDocument = myCollection.findOne( { sku: 'abc123' } );
43+
:doc:`Two Phase Commit </tutorial/perform-two-phase-commits>` is a general
44+
protocol for multi-document transactions that you may implement in your
45+
application logic.
6446

65-
if (myDocument) {
47+
Unique Indexes
48+
~~~~~~~~~~~~~~
6649

67-
var oldQty = myDocument.qty;
50+
Create a :ref:`unique index <index-type-unique>` to ensure that each document
51+
has a distinct value for a specific field. In situations where you assign a
52+
value that does not already exist in a collection, this prevents a second
53+
process from assigning the same value elsewhere at the same time.
6854

69-
if (myDocument.qty < 10) {
70-
myDocument.qty *= 4;
71-
} else if ( myDocument.qty < 20 ) {
72-
myDocument.qty *= 3;
73-
} else {
74-
myDocument.qty *= 2;
75-
}
76-
77-
var results = myCollection.update(
78-
{
79-
_id: myDocument._id,
80-
qty: oldQty
81-
},
82-
{
83-
$set: { qty: myDocument.qty }
84-
}
85-
);
86-
87-
if ( results.hasWriteError() ) {
88-
print("unexpected error updating document: " + tojson( results ));
89-
} else if ( results.nMatched == 0 ) {
90-
print("No update: no matching document for { _id: " + myDocument._id + ", qty: " + oldQty + " }")
91-
}
92-
93-
}
94-
95-
Your application may require some modifications of this pattern, such
96-
as:
97-
98-
- Use the entire document as the query in the
99-
:method:`~db.collection.update()` operation, to generalize the
100-
operation and guarantee that the original document was not modified,
101-
rather than ensuring that as single field was not changed.
102-
103-
- Add a version variable to the document that applications increment
104-
upon each update operation to the documents. Use this version
105-
variable in the query expression. You must be able to ensure that
106-
*all* clients that connect to your database obey this constraint.
107-
108-
- Use :update:`$set` in the update expression to modify only your
109-
fields and prevent overriding other fields.
110-
111-
- Use one of the methods described in :doc:`/tutorial/create-an-auto-incrementing-field`.
55+
Update if Current
56+
~~~~~~~~~~~~~~~~~
11257

113-
.. Maybe incorporate the blurb: "MongoDB does not
114-
support traditional locking and complex transactions for a number of
115-
reasons: First, in sharded environments, distributed locks could be
116-
expensive and slow. MongoDB's goal is to be lightweight and fast. We
117-
dislike the concept of deadlocks. We want the system to be simple and
118-
predictable without these sort of surprises. We want MongoDB to work
119-
well for realtime problems. If an operation may execute which locks
120-
large amounts of data, it might stop some small light queries for an
121-
extended period of time."
58+
:doc:`Update if Current </tutorial/update-if-current>` is a pattern for
59+
changing a document without potentially overwriting the changes of other
60+
processes.

source/tutorial/update-if-current.txt

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
==========================
2+
Update Document if Current
3+
==========================
4+
5+
.. default-domain:: mongodb
6+
7+
Overview
8+
------------
9+
10+
To isolate a group of document modifications on a set of documents, always
11+
attempt to use a combination of unique indexes and a single
12+
:method:`db.collection.update()` operation. Because all modifications are atomic
13+
within a single document, a well-designed data model allows applications to
14+
group related operations together.
15+
16+
Nevertheless there are cases where a logical data modification operation requires
17+
application-based logic. Because MongoDB cannot isolate application-based
18+
update logic, your code must ensure that its multi-stage update operations
19+
are logically valid and not affected by other concurrent operations in your
20+
system.
21+
22+
.. _tutorial-atomic-update-if-current:
23+
24+
Update if Current Pattern
25+
-------------------------
26+
27+
Overview
28+
~~~~~~~~
29+
30+
In the :ref:`Update if Current
31+
<tutorial-atomic-update-if-current>` pattern, you will:
32+
33+
- query for a document,
34+
35+
- modify the fields in that document,
36+
37+
- and update the fields of a document *only if* the fields have not
38+
changed in the collection since the query.
39+
40+
Example
41+
~~~~~~~
42+
43+
Consider the following example in the MongoDB shell which attempts to update
44+
the ``qty`` field of a document in the ``products`` collection:
45+
46+
.. versionchanged:: 2.6
47+
The :method:`db.collection.update()` method now returns a
48+
:method:`WriteResult()` object that contains the status of
49+
the operation. Previous versions required an extra
50+
:method:`db.getLastErrorObj()` method call.
51+
52+
.. code-block:: javascript
53+
54+
// Update a document only if it is safe to do so
55+
function updateIfCurrent( collection, query, modifyFunc ) {
56+
var myDocument = collection.findOne( query );
57+
58+
if ( myDocument ) {
59+
var newField = modifyFunc( myDocument );
60+
var updateDoc = { $set: newField }
61+
62+
// This will fail to match any documents if the underlying document
63+
// has some field different from myDocument.
64+
var results = collection.update( myDocument, updateDoc );
65+
66+
if ( results.hasWriteError() ) {
67+
print( "unexpected error updating document: " + tojson( results ) );
68+
} else if ( results.nMatched === 0 ) {
69+
print( "No update: no matching document for " + tojson( query ) );
70+
}
71+
}
72+
}
73+
74+
// Apply an update function to a db.products document, or fail.
75+
updateIfCurrent( db.products, { sky: "abc123" }, function( doc ) {
76+
var qty = doc.qty
77+
78+
if ( qty < 10 ) {
79+
qty *= 4;
80+
} else if ( qty < 20 ) {
81+
qty *= 3;
82+
} else {
83+
qty *= 2;
84+
}
85+
86+
return { qty: qty };
87+
} );
88+
89+
Additional Information
90+
----------------------
91+
92+
A different approach is to add a ``version`` field to the document that
93+
applications increment upon each update operation to the documents. Use this
94+
version variable in the query expression. You must be able to ensure that
95+
*all* clients that connect to your database obey this constraint.
96+
97+
If you need to associate increasing numbers with documents in a collection,
98+
you can use one of the methods described in
99+
:doc:`/tutorial/create-an-auto-incrementing-field`.
100+
101+
Use :update:`$set` in the update expression to modify only your fields and
102+
prevent overriding other fields.
103+
104+
:doc:`Two Phase Commit </tutorial/perform-two-phase-commits>` is a general
105+
protocol for multi-document transactions that you may implement in your
106+
application logic.

0 commit comments

Comments
 (0)