mvcc: avoid panic in cancelWatcher during concurrent victim processing #21185
+147
−6
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Overview
Fixed a critical race condition in the MVCC watcher lifecycle where concurrent watcher cancellation and victim processing could trigger a fatal panic in the etcd server.
Under realistic high-load scenarios with slow watchers, a watcher may transiently exist outside of all watcher groups due to non-atomic state transitions. The existing code treated this state as impossible and crashed the server.
This PR makes the watcher cancellation logic resilient to this transient state by retrying until lifecycle convergence, preventing process termination.
The Issue
In
server/storage/mvcc/watchable_store.go, thecancelWatcherfunction assumes the following invariant:During concurrent execution of:
syncWatchers/moveVictims)cancelWatcher)this invariant does not always hold.
A watcher may be removed from the victim batch and have
wa.victimcleared while a concurrent cancellation is in progress. WhencancelWatcherobserves this transient state, it panics with:This panic crashes the etcd process and disrupts clients such as Kubernetes API servers.
Fix Applied
server/storage/mvcc/watchable_store.gocancelWatcherto retry when a watcher is transiently in-flight between lifecycle states instead of panicking.This aligns
cancelWatcherbehavior with other watcher lifecycle code paths that already tolerate concurrent state transitions.Testing & Verification
TestCancelWatcherDuringVictimProcessingin:server/storage/mvcc/watchable_store_test.goVerified locally with: