Filter concepts by levelShowing all levels.

MongoDB · Section 23

Read Concern and Write Concern

Level
advanced
Read
30 min
Concepts
5

How write concern (w, j, wtimeout) controls how many replica set members must acknowledge a write, and how durably, before it is reported successful; how read concern (local, majority, linearizable, snapshot) controls whether the data a read returns could still be rolled back; how to choose the strength of each based on what is actually at risk; and why every one of these is a genuine trade-off between consistency, latency, and availability, never a free upgrade.

MongoDB overview

What is true here

  1. w: "majority" is safe against a single primary failure, because the next primary is always elected from that same majority.
  2. w, j, and wtimeout are three independent settings — how many copies, how durable each copy is, and how long to wait for both.
  3. readConcern "local" can return data that has not yet been confirmed by a majority and could still be rolled back; "majority" cannot.
  4. A stronger concern costs latency always, and can cost availability outright during a partition — scale it to what the data is actually worth.
  5. Consistency, latency, and availability trade against each other — every setting in this section is spending that trade-off deliberately, not avoiding it.

What you will be able to do

  • Choose an appropriate write concern for a given write's real-world stakes
  • Distinguish w, j, and wtimeout and set each independently
  • Choose an appropriate read concern level for a given read's consistency requirement
  • Explain the availability cost of "majority" concern during a partition
  • Reason about read/write concern as a deliberate trade-off, not a correctness dial to maximize by default

Write concern

What acknowledgement and durability actually mean, and the three independent settings that control them.

Write concern: acknowledgement and durability

coreadvanced

Write concern controls how many replica set members must confirm a write before MongoDB reports it as successful. A weaker write concern returns faster but risks that acknowledged write later being rolled back if the primary fails before it replicates; a stronger one waits longer but makes that outcome far less likely.

Think of it as

Every write concern level is really answering one question: "if the primary died the instant after this write returned, could the write still be lost?" w: 1 says yes, possibly. w: "majority" says no, because a majority already has it, and a majority is exactly what a new primary election requires — so the new primary is guaranteed to include this write.

text
{ writeConcern: { w: 1 | "majority" | <n>, j: true | false, wtimeout: <ms> } }

What we're doing: Show a write concern choice tied to what the data is worth protecting.

write-concern-by-risk.jsjavascript
// A financial transaction — worth the extra latency for durability
db.payments.insertOne(
  { userId, amount, status: "completed" },
  { writeConcern: { w: "majority", j: true } }
)

// A high-volume, low-stakes analytics event — default write concern is fine
db.pageViews.insertOne({ userId, path, at: new Date() })
2
w: "majority" with j: true means the payment is acknowledged only once a majority of members have it durably on disk — losing it on a primary failure would require losing a majority at once.
7
A page view is cheap to lose and expensive to slow down at this volume — the default write concern trades some durability risk for much lower latency, which fits data this disposable.

Why this works: The right write concern is a direct function of what the data is worth — the same choice ("majority" for everything, or "1" for everything) is wrong in one direction or the other for a system handling both payments and page-view analytics.

Using the default write concern uniformly across an application with very different data criticality

Wrong

text
// Every write, from payments to page views, using the driver's default write concern with no per-operation consideration

Better

text
// Set writeConcern: { w: "majority" } explicitly for writes where losing an acknowledged one would be a real incident

What you see: A financial write is acknowledged, the primary fails moments later before replicating, and the write is gone — the application already told the user it succeeded.

Why: A weaker write concern is a real risk, not a theoretical one — an acknowledged write is not yet safe from a primary failure unless enough members already have it, and "enough" is exactly what write concern controls.

w: 1 vs. w: "majority"

w: 1

  • +primary applies the write, acknowledges immediately
  • +fastest response
  • +if the primary fails before replicating, the write can be lost on failover

w: "majority"

  • waits for a majority of members to apply it
  • higher latency
  • safe against single-primary failure — a new primary is elected from that same majority
  • w: 1
    • primary applies the write, acknowledges immediately
    • fastest response
    • if the primary fails before replicating, the write can be lost on failover
  • w: "majority"
    • waits for a majority of members to apply it
    • higher latency
    • safe against single-primary failure — a new primary is elected from that same majority

Remember: Write concern trades latency for the durability of an acknowledged write — w: "majority" is safe against a single primary failure because a new primary is always elected from that same majority.

See also: write concern w j and wtimeout · read write concern in transactions

w, j, and wtimeout

standardadvanced

A write concern has three independent settings: w (how many members must acknowledge), j (whether acknowledgement requires an on-disk journal write, not just an in-memory apply), and wtimeout (how long to wait before giving up on the requested w and j).

Think of it as

w answers "how many copies", j answers "how durable is each copy" (survives a crash, not just visible in memory), and wtimeout answers "how long am I willing to wait for that". They are independent dials — you can ask for a lot of copies without journaling, or journaling without many copies, though most production configurations reasonably pair a higher w with j: true.

text
{ writeConcern: { w, j, wtimeout } }

What we're doing: Show a write concern timeout occurring and clarify what it does and does not mean.

javascript
try {
  await db.orders.insertOne(order, {
    writeConcern: { w: "majority", wtimeout: 3000 }
  })
} catch (err) {
  if (err.name === "MongoServerError" && err.codeName === "WriteConcernTimeout") {
    // The write may still have applied on the primary — it just was not
    // confirmed by a majority within 3 seconds. Do not assume it failed.
  }
}
8
A WriteConcernTimeout means the requested acknowledgement level was not confirmed in time — it is not the same as the write never having happened.

