MongoDB's Consistency Model
MongoDB is often described as "eventually consistent," but that undersells it — it offers a genuinely tunable consistency model, letting each operation choose its own point between fast-but-loose and slow-but-strict. This page ties together replica set mechanics, read/write concerns, and CAP theorem positioning into one coherent picture.
Replica Set Consistency Basics
Every replica set has one primary accepting writes at a time, and secondaries that asynchronously replicate the primary's oplog. Because replication is asynchronous by default, a secondary can lag behind the primary by anywhere from milliseconds to (under load or network issues) seconds.
Reads directed to the primary always see the latest acknowledged write.
Reads directed to a secondary may see slightly stale data, depending on replication lag.
A write acknowledged with
w: 1and then immediately followed by a primary failover, before replication completed, can in rare cases be rolled back.
Read-Your-Own-Writes with Majority + Causal Sessions
The combination of writeConcern: "majority" and a causally consistent client session gives you a strong, practical guarantee: your own subsequent reads will reflect your own prior writes, regardless of which replica set member actually serves the read.
const session = client.startSession({ causalConsistency: true })
await orders.updateOne(
{ _id: orderId },
{ $set: { status: "shipped" } },
{ session, writeConcern: { w: "majority" } }
)
// This read is guaranteed to observe the update above
const order = await orders.findOne(
{ _id: orderId },
{ session, readConcern: { level: "majority" } }
)Stale Reads from Secondaries
Without a causally consistent session, reading from a secondary (readPreference: "secondary" or "secondaryPreferred") can return data that is behind the primary by however much that secondary is lagging. This is a deliberate tradeoff you opt into for read scaling or geographic locality — not a bug.
// Explicitly accepting potentially stale reads in exchange for
// distributing read load across secondaries
db.products.find({ category: "electronics" })
.readPref("secondaryPreferred")Tunable Consistency via Concerns
This is the actual mechanism behind "tunable consistency" — you are not choosing a single global consistency level for the deployment, you choose it per operation via read concern and write concern (see the dedicated Read/Write Concerns page for the full option reference).
Goal | Configuration |
|---|---|
Maximum speed, can tolerate rare data loss/staleness | w:1, readConcern:local |
Safe default for most application code | w:majority, readConcern:local |
Strong read-after-write guarantee for a user's own actions | w:majority + causal session |
Strongest single-document real-time guarantee | w:majority, readConcern:linearizable (primary only) |
Consistent snapshot across multiple documents | Multi-document transaction with readConcern:snapshot |
CAP Positioning
CAP theorem says a distributed system can only fully guarantee two of Consistency, Availability, and Partition tolerance at once. MongoDB is generally categorized as CP (consistent, partition-tolerant) by default: during a network partition, a replica set will not elect a primary without a majority of voting members, sacrificing write availability in the minority partition rather than risking inconsistent writes.
With a majority-based configuration, the minority side of a partition cannot accept writes — it has no primary. This favors consistency over availability during a partition.
Reading from secondaries with a weak read concern shifts you toward the AP end of the spectrum for that specific read — you trade consistency for availability/latency.
This is why MongoDB is more accurately described as "tunable" rather than strictly CP or AP — the classification depends on the concerns you choose per operation.
Practical Configuration Recipes
E-commerce checkout — must not lose or misread the order
await session.withTransaction(async () => {
await orders.insertOne(orderDoc, { session })
await inventory.updateOne(
{ sku }, { $inc: { qty: -1 } }, { session }
)
}, {
readConcern: { level: "snapshot" },
writeConcern: { w: "majority" }
})Product catalog browsing — favor speed, staleness is fine
db.products.find({ category: "electronics" })
.readPref("secondaryPreferred")
.readConcern("local")Analytics dashboard — read scaled out, slight lag acceptable
db.orders.aggregate(pipeline, {
readPreference: "secondary",
readConcern: { level: "available" }
})Summary
Replication is asynchronous by default — secondaries can lag, and reads there can be stale.
Majority write concern + a causally consistent session gives practical read-your-own-writes guarantees.
MongoDB defaults toward CP under partition (no primary without a majority), but read concern lets individual operations lean toward AP when staleness is acceptable.
Configure consistency per operation, matched to what that specific read or write actually requires.