Skip to content

DOCS-5324: quorum reads with findAndModify() #2518

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion source/includes/fact-findAndModify-update-comparison.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
55 changes: 55 additions & 0 deletions source/includes/steps-findAndModify-quorum-reads.yaml
Original file line number Diff line number Diff line change
@@ -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.
...
4 changes: 4 additions & 0 deletions source/includes/toc-crud-tutorials.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
...
2 changes: 2 additions & 0 deletions source/reference/command/findAndModify.txt
Original file line number Diff line number Diff line change
Expand Up @@ -390,3 +390,5 @@ The method returns the deleted document:
},
"ok" : 1
}

.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads`
2 changes: 2 additions & 0 deletions source/reference/method/db.collection.findAndModify.txt
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,5 @@ The method returns the deleted document:
"state" : "active",
"rating" : 3
}

.. seealso:: :doc:`/tutorial/perform-findAndModify-quorum-reads`
3 changes: 2 additions & 1 deletion source/reference/write-concern.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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: <value>`` write concerns are
available:

.. note::

Expand Down
92 changes: 92 additions & 0 deletions source/tutorial/perform-findAndModify-quorum-reads.txt
Original file line number Diff line number Diff line change
@@ -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 <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 </core/index-unique>` **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