-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Critical - P2
-
None
-
Affects Version/s: None
-
Component/s: Transactions
-
Storage Engines - Transactions
-
158.88
-
SE Transactions - 2026-07-31
-
13
On a disaggregated follower, a layered cursor's stable constituent is bound at first use to the latest adopted checkpoint (__clayered_open_stable_follower always opens the last checkpoint; the first-open path in __clayered_update_stable performs no snapshot check).
A transaction at snapshot isolation with no read timestamp that began before a checkpoint pickup and opens a new cursor afterwards therefore reads the new checkpoint — whose transaction ids were wiped on open (foreign id space), so the transaction's snapshot cannot exclude content committed after it began. The read silently returns post-snapshot data.
Failure cases
All cases require: follower role, snapshot isolation, no read timestamp, snapshot established before a checkpoint pickup completes. The adopted checkpoint's transaction ids are wiped, so everything in it is visible to every snapshot; correctness therefore depends entirely on which checkpoint the stable cursor is bound to.
- First stable open after pickup (primary case). The transaction opens its first cursor on a layered table after the pickup. The first-open path binds the stable to the new checkpoint. Updates that were replayed into the ingest table after the snapshot began (invisible there by transaction id) are visible in the new stable. The read silently returns post-snapshot data.
- First-ever checkpoint pickup. The transaction began when the follower had no checkpoint at all (stable cursor never opened, only ingest reads). The first pickup publishes a checkpoint; the transaction's next cursor open binds it. Same violation as case 1.
- Deferred stable open on an existing layered cursor. A layered cursor opened before the pickup may not have opened its stable constituent yet (no checkpoint available at the time, or the stable was skipped for the operation). When a later operation in the same transaction opens the stable, it binds the new checkpoint. Same violation, but through a cursor that already returned pre-pickup results — so a single cursor returns results from two different checkpoints within one transaction.
Resulting anomalies
- Post-snapshot reads: data committed (locally, via oplog application into ingest) after the transaction began is returned.
- Torn writer transactions: for a multi-key writer transaction captured by the new checkpoint, a key that still has an older snapshot-visible version in ingest returns the old value (ingest shadows stable), while a key with no visible ingest version (e.g. a fresh insert) leaks the new value from the stable. The reader observes half of the writer's transaction.
- Non-repeatable reads within one transaction: a cursor opened before the pickup (correctly pinned to the old checkpoint by the advance-time check in __clayered_can_advance_stable) and a cursor opened after the pickup return different data for the same key in the same transaction.
Not affected
- Readers with a read timestamp (the history store provides a consistent view at any checkpoint).
- Read-committed readers (their snapshot is refreshed per operation, after the pickup's publish).
- Stable cursors already open before the pickup: __clayered_can_advance_stable refuses to advance the stable under an untimestamped snapshot. The bug is that the first-open path lacks the symmetric check.
- Leaders (no checkpoint adoption).
- is related to
-
WT-18190 Apply the disaggregated checkpoint pickup's local metadata updates in a single transaction
-
- Open
-