Skip to content

Commit 271c848

Browse files
authored
Merge pull request #2279 from murgatroid99/grpc-js-xds_failure_mode_behavior
grpc-js-xds: Update failure mode behavior
2 parents 8bee12e + 641ed45 commit 271c848

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

packages/grpc-js-xds/src/xds-client.ts

+34-8
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
import * as protoLoader from '@grpc/proto-loader';
1919
// This is a non-public, unstable API, but it's very convenient
2020
import { loadProtosWithOptionsSync } from '@grpc/proto-loader/build/src/util';
21-
import { loadPackageDefinition, StatusObject, status, logVerbosity, Metadata, experimental, ChannelOptions, ClientDuplexStream, ServiceError, ChannelCredentials, Channel } from '@grpc/grpc-js';
21+
import { loadPackageDefinition, StatusObject, status, logVerbosity, Metadata, experimental, ChannelOptions, ClientDuplexStream, ServiceError, ChannelCredentials, Channel, connectivityState } from '@grpc/grpc-js';
2222
import * as adsTypes from './generated/ads';
2323
import * as lrsTypes from './generated/lrs';
2424
import { loadBootstrapInfo } from './xds-bootstrap';
@@ -255,6 +255,7 @@ export class XdsClient {
255255
DiscoveryRequest,
256256
DiscoveryResponse__Output
257257
> | null = null;
258+
private receivedAdsResponseOnCurrentStream = false;
258259

259260
private lrsNode: Node | null = null;
260261
private lrsClient: LoadReportingServiceClient | null = null;
@@ -373,6 +374,9 @@ export class XdsClient {
373374
{channelOverride: channel}
374375
);
375376
this.maybeStartAdsStream();
377+
channel.watchConnectivityState(channel.getConnectivityState(false), Infinity, () => {
378+
this.handleAdsConnectivityStateUpdate();
379+
})
376380

377381
this.lrsClient = new protoDefinitions.envoy.service.load_stats.v3.LoadReportingService(
378382
serverUri,
@@ -394,7 +398,29 @@ export class XdsClient {
394398
clearInterval(this.statsTimer);
395399
}
396400

401+
private handleAdsConnectivityStateUpdate() {
402+
if (!this.adsClient) {
403+
return;
404+
}
405+
const state = this.adsClient.getChannel().getConnectivityState(false);
406+
if (state === connectivityState.READY && this.adsCall) {
407+
this.reportAdsStreamStarted();
408+
}
409+
if (state === connectivityState.TRANSIENT_FAILURE) {
410+
this.reportStreamError({
411+
code: status.UNAVAILABLE,
412+
details: 'No connection established to xDS server',
413+
metadata: new Metadata()
414+
});
415+
}
416+
this.adsClient.getChannel().watchConnectivityState(state, Infinity, () => {
417+
this.handleAdsConnectivityStateUpdate();
418+
});
419+
}
420+
397421
private handleAdsResponse(message: DiscoveryResponse__Output) {
422+
this.receivedAdsResponseOnCurrentStream = true;
423+
this.adsBackoff.reset();
398424
let handleResponseResult: {
399425
result: HandleResponseResult;
400426
serviceKind: AdsServiceKind;
@@ -466,7 +492,7 @@ export class XdsClient {
466492
'ADS stream ended. code=' + streamStatus.code + ' details= ' + streamStatus.details
467493
);
468494
this.adsCall = null;
469-
if (streamStatus.code !== status.OK) {
495+
if (streamStatus.code !== status.OK && !this.receivedAdsResponseOnCurrentStream) {
470496
this.reportStreamError(streamStatus);
471497
}
472498
/* If the backoff timer is no longer running, we do not need to wait any
@@ -496,7 +522,9 @@ export class XdsClient {
496522
if (this.adsCall !== null) {
497523
return;
498524
}
499-
this.adsCall = this.adsClient.StreamAggregatedResources();
525+
this.receivedAdsResponseOnCurrentStream = false;
526+
const metadata = new Metadata({waitForReady: true});
527+
this.adsCall = this.adsClient.StreamAggregatedResources(metadata);
500528
this.adsCall.on('data', (message: DiscoveryResponse__Output) => {
501529
this.handleAdsResponse(message);
502530
});
@@ -515,7 +543,9 @@ export class XdsClient {
515543
this.updateNames(service);
516544
}
517545
}
518-
this.reportAdsStreamStarted();
546+
if (this.adsClient.getChannel().getConnectivityState(false) === connectivityState.READY) {
547+
this.reportAdsStreamStarted();
548+
}
519549
}
520550

521551
private maybeSendAdsMessage(typeUrl: string, resourceNames: string[], responseNonce: string, versionInfo: string, errorMessage?: string) {
@@ -547,10 +577,6 @@ export class XdsClient {
547577
* version info are updated so that it sends the post-update values.
548578
*/
549579
ack(serviceKind: AdsServiceKind) {
550-
/* An ack is the best indication of a successful interaction between the
551-
* client and the server, so we can reset the backoff timer here. */
552-
this.adsBackoff.reset();
553-
554580
this.updateNames(serviceKind);
555581
}
556582

packages/grpc-js-xds/src/xds-stream-state/xds-stream-state.ts

+3
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,9 @@ export abstract class BaseXdsStreamState<ResponseType> implements XdsStreamState
213213
}
214214

215215
reportAdsStreamStart() {
216+
if (this.isAdsStreamRunning) {
217+
return;
218+
}
216219
this.isAdsStreamRunning = true;
217220
for (const subscriptionEntry of this.subscriptions.values()) {
218221
if (subscriptionEntry.cachedResponse === null) {

0 commit comments

Comments
 (0)