Skip to content

[Active-passive] Phase1: Introduces a native, lightweight C++ leader election client - #63773

Merged
edoakes merged 13 commits into
ray-project:masterfrom
YoyinZyc:active_passive
Jul 23, 2026
Merged

edoakes merged 13 commits into
ray-project:masterfrom
YoyinZyc:active_passive

Conversation

@YoyinZyc

@YoyinZyc YoyinZyc commented Jun 1, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR implements the phase 1 of Active-passive Ray head replicas.
It introduces a native, lightweight C++ Kubernetes Lease client and a robust background leader elector state machine with an integrated watchdog.

Kubernetes API Integration

Extended the internal Kubernetes client (k8s_util) to support SSL-verified GET and PUT REST requests

Kubernetes Lease Client

  • LeaderLeaseClientInterface: Defined a platform-agnostic interface for lease acquisition, renewal, and release.
  • K8sLeaseClient: Implemented a concrete client that interacts with the Kubernetes /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases API.
  • Best practice adopted from client-go lib:
    • Clock-skew resilience
    • Cache lease object and direct-put the renewed lease object
    • Implement both graceful exit and fail-fast step-down

Generic Leader Elector State Machine with Watchdog

  • Election Thread: Implements a continuous loop with two modes:
    • Standby Mode: Polls the lease client periodically to acquire the lease. Sleeps on a condition variable to avoid high CPU spin cycles.
    • Active Mode: Periodically renews the lease heartbeat. Steps down immediately if the lease has been pre-empted by another node.
  • Safety Watchdog Thread: Continuously monitors the elapsed time since the last successful lease renewal using monotonic clock time. If renewals fail persistently and exceed the safety renew_deadline_seconds, the watchdog forces an immediate step-down (triggering the on_stopped_leading suicide callback) before another candidate can steal the lease.
  • Responsive Thread Wakeups: Enabled immediate thread wakeup signals so that when the watchdog triggers a step-down, the election thread immediately stops any ongoing renewal attempts instead of waiting for retry timeouts.

Unit tests

  • k8s_lease_client_test.cc: Tests lease acquisition, heartbeats, conflicts, and error handling.
  • leader_elector_test.cc: Tests promotion state transitions, transient network failures, persistent renewal timeouts, watchdog expiration steps, and multi-replica election with graceful/non-graceful exits.

Risk

Low, as it is just a unused library.

Related issues

#63643

Additional information

Optional: Add implementation details, API changes, usage examples, screenshots, etc.

@YoyinZyc
YoyinZyc requested a review from a team as a code owner June 1, 2026 16:43
@YoyinZyc
YoyinZyc marked this pull request as draft June 1, 2026 16:44

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a distributed leader election library for GCS using Kubernetes Leases, including a lease client, background elector, watchdog thread, and associated unit tests. While the implementation is comprehensive, several critical issues must be addressed: a race condition in Stop() that can cause a stopped node to re-acquire the lease, a compilation error when calling IsNotFound() on StatusOr, potential crashes in the K8s API utility functions if uninitialized, full-replacement PUT requests in Renew() that wipe out existing Lease metadata, a race condition in the watchdog's elapsed time calculation, and a discrepancy in process termination logging when the renewal deadline is breached.

Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
Comment thread src/ray/gcs/leader_election/BUILD.bazel Resolved
Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
@YoyinZyc
YoyinZyc force-pushed the active_passive branch 3 times, most recently from 9475cec to 5e44ee8 Compare June 2, 2026 21:38
@YoyinZyc

YoyinZyc commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a Kubernetes-based distributed leader election library for the GCS, including a lease client, elector, and associated tests, while also refactoring the Kubernetes API utility functions to support GET and PUT requests with proper status returns. The review feedback highlights several critical improvements: consistently using absl::Now() to prevent clock skew issues during lease renewals, making the Stop() method thread-safe and idempotent, explicitly setting timeouts before each HTTP write and read operation to avoid indefinite blocking, and adding type checks during JSON parsing to prevent potential crashes.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
@YoyinZyc

YoyinZyc commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a platform-agnostic distributed leader election library for Ray's GCS using Kubernetes Leases, including the K8sLeaseClient for API interactions, a background LeaderElector loop with an asynchronous watchdog, and corresponding unit tests. Feedback highlights critical concurrency and lifecycle issues in LeaderElector: multiple calls to Run() can trigger std::terminate() by overwriting joinable threads, the watchdog thread's condition variable predicate prevents early wakeups on successful renewals, and detaching threads during self-stopping can lead to use-after-free vulnerabilities.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@YoyinZyc

