xds: CdsLoadBalancer2 keeps stale READY picker after EDS update with no usable endpoints
Maintainer antworten meist innerhalb von 1 Tag
Bewertung
- Schwierigkeit
- 3/5
- Geschätzter Aufwand
- 1-2 Tage
- Anfängerfreundlichkeit
- 64/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- java
- Bereich
- backend, networking
Rechercherichtung
Start by reading CdsLoadBalancer2.acceptResolvedAddresses() and trace how the empty-address case updates balancing state; compare it with the existing fail() callers and the earlier ClusterResolverLoadBalancer behavior described in the issue. Add a regression test for an empty EDS update after the cluster is READY, checking that picks fail with UNAVAILABLE and that a later non-empty update recovers.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
What version of gRPC-Java are you using?
1.85.1 (the same code is still on master as of 2026-10-27).
What is your environment?
macOS / Linux, JDK 17. xDS control plane: Istio 1.25 (proxyless), also reproduced with an in-process fake ADS server.
What did you expect to see?
When a cluster that was previously READY receives an EDS update (ACKed) whose ClusterLoadAssignment has no usable endpoints, the CDS policy should go to TRANSIENT_FAILURE with an error picker (UNAVAILABLE: No usable endpoint from cluster: ...) and RPCs routed to that cluster should fail fast.
This is what gRPC-Java did before ClusterResolverLoadBalancer was merged into CdsLoadBalancer2. In 1.56.x, ClusterResolverLoadBalancer.handleEndpointResourceUpdate() did:
if (addresses.isEmpty()) {
...
helper.updateBalancingState(TRANSIENT_FAILURE, new ErrorPicker(endpointNotFound));
if (childLb != null) {
childLb.shutdown();
childLb = null;
}
return;
}
What did you see instead?
The cluster keeps its previous READY picker and keeps picking the endpoints that were removed by EDS.
In CdsLoadBalancer2.acceptResolvedAddresses(), the empty-address branch calls handleNameResolutionError on the delegate instead of failing:
if (addresses.isEmpty()) {
Status status = Status.UNAVAILABLE
.withDescription("No usable endpoint from cluster: " + clusterName);
delegate.handleNameResolutionError(status);
return status;
}
The error goes down to PriorityLoadBalancer -> ... -> MultiChildLoadBalancer.handleNameResolutionError(), which only publishes TRANSIENT_FAILURE when the current state is not READY. So a READY subtree keeps its old picker, even though the endpoint list is now authoritatively empty.
Impact: the stale picker stays in use for as long as the old subchannels stay READY. For example, an endpoint can be removed from EDS but still be alive. This happens with subset/label changes, draining, or Istio scaling a subset to 0 while the connection hasn't been torn down yet. In those cases RPCs keep going to endpoints the control plane has removed, instead of failing fast with UNAVAILABLE.
This looks like it came in with ebb9420948 ("xds: Merge ClusterResolverLB into CdsLB2").
Steps to reproduce the bug
- Configure an xDS channel with a route to an EDS cluster with a few endpoints (round_robin; also reproduced with a custom LB policy, so it is independent of the leaf policy).
- Make RPCs until the cluster is READY.
- From the control plane, push an empty
ClusterLoadAssignmentfor that cluster (cluster_nameset, noendpoints). Confirm viaXdsClient.getSubscribedResourcesMetadataSnapshot()that it is ACKED with 0 endpoint groups. - Make more RPCs to that cluster.
Result: 20/20 picks still return the old endpoints (same result whether or not the EDS version changes).
With this one-line change, all picks fail fast with UNAVAILABLE, other clusters are unaffected, and a later non-empty EDS update recovers normally:
if (addresses.isEmpty()) {
Status status = Status.UNAVAILABLE
.withDescription("No usable endpoint from cluster: " + clusterName);
- delegate.handleNameResolutionError(status);
- return status;
+ return fail(status);
}
(fail() shuts down the delegate, which tears down the priority children, and publishes TF + an error picker. On the next non-empty update the same PriorityLoadBalancer instance rebuilds its children, which is the same recovery path the existing fail() callers already rely on. RPCs already in flight are not affected, because subchannel shutdown is graceful.)
Ambient errors don't reach this branch, since XdsDependencyManager keeps them separate from the data. So this change only affects the authoritative "no usable endpoints" case.
Happy to send a PR with a test if this approach looks right.
- Vorherrschende Sprache
- Java
- Sterne
- 12.1k
- Forks
- 4k
- Ø Merge
- 2 T. 1 Std.
- Gemergte PRs (30 T.)
- 48
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Keine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus grpc/grpc-java
-
docs enhancement
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
grpc/grpc-java#10824 · 8 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
question
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 25/100
grpc/grpc-java#13129 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
binder: `ServerStream.close()` marks the stream closed before the suffix is sent, so a flow-control-deferred send failure leaks the callEvtl. vergeben @kkmurthyt21 hat das vor 16 Tagen übernommen. Offenbug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 45/100
grpc/grpc-java#13097 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
binder: RuntimeException during outbound serialization leaks the call and never notifies the peerEvtl. vergeben @mvanhorn hat das vor 21 Tagen übernommen. Offenbug
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 48/100
grpc/grpc-java#13093 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Interest check: Peak EWMA (latency-aware P2C) LB policyEvtl. vergeben @markdroth hat das vor 17 Tagen übernommen. Offenenhancement Type: Feature
Schwierigkeit 5/5 Über eine Woche Anfängerfreundlichkeit 35/100
grpc/grpc-java#13092 · 2 Kommentare · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
Ähnliche Issues
-
bug/错误
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
FCL-Team/FoldCraftLauncher#1982 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
[BUG] micronaut-serde-oracle-jdbc-json brings a second OJDBC driver onto the runtime classpathEvtl. vergeben @ZARAYACH hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 72/100
micronaut-projects/micronaut-serialization#1494 · 4 Kommentare ·
Maintainer antworten meist innerhalb von 2 Tagen
-
bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 73/100
Maintainer antworten meist innerhalb von 2 Tagen
-
bug
Schwierigkeit 1/5 Unter einer Stunde Anfängerfreundlichkeit 85/100
vitruv-tools/Vitruv-Change#600 ·
Maintainer antworten meist innerhalb von 1 Tag
-
code-health feature/sync OS/Android QA/Test-Plan-Specified QA/Yes
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 75/100
brave/brave-browser#60327 ·
Maintainer antworten meist innerhalb von 1 Tag