Skip to content

Concurrency

Optimistic Concurrency

A version field, a filter that includes it, and a retry when the update matches nothing.

AdvancedAbout 6 minutes

When the write is not a delta ($inc) but a new document shape two editors built from a stale read — a profile, a cart JSON, a CMS page — put a `version` (or updatedAt you treat as one) on the document. The update must include that version. Miss → someone else wrote; reload, merge or abort, retry.

const r = await db.users.updateOne(
  { _id: ObjectId("64a1b2c3d4e5f6a7b8c90001"), version: 4 },
  {
    $set: { city: "Pune", skills: ["Node.js", "MongoDB", "Redis"] },
    $inc: { version: 1 }
  }
)
if (r.matchedCount === 0) {
  // reload, tell the client to retry — do not keep $set on a blind _id
}
Rahul's profile. Client sent version 4.

That is optimistic locking: assume no conflict, detect with matchedCount. Pessimistic is holding a transaction (or a lock document) for the whole edit — slower, easy to timeout. Mongoose's __v is this field; do not rely on it if some writes go through the raw driver and skip $inc: { __v: 1 }. updatedAt equality works until two writes share a millisecond — version as an integer is clearer.

  • Use version for lost-update on a whole record (admin form, cart replace).
  • Use `$inc` + filter for counters and stock — no version required.
  • Retry with a cap. Infinite retry under load is a live-lock.
  • Don't $inc version in a different updateOne than the $set — not one atomic write.

Interview question

How does optimistic concurrency work with MongoDB?

Think about it first.