YoyinZyc commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a platform-agnostic distributed leader election library for GCS using Kubernetes Leases, including the lease client, elector, watchdog, and associated tests, alongside updates to the Kubernetes RPC utilities to support GET and PUT operations with proper status propagation. The review feedback highlights several robustness and reliability improvements: adding defensive checks to prevent JSON parsing exceptions in ParseLeaseMetadata, enforcing that the retry period is smaller than the renew deadline via a RAY_CHECK, using non-throwing error_code overloads for Boost.Asio/Beast operations in EstablishSslConnection, and logging failures during graceful lease release.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
@YoyinZyc

YoyinZyc commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic distributed leader election library for Ray GCS using Kubernetes Leases, including a platform-agnostic client interface, a concrete K8s lease client, a background leader elector with a safety watchdog loop, and corresponding unit tests. Additionally, the Kubernetes utility functions are updated to support GET and PUT requests with SSL connection establishment and timeout handling. The review feedback highlights critical improvements: removing the static keyword from service account token reads to handle Kubernetes token rotation gracefully, declaring EstablishSslConnection with internal linkage to avoid polluting the global namespace, and notifying the election thread immediately when the watchdog triggers a step-down to prevent delayed renewal attempts.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
@YoyinZyc

YoyinZyc commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a platform-agnostic distributed leader election library for GCS using Kubernetes Leases. It includes a K8sLeaseClient for interacting with the Kubernetes API server, a LeaderElector to manage background lease acquisition and renewal loops, and an asynchronous watchdog to ensure timely step-downs. Additionally, the Kubernetes RPC utility is extended to support GET and PUT requests with SSL connection reuse and timeout handling. The review feedback highlights three key areas for improvement: adding validation to ensure configuration durations are strictly positive to prevent busy-wait loops, avoiding thread detaching in Stop() to prevent potential use-after-free bugs, and optimizing the watchdog's standby loop by replacing the periodic 100ms sleep with a conditional wait.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@YoyinZyc

YoyinZyc commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic distributed leader election library for Ray's GCS using Kubernetes Leases, adding K8sLeaseClient, LeaderElector, and extending k8s_util to support GET and PUT requests. The review feedback identifies several critical issues: a potential split-brain vulnerability if renewTime parsing fails and defaults to Unix Epoch; concurrent invocation risks for the on_stopped_leading callback; use-after-free hazards from detaching threads in Stop(); potential crashes when calling erase on invalid JSON records; clock-jump vulnerabilities from using wall time instead of a steady clock; and false-positive step-downs if the renewal timestamp is uninitialized.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@YoyinZyc

YoyinZyc commented Jun 3, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a generic distributed leader election library for Ray, featuring a Kubernetes Lease-based client (K8sLeaseClient) and a background leader elector (LeaderElector) with watchdog capabilities. It also updates the Kubernetes API utility functions to return Status objects and support timeouts. The review feedback highlights several critical improvements: setting renew_time to absl::UnixEpoch() on parse failure to prevent deadlocks, adding a timeout before the SSL handshake to avoid indefinite blocking, preventing self-join deadlocks in Stop() when called from callbacks, and optimizing Release() to use cached lease records instead of performing a synchronous GET request.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/ray/rpc/authentication/k8s_util.cc Resolved
@YoyinZyc
YoyinZyc marked this pull request as ready for review June 3, 2026 19:23
@YoyinZyc YoyinZyc changed the title Active passive [Active-passive] Phase1: Introduces a native, lightweight C++ leader election client Jun 3, 2026
@rueian rueian added the go add ONLY when ready to merge, run all tests label Jul 9, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

There are 3 total unresolved issues (including 1 from previous review).

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 2c42e43. Configure here.

@YoyinZyc

Copy link
Copy Markdown
Contributor Author

@rueian I addressed the above comments and please let me know if you have further concerns. Thanks:)

@rueian rueian left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM!

YoyinZyc added 13 commits July 20, 2026 17:15
…ubernetes client

Signed-off-by: Yuchen Zhou <yczhou@google.com>
Signed-off-by: Yuchen Zhou <yczhou@google.com>
Signed-off-by: Yuchen Zhou <yczhou@google.com>
…tchdog

Signed-off-by: Yuchen Zhou <yczhou@google.com>
…rnal field when update

Signed-off-by: Yuchen Zhou <yczhou@google.com>
Signed-off-by: Yuchen Zhou <yczhou@google.com>
Signed-off-by: Yuchen Zhou <yczhou@google.com>
…d simplify standby wait

- Protect modifications and condition checks of atomic state variables (is_leading_, is_stopped_) using appropriate mutexes (watchdog_mutex_, election_mutex_) to prevent lost wake-ups on condition variables.

Signed-off-by: Yuchen Zhou <yczhou@google.com>
…l observed time tracking

- Replace local_observed_time_ (absl::Time, system clock) with local_observed_time_steady_ (std::chrono::steady_clock) to prevent local observed time elapsed comparisons from being affected by local system clock jumps or NTP adjustments.
- Update comments in leader_elector.h.

