findAndModify and concurrency

Imagine I have a collection called foo with documents like this:

{ _id: 1, done: false },
{ _id: 2, done: false }
...

I have X concurrent threads that want to flip a document’s done from false to true. It is important that no document is modified more than once. To do this, I am using findAndModify like this:

db.foo.findAndModify({
  query: { done: false },
  update: { done: true }
}

I am satisfied that findAndModify guarantees that my documents cannot be updated twice, even by two concurrent threads.

However, I am noticing that when I have thousands of documents and hundreds of threads performing this update, the rate at which documents are processed is much faster than towards the end.

I want to gain a better understanding of what Mongo is doing under the hood.

Does Mongo effectively following this algorithm:

  1. Select a document that matches the query at random (as I have no sort)
  2. Acquire a lock
  3. Check the document still matches the query -> If not go back to 1)
  4. Update the document

Therefore, is the case that for small numbers of documents, and large numbers of threads, I am seeing contention due to collisions in which documents are being locked?

If so, is there anything I can do to reduce the likelihood of these collisions? I guess I could pre-partition my documents according to the threads I have (e.g. add a field called “thread” with the name of the thread) and include this in the query to the findAndModify to guarantee there were no collisions.

Hello @Rupert_Madden-Abbott, welcome to the MongoDB Community forum.

The scenario is that the update operation happens for a set of documents which match the same condition - query: { done: false }. The updateMany method can be used to submit the operation as one command to the database server. The update happens atomically on each of the documents matching the condition on the server.

Thanks very much for your response.

I am deliberately not using updateMany because I also need to store a unique piece of information against each document. This piece of information is generated by each thread dynamically and is not known in advance for all documents.

So my query actually looks like this:

db.foo.findAndModify({
  query: { done: false },
  update: { done: true, someImportantPieceOfInformation: "a value known only to running thread" }
}

Therefore, I cannot use updateMany.

Another way of putting it is that I am implementing a variation of a queue in Mongo but one in which receives could come from any part of the queue instead of just the tip.

update: { done: true, someImportantPieceOfInformation: “a value known only to running thread” }

How does the thread know which document to update? What is the relationship between the thread and a document?

How does the thread know which document to update? What is the relationship between the thread and a document?

It doesn’t know and it doesn’t need to. It just needs to update any document that isn’t done.

That is really part of my question: When you provide findAndModify with a query that matches multiple documents (because it does not matter which one is updated as long as exactly one is updated), then how does Mongo choose which document you get back? I know that it does choose one but the docs don’t say how (unless you sort the query in which case they do).

Assume there are some documents in the collection. To submit each of the update using a thread, I am assuming that your process is like this:

while true {
	create/get a thread _and_ the important_info
	submit the update using the thread:
		doc = db.collection.findAndModify( { query: { done: false }, update: { done: true, importantInfo: important_info } } )
	if doc is null
		all docs are updated (no update happened)
	else
		the updated doc
}

Yes, that is a valid example.