diff --git a/source/includes/fact-findAndModify-update-comparison.rst b/source/includes/fact-findAndModify-update-comparison.rst index c0c9f376df6..cdeaa909f86 100644 --- a/source/includes/fact-findAndModify-update-comparison.rst +++ b/source/includes/fact-findAndModify-update-comparison.rst @@ -13,7 +13,7 @@ When updating a document, |operation| and the method, you cannot specify which single document to update when multiple documents match. -- By default, |operation| method returns |return-object|. To +- By default, |operation| returns |return-object|. To obtain the updated document, use the ``new`` option. The :method:`~db.collection.update()` method returns a diff --git a/source/includes/steps-findAndModify-quorum-reads.yaml b/source/includes/steps-findAndModify-quorum-reads.yaml new file mode 100644 index 00000000000..56bcd4f95fc --- /dev/null +++ b/source/includes/steps-findAndModify-quorum-reads.yaml @@ -0,0 +1,55 @@ +title: Create a unique index. +stepnum: 1 +ref: quorum-read-unique-index +pre: | + Create a unique index on the fields that will be used to specify an + exact match in the :method:`db.collection.findAndModify()` operation. + + This tutorial will use an exact match on the ``sku`` field. As such, + create a unique index on the ``sku`` field. +action: + language: javascript + code: | + db.products.createIndex( { sku: 1 }, { unique: true } ) +--- +title: Use ``findAndModify`` to read committed data. +stepnum: 2 +ref: quorum-read-findAndModify +pre: | + Use the :method:`db.collection.findAndModify()` method to make a + trivial update to the document you want to read and return the + modified document. A write concern of :writeconcern:`{ w: "majority" } + <"majority">` is required. To specify the document to read, you must + use an exact match query that is supported by a unique index. + + The following :method:`~db.collection.findAndModify()` operation + specifies an exact match on the uniquely indexed field ``sku`` and + increments the field named ``_dummy_field`` in the matching document. + While not necessary, the write concern for this command also includes + a :ref:`wc-wtimeout` value of ``5000`` milliseconds to prevent the + operation from blocking forever if the write cannot propagate to a + majority of voting members. +action: + language: javascript + code: | + var updatedDocument = db.products.findAndModify( + { + query: { sku: "abc123" }, + update: { $inc: { _dummy_field: 1 } }, + new: true, + writeConcern: { w: "majority", wtimeout: 5000 } + }, + ); +post: | + Even in situations where two nodes in the replica set believe that + they are the primary, only one will be able to complete the write with + :writeconcern:`w: "majority" <"majority">`. As such, the + :method:`~db.collection.findAndModify()` method with + :writeconcern:`"majority"` write concern will be successful only when + the client has connected to the true primary to perform the operation. + + Since the quorum read procedure only increments a dummy field in the + document, you can safely repeat invocations of + :method:`~db.collection.findAndModify()`, adjusting the + :ref:`wc-wtimeout` as necessary. +... diff --git a/source/includes/toc-crud-tutorials.yaml b/source/includes/toc-crud-tutorials.yaml index 665064a3e54..6ef7191a3fe 100644 --- a/source/includes/toc-crud-tutorials.yaml +++ b/source/includes/toc-crud-tutorials.yaml @@ -52,4 +52,8 @@ file: /tutorial/create-an-auto-incrementing-field description: | Describes how to create an incrementing sequence number for the ``_id`` field using a Counters Collection or an Optimistic Loop. +--- +file: /tutorial/perform-findAndModify-quorum-reads +description: | + Perform quorum reads using ``findAndModify``. ... diff --git a/source/reference/command/findAndModify.txt b/source/reference/command/findAndModify.txt index 40a24ecbb22..a5a9b2cc95b 100644 --- a/source/reference/command/findAndModify.txt +++ b/source/reference/command/findAndModify.txt @@ -390,3 +390,5 @@ The method returns the deleted document: }, "ok" : 1 } + +.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads` diff --git a/source/reference/method/db.collection.findAndModify.txt b/source/reference/method/db.collection.findAndModify.txt index a827b1a6eff..4ffa59d551f 100644 --- a/source/reference/method/db.collection.findAndModify.txt +++ b/source/reference/method/db.collection.findAndModify.txt @@ -286,3 +286,5 @@ The method returns the deleted document: "state" : "active", "rating" : 3 } + +.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads` diff --git a/source/reference/write-concern.txt b/source/reference/write-concern.txt index aff8bda6f62..9b57e2f2638 100644 --- a/source/reference/write-concern.txt +++ b/source/reference/write-concern.txt @@ -71,7 +71,8 @@ The ``w`` option requests acknowledgement that the write operation has propagated to a specified number of :program:`mongod` instances or to :program:`mongod` instances with specified tags. -The ``w`` option accepts the following values: +Using the ``w`` option, the following ``w: `` write concerns are +available: .. note:: diff --git a/source/tutorial/perform-findAndModify-quorum-reads.txt b/source/tutorial/perform-findAndModify-quorum-reads.txt new file mode 100644 index 00000000000..1a104f28e7c --- /dev/null +++ b/source/tutorial/perform-findAndModify-quorum-reads.txt @@ -0,0 +1,92 @@ +==================================== +Perform Quorum Reads on Replica Sets +==================================== + +.. default-domain:: mongodb + +.. versionadded:: 3.2 + +Overview +-------- + +When reading from the primary of a replica set, it is possible to read +data that is stale or not durable, depending on the read concern used +[#edge-cases-2-primaries]_. With a read concern level of +:readconcern:`"local"`, a client can read data before it is +:term:`durable`; that is, before they have propagated to enough replica +set members to avoid a rollback. A read concern level of +:readconcern:`"majority"` guarantees durable reads but may return stale +data that has been overwritten by another write operation. + +This tutorial outlines a procedure that uses +:method:`db.collection.findAndModify()` to read data that is not stale +and cannot be rolled back. To do so, the procedure uses the +:method:`~db.collection.findAndModify()` method with a :ref:`write +concern ` to modify a dummy field in a document. +Specifically, the procedure requires that: + +- :method:`db.collection.findAndModify()` use an **exact** match query, + and a :doc:`unique index ` **must exist** to + satisfy the query. + +- :method:`~db.collection.findAndModify()` must actually modify a + document; i.e. result in a change to the document. + +- :method:`~db.collection.findAndModify()` must use the write concern + :writeconcern:`{ w: "majority" } <"majority">`. + +.. important:: + + The "quorum read" procedure has a substantial cost over simply using + a read concern of :readconcern:`"majority"` because it incurs write + latency rather than read latency. This technique should only be used + if staleness is absolutely intolerable. + +Prerequisites +------------- + +This tutorial reads from a collection named ``products``. Initialize +the collection using the following operation. + +.. code-block:: javascript + + db.products.insert( [ + { + _id: 1, + sku: "xyz123", + description: "hats", + available: [ { quantity: 25, size: "S" }, { quantity: 50, size: "M" } ], + _dummy_field: 0 + }, + { + _id: 2, + sku: "abc123", + description: "socks", + available: [ { quantity: 10, size: "L" } ], + _dummy_field: 0 + }, + { + _id: 3, + sku: "ijk123", + description: "t-shirts", + available: [ { quantity: 30, size: "M" }, { quantity: 5, size: "L" } ], + _dummy_field: 0 + } + ] ) + +The documents in this collection contain a dummy field named +``_dummy_field`` that will be incremented by the +:method:`db.collection.findAndModify()` in the tutorial. If the field +does not exist, the :method:`db.collection.findAndModify()` operation +will add the field to the document. The purpose of the field is to +ensure that the :method:`db.collection.findAndModify()` results in a +modification to the document. + +Procedure +--------- + +.. include:: /includes/steps/findAndModify-quorum-reads.rst + +.. [#edge-cases-2-primaries] + + .. include:: /includes/footnote-two-primaries-edge-cases.rst