-
Type:
Bug
-
Resolution: Unresolved
-
Priority:
Major - P3
-
None
-
Affects Version/s: None
-
Component/s: Layered Tables
-
Storage Engines - Foundations
-
0.756
-
None
-
None
A cursor positioned on the stable table and reused after a step-down (by a later transaction if the positioning one committed, or by the same transaction if it holds a read timestamp) reopens the stable table for the new role on its first operation. The reopen transfers the position — a transactional search for the positioned key by the current transaction. When that search cannot see the key, the cursor is left claiming a position the new view cannot reconstruct; repositioning from there is not guaranteed to be correct, and a production assertion ("upgrading a positioned stable cursor") aborts rather than risk skipped or duplicated keys.
Only next and prev are affected: they alone rely on the retained position. Every other operation localizes the key on entry and proceeds keyed — verified to match fresh-cursor results (writes with contract-valid input, targeting a key guaranteed to exist).
The transfer search misses in exactly three cases:
- The key was removed — the tombstone is visible to the reusing transaction.
- The transaction's read timestamp is below the key's insert — physically present, invisible.
- (WT-17968) The picked-up checkpoint's oldest timestamp passed the reader's timestamp, so no version survives — extreme form: the fresh stable tree a new leader builds at step-up.
Otherwise repositioning succeeds: a timestamped spanning reader is served by checkpoint + history store, and a post-step-down transaction reads the step-down checkpoint, which contains every committed key (the step-down timestamp cannot be set below the newest durable timestamp). A spanning transaction without a read timestamp never reaches the transfer — the era check rolls it back first.
Applicability to MongoDB: the server's RecordStore/index cursors reset the WT cursor on save() and re-seek on restore(), and rollback resets session cursors, so the new-transaction triggers (the first two cases) are not reachable through normal query paths. The reachable shape is the spanning reader.
Possible solution
Return WT_ROLLBACK instead of asserting: the transaction cannot continue from a position its snapshot cannot reconstruct, and a retryable rollback says so within the contract.
If we go this way, an FTDC counter would be a nice addition: it would show how many transactions roll back at the role change versus cross it successfully.
Another (and probably better, but more time-consuming) option is to check whether the cursor can be safely repositioned from the retained key instead — the escape that already exists for ingest-positioned cursors does exactly that (clear the iteration flags, re-search from the retained key across both constituents). Given that only next and prev are affected, there might not be many cases to consider.
Note the two options are complementary rather than alternatives: repositioning is well-defined for the first two miss cases, but in the third (WT-17968) the neighbors a re-search returns are equally unserveable for the reader's timestamp, so that case likely still needs the WT_ROLLBACK fallback.