Skip to content

[client] Recover admin writes after coordinator leader failover - #4216

Open
litiliu wants to merge 1 commit into
apache:mainfrom
litiliu:fix/admin-coordinator-failover-retry
Open

[client] Recover admin writes after coordinator leader failover#4216
litiliu wants to merge 1 commit into
apache:mainfrom
litiliu:fix/admin-coordinator-failover-retry

Conversation

@litiliu

@litiliu litiliu commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Purpose

Linked issue: close #4027

A long-lived Java Admin client caches the coordinator leader in MetadataUpdater. After coordinator leadership moves to a standby, coordinator write operations (e.g. dropDatabase, createTable, alterTable) keep being sent to the old coordinator and fail with NotCoordinatorLeaderException until the connection is recreated.

Two root causes:

  1. FlussAdmin wrapped only the read-only gateway with RetryableGatewayClientProxy; the write gateway never refreshed metadata after a failover.
  2. Even after refreshing to the new leader's address, NettyClient reused the connection cached under the coordinator uid cs-0 (both coordinators share id 0), so requests kept hitting the old leader that is still alive as a standby.

This is an alternative implementation of #4200 and incorporates @loserwang1024's review feedback there: refresh metadata on any recoverable error, but auto-retry only the provably-safe NotCoordinatorLeaderException.

Brief change log

  • RetryableGatewayClientProxy now takes separate refreshPredicate and retryPredicate. On failure it refreshes metadata when refreshPredicate matches, and additionally retries once only when retryPredicate matches. Read-only gateways keep using RetriableException for both (unchanged behavior).
  • FlussAdmin write gateway:
    • refreshPredicate = NotCoordinatorLeaderException || RetriableException — refreshes metadata on any recoverable error, including NetworkException/TimeoutException when the old coordinator's IP is not reused after an upgrade.
    • retryPredicate = NotCoordinatorLeaderException only — safe to auto-retry because FlussRequestHandler rejects the request before invoking the write API, so the mutation was never executed. Network/timeout failures refresh metadata but surface the original error for a manual retry (non-idempotent-safe).
  • NettyClient recreates the connection when the address behind a server uid changes (a coordinator failover reuses cs-0 at a new host/port) and closes the stale connection.

Tests

Run: ./mvnw -pl fluss-rpc,fluss-client -am -Dtest=RetryableGatewayClientProxyTest,NettyClientTest,CoordinatorFailoverAdminITCase -DfailIfNoSpecifiedTests=false test

  • RetryableGatewayClientProxyTest: retry on the safe error; refresh-but-no-retry on network errors; no refresh/retry when neither predicate matches.
  • NettyClientTest#testReconnectWhenServerAddressChangesForSameUid: a same-uid address change reconnects instead of reusing the stale connection.
  • CoordinatorFailoverAdminITCase#testAdminWriteRecoversAfterCoordinatorFailover: keeps one Admin open across a coordinator leader failover and verifies dropDatabase succeeds afterward.

API and Format

No public API or storage format changes. RetryableGatewayClientProxy (@Internal) gains a two-predicate factory overload; the existing signature is preserved.

Documentation

No. Bug fix; no user-facing feature or documentation change.


Generative AI disclosure: Yes — GitHub Copilot (Claude Opus 4.8) was used to help author this PR.

A long-lived Admin client caches the coordinator leader. After failover
to a standby, coordinator write operations (dropDatabase, etc.) kept
failing with NotCoordinatorLeaderException because:

1. FlussAdmin wrapped only the read-only gateway with retry, so the write
   gateway never refreshed metadata after a failover.
2. Even after refreshing to the new leader's address, NettyClient reused
   the stale connection cached under the coordinator uid "cs-0" (both
   coordinators share id 0), so requests kept hitting the old leader that
   is still alive as a standby.

Fixes:
- RetryableGatewayClientProxy now takes separate refresh and retry
  predicates. The write gateway refreshes metadata on any recoverable error
  (NotCoordinatorLeaderException or network errors after a failover/upgrade)
  so the stale coordinator connection is repointed and a manual retry can
  recover, but auto-retries only NotCoordinatorLeaderException -- which the
  server rejects before invoking the write API, so a retry cannot duplicate
  an already-executed, non-idempotent mutation. Read-only gateways keep
  retrying any RetriableException.
- NettyClient recreates the connection when the address for a server uid
  changes, closing the stale connection.

Tests:
- RetryableGatewayClientProxyTest: retry on the safe error; refresh-but-no-retry
  on network errors; no refresh/retry when neither predicate matches.
- NettyClientTest: reconnect when a uid's address changes.
- CoordinatorFailoverAdminITCase: keeps one Admin open across a coordinator
  leader failover and verifies a write succeeds afterward.

Closes apache#4027
@litiliu

litiliu commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

@loserwang1024 Could you take a look on this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[client] Admin write operations do not recover after coordinator leader failover

1 participant