Files
2c0c3ed5f5 fix: re-watch etcd on auth-token errors instead of panicking (#50614)
## Issue

issue: #50102

## Problem

With etcd auth enabled and `mixCoord.enableActiveStandby: true`, a
long-running **standby** MixCoord panics when promoted to active:

```
panic: rpc error: code = Unauthenticated desc = etcdserver: invalid auth token
  ProxyWatcher.startWatchEtcd (proxy_watcher.go:146)
```

This is **not** a credential misconfiguration (the same config works for
the active node and all other components, and a restart of the same pod
recovers).

Root cause:
- etcd's default `simple` auth token is stateful and server-local: GC'd
after idle TTL (`--auth-token-ttl`, default 300s), member-local, and
lost on member restart. A long-idle standby's token gets invalidated
server-side without the client knowing.
- When an **already-established watch stream** then hits
`Unauthenticated`, clientv3 classifies it as a halt error (`isHaltErr` —
anything that isn't `Unavailable`/`Internal`) and tears the stream down
**without** refreshing the token. clientv3's token refresh only covers
unary calls and a stream's first recv, not a live watch stream.
- `ProxyWatcher.startWatchEtcd` and `sessionutil.handleWatchErr` only
handled `ErrCompacted` and `panic()`ed on every other watch error.

## Fix

Treat auth-token watch errors as recoverable and re-establish the watch
with a bounded retry, instead of panicking. Re-watching issues a unary
request first (`getSessionsOnEtcd` / `GetSessions`), which refreshes the
auth token via clientv3's unary retry interceptor, so the new watch
stream recovers.

- New shared predicate `etcd.IsRetriableWatchErr` classifies recoverable
watch errors (`ErrCompacted` + auth-token errors: `ErrInvalidAuthToken`
/ `ErrUserEmpty` / `ErrAuthOldRevision` / raw `Unauthenticated`).
- `ProxyWatcher.startWatchEtcd` and `sessionutil.handleWatchErr` now
re-watch on recoverable errors via `retry.Do(...,
retry.RetryErr(etcd.IsRetriableWatchErr))`. The retry is bounded by
`retry.Do`'s default attempt count / backoff; non-recoverable errors
still fail fast.

The CDC controller and streaming session discoverer already self-heal
(return-and-re-list on watch error), so they are unchanged.

> Operational note for affected users: configuring etcd with **JWT**
auth tokens (`--auth-token jwt,...`) instead of the default `simple`
token avoids the unexpected server-side invalidation entirely (JWT is
stateless — valid across members, survives restart, no idle GC).

## Test

- Added `TestIsRetriableWatchErr` (pkg/util/etcd) — predicate
classification.
- Added subtests in `TestWatcherHandleWatchResp` (sessionutil) —
auth-token error triggers re-watch (not channel close), non-retriable
error closes the channel.
- Existing `TestProxyManager_ErrCompacted` still passes (non-retriable
bad-session-data fails fast).

> Note: the watcher integration tests link the C++ core; they were not
runnable in my local/remote env at PR time and will be validated in CI.

Signed-off-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: xiaofanluan <xf@hjjaq.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 17:06:27 +08:00
..