storage: fix wrong commit_ts in Get/TableScan when latest write is Op_Lock (#19374)#19394
Conversation
…_Lock (tikv#19374) close tikv#19312 storage: fix wrong commit_ts in Get/TableScan when latest write is Op_Lock Problem Summary: - When `load_commit_ts=true` and a key has newer committed `Op_Lock` versions on top of the visible `Op_Put`, TiKV may return the lock commit_ts instead of the put commit_ts. What changed: - Fix `PointGetter` to return the commit_ts of the actual write version loaded by `last_change` shortcut, instead of the current cursor version. - Fix backward scanner to only update `loaded_commit_ts` when selecting a `Put` version, avoiding ts overwrite from `Lock/Rollback` versions. Tests: - Add `test_point_get_load_commit_ts_with_top_lock_versions` in `point_getter.rs`. - Add `test_load_commit_ts_with_top_lock_versions` in `scanner/backward.rs`. - Verify existing related tests: - `test_point_get_load_commit_ts` - `test_scan_with_load_commit_ts` Signed-off-by: Chao Wang <cclcwangchao@hotmail.com>
📝 WalkthroughWalkthroughThis PR fixes a bug where TiKV returns incorrect commit timestamps for point-get and reverse-scan operations. It introduces a commit timestamp caching mechanism in the point getter to correctly handle cases where newer lock versions exist above visible put versions, and propagates commit timestamp loading in backward scanners. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Fixes incorrect _tidb_commit_ts being returned by point-get and scans when load_commit_ts is enabled and newer committed Op_Lock writes exist above the latest visible Op_Put (per Close #19393 / ref #19374).
Changes:
PointGetter: track and return the commit_ts of the actualWriteloaded via thelast_changeshortcut, instead of decoding ts from the cursor’s current (newer) position.- Backward scanner: only set
loaded_commit_tswhen aPutversion is selected (avoids overwriting withLock/Rollbackcommit_ts). - Adds targeted regression tests covering top-lock stacks across seek-bound thresholds and delete visibility.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
src/storage/mvcc/reader/scanner/mod.rs |
Adds a forward-scan regression test validating load_commit_ts correctness with many top LOCK writes (both < SEEK_BOUND and >= SEEK_BOUND) and deleted keys. |
src/storage/mvcc/reader/scanner/backward.rs |
Fixes reverse-scan loaded_commit_ts bookkeeping so commit_ts comes from the returned PUT, plus adds a regression test. |
src/storage/mvcc/reader/point_getter.rs |
Fixes point-get commit_ts derivation when last_change triggers a get_cf jump, plus adds regression tests for both </>= SEEK_BOUND and delete cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/storage/mvcc/reader/scanner/backward.rs (1)
383-391: The seek-loop fix is correct, but the new test does not exercise this code path.In
test_load_commit_ts_with_top_lock_versions, the PUT@ts=1 is the earliest entry for key "k". Theprev()loop starts there (i=0), capturesloaded_commit_ts = Some(1), and subsequent iterations only see LOCKs. After the loop, the seek path re-scans ts=REVERSE_SEEK_BOUND+2 down tolast_checked_commit_tsand terminates withhandle_last_version, which already carries the correctloaded_commit_tsfrom theprev()loop. Lines 383–386 are never reached in that test.The specific scenario these lines guard against is when the
prev()loop exhausts allREVERSE_SEEK_BOUNDiterations without finding a PUT (e.g., all encountered versions are LOCKs), and then the seek loop finds a PUT in the range(last_checked_commit_ts, scan_ts]. A concrete setup:// PUT above last_checked_commit_ts so seek loop hits it directly must_prewrite_put(&mut engine, b"k", b"v", b"k", 1); must_commit(&mut engine, b"k", 1, 1); // enough LOCKs so prev() loop is exhausted without finding the PUT for ts in 2..=REVERSE_SEEK_BOUND + 2 { must_prewrite_lock(&mut engine, b"k", b"k", ts); must_commit(&mut engine, b"k", ts, ts); } // a second PUT above everything, visible, so seek loop finds it first must_prewrite_put(&mut engine, b"k", b"v2", b"k", REVERSE_SEEK_BOUND + 3); must_commit(&mut engine, b"k", REVERSE_SEEK_BOUND + 3, REVERSE_SEEK_BOUND + 3); // scan at REVERSE_SEEK_BOUND + 5 so the PUT@REVERSE_SEEK_BOUND+3 is between // last_checked_commit_ts and scan_tsConsider adding a test case that forces this code path.
Would you like me to draft the missing test case for the seek-loop PUT path?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/storage/mvcc/reader/scanner/backward.rs` around lines 383 - 391, Add a unit test that reproduces the seek-loop PUT path by exhausting the prev() loop with REVERSE_SEEK_BOUND LOCK entries and ensuring the seek loop then finds a PUT in (last_checked_commit_ts, scan_ts] so lines that call reverse_load_data_by_write and ValueEntry::new are exercised; specifically, create a PUT at ts=1, then for ts in 2..=REVERSE_SEEK_BOUND+2 create LOCKs (prewrite_lock + commit) so prev() is exhausted, then create a PUT at REVERSE_SEEK_BOUND+3 (prewrite_put + commit) and run the same scan at scan_ts = REVERSE_SEEK_BOUND+5 (or similar) asserting that the returned ValueEntry uses reverse_load_data_by_write and carries the correct loaded_commit_ts; name it e.g. test_load_commit_ts_seek_loop_put_path and use must_prewrite_put, must_commit, must_prewrite_lock, REVERSE_SEEK_BOUND, reverse_load_data_by_write, and ValueEntry assertions to locate the behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/storage/mvcc/reader/scanner/backward.rs`:
- Around line 383-391: Add a unit test that reproduces the seek-loop PUT path by
exhausting the prev() loop with REVERSE_SEEK_BOUND LOCK entries and ensuring the
seek loop then finds a PUT in (last_checked_commit_ts, scan_ts] so lines that
call reverse_load_data_by_write and ValueEntry::new are exercised; specifically,
create a PUT at ts=1, then for ts in 2..=REVERSE_SEEK_BOUND+2 create LOCKs
(prewrite_lock + commit) so prev() is exhausted, then create a PUT at
REVERSE_SEEK_BOUND+3 (prewrite_put + commit) and run the same scan at scan_ts =
REVERSE_SEEK_BOUND+5 (or similar) asserting that the returned ValueEntry uses
reverse_load_data_by_write and carries the correct loaded_commit_ts; name it
e.g. test_load_commit_ts_seek_loop_put_path and use must_prewrite_put,
must_commit, must_prewrite_lock, REVERSE_SEEK_BOUND, reverse_load_data_by_write,
and ValueEntry assertions to locate the behavior.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/storage/mvcc/reader/point_getter.rssrc/storage/mvcc/reader/scanner/backward.rssrc/storage/mvcc/reader/scanner/mod.rs
|
/retest |
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: cfzjywxk, Connor1996 The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
[LGTM Timeline notifier]Timeline:
|
|
/cherry-pick release-8.5 |
|
@lcwangchao: new pull request created to branch DetailsIn response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the ti-community-infra/tichi repository. |
…_Lock (#19374) (#19394) (#19397) close #19393 storage: fix wrong commit_ts in Get/TableScan when latest write is Op_Lock Problem Summary: - When `load_commit_ts=true` and a key has newer committed `Op_Lock` versions on top of the visible `Op_Put`, TiKV may return the lock commit_ts instead of the put commit_ts. What changed: - Fix `PointGetter` to return the commit_ts of the actual write version loaded by `last_change` shortcut, instead of the current cursor version. - Fix backward scanner to only update `loaded_commit_ts` when selecting a `Put` version, avoiding ts overwrite from `Lock/Rollback` versions. Tests: - Add `test_point_get_load_commit_ts_with_top_lock_versions` in `point_getter.rs`. - Add `test_load_commit_ts_with_top_lock_versions` in `scanner/backward.rs`. - Verify existing related tests: - `test_point_get_load_commit_ts` - `test_scan_with_load_commit_ts` Signed-off-by: Chao Wang <cclcwangchao@hotmail.com> Co-authored-by: Chao Wang <cclcwangchao@hotmail.com>
…_Lock (tikv#19374) (tikv#19394) close tikv#19393 storage: fix wrong commit_ts in Get/TableScan when latest write is Op_Lock Problem Summary: - When `load_commit_ts=true` and a key has newer committed `Op_Lock` versions on top of the visible `Op_Put`, TiKV may return the lock commit_ts instead of the put commit_ts. What changed: - Fix `PointGetter` to return the commit_ts of the actual write version loaded by `last_change` shortcut, instead of the current cursor version. - Fix backward scanner to only update `loaded_commit_ts` when selecting a `Put` version, avoiding ts overwrite from `Lock/Rollback` versions. Tests: - Add `test_point_get_load_commit_ts_with_top_lock_versions` in `point_getter.rs`. - Add `test_load_commit_ts_with_top_lock_versions` in `scanner/backward.rs`. - Verify existing related tests: - `test_point_get_load_commit_ts` - `test_scan_with_load_commit_ts` Signed-off-by: Chao Wang <cclcwangchao@hotmail.com>
…_Lock (tikv#19374) (tikv#19394) close tikv#19393 storage: fix wrong commit_ts in Get/TableScan when latest write is Op_Lock Problem Summary: - When `load_commit_ts=true` and a key has newer committed `Op_Lock` versions on top of the visible `Op_Put`, TiKV may return the lock commit_ts instead of the put commit_ts. What changed: - Fix `PointGetter` to return the commit_ts of the actual write version loaded by `last_change` shortcut, instead of the current cursor version. - Fix backward scanner to only update `loaded_commit_ts` when selecting a `Put` version, avoiding ts overwrite from `Lock/Rollback` versions. Tests: - Add `test_point_get_load_commit_ts_with_top_lock_versions` in `point_getter.rs`. - Add `test_load_commit_ts_with_top_lock_versions` in `scanner/backward.rs`. - Verify existing related tests: - `test_point_get_load_commit_ts` - `test_scan_with_load_commit_ts` Signed-off-by: Chao Wang <cclcwangchao@hotmail.com> Signed-off-by: rishabh mittal <mittalrishabh@gmail.com>
What is changed and how it works?
Issue Number: Close #19393
What's Changed:
Related changes
pingcap/docs/pingcap/docs-cn:Check List
Tests
Side effects
Release note
Summary by CodeRabbit
New Features
Bug Fixes
Tests