I'm trying to get the full picture... When I create a session, I know all write operations that are associated with this session will either succeed together or roll back together.
I didn't find any official mongo documentation that explains what transactions lock exactly and when does the lock occur during the lifetime of the transaction (by lock I refer to both pessimistic lock or optimistic lock)
post here seems to be based on the assumption that a lock on a document starts after it's been updated and released at the end of the session.
But does the document even needs to lock? Does it indeed lock in that instant? Where can I find documentation on that?
That means that if I do
const person = await findOne({ id },{ session })
const updatedPerson = await updateOne({ id },{ person },{ session , new: true})
There is absolutely no meaning for session
being on findOne
? Because the specific person
document doesn't get locked?
So if between me finding the person and updating the person, some other request have updated Person
, updatedPerson
could actually be different than person
, is that correct? There is no mongoDB built in way with sessions to ensure person
will be locked? ( I know there is a schema option for optimisticConcurrency
, but I want to understand sessions, and also this option seems to be limited to only throwing an error instead of retrying which seems a bit odd considering usually the behavior you want with optimisticConcurrency is to retry or atleast have the option to)
If that's correct, then the only reason for session
to be on strictly read
operations would be to be able to view write
results that are part of the session.
const updatedPerson = await updateOne({ id },{ field1: 'changed' },{ session , new: true})
const person = await findOne({ id} ,{ session })
Associating person
with session
here lets me view the updatedPerson
post update.
Am I correct in my understanding? If so, that leads me to the next question, specifically on mongoose with .save()
. According to mongoose documentation
For example, if you're using save() to update a document, the document can change in MongoDB in between when you load the document using findOne() and when you save the document using save() as show below. For many use cases, the save() race condition is a non-issue. But you can work around it with findOneAndUpdate() (or transactions) if you need to.
Which raises my question, how can you fix save()
race condition with transactions considering transactions do not lock read documents?
I did some manual testings using await new Promise((r) => setTimeout(r, 5000));
and updating the document while a session is ongoing on it to observe its behavior.
My findings are as follows:
In this example:
const person = await findOne({ id },{ session })
const updatedPerson = await updateOne({ id },{ $set: { ...person } },{ session , new: true})
There is meaning for the session
on findOne
, even though the findOne
operation doesn't lock the document, it causes updateOne
to fail and abort the transaction if the document that was fetched by findOne
, in our case person
, was changed by something that is not part of the transaction.
That means you can trust that updatedPerson
will be person
because person
is part of the session. This answer renders the rest of my question irrelevant.