grpc-js: Add support for grpc.service_config_disable_resolution

This commit is contained in:
Nick Kleinschmidt 2022-12-17 15:19:32 -07:00
parent c9f8f93a74
commit 677c009385
4 changed files with 67 additions and 1 deletions

View File

@ -62,6 +62,7 @@ Many channel arguments supported in `grpc` are not supported in `@grpc/grpc-js`.
- `grpc.enable_retries` - `grpc.enable_retries`
- `grpc.per_rpc_retry_buffer_size` - `grpc.per_rpc_retry_buffer_size`
- `grpc.retry_buffer_size` - `grpc.retry_buffer_size`
- `grpc.service_config_disable_resolution`
- `grpc-node.max_session_memory` - `grpc-node.max_session_memory`
- `channelOverride` - `channelOverride`
- `channelFactoryOverride` - `channelFactoryOverride`

View File

@ -55,6 +55,7 @@ export interface ChannelOptions {
'grpc.max_connection_age_ms'?: number; 'grpc.max_connection_age_ms'?: number;
'grpc.max_connection_age_grace_ms'?: number; 'grpc.max_connection_age_grace_ms'?: number;
'grpc-node.max_session_memory'?: number; 'grpc-node.max_session_memory'?: number;
'grpc.service_config_disable_resolution'?: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
[key: string]: any; [key: string]: any;
} }
@ -87,6 +88,7 @@ export const recognizedOptions = {
'grpc.max_connection_age_ms': true, 'grpc.max_connection_age_ms': true,
'grpc.max_connection_age_grace_ms': true, 'grpc.max_connection_age_grace_ms': true,
'grpc-node.max_session_memory': true, 'grpc-node.max_session_memory': true,
'grpc.service_config_disable_resolution': true,
}; };
export function channelOptionsEqual( export function channelOptionsEqual(

View File

@ -98,6 +98,7 @@ class DnsResolver implements Resolver {
private continueResolving = false; private continueResolving = false;
private nextResolutionTimer: NodeJS.Timer; private nextResolutionTimer: NodeJS.Timer;
private isNextResolutionTimerRunning = false; private isNextResolutionTimerRunning = false;
private isServiceConfigEnabled = true;
constructor( constructor(
private target: GrpcUri, private target: GrpcUri,
private listener: ResolverListener, private listener: ResolverListener,
@ -127,6 +128,10 @@ class DnsResolver implements Resolver {
} }
this.percentage = Math.random() * 100; this.percentage = Math.random() * 100;
if (channelOptions['grpc.service_config_disable_resolution'] === 1) {
this.isServiceConfigEnabled = false;
}
this.defaultResolutionError = { this.defaultResolutionError = {
code: Status.UNAVAILABLE, code: Status.UNAVAILABLE,
details: `Name resolution failed for target ${uriToString(this.target)}`, details: `Name resolution failed for target ${uriToString(this.target)}`,
@ -255,7 +260,7 @@ class DnsResolver implements Resolver {
); );
/* If there already is a still-pending TXT resolution, we can just use /* If there already is a still-pending TXT resolution, we can just use
* that result when it comes in */ * that result when it comes in */
if (this.pendingTxtPromise === null) { if (this.isServiceConfigEnabled && this.pendingTxtPromise === null) {
/* We handle the TXT query promise differently than the others because /* We handle the TXT query promise differently than the others because
* the name resolution attempt as a whole is a success even if the TXT * the name resolution attempt as a whole is a success even if the TXT
* lookup fails */ * lookup fails */

View File

@ -207,6 +207,64 @@ describe('Name Resolver', () => {
const resolver = resolverManager.createResolver(target, listener, {}); const resolver = resolverManager.createResolver(target, listener, {});
resolver.updateResolution(); resolver.updateResolution();
}); });
// Created DNS TXT record using TXT sample from https://github.com/grpc/proposal/blob/master/A2-service-configs-in-dns.md
// "grpc_config=[{\"serviceConfig\":{\"loadBalancingPolicy\":\"round_robin\",\"methodConfig\":[{\"name\":[{\"service\":\"MyService\",\"method\":\"Foo\"}],\"waitForReady\":true}]}}]"
it.skip('Should resolve a name with TXT service config', done => {
const target = resolverManager.mapUriDefaultScheme(parseUri('grpctest.kleinsch.com')!)!;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
if (serviceConfig !== null) {
assert(
serviceConfig.loadBalancingPolicy === 'round_robin',
'Should have found round robin LB policy'
);
done();
}
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {});
resolver.updateResolution();
});
it.skip(
'Should not resolve TXT service config if we disabled service config',
(done) => {
const target = resolverManager.mapUriDefaultScheme(
parseUri('grpctest.kleinsch.com')!
)!;
let count = 0;
const listener: resolverManager.ResolverListener = {
onSuccessfulResolution: (
addressList: SubchannelAddress[],
serviceConfig: ServiceConfig | null,
serviceConfigError: StatusObject | null
) => {
assert(
serviceConfig === null,
'Should not have found service config'
);
count++;
},
onError: (error: StatusObject) => {
done(new Error(`Failed with status ${error.details}`));
},
};
const resolver = resolverManager.createResolver(target, listener, {
'grpc.service_config_disable_resolution': 1,
});
resolver.updateResolution();
setTimeout(() => {
assert(count === 1, 'Should have only resolved once');
done();
}, 2_000);
}
);
/* The DNS entry for loopback4.unittest.grpc.io only has a single A record /* The DNS entry for loopback4.unittest.grpc.io only has a single A record
* with the address 127.0.0.1, but the Mac DNS resolver appears to use * with the address 127.0.0.1, but the Mac DNS resolver appears to use
* NAT64 to create an IPv6 address in that case, so it instead returns * NAT64 to create an IPv6 address in that case, so it instead returns