How to implement document level locking in a transaction

How do I implement something of this sort:

BEGIN Transaction 
1. Change something in collection 1 ( a single document)
--- In the next steps aquire lock on Step 1's document - don't let people modify it.
2. Change something in collection 2
3. Change something in collection 3
-- Now release lock here from collection's 1 document. 
END Transaction

I know about the transaction part - but not about the locking part. How do I implement something like that?

Hi @Yash_Dixit welcome to the community!

If I understand correctly, you wanted to modify a document in collection 1 and “lock” it so that nothing outside this transaction would be able to change that document, at least until the modifications in collections 2 and 3 can finish and the transaction ended. Is this accurate?

If yes, this is covered in the section In-progress Transactions and Write Conflicts:

If a transaction is in progress and a write outside the transaction modifies a document that an operation in the transaction later tries to modify, the transaction aborts because of a write conflict.

If a transaction is in progress and has taken a lock to modify a document, when a write outside the transaction tries to modify the same document, the write waits until the transaction ends.

So if you modify the document inside a transaction, it will in effect be locked so nothing else outside the transaction can change it until the transaction ends.

If you don’t modify it, then another process outside the transaction can modify the document, and the transaction would auto-abort due to write conflict.

Here’s an illustration using mongosh:

// Create the collection
replset [direct: primary] test> db.test.insertOne({_id:0})
{ acknowledged: true, insertedId: 0 }

// Start a session
replset [direct: primary] test> session = db.getMongo().startSession()
{ id: UUID("707613dd-2989-4c1b-a0c8-a815220335cc") }

// Start transaction
replset [direct: primary] test> session.startTransaction()

// Update one document inside the transaction
replset [direct: primary] test> session.getDatabase('test').test.updateOne({_id:0}, {$set:{a:1}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 1,
  modifiedCount: 1,
  upsertedCount: 0
}

// Update the same document outside of the transaction
replset [direct: primary] test> db.test.updateOne({_id:0}, {$set:{a:2}})
// This command will hang until the transaction aborts, commits, 
// or if the transactionLifetimeLimitSeconds elapsed

Best regards
Kevin

1 Like