I have implemented two-phase commits for MongoDB as described in MongoDB’s documentation. Basically, this works fine. It was especially easy as all I care about is inserting new documents, I never update them.
Yet, I worry about a particular scenario where I am not sure how to solve this. So any help would be appreciated.
Given: A collection containing documents with the following structure:
id: ...,
subId: ...
payload: {
...
}
There may be more than one document with the same id, but all documents with the same id have an increasing subId. So, you have something like:
- 1 – 1
- 1 – 2
- 2 – 1
- 3 – 1
- 3 – 2
- 3 – 3
Each insert only deals with one id, but potentially provides more than one document. So this means that there may be an insert with documents 3 - 4 and 3 - 5, but never an insert with documents 3 - 4 and 4 - 1.
The id and subId fields are calculated by the client inserting the data.
When inserting more than one document, this insert is covered by a 2PC. So either all documents are committed or none.
The problem now arises with the second phase of the 2PC. As I do not want other clients to be able to read uncommitted data, there is a flag within each document which tells me whether the document has already been committed. Once all documents have been written and the transaction has been set to ‘completed’, as a very last step the documents are marked as committed.
So now what happens if I try to insert 3 - 4 and 3 - 5, both get written, the transaction is completed, and 3 - 4 is marked as committed, and just before 3 - 5 gets marked as committed, another client reads all documents with id 3, sees subId 4as highest one, creates a new document with 3 - 5 and inserts it?
Then the commit fails and I have mixed version numbers from two transactions.
How to solve this?
Oh, forget about it … typical case of “I see it when I write it”. The scenario I described is no problem as
3 - 5was already written by the first transaction, it is just not committed. So as there is a unique index constraint onid - subIdanother transaction can not interfere.Problem solved.