Summary
MongoDB’s $expr with $lt in the query filter and a single $push operation is atomic at the document level, so the array will not exceed the specified maximum when using this pattern alone.
However, real-world production environments introduce additional pitfalls that can lead to race conditions or inconsistent state.
Root Cause
- Atomicity is confined to a single document update only.
- Concurrent writes must hit the same document lock; other fields or indexes are not affected.
- Improper client‑side retry logic or transaction boundaries can break the guarantee.
Why This Happens in Real Systems
- Replica sets with delayed oplog replication – a concurrent update may be applied out of order on secondary nodes.
- Multi‑document transactions – if the update is part of a larger transaction that also modifies another document, a rollback can leave the array unchanged while other changes persist.
- Application-level caching or optimistic concurrency – stale reads can cause duplicate pushes if the cache is not refreshed.
Real-World Impact
- Data integrity violation – arrays growing beyond the intended limit can trigger downstream logic errors or violate business rules.
- Performance degradation – oversized arrays increase document size, leading to longer read/write times and higher storage costs.
- Security and compliance risks – exceeding quotas or limits defined by regulations.
Example or Code
db.collection.updateOne(
{
_id: docId,
$expr: { $lt: [{ $size: "$members" }, 3] }
},
{ $push: { members: newMember } }
)
How Senior Engineers Fix It
- Use a capped array size via
$maxon the field – create a unique hashed index to enforce length (if feasible). - Wrap the operation in a single‑document transaction to guarantee isolation with other writes.
- Apply
multi: falseand ensure client retries only when aDuplicateKeyor write conflict occurs. - Leverage
findAndModifywith$pull+$pushin one atomic operation if pruning is needed. - Monitor oplog delays and set an appropriate write concern (
w: "majority", j: true) to ensure durability.
Why Juniors Miss It
- Assuming
$exprprovides full transactional guarantees without understanding the scope of MongoDB’s document‑level atomicity. - Ignoring replica set quirks like oplog ordering and write concern implications.
- Overlooking driver‑level retry logic that can silently retry after a write conflict, causing duplicate pushes.
- Lacking knowledge of best‑practice patterns such as using transactions or
findAndModifyfor complex updates.