Skip to content

Commit 46ed02e

Browse files
authored
xds:Update logic to match A57 (#9745)
* xds:Change timer creation logic to wait until the adsStream is ready before creating the timer to mark resources absent. * xds:When the ads stream is closed only send errors to subscribers that haven't yet gotten results to match spec. * Use a blocking queue to avoid the 2-second sleep. For some inexplicable reason the following call.verifyRequest fails only for the V2 test and only from command line not IDE unless there is some Thread.sleep, even if it is only 1-millis.
1 parent ccb5d94 commit 46ed02e

File tree

4 files changed

+169
-31
lines changed

4 files changed

+169
-31
lines changed

xds/src/main/java/io/grpc/xds/AbstractXdsClient.java

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import io.grpc.SynchronizationContext;
3737
import io.grpc.SynchronizationContext.ScheduledHandle;
3838
import io.grpc.internal.BackoffPolicy;
39+
import io.grpc.stub.ClientCallStreamObserver;
40+
import io.grpc.stub.ClientResponseObserver;
3941
import io.grpc.stub.StreamObserver;
4042
import io.grpc.xds.Bootstrapper.ServerInfo;
4143
import io.grpc.xds.EnvoyProtoData.Node;
@@ -71,6 +73,7 @@ final class AbstractXdsClient {
7173
private final BackoffPolicy.Provider backoffPolicyProvider;
7274
private final Stopwatch stopwatch;
7375
private final Node bootstrapNode;
76+
private final XdsClient.TimerLaunch timerLaunch;
7477

7578
// Last successfully applied version_info for each resource type. Starts with empty string.
7679
// A version_info is used to update management server with client's most recent knowledge of
@@ -98,7 +101,8 @@ final class AbstractXdsClient {
98101
timeService,
99102
SynchronizationContext syncContext,
100103
BackoffPolicy.Provider backoffPolicyProvider,
101-
Supplier<Stopwatch> stopwatchSupplier) {
104+
Supplier<Stopwatch> stopwatchSupplier,
105+
XdsClient.TimerLaunch timerLaunch) {
102106
this.serverInfo = checkNotNull(serverInfo, "serverInfo");
103107
this.channel = checkNotNull(xdsChannelFactory, "xdsChannelFactory").create(serverInfo);
104108
this.xdsResponseHandler = checkNotNull(xdsResponseHandler, "xdsResponseHandler");
@@ -108,6 +112,7 @@ final class AbstractXdsClient {
108112
this.timeService = checkNotNull(timeService, "timeService");
109113
this.syncContext = checkNotNull(syncContext, "syncContext");
110114
this.backoffPolicyProvider = checkNotNull(backoffPolicyProvider, "backoffPolicyProvider");
115+
this.timerLaunch = checkNotNull(timerLaunch, "timerLaunch");
111116
stopwatch = checkNotNull(stopwatchSupplier, "stopwatchSupplier").get();
112117
logId = InternalLogId.allocate("xds-client", serverInfo.target());
113118
logger = XdsLogger.withLogId(logId);
@@ -199,6 +204,22 @@ boolean isInBackoff() {
199204
return rpcRetryTimer != null && rpcRetryTimer.isPending();
200205
}
201206

207+
boolean isReady() {
208+
return adsStream != null && adsStream.isReady();
209+
}
210+
211+
/**
212+
* Starts a timer for each requested resource that hasn't been responded to and
213+
* has been waiting for the channel to get ready.
214+
*/
215+
void readyHandler() {
216+
if (!isReady()) {
217+
return;
218+
}
219+
220+
timerLaunch.startSubscriberTimersIfNeeded(serverInfo);
221+
}
222+
202223
/**
203224
* Establishes the RPC connection by creating a new RPC stream on the given channel for
204225
* xDS protocol communication.
@@ -262,6 +283,8 @@ private abstract class AbstractAdsStream {
262283

263284
abstract void sendError(Exception error);
264285

286+
abstract boolean isReady();
287+
265288
/**
266289
* Sends a discovery request with the given {@code versionInfo}, {@code nonce} and
267290
* {@code errorDetail}. Used for reacting to a specific discovery response. For
@@ -344,13 +367,26 @@ private void cleanUp() {
344367
private final class AdsStreamV2 extends AbstractAdsStream {
345368
private StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryRequest> requestWriter;
346369

370+
@Override
371+
public boolean isReady() {
372+
return requestWriter != null && ((ClientCallStreamObserver<?>) requestWriter).isReady();
373+
}
374+
347375
@Override
348376
void start() {
349377
io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc
350378
.AggregatedDiscoveryServiceStub stub =
351379
io.envoyproxy.envoy.service.discovery.v2.AggregatedDiscoveryServiceGrpc.newStub(channel);
352380
StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryResponse> responseReaderV2 =
353-
new StreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryResponse>() {
381+
new ClientResponseObserver<io.envoyproxy.envoy.api.v2.DiscoveryRequest,
382+
io.envoyproxy.envoy.api.v2.DiscoveryResponse>() {
383+
384+
@Override
385+
public void beforeStart(
386+
ClientCallStreamObserver<io.envoyproxy.envoy.api.v2.DiscoveryRequest> reqStream) {
387+
reqStream.setOnReadyHandler(AbstractXdsClient.this::readyHandler);
388+
}
389+
354390
@Override
355391
public void onNext(final io.envoyproxy.envoy.api.v2.DiscoveryResponse response) {
356392
syncContext.execute(new Runnable() {
@@ -427,11 +463,23 @@ void sendError(Exception error) {
427463
private final class AdsStreamV3 extends AbstractAdsStream {
428464
private StreamObserver<DiscoveryRequest> requestWriter;
429465

466+
@Override
467+
public boolean isReady() {
468+
return requestWriter != null && ((ClientCallStreamObserver<?>) requestWriter).isReady();
469+
}
470+
430471
@Override
431472
void start() {
432473
AggregatedDiscoveryServiceGrpc.AggregatedDiscoveryServiceStub stub =
433474
AggregatedDiscoveryServiceGrpc.newStub(channel);
434-
StreamObserver<DiscoveryResponse> responseReader = new StreamObserver<DiscoveryResponse>() {
475+
StreamObserver<DiscoveryResponse> responseReader =
476+
new ClientResponseObserver<DiscoveryRequest,DiscoveryResponse>() {
477+
478+
@Override
479+
public void beforeStart(ClientCallStreamObserver<DiscoveryRequest> requestStream) {
480+
requestStream.setOnReadyHandler(AbstractXdsClient.this::readyHandler);
481+
}
482+
435483
@Override
436484
public void onNext(final DiscoveryResponse response) {
437485
syncContext.execute(new Runnable() {

xds/src/main/java/io/grpc/xds/XdsClient.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,4 +373,12 @@ Collection<String> getSubscribedResources(ServerInfo serverInfo,
373373

374374
Map<String, XdsResourceType<?>> getSubscribedResourceTypesWithTypeUrl();
375375
}
376+
377+
interface TimerLaunch {
378+
/**
379+
* For all subscriber's for the specified server, if the resource hasn't yet been
380+
* resolved then start a timer for it.
381+
*/
382+
void startSubscriberTimersIfNeeded(ServerInfo serverInfo);
383+
}
376384
}

xds/src/main/java/io/grpc/xds/XdsClientImpl.java

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import io.grpc.xds.LoadStatsManager2.ClusterDropStats;
4848
import io.grpc.xds.LoadStatsManager2.ClusterLocalityStats;
4949
import io.grpc.xds.XdsClient.ResourceStore;
50+
import io.grpc.xds.XdsClient.TimerLaunch;
5051
import io.grpc.xds.XdsClient.XdsResponseHandler;
5152
import io.grpc.xds.XdsLogger.XdsLogLevel;
5253
import java.net.URI;
@@ -65,9 +66,10 @@
6566
import javax.annotation.Nullable;
6667

6768
/**
68-
* XdsClient implementation for client side usages.
69+
* XdsClient implementation.
6970
*/
70-
final class XdsClientImpl extends XdsClient implements XdsResponseHandler, ResourceStore {
71+
final class XdsClientImpl extends XdsClient
72+
implements XdsResponseHandler, ResourceStore, TimerLaunch {
7173

7274
private static boolean LOG_XDS_NODE_ID = Boolean.parseBoolean(
7375
System.getenv("GRPC_LOG_XDS_NODE_ID"));
@@ -152,7 +154,8 @@ private void maybeCreateXdsChannelWithLrs(ServerInfo serverInfo) {
152154
timeService,
153155
syncContext,
154156
backoffPolicyProvider,
155-
stopwatchSupplier);
157+
stopwatchSupplier,
158+
this);
156159
LoadReportClient lrsClient = new LoadReportClient(
157160
loadStatsManager, xdsChannel.channel(), context, serverInfo.useProtocolV3(),
158161
bootstrapInfo.node(), syncContext, timeService, backoffPolicyProvider, stopwatchSupplier);
@@ -188,7 +191,9 @@ public void handleStreamClosed(Status error) {
188191
for (Map<String, ResourceSubscriber<? extends ResourceUpdate>> subscriberMap :
189192
resourceSubscribers.values()) {
190193
for (ResourceSubscriber<? extends ResourceUpdate> subscriber : subscriberMap.values()) {
191-
subscriber.onError(error);
194+
if (!subscriber.hasResult()) {
195+
subscriber.onError(error);
196+
}
192197
}
193198
}
194199
}
@@ -386,6 +391,30 @@ public String toString() {
386391
return logId.toString();
387392
}
388393

394+
@Override
395+
public void startSubscriberTimersIfNeeded(ServerInfo serverInfo) {
396+
if (isShutDown()) {
397+
return;
398+
}
399+
400+
syncContext.execute(new Runnable() {
401+
@Override
402+
public void run() {
403+
if (isShutDown()) {
404+
return;
405+
}
406+
407+
for (Map<String, ResourceSubscriber<?>> subscriberMap : resourceSubscribers.values()) {
408+
for (ResourceSubscriber<?> subscriber : subscriberMap.values()) {
409+
if (subscriber.serverInfo.equals(serverInfo) && subscriber.respTimer == null) {
410+
subscriber.restartTimer();
411+
}
412+
}
413+
}
414+
}
415+
});
416+
}
417+
389418
private void cleanUpResourceTimers() {
390419
for (Map<String, ResourceSubscriber<?>> subscriberMap : resourceSubscribers.values()) {
391420
for (ResourceSubscriber<?> subscriber : subscriberMap.values()) {
@@ -537,6 +566,10 @@ void restartTimer() {
537566
if (data != null || absent) { // resource already resolved
538567
return;
539568
}
569+
if (!xdsChannel.isReady()) { // When channel becomes ready, it will trigger a restartTimer
570+
return;
571+
}
572+
540573
class ResourceNotFound implements Runnable {
541574
@Override
542575
public void run() {
@@ -554,6 +587,7 @@ public String toString() {
554587

555588
// Initial fetch scheduled or rescheduled, transition metadata state to REQUESTED.
556589
metadata = ResourceMetadata.newResourceMetadataRequested();
590+
557591
respTimer = syncContext.schedule(
558592
new ResourceNotFound(), INITIAL_RESOURCE_FETCH_TIMEOUT_SEC, TimeUnit.SECONDS,
559593
timeService);
@@ -585,6 +619,10 @@ boolean isWatched() {
585619
return !watchers.isEmpty();
586620
}
587621

622+
boolean hasResult() {
623+
return data != null || absent;
624+
}
625+
588626
void onData(ParsedResource<T> parsedResource, String version, long updateTime) {
589627
if (respTimer != null && respTimer.isPending()) {
590628
respTimer.cancel();

0 commit comments

Comments
 (0)