-
Type:
Improvement
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: Cursors, Documentation, Layered Tables
-
None
-
Storage Engines - Foundations
-
134.206
-
None
-
None
Motivation
Nearly every bug and fragility ticket in the Layered Cursors Rework epic (SPM-4736) traces back to an implicit contract: state that one function establishes, another relies on, and nothing documents or asserts. Reviews repeatedly stall on re-deriving these contracts from scratch (WT-17967 exists because the rationale behind WT-16955's search_near side-selection was lost; WT-17994 lists six spots in the iteration logic whose correctness rests on unstated invariants). WT-17781 additionally requires that every state covered (or intentionally excluded) by the grey-box unit tests be documented in some form.
Goal
Decide where this documentation should live and write it there. The deliverable is that a working engineer can answer "what state is this cursor guaranteed to be in here?" from documentation rather than archaeology. A single canonical home is preferred over spreading the same contract across several places that will drift apart.
Candidate places to consider
Ideas, not requirements - pick what fits, or propose another:
- src/docs/arch-disagg-layered-cursor.dox - already exists (~47 lines: structure basics and the definition of "positioned") and is the natural canonical home; the architecture guide is where WiredTiger already keeps this kind of prose.
- A contract comment at the top of src/cursor/cur_layered.c - lives next to the code that must honor it, visible in every review; risks drifting from a larger external page if both carry substance.
- Function-header comments on the few contract-bearing functions (enter/leave, update_state, the iteration entry point) stating only their entry/exit guarantees - smallest form, enforced by review.
- A state-transition table or diagram (in the arch page, or a separate artifact) - per API call: required entry state, guaranteed exit state on success, WT_NOTFOUND, WT_PREPARE_CONFLICT, WT_ROLLBACK, generic error.
- Asserts as executable documentation - an invariant that can be asserted arguably should not be prose at all; whatever remains prose should be explicitly marked as unchecked.
Content ideas
- Cursor cast and "positioned". iface, ingest/stable constituents, current_cursor, the alternate. What "positioned" means at each level (WT_CURSTD_KEY_INT, current_cursor set, constituent parked on the same key), what a "parked" alternate is, when the alternate is expected positioned versus legitimately exhausted or unopened.
- Durable vs. per-operation state. Which fields persist between API calls versus live only in WT_CLAYERED_OP; expected cursor state after each operation on success and on every failure class (positioned / key-only after remove / external key after largest_key / unpositioned).
- Iteration state machine. Direction-flag lifecycle, alternate reuse versus repositioning, the prepare-conflict stall and resumption (the blocked constituent re-drives the blocked key), why repositioning the alternate mid-stall may have no viable anchor key (WT-17960 / BF-43990 as a worked example).
- Context-change detection. How a checkpoint pickup, a snapshot change, and a read-timestamp change are each detected, which cursor state each invalidates, constituent reopen rules on both roles - including when reopening must not happen.
- Tombstones and value encoding. Why deletes must land in ingest as tombstones (masking stable keys), the encode/decode scheme for user values colliding with the tombstone encoding, tombstone lifetime.
- Prepared transactions. Why only ingest can raise a prepare conflict, what state is kept while stalled, exit contract when the prepare resolves as commit versus rollback (including resolution with a tombstone).
- Write path. Why writes look up before the put and the cost implications; overwrite versus non-overwrite; conflict detection on keys not visible in ingest (__clayered_modify_check); the required reset of the non-target constituent before a write; remove on a cursor positioned in a previous transaction.
- Roles. Leader/follower differences: which constituent receives writes, when constituents are skipped or absent (no stable before the first checkpoint on a follower, no ingest cursor on a leader), role-agnostic operation bodies, invariants across promotion/step-down.
- Session interaction. enter/leave lifecycle, ncursors accounting, read-committed snapshot release, eviction back-pressure delivery.
- search_near. Merged-result semantics, side selection in the straddle case, the recovered rationale for the current rule (input to WT-17967).
- Deliberately disabled behaviors. Cursor reposition and other file-cursor features layered cursors opt out of, with reasons.
- Testing map. Which suite owns which class of behavior (differential stress test, grey-box unit tests, Python integration) and what is intentionally out of scope of each.
Possible extras: an error-exit contract table (what a caller may legally do after each error class), truncate semantics on layered tables, bounds / next_random / reserve / modify support status, observability (stats and verbose categories for diagnosing layered cursor issues), a short glossary (ingest, stable, constituent, alternate, parked, stall, pickup).
Definition of Done
- Chosen location(s) agreed with the team and documentation written there.
- Contracts either asserted in code or explicitly documented as unchecked.
- WT-17781's state-coverage documentation requirement satisfied or explicitly delegated.
- is related to
-
WT-16955 (Disagg=switch) Test/format data mismatch during step-down
-
- Closed
-
-
WT-17967 Evaluate simplifying layered search_near side-selection.
-
- Open
-
-
WT-17781 Add grey-box unit-test coverage for layered cursors
-
- Backlog
-
-
WT-17994 Rework fragile iteration state logic in __clayered_iterate_constituents()
-
- Open
-
- related to
-
WT-17967 Evaluate simplifying layered search_near side-selection.
-
- Open
-
-
WT-17781 Add grey-box unit-test coverage for layered cursors
-
- Backlog
-
-
WT-17994 Rework fragile iteration state logic in __clayered_iterate_constituents()
-
- Open
-