Skip to content

Commit eef4b08

Browse files
authored
Merge pull request #2960 from ekscentrysytet/fix_retry_jitter
fix(grpc-js): fix jitter for client retries
2 parents 21d40b0 + 82b331d commit eef4b08

File tree

1 file changed

+33
-8
lines changed

1 file changed

+33
-8
lines changed

packages/grpc-js/src/retrying-call.ts

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,12 @@ interface UnderlyingCall {
136136
* sent
137137
* NO_RETRY: Retries are disabled. Exists to track the transition to COMMITTED
138138
*/
139-
type RetryingCallState = 'RETRY' | 'HEDGING' | 'TRANSPARENT_ONLY' | 'COMMITTED' | 'NO_RETRY';
139+
type RetryingCallState =
140+
| 'RETRY'
141+
| 'HEDGING'
142+
| 'TRANSPARENT_ONLY'
143+
| 'COMMITTED'
144+
| 'NO_RETRY';
140145

141146
/**
142147
* The different types of objects that can be stored in the write buffer, with
@@ -217,7 +222,9 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
217222
private readonly bufferTracker: MessageBufferTracker,
218223
private readonly retryThrottler?: RetryThrottler
219224
) {
220-
const maxAttemptsLimit = channel.getOptions()['grpc-node.retry_max_attempts_limit'] ?? DEFAULT_MAX_ATTEMPTS_LIMIT;
225+
const maxAttemptsLimit =
226+
channel.getOptions()['grpc-node.retry_max_attempts_limit'] ??
227+
DEFAULT_MAX_ATTEMPTS_LIMIT;
221228
if (callConfig.methodConfig.retryPolicy) {
222229
this.state = 'RETRY';
223230
const retryPolicy = callConfig.methodConfig.retryPolicy;
@@ -230,7 +237,10 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
230237
this.maxAttempts = Math.min(retryPolicy.maxAttempts, maxAttemptsLimit);
231238
} else if (callConfig.methodConfig.hedgingPolicy) {
232239
this.state = 'HEDGING';
233-
this.maxAttempts = Math.min(callConfig.methodConfig.hedgingPolicy.maxAttempts, maxAttemptsLimit);
240+
this.maxAttempts = Math.min(
241+
callConfig.methodConfig.hedgingPolicy.maxAttempts,
242+
maxAttemptsLimit
243+
);
234244
} else if (channel.getOptions()['grpc.enable_retries'] === 0) {
235245
this.state = 'NO_RETRY';
236246
this.maxAttempts = 1;
@@ -247,10 +257,17 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
247257
const deadlineInfo: string[] = [];
248258
const latestCall = this.underlyingCalls[this.underlyingCalls.length - 1];
249259
if (this.underlyingCalls.length > 1) {
250-
deadlineInfo.push(`previous attempts: ${this.underlyingCalls.length - 1}`);
260+
deadlineInfo.push(
261+
`previous attempts: ${this.underlyingCalls.length - 1}`
262+
);
251263
}
252264
if (latestCall.startTime > this.startTime) {
253-
deadlineInfo.push(`time to current attempt start: ${formatDateDifference(this.startTime, latestCall.startTime)}`);
265+
deadlineInfo.push(
266+
`time to current attempt start: ${formatDateDifference(
267+
this.startTime,
268+
latestCall.startTime
269+
)}`
270+
);
254271
}
255272
deadlineInfo.push(...latestCall.call.getDeadlineInfo());
256273
return deadlineInfo;
@@ -412,12 +429,18 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
412429
);
413430
}
414431

432+
private getNextRetryJitter() {
433+
/* Jitter of +-20% is applied: https://github.com/grpc/proposal/blob/master/A6-client-retries.md#exponential-backoff */
434+
return Math.random() * (1.2 - 0.8) + 0.8;
435+
}
436+
415437
private getNextRetryBackoffMs() {
416438
const retryPolicy = this.callConfig?.methodConfig.retryPolicy;
417439
if (!retryPolicy) {
418440
return 0;
419441
}
420-
const nextBackoffMs = Math.random() * this.nextRetryBackoffSec * 1000;
442+
const jitter = this.getNextRetryJitter();
443+
const nextBackoffMs = jitter * this.nextRetryBackoffSec * 1000;
421444
const maxBackoffSec = Number(
422445
retryPolicy.maxBackoff.substring(0, retryPolicy.maxBackoff.length - 1)
423446
);
@@ -669,7 +692,7 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
669692
state: 'ACTIVE',
670693
call: child,
671694
nextMessageToSend: 0,
672-
startTime: new Date()
695+
startTime: new Date(),
673696
});
674697
const previousAttempts = this.attempts - 1;
675698
const initialMetadata = this.initialMetadata!.clone();
@@ -862,7 +885,9 @@ export class RetryingCall implements Call, DeadlineInfoProvider {
862885
}
863886
getAuthContext(): AuthContext | null {
864887
if (this.committedCallIndex !== null) {
865-
return this.underlyingCalls[this.committedCallIndex].call.getAuthContext();
888+
return this.underlyingCalls[
889+
this.committedCallIndex
890+
].call.getAuthContext();
866891
} else {
867892
return null;
868893
}

0 commit comments

Comments
 (0)