Skip to content

server/storage/mvcc: fix progressIfSync lock-order inversion that deadlocks the apply loop#22185

Open
lazureykis wants to merge 1 commit into
etcd-io:mainfrom
lazureykis:fix/progressifsync-deadlock
Open

server/storage/mvcc: fix progressIfSync lock-order inversion that deadlocks the apply loop#22185
lazureykis wants to merge 1 commit into
etcd-io:mainfrom
lazureykis:fix/progressifsync-deadlock

Conversation

@lazureykis

Copy link
Copy Markdown

Fixes #22184.

progressIfSync held watchableStore.mu and then acquired store.mu (via s.rev()store.Read()), the reverse of the order documented on watchableStore.mu ("should never be locked before locking store.mu"). With a concurrent KV().Commit() queuing a writer on store.mu (as the snapshot-send path in createMergedSnapshotMessage does) and an in-flight write txn, this formed a three-way deadlock that wedged the apply loop. Full analysis and a deterministic reproduction are in #22184.

This reads the current revision directly under revMu — the same watchable.mu → revMu ordering already used by watch(), syncWatchers(), and moveVictims() — instead of opening a full read transaction that reaches for store.mu.

Adds TestProgressIfSyncDeadlock, which deadlocks (blocks at a 3s deadline) before this change and passes after. The existing mvcc watch/sync tests remain green under -race.


AI disclosure: this change was prepared with AI assistance (Claude). I reviewed the analysis, verified the fix and the regression test against the source, and ran the tests myself. The commit carries an Assisted-by: trailer accordingly.

@kubernetes-prow

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: lazureykis
Once this PR has been reviewed and has the lgtm label, please assign serathius for approval. For more information see the Code Review Process.

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

@kubernetes-prow

Copy link
Copy Markdown

Hi @lazureykis. Thanks for your PR.

I'm waiting for a etcd-io member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work.

Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

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 kubernetes-sigs/prow repository.

…dlocks the apply loop

progressIfSync held watchableStore.mu and then acquired store.mu (via
s.rev() -> store.Read()), the reverse of the order documented on
watchableStore.mu ("should never be locked before locking store.mu").
With a concurrent KV().Commit() queuing a writer on store.mu (as the
snapshot-send path does) and an in-flight write txn, this formed a
three-way deadlock that wedged the apply loop.

Read the current revision directly under revMu instead, matching the
watchable.mu -> revMu ordering already used by watch(), syncWatchers()
and moveVictims().

Adds TestProgressIfSyncDeadlock, which deadlocks before this change and
passes after.

Fixes etcd-io#22184

Assisted-by: Claude Opus 4.8
Signed-off-by: Pavel Lazureykis <pavel@lazureykis.dev>
@lazureykis
lazureykis force-pushed the fix/progressifsync-deadlock branch from 999c375 to aad3f3a Compare July 23, 2026 18:58
@lazureykis
lazureykis marked this pull request as ready for review July 23, 2026 19:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Development

Successfully merging this pull request may close these issues.

mvcc: lock-order inversion in progressIfSync can deadlock the apply loop

1 participant