diff --git a/source/reference/method/db.collection.findAndModify.txt b/source/reference/method/db.collection.findAndModify.txt index 42c6ad8d256..15277a1a044 100644 --- a/source/reference/method/db.collection.findAndModify.txt +++ b/source/reference/method/db.collection.findAndModify.txt @@ -111,7 +111,7 @@ db.collection.findAndModify() #. The ``query`` finds a document in the ``people`` collection where the ``name`` field has the value ``Tom``, the ``state`` field has the value ``active`` and the ``rating`` field has a value - : operator:`greater than <$gt>` 10. + :operator:`greater than <$gt>` 10. #. The ``sort`` orders the results of the query in ascending order. diff --git a/source/reference/method/db.collection.remove.txt b/source/reference/method/db.collection.remove.txt index d446067ac0d..0142c236777 100644 --- a/source/reference/method/db.collection.remove.txt +++ b/source/reference/method/db.collection.remove.txt @@ -76,7 +76,7 @@ db.collection.remove() write operations to that collection. For an unsharded collection, you have the option to override this behavior with the :operator:`$atomic` isolation operator, effectively isolating the - delete operation and blocking all other operations during the + delete operation and blocking other write operations during the delete. To isolate the query, include ``$atomic: 1`` in the ``query`` parameter as in the following example: diff --git a/source/reference/method/db.collection.update.txt b/source/reference/method/db.collection.update.txt index 43c36edba5f..7e314a8dcb7 100644 --- a/source/reference/method/db.collection.update.txt +++ b/source/reference/method/db.collection.update.txt @@ -106,6 +106,15 @@ db.collection.update() In version 2.2 of the :program:`mongo` shell, you may also specify ``multi`` in the ``options`` parameter. + .. note:: + + The ``multi`` update operation may interleave with other + write operations to that collection. For an unsharded + collection, you have the option to override this behavior + with the :operator:`$atomic` isolation operator, + effectively isolating the update operation and blocking + other write operations during the update. See the + :doc:`isolation operator `. Although the update operation may apply mostly to updating the values of the fields, the :method:`update() diff --git a/source/reference/operator/atomic.txt b/source/reference/operator/atomic.txt index 910ca353ae7..4d395ce13a7 100644 --- a/source/reference/operator/atomic.txt +++ b/source/reference/operator/atomic.txt @@ -6,14 +6,19 @@ $atomic .. operator:: $atomic - In multi-update mode, it's possible to specify an - :operator:`$atomic` "operator" that allows you to **isolate** some - updates from each other within this operation. Consider the - following example: + :operator:`$atomic` isolation operator **isolates** a write + operation that affect multiple documents from other write operations. + + .. note:: + + The :operator:`$atomic` isolation operator does **not** mean + "all-or-nothing" atomicity to the write operation. + + Consider the following example: .. code-block:: javascript - db.foo.update( { field1 : 1 , $atomic : 1 }, { $inc : { field2 : 1 } } , false , true ) + db.foo.update( { field1 : 1 , $atomic : 1 }, { $inc : { field2 : 1 } } , { multi: true } ) Without the :operator:`$atomic` operator, multi-updates will allow other operations to interleave with this updates. If these diff --git a/source/tutorial.txt b/source/tutorial.txt index 6a32a29bb27..e2ae648fe32 100644 --- a/source/tutorial.txt +++ b/source/tutorial.txt @@ -37,6 +37,7 @@ Development Patterns :maxdepth: 1 tutorial/perform-two-phase-commits + tutorial/isolate-sequence-of-operations tutorial/enforce-unique-keys-for-sharded-collections tutorial/aggregation-examples diff --git a/source/tutorial/isolate-sequence-of-operations.txt b/source/tutorial/isolate-sequence-of-operations.txt new file mode 100644 index 00000000000..e9ed7bcca2a --- /dev/null +++ b/source/tutorial/isolate-sequence-of-operations.txt @@ -0,0 +1,108 @@ +============================== +Isolate Sequence of Operations +============================== + +.. default-domain:: mongodb + +Background +---------- + +Write operations are atomic on the level of a single document: no +single write operation can atomically affect more than one document or +more than one collection. + +When a single write operation modifies multiple documents, the +operation as a whole is not atomic, and other operations may +interleave. The modification of a single document, or record, is always +atomic, even if the write operation modifies multiple sub-document +*within* the single record. + +No other operations are atomic; however, you can *isolate* a +single write operation that affects multiple documents using the +:doc:`isolation operator `. + +Additionally, the following patterns can manage a sequence of +operations: + +- :method:`findAndModify() ` + +- :ref:`tutorial-atomic-update-if-current` + +- :doc:`/tutorial/perform-two-phase-commits` + +- :method:`ensureIndex() ` to create a + ``unique`` index on a field + +.. _tutorial-atomic-update-if-current: + +Update if Current +----------------- + +The "Update if Current" pattern queries a document, locally modifies +various fields of the document, and tries to update the fields of a +document *if* the fields have not changed in the collection since the +query. + +Consider the following example which attempts to update the ``qty`` +field of a document in the ``products`` collection: + +.. code-block:: javascript + + var myCollection = db.products; + var myDocument = myCollection.findOne( { sku: 'abc123' } ); + + if (myDocument) { + + var oldQty = myDocument.qty; + + if (myDocument.qty < 10) { + myDocument.qty *= 4; + } else if ( myDocument.qty < 20 ) { + myDocument.qty *= 3; + } else { + myDocument.qty *= 2; + } + + myCollection.update( + { + _id: myDocument._id, + qty: oldQty + }, + { + $set: { qty: myDocument.qty } + } + ) + + var err = db.getLastErrorObj(); + + if ( err && err.code ) { + print("unexpected error updating document: " + tojson( err )); + } else if ( err.n == 0 ) { + print("No update: no matching document for { _id: " + myDocument._id + ", qty: " + oldQty + " }") + } + + } + +Consider the following modifications to the "Update if Current" strategy: + +- To generalize the strategy to guarantee that the whole document has + not changed rather than just certain fields, use the entire document in + the query expression. + +- Add a version variable that is incremented upon each update operation + to the documents. Use this version variable in the query expression. + +- Use :operator:`$set` in the update expression to modify only your + fields and prevent overriding other fields. + +.. Add link to :doc:`/tutorial/create-an-auto-increment-field` once that branch is merged since it's a special case + +.. Maybe incorporate the blurb: "MongoDB does not + support traditional locking and complex transactions for a number of + reasons: First, in sharded environments, distributed locks could be + expensive and slow. Mongo DB's goal is to be lightweight and fast. We + dislike the concept of deadlocks. We want the system to be simple and + predictable without these sort of surprises. We want Mongo DB to work + well for realtime problems. If an operation may execute which locks + large amounts of data, it might stop some small light queries for an + extended period of time."