Skip to content

[release-3.5] mvcc: avoid double decrement of watcher gauge on close/cancel race#20066

Merged
ahrtr merged 1 commit intoetcd-io:release-3.5from
kjgorman:cherry-pick19600-to-release-3.5
May 30, 2025
Merged

[release-3.5] mvcc: avoid double decrement of watcher gauge on close/cancel race#20066
ahrtr merged 1 commit intoetcd-io:release-3.5from
kjgorman:cherry-pick19600-to-release-3.5

Conversation

@kjgorman
Copy link
Copy Markdown
Contributor

This occurs specifically when the watch is for a compacted revision, as there's a possible interleaving of cancel/close that invokes the cancelWatch function twice.

It's fairly difficult to provoke the race condition but it is possible to observe on main the racing test can fail with a negative gauge:

$ go test ./...  -run TestNewWatcherCountGauge/compacted_watch,_close/cancel_race
--- FAIL: TestNewWatcherCountGauge (0.34s)
    watchable_store_test.go:86:  # HELP etcd_debugging_mvcc_watcher_total Total number of watchers.
         # TYPE etcd_debugging_mvcc_watcher_total gauge
        -etcd_debugging_mvcc_watcher_total -1
        +etcd_debugging_mvcc_watcher_total 0

FAIL
FAIL    go.etcd.io/etcd/server/v3/storage/mvcc  0.830s
?       go.etcd.io/etcd/server/v3/storage/mvcc/testutil [no test files]
FAIL

It seems as though it is partially expected for the cancel function to be invoked multiple times and to handle that safely (i.e., the existing ch == nil check) - the bug here is that in the if/else if branches it comes "too late", and multiple invocations where wa.compacted is true will both decrement the counter. Shifting the case up one ensures that we can't follow that decrement branch multiple times.

In fact, it seems logically more sensible to put this wa.ch == nil case first, as a guard for the function being invoked multiple times, but moving i before the sync/unsynced watch set delete functions could have a greater inadvertent functional impact (i.e., if we never deleted cancelled watches from these sets it would presumably introduce a leak), so from an abundance of caution I've made the smallest change I think will fix my issue.

Please read https://github.com/etcd-io/etcd/blob/main/CONTRIBUTING.md#contribution-flow.

This occurs specifically when the watch is for a compacted revision, as there's
a possible interleaving of cancel/close that invokes the `cancelWatch` function
twice.

It's fairly difficult to provoke the race condition but it is possible to
observe on `main` the racing test can fail with a negative gauge:

```
$ go test ./...  -run TestNewWatcherCountGauge/compacted_watch,_close/cancel_race
--- FAIL: TestNewWatcherCountGauge (0.34s)
    watchable_store_test.go:86:  # HELP etcd_debugging_mvcc_watcher_total Total number of watchers.
         # TYPE etcd_debugging_mvcc_watcher_total gauge
        -etcd_debugging_mvcc_watcher_total -1
        +etcd_debugging_mvcc_watcher_total 0

FAIL
FAIL    go.etcd.io/etcd/server/v3/storage/mvcc  0.830s
?       go.etcd.io/etcd/server/v3/storage/mvcc/testutil [no test files]
FAIL
```

It seems as though it is partially expected for the cancel function to be
invoked multiple times and to handle that safely (i.e., the existing `ch == nil`
check) - the bug here is that in the `if/else if` branches it comes "too late",
and multiple invocations where `wa.compacted` is true will both decrement the
counter. Shifting the case up one ensures that we can't follow that decrement
branch multiple times.

In fact, it seems logically more sensible to put this `wa.ch == nil` case
_first_, as a guard for the function being invoked multiple times, but moving i
before the sync/unsynced watch set delete functions could have a greater
inadvertent functional impact (i.e., if we never deleted cancelled watches from
these sets it would presumably introduce a leak), so from an abundance of
caution I've made the smallest change I think will fix my issue.

Signed-off-by: Kieran Gorman <kieran@kjgorman.com>
@k8s-ci-robot
Copy link
Copy Markdown

Hi @kjgorman. 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.

@kjgorman kjgorman changed the title mvcc: avoid double decrement of watcher gauge on close/cancel race mvcc: avoid double decrement of watcher gauge on close/cancel race (3.5 backport) May 30, 2025
@kjgorman kjgorman marked this pull request as ready for review May 30, 2025 09:55
@ahrtr
Copy link
Copy Markdown
Member

ahrtr commented May 30, 2025

/ok-to-test

@ahrtr
Copy link
Copy Markdown
Member

ahrtr commented May 30, 2025

link to #19600

@ahrtr ahrtr changed the title mvcc: avoid double decrement of watcher gauge on close/cancel race (3.5 backport) [release-3.5] mvcc: avoid double decrement of watcher gauge on close/cancel race (3.5 backport) May 30, 2025
Copy link
Copy Markdown
Member

@ahrtr ahrtr left a comment

Choose a reason for hiding this comment

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

LGTM & thx

@k8s-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: ahrtr, kjgorman, serathius

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

The pull request process is described 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

@ahrtr ahrtr merged commit 94575ba into etcd-io:release-3.5 May 30, 2025
29 of 31 checks passed
@kjgorman kjgorman changed the title [release-3.5] mvcc: avoid double decrement of watcher gauge on close/cancel race (3.5 backport) [release-3.5] mvcc: avoid double decrement of watcher gauge on close/cancel race Jun 2, 2025
@ivanvc ivanvc mentioned this pull request Jul 18, 2025
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.

4 participants