Skip to content

Commit

Permalink
add retry.delay option to control delay
Browse files Browse the repository at this point in the history
  • Loading branch information
rclarey committed Sep 29, 2023
1 parent 577dd4c commit 6804448
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 6 deletions.
4 changes: 2 additions & 2 deletions source/core/Ky.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,8 +234,8 @@ export class Ky {
}
}

const BACKOFF_FACTOR = 0.3;
return Math.min(this._options.retry.backoffLimit, BACKOFF_FACTOR * (2 ** (this._retryCount - 1)) * 1000);
const retryDelay = this._options.retry.delay(this._retryCount);
return Math.min(this._options.retry.backoffLimit, retryDelay);
}

return 0;
Expand Down
2 changes: 1 addition & 1 deletion source/types/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ export interface Options extends Omit<RequestInit, 'headers'> { // eslint-disabl
If `maxRetryAfter` is set to `undefined`, it will use `options.timeout`. If [`Retry-After`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After) header is greater than `maxRetryAfter`, it will cancel the request.
Delays between retries is calculated with the function `0.3 * (2 ** (retry - 1)) * 1000`, where `retry` is the attempt number (starts from 1).
By default delays between retries are calculated with the function `0.3 * (2 ** (retry - 1)) * 1000`, where `retry` is the attempt number (starts from 1), however this can be changed by passing a `delay` function.
Retries are not triggered following a timeout.
Expand Down
7 changes: 7 additions & 0 deletions source/types/retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,11 @@ export type RetryOptions = {
@default Infinity
*/
backoffLimit?: number;

/**
A function to calculate the delay between retries given `attemptCount` (starts from 1).
@default attemptCount => 0.3 * (2 ** (attemptCount - 1)) * 1000
*/
delay?: (attemptCount: number) => number;
};
1 change: 1 addition & 0 deletions source/utils/normalize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const defaultRetryOptions: Required<RetryOptions> = {
afterStatusCodes: retryAfterStatusCodes,
maxRetryAfter: Number.POSITIVE_INFINITY,
backoffLimit: Number.POSITIVE_INFINITY,
delay: attemptCount => 0.3 * (2 ** (attemptCount - 1)) * 1000,
};

export const normalizeRetryOptions = (retry: number | RetryOptions = {}): Required<RetryOptions> => {
Expand Down
48 changes: 45 additions & 3 deletions test/retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const fixture = 'fixture';
const defaultRetryCount = 2;
const retryAfterOn413 = 2;
const lastTried413access = Date.now();
// We allow the tests to take more time on CI than locally, to reduce flakiness
const allowedOffset = process.env.CI ? 1000 : 300;

test('network error', async t => {
let requestCount = 0;
Expand Down Expand Up @@ -458,9 +460,6 @@ test('respect maximum backoff', async t => {
}
});

// We allow the test to take more time on CI than locally, to reduce flakiness
const allowedOffset = process.env.CI ? 1000 : 300;

// Register observer that asserts on duration when a measurement is performed
const obs = new PerformanceObserver(items => {
const measurements = items.getEntries();
Expand Down Expand Up @@ -499,3 +498,46 @@ test('respect maximum backoff', async t => {

await server.close();
});

test('respect custom retry.delay', async t => {
const retryCount = 5;
let requestCount = 0;

const server = await createHttpTestServer();
server.get('/', (_request, response) => {
requestCount++;

if (requestCount === retryCount) {
response.end(fixture);
} else {
response.sendStatus(500);
}
});

// Register observer that asserts on duration when a measurement is performed
const obs = new PerformanceObserver(items => {
const measurements = items.getEntries();

const duration = measurements[0].duration ?? Number.NaN;
const expectedDuration = 200 + 300 + 400 + 500;

t.true(Math.abs(duration - expectedDuration) < allowedOffset, `Duration of ${duration}ms is not close to expected duration ${expectedDuration}ms`);

obs.disconnect();
});
obs.observe({entryTypes: ['measure']});

// Start measuring
performance.mark('start');
t.is(await ky(server.url, {
retry: {
limit: retryCount,
delay: n => 100 * (n + 1),
},
}).text(), fixture);
performance.mark('end');

performance.measure('linear', 'start', 'end');

await server.close();
});

0 comments on commit 6804448

Please sign in to comment.