-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: None
-
Replication
-
Repl 2026-06-08, Repl 2026-06-22, Repl 2026-06-22, Repl 2026-07-06, Repl 2026-07-20, Repl 2026-08-03, Repl 2026-09-14
-
None
-
None
-
None
-
None
-
None
-
None
-
None
Bug Summary
Secondary find cursors on a replica set with readConcern: local can return a torn read: a single batch that mixes pre-commit and post-commit values from the same transaction. This is a G-single-item anomaly — the observed state never existed at any real instant on the primary.
Affected Configurations
- Affected: MongoDB 4.4–8.0, readConcern: local or readConcern: available, secondary reads on a replica set under concurrent write load
- Not affected: primary reads; readConcern: majority or readConcern: snapshot; multi-document transactions (client-side); single-document updates
Root Cause
When a secondary cursor yields mid-scan (to allow oplog application to proceed), PlanYieldPolicy::performYieldWithAcquisitions() calls setTimestampReadSource(kLastApplied) after abandonSnapshot() without preserving the pre-yield timestamp. On restore, it calls getLastApplied() fresh, which may have advanced while the cursor was yielded. The result:
- Cursor opens at snapshot T₁.
- Cursor yields. Oplog application commits a transaction, advancing lastApplied to T₂.
- Cursor restores at T₂ instead of T₁.
- The first part of the batch was read at T₁; the remainder is read at T₂.
- Single firstBatch contains values from both snapshots — a torn read.
Observable Symptom
A find on a collection returns documents whose field values are inconsistent with any single committed snapshot. In a 3-field transaction that atomically writes x=N, y=N, z=N, a reader might observe x=N-1, y=N, z=N — partial application of the transaction.
How to Reason About the Race
The race window is:
cursor reads doc A (snapshot T₁)
→ yield
→ oplog applies txn committing x,y,z at T₂
→ restore, snapshot advanced to T₂
cursor reads doc B, C (snapshot T₂)
Any test that runs sustained secondary reads alongside concurrent multi-document transactions can expose this. The window is reliably hit when scan time exceeds lock-acquisition intervals on a busy secondary.
Proposed Fix (Needs Expert Review — CI Failures Observed)
A fix was attempted in PR #53793:
- Add RecoveryUnit::prepareForYield() (virtual, no-op default).
- Implement in WiredTigerRecoveryUnit::prepareForYield(): capture _readAtTimestamp into _readTimestampForYieldRestore.
- In setTimestampReadSource(kLastApplied): if _readTimestampForYieldRestore is set (restore path), use that saved value instead of getLastApplied().
- _txnOpen() clears the field so it does not bleed into subsequent getMore batches.
- Call prepareForYield() immediately before abandonSnapshot() in performYieldWithAcquisitions().
The fix logic is believed to be directionally correct, but CI showed unexpected failures that are outside the submitter's area of expertise. The implementation needs review by the Storage / Query Execution team before landing.
Files changed: src/mongo/db/query/plan_yield_policy.cpp, src/mongo/db/storage/recovery_unit.h, src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h, src/mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.cpp
Detection
Detected using an Elle-based consistency checker on a 2-node replica set with 2 writers / 2 secondary readers under 5 minutes of sustained load. Elle output showing the anomaly:
G-single-item: 2 case(s)
Case 1:
Reader (idx 35793): r(x=20002024) r(y=20002025) r(z=20002025)
Writer (idx 35787): w(x=20002025) w(y=20002025) w(z=20002025)
The reader saw y and z at the new value but x at the previous value — a state that never existed as a committed snapshot. The Elle test suite is on an experimental branch and is not available to the receiving team; the bug is reproducible with any tooling that can verify snapshot isolation on secondary reads.
- is related to
-
SERVER-120304 Snapshot time may advance before fetching a command's operationTime
-
- Closed
-