Repository navigation
Conversation
Async-commit and 1PC prewrites were sent with `min_commit_ts = 0` and no `max_commit_ts` (the FIXME in `Committer::prewrite`), and the committer assumed TiKV always used the protocol it asked for: - TiKV picks the commit ts of an async-commit or 1PC transaction from the largest timestamp any reader has used on its keys. With no upper bound, a reader with a timestamp far in the future could push it arbitrarily far. - When TiKV cannot use async commit in a region (with a `max_commit_ts`, because the commit ts would exceed it), it writes an ordinary 2PC lock and answers `min_commit_ts = 0`. The committer took the maximum over the regions anyway (`min_commit_ts.unwrap()`, "FIXME: min_commit_ts == 0 => fallback to normal 2PC") and committed every key at a timestamp that is not valid for the fallen-back region, or at 0 if every region fell back. - When a single-region 1PC prewrite fell back to an ordinary prewrite, commit returned `Error::OnePcFailure` and left the written locks behind. Now: - async-commit and 1PC prewrites carry `min_commit_ts = max(start_ts, for_update_ts) + 1` and `max_commit_ts = start_ts + elapsed + 2 s` (client-go's `calculateMaxCommitTS` with its default safe window); - if any region answers `min_commit_ts = 0`, the transaction falls back to 2PC: the primary is committed with a fresh timestamp and then the secondaries, as client-go does; - a 1PC prewrite that did not commit in one phase continues with async commit or 2PC instead of failing. `Error::OnePcFailure` is no longer returned by commit (the variant stays for compatibility); - a missing min_commit_ts is an error instead of a panic. 2PC prewrites are unchanged (no commit ts bounds). Tests: unit tests with a mock TiKV check the bounds on async-commit prewrites and their absence on 2PC ones, the fallback to 2PC when one of two regions fell back, the 1PC fallback, and a 1PC commit; three of them fail without the change. Signed-off-by: Dinakaran <dinakaranvijayakumar@outlook.com>
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Warning Review limit reachedNext included review available in 59 minutes. View limit detailsLimit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
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 |
Motivation
Committer::prewritesends async-commit and 1PC prewrites withmin_commit_ts = 0and nomax_commit_ts(// FIXME set max_commit_ts and min_commit_ts).Committer::commitalso assumes TiKV always used the protocol it asked for (// FIXME: min_commit_ts == 0 => fallback to normal 2PC, thenmin_commit_ts.unwrap()). Three problems follow:max_ts, the largest timestamp any reader has used on the keys. Withoutmax_commit_ts, a reader with a timestamp far in the future pushes the commit ts arbitrarily far. client-go bounds it withcalculateMaxCommitTS(start ts + elapsed +async-commit.safe-window, 2 s by default).max_commit_ts, because the commit ts would exceed it), it writes an ordinary 2PC lock and answersmin_commit_ts = 0. The committer took the maximum over all regions and committed every key at that timestamp. For the fallen-back region that timestamp is not valid: it can be below a read that TiKV already allowed there. If every region fell back, the commit ts was 0.one_pc_commit_ts = 0), TiKV had still written ordinary locks, butcommitreturnedError::OnePcFailureand left them for readers to resolve.What is changed and how it works
min_commit_ts = max(start_ts, for_update_ts) + 1andmax_commit_ts = start_ts + ((elapsed + 2 s) << 18), as in client-go'sbuildPrewriteRequestandcalculateMaxCommitTS. 2PC prewrites are unchanged.min_commit_ts = 0for an async-commit prewrite, the transaction falls back to 2PC, as client-go does (c.setAsyncCommit(false)). The primary is committed with a fresh timestamp (the same retry and undetermined handling as any 2PC primary commit), then the secondaries.commitno longer returnsError::OnePcFailure; the variant stays for compatibility.min_commit_tsis an error instead of anunwrap()panic.The safe window is a constant (
ASYNC_COMMIT_SAFE_WINDOW, 2 s). Making it aTransactionOptionssetting is easy if maintainers want it.Tests
Unit tests in
transaction::transaction::tests, with a mock TiKV that records prewrite and commit requests:async_commit_prewrite_bounds_the_commit_ts:min_commit_ts = start + 1,max_commit_ts≥ start + 2 s, and the commit ts is the max of the regions'min_commit_ts.two_phase_prewrite_carries_no_commit_ts_boundsasync_commit_falls_back_to_2pc_when_a_region_did: one of two regions answers 0, and the primary alone is committed beforecommitreturns.one_pc_fallback_commits_the_locks_instead_of_failingone_pc_commit_returns_the_one_pc_commit_tsThree of these fail on master. The integration suites (including the async-commit and 1PC tests) pass on a tiup playground v8.5.8.
cargo test --lib, clippy with-D clippy::allandcargo fmt --checkare clean.Notes for reviewers
resolve_locks(cleanup_locksalready had it).Error::OnePcFailureis still in the public enum but is no longer produced bycommit.Check list
Release note: async-commit and 1PC prewrites set
max_commit_ts. The client falls back to 2PC when TiKV does, and commits a 1PC transaction that fell back instead of failing withOnePcFailure.