-
Type:
Improvement
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Layered Tables
-
None
-
Storage Engines - Foundations
-
54.713
-
None
-
1
The step-down design relies in many places on two requirements, discussed and agreed with the server team:
- no transaction (read or write) spans a step-down unless the step-down timestamp was set, and
- no transaction spans a step-up.
They are relied upon but never verified. If they silently break, readers get wrong data with no diagnostic:
- across an un-announced step-down, a reader with a read timestamp skips the stable-bind role check by design and is rebound onto the newest checkpoint, which can be older than the live stable content it already read — non-repeatable reads within one transaction (an announced step-down is safe: the step-down checkpoint equals the frozen stable tree);
- across a step-up, a spanning snapshot cannot be consistent with the drained and adopted stable content;
- the same staleness exists at the cursor level even without a spanning transaction (FIXME-WT-14545): a layered cursor left idle across a step-down + step-up pair keeps its stable cursor bound to the outdated pre-step-down btree, and even a new transaction reading through it silently gets pre-step-down data.
Since txn->disagg_role_leader now records every snapshot's role era, all of this can be asserted on every layered cursor operation: assert !(!txn->disagg_role_leader && leader) (no step-up crossing) and !(txn->disagg_role_leader && !leader && !txn->stepdown_ts_set) (a step-down crossing is only legal for a transaction that began while the step-down timestamp was set), plus the per-cursor equivalent — record the role-change generation (or the role) at stable bind and verify it in __clayered_enter, which is what catches the idle-cursor case above.