Skip to content

Commit 0461523

Browse files
committed
DOCS-5324 quorum reads with findAndModify
1 parent 12ef344 commit 0461523

File tree

7 files changed

+194
-11
lines changed

7 files changed

+194
-11
lines changed

source/includes/fact-findAndModify-update-comparison.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ When updating a document, |operation| and the
1313
method, you cannot specify which single document to update when
1414
multiple documents match.
1515

16-
- By default, |operation| method returns |return-object|. To
16+
- By default, |operation| returns |return-object|. To
1717
obtain the updated document, use the ``new`` option.
1818

1919
The :method:`~db.collection.update()` method returns a
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
title: Create a unique index.
2+
stepnum: 1
3+
ref: quorum-read-unique-index
4+
pre: |
5+
Create a unique index on the fields that will be used to specify an
6+
exact match in the :method:`db.collection.findAndModify()` operation.
7+
8+
This tutorial will use an exact match on the ``sku`` field. As such,
9+
create a unique index on the ``sku`` field.
10+
action:
11+
language: javascript
12+
code: |
13+
db.products.createIndex( { sku: 1 }, { unique: true } )
14+
---
15+
title: Use ``findAndModify`` to read committed data.
16+
stepnum: 2
17+
ref: quorum-read-findAndModify
18+
pre: |
19+
Use the :method:`db.collection.findAndModify()` method to make a
20+
trivial update to the document you want to read and return the
21+
modified document. To specify the document to read, you must use an
22+
exact match query that is supported by a unique index.
23+
24+
The following :method:`~db.collection.findAndModify()` operation
25+
specifies an exact match on the uniquely indexed field ``sku`` and
26+
increments the field named ``_dummy_field`` in the matching document.
27+
action:
28+
language: javascript
29+
code: |
30+
var updatedDocument = db.products.findAndModify(
31+
{
32+
query: { sku: "abc123" },
33+
update: { $inc: { _dummy_field: 1 } },
34+
new: true
35+
}
36+
);
37+
---
38+
title: Issue ``getLastError`` to determine quorum read.
39+
stepnum: 3
40+
ref: quorum-read-gle
41+
pre: |
42+
To determine if the read from the
43+
:method:`~db.collection.findAndModify()` was a true quorum read,
44+
issue a :dbcommand:`getLastError` command with :writeconcern:`w:
45+
"majority"`.
46+
47+
While not necessary to the procedure, the
48+
:dbcommand:`getLastError` command also includes a :ref:`wc-wtimeout`
49+
value of ``5000`` milliseconds to prevent the operation from blocking
50+
forever if the write cannot propagate to a majority of voting members.
51+
action:
52+
language: javascript
53+
code: |
54+
var gle = db.runCommand( { getLastError: 1, w: "majority", wtimeout: 5000 } );
55+
56+
if ( (gle.ok != 1) || (gle.err != null) ) {
57+
print("The document returned from findAndModify() may reflect data that is not durable and subject to rollback.");
58+
printjson(gle);
59+
} else {
60+
printjson(updatedDocument);
61+
}
62+
post: |
63+
The :dbcommand:`getLastError` determines whether the update from
64+
the :method:`~db.collection.findAndModify()` operation has
65+
propagated to the majority of the replica set's voting members.
66+
67+
Even in situations where two nodes in the replica set believe that
68+
they are the primary, only one will be able to complete the write
69+
with :writeconcern:`w: "majority"`. As such, the
70+
:dbcommand:`getLastError` with :writeconcern:`w: "majority"` write
71+
concern can confirm whether the client has connected to the true
72+
primary to perform the :method:`~db.collection.findAndModify()`
73+
operation.
74+
75+
.. note::
76+
:ref:`wc-wtimeout` causes :dbcommand:`getLastError` to return
77+
with an error after the specified time limit, even if the
78+
required write concern will eventually succeed. As such, if the
79+
:dbcommand:`getLastError` times out, it cannot determine whether
80+
the document returned by :method:`~db.collection.findAndModify()`
81+
is or is not the result of a quorum read. For quorum reads,
82+
ignore the document returned by
83+
:method:`~db.collection.findAndModify()` and repeat the
84+
:method:`~db.collection.findAndModify()` and
85+
:dbcommand:`getLastError`.
86+
87+
Since the quorum read procedure only increments a dummy field in
88+
the document, you can safely repeat the
89+
:method:`~db.collection.findAndModify()` and
90+
:dbcommand:`getLastError`, adjusting the :ref:`wc-wtimeout` as
91+
necessary.
92+
...

source/includes/toc-crud-tutorials.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@ file: /tutorial/create-an-auto-incrementing-field
5252
description: |
5353
Describes how to create an incrementing sequence number for the
5454
``_id`` field using a Counters Collection or an Optimistic Loop.
55+
---
56+
file: /tutorial/perform-findAndModify-quorum-reads
57+
description: |
58+
Perform quorum reads using ``findAndModify``.
5559
...

source/reference/command/findAndModify.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,5 @@ The method returns the deleted document:
418418
},
419419
"ok" : 1
420420
}
421+
422+
.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads`

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,5 @@ The method returns the deleted document:
287287
"state" : "active",
288288
"rating" : 3
289289
}
290+
291+
.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads`

source/reference/write-concern.txt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -40,31 +40,32 @@ concern on to the shard.
4040
``w`` Option
4141
~~~~~~~~~~~~
4242

