Which MongoDB settings must you combine so a user always reads their own write from a secondary?
answer
- one setting on the session, two on the operations
- both concerns must be the same word
- the read carries a timestamp with it
- the secondary waits rather than answering stale
basics
~20 sRun both operations inside one causally consistent client session, write with w: "majority" and read with readConcern: "majority". The session carries a cluster timestamp so the secondary waits until it has applied that write before answering.
solid answer
~50 sRead preference alone cannot fix this — a secondary is asynchronous and may simply not have the write yet. Three things must line up. First, both the write and the read must run **inside the same client session** started with causal consistency enabled (the default for a session). Second, the write must use `writeConcern: "majority"`. Third, the read must use `readConcern: "majority"`. MongoDB's causal guarantees are defined only for that combination. Under the hood the server returns an `operationTime` with the write, the session remembers it, and the subsequent read is sent with `afterClusterTime` set to that value, so the secondary blocks until its own applied state has caught up. The trap in real systems is that the session is per client object: if the write and the read are handled by different app instances or different services, you must propagate the cluster time yourself, or route that read to the primary instead.
code
javascript · 10 linesconst session = db.getMongo().startSession({ causalConsistency: true })
const orders = session.getDatabase("shop").orders
orders.insertOne({ _id: "o-9", status: "paid" },
{ writeConcern: { w: "majority" } })
// Same session: the secondary waits until it has this write
orders.find({ _id: "o-9" })
.readPref("secondaryPreferred")
.readConcern("majority")go deeper
Recall that a secondary can be behind the primary, so re-reading your own write from a secondary may return old data, and that reading from the primary is the simple fix.
Explain the three required pieces — one causally consistent session, majority write concern, majority read concern — and what the session carries so the secondary knows to wait.
Diagnose it in production: recognise the symptom, weigh primary reads against causal sessions, and know the failure modes when the write and the read are handled by different processes, services or devices.
Decide the platform policy — where causal tokens are propagated across service boundaries, which flows are allowed off the primary at all, and how the added read latency and majority write cost fit the product's latency budget.
## Why the naive fix fails The scenario is familiar: a user saves their profile, the app immediately re-reads it, and the old values come back. Sending the read to a secondary is the cause — secondaries apply the oplog asynchronously, so at the moment of the read the write may simply not be there yet. Retrying, sleeping, or raising the read concern to `majority` does not fix it: `majority` is a durability filter, and a lagging secondary's majority view is still behind. ## The two real options **Option A — read from the primary.** Setting `readPreference: primary` for that read makes it see the write, because the primary applied it before acknowledging. This is the simplest fix and the right one for most write-then-read flows. Its limits are that it puts the load on the primary and that with `w: 1` the write you just read could still be rolled back by a failover, so a durability-sensitive flow should pair it with `w: "majority"`. **Option B — a causally consistent session.** This is what lets the read stay on a secondary and still see the write. ## How causal sessions work Every server response carries an `operationTime` and a gossiped `$clusterTime`. A client session records the `operationTime` of the operations run inside it. When the next read in that session is sent, the driver attaches `afterClusterTime` set to that recorded value. The receiving member then **waits until it has applied oplog entries up to that cluster time** before it evaluates the query. A lagging secondary therefore blocks briefly rather than answering with stale data, and if it lags too long the read hits its timeout instead of quietly lying. The requirements are strict and are the part candidates most often miss: 1. The write and the read must be in the **same session object**, passed explicitly to every operation (in most drivers an operation without the session argument is simply not part of it). 2. The session must be causally consistent — this is the default when you start one, but it can be turned off, and it is incompatible with `readConcern: "available"`. 3. The write must use `writeConcern: { w: "majority" }` and the read `readConcern: "majority"`. MongoDB documents the causal guarantees for exactly this pairing; weaken either and the guarantee no longer holds. ## Where it breaks in real deployments **Sessions do not cross processes.** A session lives in one driver client. If the write is handled by app instance A and the read arrives at instance B behind a load balancer, B's session knows nothing about A's write. The fixes are to return the write's cluster time to the caller and have the next request advance its session to that time, or to route that specific read to the primary. **Sessions do not cross services.** In a system where the write goes through the orders service and the read through the reporting service, the cluster time must be carried in the request context, exactly as you would carry a trace ID. **Sessions do not cross devices.** A user writing on their phone and reading on their laptop shares nothing at the driver level. **Forgetting the argument.** The single most common bug is starting a session, then issuing the read without passing it, so the read silently loses the guarantee and no error is raised. ## Costs A causal read may **wait**, so p99 read latency now includes replication lag on the chosen member. Combined with `maxStalenessSeconds` to keep badly lagging members out of selection, this is manageable; without it, a single stuck secondary can turn into a queue of blocked reads. And because the guarantee requires `majority` on both sides, you are also paying majority write latency on the write path. ## Choosing between the options Use the primary for the read when the flow is a simple save-then-show and the primary has headroom — it is one line, has no cross-process fragility, and adds no waiting. Reach for causal sessions when the read genuinely must be offloaded (a heavy dashboard query right after a write, a geographically distant reader), when you have one process handling both operations, or when you are already threading a session through for other reasons. What you should never do is claim read-your-writes on secondaries from read preference alone.
- What does the driver actually send on the wire to make a causal read work?The write's response carries an `operationTime`, which the session records along with the gossiped `$clusterTime`. The next read in that session is sent with `afterClusterTime` set to that value. The member receiving it waits until it has applied oplog entries up to that cluster time before evaluating the query, so it cannot answer from a state older than the write.
- Two app instances sit behind a load balancer, so the write and the read land on different processes. How do you keep read-your-writes?Either route that read to the primary, or propagate the causal token: return the write's cluster time to the client, send it back on the follow-up request, and have the receiving instance advance its session to that time before reading. Sessions live inside one driver client, so nothing is shared across processes automatically.
- What is the latency cost of causally consistent secondary reads?The read can block until the chosen member has caught up, so read latency now includes that member's replication lag, and the write side pays majority acknowledgment. Pair it with `maxStalenessSeconds` so badly lagging members are excluded from selection, otherwise one stuck secondary produces a queue of waiting reads.
It is like handing the branch office a receipt stamped with a timestamp: the clerk will not answer your question until their own ledger has caught up to that stamp.
saying these in an interview costs you the question
- Claims readPreference primaryPreferred alone gives read-your-writes
- Thinks a retry or a short sleep is an acceptable fix
- Uses a causal session but forgets to pass it to the read
- Sets majority on only one of the write and the read
- Assumes one session covers all app instances or user devices