Signed-off-by: Yuchen Zhou <yczhou@google.com>
… threads and CAS callback gate

- Remove thread self-join check and detach logic from Stop(), enforcing the contract that Stop() must be called from external threads.
- Implement was_leading = is_leading_.exchange(false) gate under locks in both Renew() and WatchdogLoop() to guarantee on_stopped_leading is executed at most once.

Signed-off-by: Yuchen Zhou <yczhou@google.com>
…ne timeouts

- Modify EstablishSslConnection to accept a steady_clock::time_point deadline.
- Refactor K8sApiPost, K8sApiGet, and K8sApiPut to calculate a single global 5-second deadline and apply it across all connection and read/write operations using expires_at(deadline). This prevents sequential timeouts from accumulating and causing unexpected long hangs.

Signed-off-by: Yuchen Zhou <yczhou@google.com>
… of mutex

Signed-off-by: Yuchen Zhou <yczhou@google.com>
- Default renew_time to InfiniteFuture() so a missing/unparseable
  renewTime no longer instantly preempts a possibly-live leader.
- Refuse lease takeover from another holder without a resourceVersion
  (optimistic-concurrency split-brain guard).
- Init K8s client config via call_once in the factory so it no longer
  depends on the auth-token path running first.
- Add tests; update two expired-lease tests to include a resourceVersion.

Signed-off-by: Yuchen Zhou <yczhou@google.com>
@YoyinZyc

Copy link
Copy Markdown
Contributor Author

Thank you @rueian. I just rebased the change but it seems like I cannot merge the code, can you help me merge it if everything looks ok?

@andrewsykim andrewsykim left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Overall LGTM, @YoyinZyc were you able to test the library manually using the changes in your other PR?

@edoakes
edoakes merged commit 2b4bc7e into ray-project:master Jul 23, 2026
6 checks passed
@YoyinZyc

YoyinZyc commented Jul 23, 2026 •

Copy link
Copy Markdown
Contributor Author

@andrewsykim yes the change can only be tested locally with the local kuberay change.

400Ping pushed a commit to 400Ping/ray that referenced this pull request Aug 18, 2026
…election client (ray-project#63773)

## Description
This PR implements the phase 1 of Active-passive Ray head replicas.
It introduces a native, lightweight C++ Kubernetes Lease client and a
robust background leader elector state machine with an integrated
watchdog.

### Kubernetes API Integration
Extended the internal Kubernetes client (k8s_util) to support
SSL-verified GET and PUT REST requests

### Kubernetes Lease Client
- LeaderLeaseClientInterface: Defined a platform-agnostic interface for
lease acquisition, renewal, and release.
- K8sLeaseClient: Implemented a concrete client that interacts with the
Kubernetes /apis/coordination.k8s.io/v1/namespaces/{namespace}/leases
API.
- Best practice adopted from client-go lib:
  - Clock-skew resilience
  - Cache lease object and direct-put the renewed lease object
  - Implement both graceful exit and fail-fast step-down

### Generic Leader Elector State Machine with Watchdog
- Election Thread: Implements a continuous loop with two modes:
- Standby Mode: Polls the lease client periodically to acquire the
lease. Sleeps on a condition variable to avoid high CPU spin cycles.
- Active Mode: Periodically renews the lease heartbeat. Steps down
immediately if the lease has been pre-empted by another node.
- Safety Watchdog Thread: Continuously monitors the elapsed time since
the last successful lease renewal using monotonic clock time. If
renewals fail persistently and exceed the safety renew_deadline_seconds,
the watchdog forces an immediate step-down (triggering the
on_stopped_leading suicide callback) before another candidate can steal
the lease.
- Responsive Thread Wakeups: Enabled immediate thread wakeup signals so
that when the watchdog triggers a step-down, the election thread
immediately stops any ongoing renewal attempts instead of waiting for
retry timeouts.

### Unit tests
- k8s_lease_client_test.cc: Tests lease acquisition, heartbeats,
conflicts, and error handling.
- leader_elector_test.cc: Tests promotion state transitions, transient
network failures, persistent renewal timeouts, watchdog expiration
steps, and multi-replica election with graceful/non-graceful exits.

### Risk
Low, as it is just a unused library.

## Related issues
ray-project#63643

## Additional information
> Optional: Add implementation details, API changes, usage examples,
screenshots, etc.

---------

Signed-off-by: Yuchen Zhou <yczhou@google.com>
Signed-off-by: 400Ping <jiekaichang@apache.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-contribution Contributed by the community core Issues that should be addressed in Ray Core go add ONLY when ready to merge, run all tests unstale A PR that has been marked unstale. It will not get marked stale again if this label is on it.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants