httplease-retry
Version:
Retry filter for httplease
68 lines (48 loc) • 2.25 kB
JavaScript
const delay = require('./delay');
const assert = require('assert');
function createBackoffRetryFilter({retryTimeoutMillis, shouldRetryFn = defaultShouldRetryFn}) {
assert.ok(retryTimeoutMillis >= 0, 'retryTimeoutMillis has to be a non-negative number');
async function retryFilter(requestConfig, next) {
const startTimestamp = Date.now();
let attempts = 0;
let fallbackDelayMillis = 0;
for (;;) {
try {
return await next(requestConfig);
} catch (error) {
attempts++;
const backoffDelayMillis = getRetryDelayFromHeader(error)
|| fallbackDelayMillis;
fallbackDelayMillis = nextFallbackDelay(fallbackDelayMillis, retryTimeoutMillis);
throwIfNotRetryable(error, backoffDelayMillis);
await delay(backoffDelayMillis);
}
}
function throwIfNotRetryable(error, backoffDelayMillis) {
const nextCallTimestamp = Date.now() + backoffDelayMillis;
const timeoutTimestamp = startTimestamp + retryTimeoutMillis;
const exceededMaxResponseTime = nextCallTimestamp >= timeoutTimestamp;
if (exceededMaxResponseTime || !shouldRetryFn({error, attempts, requestConfig, startTimestamp})) {
error.totalFailedAttempts = attempts;
error.totalWaitingTime = Date.now() - startTimestamp;
throw error;
}
}
}
return retryFilter;
}
function nextFallbackDelay(fallbackDelayMillis, retryTimeoutMillis) {
const maxDelayProportionalToMaxWaitingTime = 1/4;
const maxDelay = Math.floor(retryTimeoutMillis * maxDelayProportionalToMaxWaitingTime);
const newDelay = Math.max(16, fallbackDelayMillis) * 2;
return newDelay > maxDelay ? maxDelay : newDelay;
}
function defaultShouldRetryFn({error}) {
const fastRetryStatusCodes = [429, 503];
return error.response && fastRetryStatusCodes.includes(error.response.statusCode);
}
function getRetryDelayFromHeader(error) {
return error.response && parseInt(error.response.headers['retry-after'], 10) * 1000;
}
module.exports = createBackoffRetryFilter;
;