mirror of https://github.com/grpc/grpc-node.git
grpc-js: Add a channel option to configure retry attempt limits
This commit is contained in:
parent
f867643d8e
commit
8ee8e99d99
|
@ -63,6 +63,7 @@ export interface ChannelOptions {
|
|||
*/
|
||||
'grpc-node.tls_enable_trace'?: number;
|
||||
'grpc.lb.ring_hash.ring_size_cap'?: number;
|
||||
'grpc-node.retry_max_attempts_limit'?: number;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
[key: string]: any;
|
||||
}
|
||||
|
@ -99,6 +100,7 @@ export const recognizedOptions = {
|
|||
'grpc.client_idle_timeout_ms': true,
|
||||
'grpc-node.tls_enable_trace': true,
|
||||
'grpc.lb.ring_hash.ring_size_cap': true,
|
||||
'grpc-node.retry_max_attempts_limit': true,
|
||||
};
|
||||
|
||||
export function channelOptionsEqual(
|
||||
|
|
|
@ -842,4 +842,8 @@ export class InternalChannel {
|
|||
propagateFlags
|
||||
);
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this.options;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -172,6 +172,8 @@ interface WriteBufferEntry {
|
|||
|
||||
const PREVIONS_RPC_ATTEMPTS_METADATA_KEY = 'grpc-previous-rpc-attempts';
|
||||
|
||||
const DEFAULT_MAX_ATTEMPTS_LIMIT = 5;
|
||||
|
||||
export class RetryingCall implements Call, DeadlineInfoProvider {
|
||||
private state: RetryingCallState;
|
||||
private listener: InterceptingListener | null = null;
|
||||
|
@ -201,6 +203,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
private initialRetryBackoffSec = 0;
|
||||
private nextRetryBackoffSec = 0;
|
||||
private startTime: Date;
|
||||
private maxAttempts: number;
|
||||
constructor(
|
||||
private readonly channel: InternalChannel,
|
||||
private readonly callConfig: CallConfig,
|
||||
|
@ -212,6 +215,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
private readonly bufferTracker: MessageBufferTracker,
|
||||
private readonly retryThrottler?: RetryThrottler
|
||||
) {
|
||||
const maxAttemptsLimit = channel.getOptions()['grpc-node.retry_max_attempts_limit'] ?? DEFAULT_MAX_ATTEMPTS_LIMIT;
|
||||
if (callConfig.methodConfig.retryPolicy) {
|
||||
this.state = 'RETRY';
|
||||
const retryPolicy = callConfig.methodConfig.retryPolicy;
|
||||
|
@ -221,10 +225,13 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
retryPolicy.initialBackoff.length - 1
|
||||
)
|
||||
);
|
||||
this.maxAttempts = Math.min(retryPolicy.maxAttempts, maxAttemptsLimit);
|
||||
} else if (callConfig.methodConfig.hedgingPolicy) {
|
||||
this.state = 'HEDGING';
|
||||
this.maxAttempts = Math.min(callConfig.methodConfig.hedgingPolicy.maxAttempts, maxAttemptsLimit);
|
||||
} else {
|
||||
this.state = 'TRANSPARENT_ONLY';
|
||||
this.maxAttempts = 1;
|
||||
}
|
||||
this.startTime = new Date();
|
||||
}
|
||||
|
@ -419,8 +426,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
callback(false);
|
||||
return;
|
||||
}
|
||||
const retryPolicy = this.callConfig!.methodConfig.retryPolicy!;
|
||||
if (this.attempts >= Math.min(retryPolicy.maxAttempts, 5)) {
|
||||
if (this.attempts >= this.maxAttempts) {
|
||||
callback(false);
|
||||
return;
|
||||
}
|
||||
|
@ -596,8 +602,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
if (!this.callConfig.methodConfig.hedgingPolicy) {
|
||||
return;
|
||||
}
|
||||
const hedgingPolicy = this.callConfig.methodConfig.hedgingPolicy;
|
||||
if (this.attempts >= Math.min(hedgingPolicy.maxAttempts, 5)) {
|
||||
if (this.attempts >= this.maxAttempts) {
|
||||
return;
|
||||
}
|
||||
this.attempts += 1;
|
||||
|
@ -616,7 +621,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
|
|||
return;
|
||||
}
|
||||
const hedgingPolicy = this.callConfig.methodConfig.hedgingPolicy;
|
||||
if (this.attempts >= Math.min(hedgingPolicy.maxAttempts, 5)) {
|
||||
if (this.attempts >= this.maxAttempts) {
|
||||
return;
|
||||
}
|
||||
const hedgingDelayString = hedgingPolicy.hedgingDelay ?? '0s';
|
||||
|
|
|
@ -281,6 +281,48 @@ describe('Retries', () => {
|
|||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('Should be able to make more than 5 attempts with a channel argument', done => {
|
||||
const serviceConfig = {
|
||||
loadBalancingConfig: [],
|
||||
methodConfig: [
|
||||
{
|
||||
name: [
|
||||
{
|
||||
service: 'EchoService',
|
||||
},
|
||||
],
|
||||
retryPolicy: {
|
||||
maxAttempts: 10,
|
||||
initialBackoff: '0.1s',
|
||||
maxBackoff: '10s',
|
||||
backoffMultiplier: 1.2,
|
||||
retryableStatusCodes: [14, 'RESOURCE_EXHAUSTED'],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
const client2 = new EchoService(
|
||||
`localhost:${port}`,
|
||||
grpc.credentials.createInsecure(),
|
||||
{
|
||||
'grpc.service_config': JSON.stringify(serviceConfig),
|
||||
'grpc-node.retry_max_attempts_limit': 8
|
||||
}
|
||||
);
|
||||
const metadata = new grpc.Metadata();
|
||||
metadata.set('succeed-on-retry-attempt', '7');
|
||||
metadata.set('respond-with-status', `${grpc.status.RESOURCE_EXHAUSTED}`);
|
||||
client2.echo(
|
||||
{ value: 'test value', value2: 3 },
|
||||
metadata,
|
||||
(error: grpc.ServiceError, response: any) => {
|
||||
assert.ifError(error);
|
||||
assert.deepStrictEqual(response, { value: 'test value', value2: 3 });
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Client with hedging configured', () => {
|
||||
|
|
Loading…
Reference in New Issue