Skip to content

Commit c3c9deb

Browse files
authored
Compute retry delay correctly when both useExponentialBackoff and retryDelay are specified (#398)
1 parent 79122f6 commit c3c9deb

File tree

3 files changed

+55
-4
lines changed

3 files changed

+55
-4
lines changed

packages/spectral/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@prismatic-io/spectral",
3-
"version": "10.16.0",
3+
"version": "10.16.1",
44
"description": "Utility library for building Prismatic connectors and code-native integrations",
55
"keywords": ["prismatic"],
66
"main": "dist/index.js",

packages/spectral/src/clients/http/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,10 @@ interface RetryConfig extends Omit<IAxiosRetryConfig, "retryDelay"> {
6161
* a retryCondition function to determine when retries should occur.
6262
*/
6363
retryAllErrors?: boolean;
64-
/** When true, double the retry delay after each attempt (e.g. 1000ms, 2000ms, 4000ms, 8000ms, etc.). */
64+
/**
65+
* When true, double the retryDelay after each attempt (e.g. 1000ms, 2000ms, 4000ms, 8000ms, etc.).
66+
* If no retryDelay is specified, defaults to axios-retry's exponentialDelay function.
67+
*/
6568
useExponentialBackoff?: boolean;
6669
}
6770

@@ -87,12 +90,15 @@ const computeRetryDelay = (
8790
useExponentialBackoff: RetryConfig["useExponentialBackoff"],
8891
): IAxiosRetryConfig["retryDelay"] => {
8992
if (useExponentialBackoff) {
90-
return exponentialDelay;
93+
return typeof retryDelay === "number"
94+
? (retryCount) => 2 ** (retryCount - 1) * retryDelay
95+
: exponentialDelay;
9196
}
97+
// retryDelay is either a number or a function
9298
return typeof retryDelay === "number" ? () => retryDelay : retryDelay;
9399
};
94100

95-
const toAxiosRetryConfig = ({
101+
export const toAxiosRetryConfig = ({
96102
retryDelay,
97103
retryAllErrors,
98104
retryCondition,
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { describe, expect } from "vitest";
2+
import { test } from "@fast-check/vitest";
3+
import type { AxiosError } from "axios";
4+
import { toAxiosRetryConfig } from ".";
5+
6+
describe("Ensure retry configuration functions work as expected", () => {
7+
test("Delay function returns fixed delay when useExponentialBackoff is false", () => {
8+
const retryDelay = 1234;
9+
const axiosConfig = toAxiosRetryConfig({
10+
retries: 3,
11+
retryDelay,
12+
useExponentialBackoff: false,
13+
});
14+
const delayFunction = axiosConfig.retryDelay;
15+
expect(delayFunction?.(3, {} as AxiosError)).toEqual(retryDelay);
16+
});
17+
test("Delay function returns retryDelay when useExponentialBackoff is true on first retry", () => {
18+
const retryDelay = 1234;
19+
const axiosConfig = toAxiosRetryConfig({ retries: 3, retryDelay, useExponentialBackoff: true });
20+
const delayFunction = axiosConfig.retryDelay;
21+
expect(delayFunction?.(1, {} as AxiosError)).toEqual(retryDelay);
22+
});
23+
test("Delay function returns retryDelay * 2 * 2 when useExponentialBackoff is true on fourth retry", () => {
24+
const retryDelay = 1234;
25+
const axiosConfig = toAxiosRetryConfig({ retries: 4, retryDelay, useExponentialBackoff: true });
26+
const delayFunction = axiosConfig.retryDelay;
27+
expect(delayFunction?.(4, {} as AxiosError)).toEqual(retryDelay * 2 ** 3);
28+
});
29+
test("Delay function returns exponential delay when useExponentialBackoff is true and retryDelay is not set", () => {
30+
const axiosConfig = toAxiosRetryConfig({ retries: 3, useExponentialBackoff: true });
31+
const delayFunction = axiosConfig.retryDelay;
32+
const delay1 = delayFunction?.(1, {} as AxiosError);
33+
const delay2 = delayFunction?.(2, {} as AxiosError);
34+
const delay3 = delayFunction?.(3, {} as AxiosError);
35+
36+
// By default, axios-retry's exponentialDelay function returns 200 (+20% random jitter) on first retry,
37+
// 400 (+20% random jitter) on second retry, 800 (+20% random jitter) on third retry, etc.
38+
expect(delay1).toBeGreaterThanOrEqual(100 * 2);
39+
expect(delay1).toBeLessThanOrEqual(100 * 2 * 1.2);
40+
expect(delay2).toBeGreaterThanOrEqual(100 * 4);
41+
expect(delay2).toBeLessThanOrEqual(100 * 4 * 1.2);
42+
expect(delay3).toBeGreaterThanOrEqual(100 * 8);
43+
expect(delay3).toBeLessThanOrEqual(100 * 8 * 1.2);
44+
});
45+
});

0 commit comments

Comments
 (0)