0

I have a few questions, hoping I can get answers.

  1. what does it mean when the Mongodb said:

    Clients can read documents while write operations are in progress

    I can see partially updated document? or the document will be under lock and no one can see it until the lock yielded?

  2. I need to know if two operation can update the same document with WiredTiger or MMAP V1? if so, what about reading at the same time of write or update?
  3. If I use $isolate is this enough to be sure that no one could write on the same document?
1
1

The locking of documents differs between the WiredTiger and the MMAPv1 storage engine. When using the Wired Tiger storage engine, write operations only hold an exclusive lock at document level. This means that multiple threads can update multiple documents in the same collection at the same time, however, they can NOT update the same document at the same time. In addition to an exclusive lock at document level, WiredTigger also holds intent locks at the global, database and collection levels. These intent locks will not block reading or writing operations,however, when the storage engine detects conflicts between two operations, one will incur a write conflict causing MongoDB to transparently retry that operation.

When using the classical 'MMAPv1` storage engine, a write operation hold an exclusive lock on the entire collection and therefore multiple threads can NOT write to the same collection at the same time, let alone write to the same document at the same time.

The locking does not occur when reading from a document, so two threads can a document at the same time. Regarding $isolated, you need to use this for write operations that affect multiple documents. Setting this flag prevents a write operation from yielding to other reads and writes once the first document is written. For example, if you are running an update query on document 1,2,...100, setting the $isolated flag will prevent document 100 being changed while the update query is updating document 1.

4
  • Thank Mr.Jaco , one more thing to ask about the RAM , in mmap v1 i can see that the data loaded into RAM via filesystem cache , is this still the same with WiredTiger ? as i can see now it is using its own Cache storage , is it still using RAM to store the working set ? Nov 23 '15 at 11:03
  • Wired Tiger still using RAM to store the working set, however, by using its own cache storage it provides a more granular control allowing to fine tune it.
    – Alex
    Nov 23 '15 at 11:14
  • Thanks again , what about this: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> "For storage engines supporting document level concurrency control, such as WiredTiger, yielding is not necessary when accessing storage as the intent locks, held at the global, database and collection level, do not block other readers and writers." Nov 23 '15 at 12:08
  • I have updated my answer
    – Alex
    Nov 23 '15 at 12:36
1

May partially updated documents be returned while concurrently reading and writing?

No, write operations on documents are guaranteed to be atomic. In general, think of a lock as a write mutex, ensuring only one thread at a time to be able to manipulate the data(Albeit I am not too sure wether locking it is implemented using actual mutexes). I would like to expand a bit on the answer given by @Jaco

With WiredTiger, the document level locking ensures atomicity. Take this pseudo-code:

var docToUpdate = db.collection.find(query)

// Ensure only 1 thread and client can manipulate the data
db.collection.writeLock({_id:docToUpdate._id})

// Do some (even lengthy) manipulation. During that time, the document will be
// returned as it was before the lock was acquired
db.collection.update({_id:docToUpdate._id},{nastyLongUpdate},{options})

// After the update, the lock is released
db.collection.unLock({_id:docToUpdate._id})
// Only now the changes of "update" will be returned

With mmapv1, things are basically the same, only with collection level locking. However, things get a bit more complicated with journaling enabled. Let's head back to WT for a second. Since we have a document level locking, as soon as the lock is released, the in-memory representation of the data simply can be synced to disk, be it the data to be written to the data files or the operations to the journal.

Note The below assumes you have journaling enabled and it is a bit simplified

With mmapv1's collection level locking, we first have to make sure that

  1. The operation applies
  2. The locks do not take too long
  3. As long as the operation is not synced to disk the change is not made available to other threads to ensure that multiple read operations during the time the update operation is in progress return a consistent result
  4. As soon as the operation is made persistent, all threads accessing the data will return the new results to reflect that the data has changed.

This is a bit more complicated to ensure and basically works like this: Our example update operation would be kept in a copy of the data private to the writing thread. When the data change is written to the journal, the changed data is made available to other threads.

The effect of both approaches is the same: Operations to documents are atomic, always. There is no way in that a reading thread may see only partially updated documents, ever.

Can documents be updated concurrently?

Well, yes and no. You can issue updates concurrently. However the update operations will be put in a queue and applied sequentially to the data set. That is nothing special to MongoDB, but common to most if not all network enabled DBMS.

There is a caveat to this most users tend to overlook: The classical "retrieve, modify, save" cycle might cause problems depending on your data model.

Let us assume a data model storing followers for a user:

{ _id: "foo", followedBy: [] }

Let us further assume two users, "bar" and "baz" concurrently decide to follow "foo". If we had a classical "retrieve, modify, return" cycle, our operation could look like this:

// Page of "foo" is called
var foodoc = db.user.findOne({_id: "foo"})

// Some time passes, user decides to follow "foo"..
foodoc.followedBy.push(me._id)

// And we save the document
db.user.update({_id:"foo"},foodoc, {options})

Now, we have a problem, because the following documents would both be processed:

{ _id: "foo", followedBy:["bar"] },
{ _id: "foo", followedBy:["baz"] }

Bad thing, because the second document processed would overwrite the changes of the first one. So you need to take special precautions to make your queries both idempotent and separated. The solution for the above problem would be simple, btw. Instead of doing a "retrieve, modify, return" cycle, you would simply call:

db.users.update({_id: "foo"},{ "$addToSet":{ "followedBy",me._id} })

In total: Yes you can do concurrent writes to MongoDB without having to think how to sync them. However, depending on your data model, you need to take care that one operation does not overwrite the changes of another.

Does the $isolated operator ensure that data is atomic for a single document?

No. Atomcity is guaranteed for single document operations, as described above. For ensuring that operations on single documents are atomic, you do not need to take any action.

The $isolated operation guarantees that new data is only visible after an operation affecting multiple documents finished, successful or not. The operation may have been successful on 10 documents and (for whatever reason) have missed on 5 others and returns. Only now the operations on the 10 documents become visible to the clients. Still sounds interesting? Well, $isolated comes with some rather nasty drawbacks:

  1. It is not available on sharded collections, since synchronizing among a distributed environment is too costly. However, this eliminates the operators usefulness (and usability!) when you plan to have any sort of scalability.
  2. It basically puts a read and write lock on the documents (WT) or collection (mmapv1) affected – let's hope your updates apply rather fast.
  3. It does not grant transaction semantics. As written, 5 operations may succeed while others mail fail – this limits the usefulness of the operator, if it does not outright eliminate it, imho

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.