etcd-io / etcd-io/etcd

concurrency: Election.Resign forgets leadership after a failed request

Open
#22,123 1 comment 0 reactions 1 assignee Claimed by @lavacat View on GitHub
Dominant language
Go
Stars
52.3k
Forks
10.5k
Avg merge
2d 21h
Merged PRs (30d)
43

Description

### Bug report criteria

- [x] This bug report is not security related, security issues should be disclosed privately via security@etcd.io.
- [x] This is not a support request or question, support requests or questions should be raised in the etcd discussion forums.
- [x] I have read the etcd bug reporting guidelines.
- [x] Existing open issues and the etcd FAQ have been checked and this is not a duplicate.

### What happened?

`concurrency.Election.Resign` clears `leaderKey` and `leaderSession` even when its delete transaction returns an error. If the request never reaches etcd—for example, because the context is already canceled—the leader key remains on the server, but the `Election` forgets it. A later `Resign` then returns `nil` without retrying the delete.

### What did you expect to happen?

`Resign` should retain its local leadership state when the transaction returns an error, allowing the caller to retry. It should clear the state after receiving a successful transaction response.

### How can we reproduce it (as minimally and precisely as possible)?

1. Create a session and successfully campaign in an election.
2. Save `Election.Key()`.
3. Call `Election.Resign` with a context canceled before the call.
4. Observe that `Resign` returns `context.Canceled` and the leader key still exists in etcd.
5. Observe that `Election.Key()` is now empty, so calling `Resign` again is a no-op and cannot remove the key.

A focused integration regression on current `main` fails as follows:

```text
--- FAIL: TestElectionResignCanRetryAfterError
election_resign_test.go:47:
expected: "/resign-retry/60979f79e2255e03"
actual : ""
```

### Anything else we need to know?

The delete is already guarded by a `CreateRevision` comparison, so retaining state and retrying is safe even if the first response was ambiguous: a stale election cannot delete a newer incarnation of the same key.

This is distinct from #21128 and #21165, which concern cleanup after `Election.Campaign` fails. They do not change `Resign`'s behavior when its own transaction fails.

### Etcd version

Current `main` at `6006f405800929b5e7e839e7a821d608a311579f` (the integration server reports `3.8.0-alpha.0`).

```console
$ go version
go version go1.26.5 darwin/arm64
```

### Etcd configuration

The reproduction uses the repository's single-member integration test cluster with its default configuration.

### Etcd debug information

Not applicable; the deterministic integration test reproduces the client-side state transition.

### Relevant log output

```text
rpc error: code = Canceled desc = context canceled
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.