fix: Place only READY SubConns on the hashring so RPCs are not queued behind a connecting or failed backend - #4
Draft
miparnisari wants to merge 6 commits into
Draft
fix: Place only READY SubConns on the hashring so RPCs are not queued behind a connecting or failed backend#4miparnisari wants to merge 6 commits into
miparnisari wants to merge 6 commits into
Conversation
… behind a connecting or failed backend
miparnisari
force-pushed
the
readiness-aware-hashring
branch
from
September 4, 2026 02:33
953a1d8 to
5563000
Compare
jzelinskie
reviewed
Sep 4, 2026
Comment on lines
+475
to
+481
| if errors.Is(err, hashring.ErrNotEnoughMembers) { | ||
| // Fewer ready backends than the configured spread: use those that are. | ||
| members, err = p.hashring.FindN(key, 1) | ||
| } | ||
| if errors.Is(err, hashring.ErrNotEnoughMembers) { | ||
| return balancer.PickResult{}, balancer.ErrNoSubConnAvailable | ||
| } |
Member
There was a problem hiding this comment.
Is this intentional? If so shouldn't it be nested?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Disclaimer: written by AI 🙃
Problem
The picker returns whichever SubConn the request key hashes to, without checking its connectivity state. If that backend is CONNECTING or in TRANSIENT_FAILURE, gRPC parks the RPC until the SubConn becomes READY or the connect attempt times out (20s by default). The ring only changes when the resolver adds or removes addresses, so until that happens every request hashed to a dead backend hangs.
How it was observed
Killing one SpiceDB node in a two-node cluster (SIGINT or SIGTERM) made the surviving node stop answering any request that dispatched to the dead peer. Requests with a 10s deadline failed with
DeadlineExceededat exactly +10s and +20s; once the dial timed out, everything failed fast withUnavailable. The killed node itself drained cleanly. In Kubernetes this shows up as a burst ofDeadlineExceededandUnavailableon the remaining replicas during every rolling deploy, lasting until the endpoint list drops the pod IP.Fix
Ring membership now follows SubConn state: a backend is placed on the ring when its connection becomes READY and removed when it leaves READY. Keys whose closest backend is down are served by the next closest ready backend, which is safe because any node can serve any request. When nothing is ready, RPCs queue until a backend comes up, or fail fast once the balancer reports TRANSIENT_FAILURE.
Two new tests cover this. A unit test flips one backend to TRANSIENT_FAILURE and checks the pick moves to the ready one. An end-to-end test runs a real gRPC server next to a TCP listener that never completes the HTTP/2 handshake and asserts an RPC hashed to it is answered promptly instead of hitting its deadline. Both failed before the change.