Skip to content

transaction: clear prewrite locks on rollback of a failed pessimistic commit#547

Open
eduralph wants to merge 3 commits into
tikv:masterfrom
getwyrd:fix/pessimistic-terminal-rollback
Open

transaction: clear prewrite locks on rollback of a failed pessimistic commit#547
eduralph wants to merge 3 commits into
tikv:masterfrom
getwyrd:fix/pessimistic-terminal-rollback

Conversation

@eduralph

@eduralph eduralph commented Jul 5, 2026

Copy link
Copy Markdown

After a 2PC commit fails at prewrite, the already-placed prewrite (2PC) locks must be rolled back. The terminal pessimistic rollback sent PessimisticRollback, which only removes LockType::Pessimistic locks, so it silently left the prewrite locks behind while still returning Ok — leaving keys locked until TTL expiry and lock resolution.

Roll back a pessimistic transaction that has started committing with BatchRollback (as the optimistic path and client-go's commit cleanup do), which clears locks by start_ts regardless of type. A pessimistic transaction rolled back before commit (locks still pessimistic) continues to use the narrower PessimisticRollback.

Adds a failpoint regression test.

Closes #545

Signed-off-by: Eduard Ralph eduard@ralphovi.net

Summary by CodeRabbit

  • Bug Fixes
    • Improved rollback handling for pessimistic transactions when commit() fails after the prewrite/2PC stage, ensuring correct cleanup of any remaining 2PC and pessimistic locks.
    • Rollback now determines the correct cleanup behavior based on the commit stage captured during commit, reducing the chance of stale locks persisting.
  • Tests
    • Added regression coverage for pessimistic transactions failing after prewrite, including a retry scenario, verifying rollback succeeds and the client ends with zero locks.

… commit

After a 2PC commit fails at prewrite, the already-placed prewrite (2PC) locks
must be rolled back. The terminal pessimistic rollback sent PessimisticRollback,
which only removes LockType::Pessimistic locks, so it silently left the prewrite
locks behind while still returning Ok — leaving keys locked until TTL expiry and
lock resolution.

Roll back a pessimistic transaction that has started committing with
BatchRollback (as the optimistic path and client-go's commit cleanup do), which
clears locks by start_ts regardless of type. A pessimistic transaction rolled
back before commit (locks still pessimistic) continues to use the narrower
PessimisticRollback.

Adds a failpoint regression test.

Closes tikv#545

Signed-off-by: Eduard Ralph <eduard@ralphovi.net>
@ti-chi-bot

ti-chi-bot Bot commented Jul 5, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by:
Once this PR has been reviewed and has the lgtm label, please assign little-wallace for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added dco-signoff: yes Indicates the PR's author has signed the dco. contribution This PR is from a community contributor. size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Jul 5, 2026
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c91c48c2-0302-4b99-9118-4f5d890a21e9

📥 Commits

Reviewing files that changed from the base of the PR and between a17836f and 173895e.

📒 Files selected for processing (2)
  • src/transaction/transaction.rs
  • tests/failpoint_tests.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/transaction/transaction.rs

📝 Walkthrough

Walkthrough

Transaction rollback now preserves whether commit reached prewrite, then uses that state to choose the rollback RPC. New failpoint tests cover lock cleanup after failed pessimistic commits and a rollback retry.

Changes

Pessimistic Rollback Lock Cleanup Fix

Layer / File(s) Summary
Rollback entry point tracks prewritten state
src/transaction/transaction.rs
Transaction initializes a prewritten flag, sets it when commit enters the commit path, and preserves it during rollback before calling Committer::rollback(prewritten).
Committer rollback branches on prewritten
src/transaction/transaction.rs
Committer::rollback accepts prewritten: bool, updates its documentation, binds shared rollback values before branching, and uses PessimisticRollback only when the transaction is pessimistic and not prewritten; otherwise it uses BatchRollback.
Regression tests for rollback cleanup
tests/failpoint_tests.rs
Two failpoint-based tests force commit to fail after prewrite, then verify rollback clears locks in the normal path and after a retrying rollback attempt.

Estimated code review effort: 3 (Moderate) | ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: fixing rollback to clear prewrite locks after a failed pessimistic commit.
Linked Issues check ✅ Passed The rollback path now uses BatchRollback after prewrite for pessimistic transactions, and the new regression tests cover the reported failure mode.
Out of Scope Changes check ✅ Passed The changes stay focused on rollback behavior and regression tests, with no obvious unrelated scope added.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/transaction/transaction.rs`:
- Around line 704-708: The rollback path in transaction logic recomputes
`prewritten` from `get_status()` after the status has already moved to
`StartedRollback`, so retrying `rollback()` can lose the fact that the txn had
started committing. Update `transaction::Transaction`/`rollback()` to persist
this rollback mode across retries, either by storing a dedicated flag or
preserving the commit-start state before the transition, and use that saved
state when choosing between `BatchRollback` and `PessimisticRollback`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 42469290-3135-4f7c-aa8c-2cbf4a56d457

📥 Commits

Reviewing files that changed from the base of the PR and between e53837d and 03bf9d3.

📒 Files selected for processing (2)
  • src/transaction/transaction.rs
  • tests/failpoint_tests.rs

Comment thread src/transaction/transaction.rs Outdated
eduralph added 2 commits July 6, 2026 01:31
…llback

rollback() recomputed `prewritten` from `get_status() == StartedCommit`,
but transit_status permits re-entry from StartedRollback and a failed
rollback leaves the status there. On retry, get_status() no longer reads
StartedCommit, so `prewritten` became false and a pessimistic txn that had
started committing fell back to PessimisticRollback, re-leaking the 2PC
prewrite locks the previous commit fixed.

Persist the flag on Transaction: set it when commit() enters the commit
path and read it in rollback(), so it survives the StartedCommit ->
StartedRollback transition and any rollback retry.

Signed-off-by: Eduard Ralph <eduard@ralphovi.net>
Add a `before-rollback` failpoint in `Committer::rollback` and a regression
test (`txn_pessimistic_rollback_retry_clears_prewrite_locks`) that forces the
first rollback attempt to fail, then asserts the retry still clears the 2PC
prewrite lock. This guards the `prewritten` flag now persisted across the
StartedCommit -> StartedRollback transition: recomputing it from status on a
retry would fall back to PessimisticRollback and leak the lock again.

Refs tikv#545

Signed-off-by: Eduard Ralph <eduard@ralphovi.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution This PR is from a community contributor. dco-signoff: yes Indicates the PR's author has signed the dco. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Pessimistic rollback() after a failed commit leaves prewrite (2PC) locks behind

1 participant