-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Critical - P2
-
Affects Version/s: None
-
Component/s: Reconciliation
-
Storage Engines, Storage Engines - Transactions
-
0.038
-
SE Transactions - 2026-05-08
-
3
Problem
Two bugs in `_rec_append_orig_value` and `_rec_fill_tw_from_upd_select` (src/reconcile/rec_visibility.c) caused data inconsistencies when reconciling prepared transactions.
Root cause: The decision to write an update as prepared (write_prepared/write_prepare) is taken against the pinned stable timestamp captured at reconcile start, which can lag the current global oldest. By the time visibility checks run later in reconciliation, the update chain may have already become globally visible — creating a mismatch between the write-prepared decision and the visibility state observed during the checks.
Bug 1 — `__rec_append_orig_value`: incorrect early return for on-page tombstone
When the on-page update is a tombstone and write_prepared=true, the existing globally-visible early-return shortcut fires and causes the function to return without writing the on-page value. The caller relies on that on-page value as a rollback fallback for the prepared update; skipping it leaves the prepared cell with no valid rollback target.
Bug 2 — `__rec_fill_tw_from_upd_select`: tombstone treated as globally visible during prepared write
`tombstone_globally_visible` was computed without accounting for `write_prepare`. When the tombstone had become globally visible by the time the check ran, the globally-visible branch was taken, skipping the selection of the value behind the tombstone. This left the on-page value unset while the time window still carried a prepared stop, breaking downstream invariants when the page is later restored from disk.
Fix
- `__rec_append_orig_value`: Skip the globally-visible early-return shortcut when write_prepared=true and the candidate update is the on-page tombstone. Introduces onpage_upd_or_tombstone to identify the first on-page update/tombstone.
- `__rec_fill_tw_from_upd_select`: Force tombstone_globally_visible = false when write_prepare=true, ensuring the not-globally-visible path is always taken for prepared tombstones so the underlying value is selected as the rollback fallback.
- Rename seen_resolved → seen_committed and fix its accumulation pattern (was incorrectly overwriting with the last update's state rather than setting-to-true-and-keeping).
Testing
Covered by existing prepared-transaction reconciliation tests and manual inspection of the reconciliation path.