diff --git a/src/v1.test.ts b/src/v1.test.ts index dd6d2d2..72fcc01 100644 --- a/src/v1.test.ts +++ b/src/v1.test.ts @@ -859,3 +859,38 @@ describe("createStructFromObject unit tests", () => { ); }); }); + +describe("gRPC retry default (#200)", () => { + // Capture the channel options the client actually applies, via grpc-js's + // channelFactoryOverride hook (called with the final merged options). + function capturedChannelOptions(extra?: grpc.ClientOptions): grpc.ChannelOptions { + let captured: grpc.ChannelOptions = {}; + const client = NewClient( + "tok", + "localhost:50051", + ClientSecurity.INSECURE_LOCALHOST_ALLOWED, + PreconnectServices.PERMISSIONS_SERVICE, + { + ...extra, + channelFactoryOverride: ( + address: string, + creds: grpc.ChannelCredentials, + options: grpc.ChannelOptions, + ) => { + captured = options; + return new grpc.Channel(address, creds, options); + }, + } as grpc.ClientOptions, + ); + client.close(); + return captured; + } + + it("disables retries by default", () => { + expect(capturedChannelOptions()["grpc.enable_retries"]).toBe(0); + }); + + it("lets the caller re-enable retries", () => { + expect(capturedChannelOptions({ "grpc.enable_retries": 1 })["grpc.enable_retries"]).toBe(1); + }); +}); diff --git a/src/v1.ts b/src/v1.ts index 3fe76f8..7454a7f 100644 --- a/src/v1.ts +++ b/src/v1.ts @@ -74,6 +74,10 @@ class ZedClient implements ProxyHandler { options: grpc.ClientOptions | undefined, ) { this.options = { + // Default to disabling gRPC retries to avoid unrecoverable failures + // through envoy/istio (see #112). Placed before ...options so callers can + // re-enable retries by passing "grpc.enable_retries" explicitly. + "grpc.enable_retries": 0, ...options, interceptors: [ ...(options?.interceptors ?? []), @@ -85,26 +89,30 @@ class ZedClient implements ProxyHandler { }; if (preconnect & PreconnectServices.PERMISSIONS_SERVICE) { - this.acl = new PermissionsServiceClient(this.endpoint, this.creds, options); + this.acl = new PermissionsServiceClient(this.endpoint, this.creds, this.options); } if (preconnect & PreconnectServices.SCHEMA_SERVICE) { - this.ns = new SchemaServiceClient(this.endpoint, this.creds, options); + this.ns = new SchemaServiceClient(this.endpoint, this.creds, this.options); } if (preconnect & PreconnectServices.WATCH_SERVICE) { - this.watch = new WatchServiceClient(this.endpoint, this.creds, options); + this.watch = new WatchServiceClient(this.endpoint, this.creds, this.options); } if (preconnect & PreconnectServices.WATCH_PERMISSIONS_SERVICE) { - this.watchPermissions = new WatchPermissionsServiceClient(this.endpoint, this.creds, options); + this.watchPermissions = new WatchPermissionsServiceClient( + this.endpoint, + this.creds, + this.options, + ); } if (preconnect & PreconnectServices.WATCH_PERMISSIONSETS_SERVICE) { this.watchPermissionSets = new WatchPermissionSetsServiceClient( this.endpoint, this.creds, - options, + this.options, ); } if (preconnect & PreconnectServices.EXPERIMENTAL_SERVICE) { - this.experimental = new ExperimentalServiceClient(this.endpoint, this.creds, options); + this.experimental = new ExperimentalServiceClient(this.endpoint, this.creds, this.options); } }