How does MongoDB deal with transactional conflicts?

If both two threads do read and write a same document:

try (ClientSession clientSession = client.startSession()) {
    clientSession.startTransaction();
    result = collection.find(clientSession, keyOfDoc);
    if (result blah blah blah) {
        // Change the doc
        collection.insertOne(clientSession, doc);
    }
    clientSession.commitTransaction();
}

From the purpose of the transaction, one of the thread should get the edited version of another thread.

However, when both threads start transaction they both acquired a read lock, and then read the doc. Both the threads got the old version of the doc. and when they need to write the doc, they try to acquire write locks, which will make the transaction not atomic.

Another situation is the write-write conflict.

try (ClientSession clientSession = client.startSession()) {
    clientSession.startTransaction();
    collection.insertOne(clientSession, docDifferent);
    collection.insertOne(clientSession, docSame);
    clientSession.commitTransaction();
}

Both thread first acquires write locks of different documents, and then they acquire the write lock of the same document, as it is another transactional conflict.

What level of lock does MongoDB use? I know they use instance level before version 2.2 while transactions are supported since 4.0. If MongoDB doesn't use database level locks, How does MongoDB deal with transactional conflicts? Or if it uses database level locks, how does it deal with read-write conflicts?

1 answer

  • answered 2018-11-09 05:12 iry

    I found some references in the MongoDB Manual that solved my own question.

    What type of locking does MongoDB use?

    MongoDB uses multi-granularity locking 1 that allows operations to lock at the global, database or collection level, and allows for individual storage engines to implement their own concurrency control below the collection level (e.g., at the document-level in WiredTiger).

    MongoDB uses multiple levels of locking from collection, database, then to global. However, though it supports multiple levels of locking, the only level you can access is the collection level, meaning you can't create or drop databases or collections in transactions. It also means that acquiring one document to be locked in a collection will cause the whole collection to get locked.

    Restricted Operations

    The following operations are not allowed in multi-document transactions:

    • Operations that affect the database catalog, such as creating or dropping a collection or an index. For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection.

      The listCollections and listIndexes commands and their helper methods are also excluded.

    • Non-CRUD and non-informational operations, such as createUser,getParameter, count, etc. and their helpers.

    For solving conflicts, MongoDB sends error messages to the visitor that could not get the lock when conflict happens.

    Retry Transaction

    The individual write operations inside the transaction are not retryable, regardless of whether retryWrites is set to true.

    If an operation encounters an error, the returned error may have an errorLabels array field. If the error is a transient error, the errorLabels array field contains "TransientTransactionError" as an element and the transaction as a whole can be retried.

    Meaning when the visitor receive MongoException and the exception .hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL), the visitor should close the session, and REDO the transaction. The visitor should redo and recommit until the commit successes.

    You can simply use this method(modified from the manual example):

    public static <T> T transactWithRetry(Callable<T> transactional) throws Exception {
        while (true) {
            try {
                return transactional.call();
            } catch (MongoException ex) {
                if (!ex.hasErrorLabel(MongoException.TRANSIENT_TRANSACTION_ERROR_LABEL)) throw ex;
            }
        }
    }
    

    See editions for more languages in the manual ;)!


    References

    Transactions — MongoDB Manual

    FAQ: Concurrency — MongoDB Manual