43-
The ``w`` option provides the ability to disable write concern entirely
44-
*as well as* specify the write concern for :term:`replica sets <replica
45-
set>`.
43+
The ``w`` option provides the ability to specify the write concern for
44+
:term:`replica sets <replica set>` *as well as* disable write concern
45+
entirely.
4646

4747
MongoDB uses ``w: 1`` as the default write concern. ``w: 1``
4848
provides basic receipt acknowledgment.
4949

50-
The ``w`` option accepts the following values:
50+
Using the ``w`` option, the following ``w: <value>`` write concerns are
51+
available:
5152

5253
.. list-table::
5354
:header-rows: 1
54-
:widths: 25 75
55+
:widths: 30 70
5556

5657
* - Value
5758

5859
- Description
5960

60-
* - ``1``
61+
* - .. writeconcern:: w: 1
6162

6263
- Provides acknowledgment of write operations on a standalone
6364
:program:`mongod` or the :term:`primary` in a replica set.
6465

6566
This is the default write concern for MongoDB.
6667

67-
* - ``0``
68+
* - .. writeconcern:: w: 0
6869

6970
- Disables basic acknowledgment of write operations, but returns
7071
information about socket exceptions and networking errors to the
@@ -75,7 +76,7 @@ The ``w`` option accepts the following values:
7576
the server will require that :program:`mongod` acknowledge
7677
the write operation.
7778

78-
* - <Number greater than 1>
79+
* - .. writeconcern:: w: <Number greater than 1>
7980

8081
- Guarantees that write operations have propagated successfully to
8182
the specified number of replica set members including the
@@ -89,7 +90,7 @@ The ``w`` option accepts the following values:
8990
members to become available, which means MongoDB blocks
9091
indefinitely.
9192

92-
* - ``"majority"``
93+
* - .. writeconcern:: w: "majority"
9394

9495
- Confirms that write operations have propagated to the majority
9596
of voting nodes: a majority of the replica set's
@@ -103,7 +104,7 @@ The ``w`` option accepts the following values:
103104

104105
.. include:: /includes/fact-master-slave-majority.rst
105106

106-
* - <tag set>
107+
* - .. writeconcern:: w: <tag set>
107108

108109
- By specifying a :ref:`tag set
109110
<replica-set-configuration-tag-sets>`, you can have fine-grained
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
====================================
2+
Perform Quorum Reads on Replica Sets
3+
====================================
4+
5+
.. default-domain:: mongodb
6+
7+
Overview
8+
--------
9+
10+
When reading from replica sets, in some edge cases, clients can
11+
read stale data even when specifying a :readmode:`primary` read
12+
preference. [#edge-cases-2-primaries]_ Clients may also see results of
13+
writes before they are made durable and before they have propagated to
14+
enough replica set members to avoid rollbacks.
15+
16+
This tutorial outlines a procedure that uses
17+
:method:`db.collection.findAndModify()` to read data that is not stale
18+
and cannot be rolled back. To do so, the procedure uses the
19+
:method:`db.collection.findAndModify()` method to modify a dummy field
20+
in a document and issues a :dbcommand:`getLastError` command to confirm
21+
that the :method:`db.collection.findAndModify()` operation has
22+
propagated to enough members to avoid rollbacks. Specifically, the
23+
procedure requires that:
24+
25+
- :method:`db.collection.findAndModify()` use an **exact** match query,
26+
and a :doc:`unique index </core/index-unique>` **must exist** to
27+
satisfy the query.
28+
29+
- :method:`db.collection.findAndModify()` must actually modify a
30+
document; i.e. result in a change to the document.
31+
32+
- :dbcommand:`getLastError` must use the write concern
33+
:writeconcern:`w: "majority"`.
34+
35+
Prerequisites
36+
-------------
37+
38+
This tutorial reads from a collection named ``products``. Initialize
39+
the collection using the following operation.
40+
41+
.. code-block:: javascript
42+
43+
db.products.insert( [
44+
{
45+
_id: 1,
46+
sku: "xyz123",
47+
description: "hats",
48+
available: [ { quantity: 25, size: "S" }, { quantity: 50, size: "M" } ],
49+
_dummy_field: 0
50+
},
51+
{
52+
_id: 2,
53+
sku: "abc123",
54+
description: "socks",
55+
available: [ { quantity: 10, size: "L" } ],
56+
_dummy_field: 0
57+
},
58+
{
59+
_id: 3,
60+
sku: "ijk123",
61+
description: "t-shirts",
62+
available: [ { quantity: 30, size: "M" }, { quantity: 5, size: "L" } ],
63+
_dummy_field: 0
64+
}
65+
] )
66+
67+
The documents in this collection contain a dummy field named
68+
``_dummy_field`` that will be incremented by the
69+
:method:`db.collection.findAndModify()` in the tutorial. If the field
70+
does not exist, the :method:`db.collection.findAndModify()` operation
71+
will add the field to the document. The purpose of the field is to
72+
ensure that the :method:`db.collection.findAndModify()` results in a
73+
modification to the document.
74+
75+
Procedure
76+
---------
77+
78+
.. include:: /includes/steps/findAndModify-quorum-reads.rst
79+
80+
.. [#edge-cases-2-primaries]
81+
82+
.. include:: /includes/footnote-two-primaries-edge-cases.rst

0 commit comments

Comments
 (0)