Why this works: wtimeout bounds how long the caller waits, not what the database does with the write itself — treating a timeout as "definitely failed, retry the insert" risks a duplicate if the original write does eventually propagate.

The three write concern dials

The three write concern dials
SettingControlsTypical production value
whow many members must acknowledge"majority"
jwhether acknowledgement requires on-disk durabilitytrue
wtimeouthow long to wait before giving upa few seconds, application-dependent

Together

javascript
{ writeConcern: { w: "majority", j: true, wtimeout: 5000 } }

Remember: w, j, and wtimeout are three independent dials — how many copies, how durable each copy is, and how long to wait. A write concern timeout means "not confirmed in time," not "did not happen."

See also: write concern acknowledgement and durability · choosing concern strength

Advertisement

Read concern and judgment

The read-side mirror of write concern, and how to choose concern strength deliberately rather than by default.

readConcern levels

coreadvanced

readConcern controls how "safe" the data a read returns is — whether it might include data that could still be rolled back, or is guaranteed to have been durably committed to a majority of the replica set before the read ever saw it.

Think of it as

Write concern asks "how sure am I this write will stick" at write time; read concern asks the mirror question at read time — "how sure am I this data I am reading will stick". "local" reads whatever the node currently has, possibly including data that has not yet propagated durably; "majority" only returns data already confirmed durable across a majority, guaranteeing it will never be rolled back.

text
{ readConcern: { level: "local" | "majority" | "linearizable" | "snapshot" } }

What we're doing: Contrast a dashboard read (local is fine) against a read that must not reflect data that could still roll back (majority).

readconcern-by-need.jsjavascript
// A live dashboard — local is fine, slightly-unconfirmed data is an acceptable trade for speed
db.metrics.find({ ... }).readConcern("local")

// Confirming a payment actually posted before releasing goods — must not be data that could roll back
db.payments.findOne({ _id: paymentId }, { readConcern: { level: "majority" } })
2
A dashboard refreshing every few seconds does not need each read guaranteed unrollbackable — local's lower latency is the better trade here.
5
Releasing goods based on a payment record that could still be rolled back on a failover would be a real business risk — majority read concern rules that out.

Why this works: The read concern decision mirrors the write concern one: match the strength of the guarantee to what is actually at risk if the guarantee turns out to be wrong, not to a single default applied everywhere.

Reading with the default (local) read concern for a decision that must not act on data that could still be rolled back

Wrong

text
// db.payments.findOne({ _id }) with default local read concern, then releasing goods based on the result

Better

text
// db.payments.findOne({ _id }, { readConcern: { level: "majority" } })

What you see: Goods are released based on a payment record that is then rolled back after a primary failover, leaving the business with no payment and shipped goods.

Why: local read concern can return data that has not yet been confirmed by a majority — if the primary fails before that confirmation happens, the read data can vanish on the next election, and any decision already made based on it cannot be undone.

Read concern, from weakest to strongest guarantee

local

whatever this node currently has — fastest, could still be rolled back

majority

only majority-committed data — guaranteed durable, never rolled back

linearizable

majority, plus guaranteed to reflect the latest committed write

  1. local — whatever this node currently has — fastest, could still be rolled back
  2. majority — only majority-committed data — guaranteed durable, never rolled back
  3. linearizable — majority, plus guaranteed to reflect the latest committed write

Remember: local is fast but can return data that could still roll back; majority guarantees durable, never-rolled-back data, possibly slightly behind the absolute latest write; linearizable adds the "latest write" guarantee on top, at more latency cost.

See also: write concern acknowledgement and durability · choosing concern strength

Choosing concern strength

standardadvanced

A stronger read or write concern is not free — it costs latency, and under a network partition or member outage it can cost availability too, since "majority" cannot be satisfied if a majority is unreachable. The right choice matches the guarantee's cost to what is actually at risk without it.

Think of it as

Ask what the real consequence is if the weaker guarantee turns out to be wrong just once. A lost page-view event costs nothing. A lost payment confirmation costs money and trust. Scale the concern strength to that consequence, not to a single project-wide default applied out of habit.

text
// Ask: what actually happens if this specific write/read is wrong once? Scale concern strength to that answer.

What we're doing: Show the availability cost of "majority" concretely during a partition, and why it is still the right choice for certain writes.

text
// Normal operation: 3-member replica set, majority = 2
// A network partition isolates the primary with 1 other member (2 of 3 — majority still reachable): writes proceed

// A worse partition isolates the primary alone (1 of 3 — no majority reachable):
// w: "majority" writes now block/timeout — the system correctly refuses to
// falsely acknowledge a payment write it cannot durably confirm
4
This is the trade-off made explicit: refusing to proceed under this partition is exactly what protects a payment write from being acknowledged and then silently lost.

Why this works: The unavailability here is not a bug — it is majority write concern doing precisely what it promises: never reporting success for a write it cannot guarantee will survive a failover, even if that means temporarily refusing writes during a severe partition.

Remember: Scale concern strength to what breaks if the weaker guarantee is wrong once — high-volume/low-stakes data can usually take the weaker, faster default; anything gating an irreversible action usually justifies the slower, safer one.

See also: write concern acknowledgement and durability · consistency is a tradeoff

Consistency is a trade-off, not a free feature

referenceadvanced

Every read/write concern choice in this section is the same underlying trade-off in different clothes: more consistency costs latency and, under a partition, availability. There is no setting that gives stronger guarantees for free — the whole point of learning the individual knobs is to spend that cost deliberately instead of by accident.

Remember: Read and write concern are not "correctness settings to max out" — they are a dial between consistency, latency, and availability, and the right position depends on what a given piece of data is actually worth protecting.

See also: choosing concern strength · majority concepts

Advertisement