@aws-sdk/middleware-retry
Advanced tools
Comparing version 3.185.0 to 3.186.0
@@ -6,2 +6,10 @@ # Change Log | ||
# [3.186.0](https://github.com/aws/aws-sdk-js-v3/compare/v3.185.0...v3.186.0) (2022-10-06) | ||
**Note:** Version bump only for package @aws-sdk/middleware-retry | ||
# [3.185.0](https://github.com/aws/aws-sdk-js-v3/compare/v3.184.0...v3.185.0) (2022-10-05) | ||
@@ -8,0 +16,0 @@ |
@@ -0,21 +1,34 @@ | ||
import { __awaiter, __extends, __generator, __rest } from "tslib"; | ||
import { RETRY_MODES } from "./config"; | ||
import { DefaultRateLimiter } from "./DefaultRateLimiter"; | ||
import { StandardRetryStrategy } from "./StandardRetryStrategy"; | ||
export class AdaptiveRetryStrategy extends StandardRetryStrategy { | ||
constructor(maxAttemptsProvider, options) { | ||
const { rateLimiter, ...superOptions } = options ?? {}; | ||
super(maxAttemptsProvider, superOptions); | ||
this.rateLimiter = rateLimiter ?? new DefaultRateLimiter(); | ||
this.mode = RETRY_MODES.ADAPTIVE; | ||
var AdaptiveRetryStrategy = (function (_super) { | ||
__extends(AdaptiveRetryStrategy, _super); | ||
function AdaptiveRetryStrategy(maxAttemptsProvider, options) { | ||
var _this = this; | ||
var _a = options !== null && options !== void 0 ? options : {}, rateLimiter = _a.rateLimiter, superOptions = __rest(_a, ["rateLimiter"]); | ||
_this = _super.call(this, maxAttemptsProvider, superOptions) || this; | ||
_this.rateLimiter = rateLimiter !== null && rateLimiter !== void 0 ? rateLimiter : new DefaultRateLimiter(); | ||
_this.mode = RETRY_MODES.ADAPTIVE; | ||
return _this; | ||
} | ||
async retry(next, args) { | ||
return super.retry(next, args, { | ||
beforeRequest: async () => { | ||
return this.rateLimiter.getSendToken(); | ||
}, | ||
afterRequest: (response) => { | ||
this.rateLimiter.updateClientSendingRate(response); | ||
}, | ||
AdaptiveRetryStrategy.prototype.retry = function (next, args) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return __generator(this, function (_a) { | ||
return [2, _super.prototype.retry.call(this, next, args, { | ||
beforeRequest: function () { return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2, this.rateLimiter.getSendToken()]; | ||
}); | ||
}); }, | ||
afterRequest: function (response) { | ||
_this.rateLimiter.updateClientSendingRate(response); | ||
}, | ||
})]; | ||
}); | ||
}); | ||
} | ||
} | ||
}; | ||
return AdaptiveRetryStrategy; | ||
}(StandardRetryStrategy)); | ||
export { AdaptiveRetryStrategy }; |
@@ -6,3 +6,3 @@ export var RETRY_MODES; | ||
})(RETRY_MODES || (RETRY_MODES = {})); | ||
export const DEFAULT_MAX_ATTEMPTS = 3; | ||
export const DEFAULT_RETRY_MODE = RETRY_MODES.STANDARD; | ||
export var DEFAULT_MAX_ATTEMPTS = 3; | ||
export var DEFAULT_RETRY_MODE = RETRY_MODES.STANDARD; |
@@ -0,1 +1,2 @@ | ||
import { __assign, __awaiter, __generator } from "tslib"; | ||
import { normalizeProvider } from "@aws-sdk/util-middleware"; | ||
@@ -5,22 +6,22 @@ import { AdaptiveRetryStrategy } from "./AdaptiveRetryStrategy"; | ||
import { StandardRetryStrategy } from "./StandardRetryStrategy"; | ||
export const ENV_MAX_ATTEMPTS = "AWS_MAX_ATTEMPTS"; | ||
export const CONFIG_MAX_ATTEMPTS = "max_attempts"; | ||
export const NODE_MAX_ATTEMPT_CONFIG_OPTIONS = { | ||
environmentVariableSelector: (env) => { | ||
const value = env[ENV_MAX_ATTEMPTS]; | ||
export var ENV_MAX_ATTEMPTS = "AWS_MAX_ATTEMPTS"; | ||
export var CONFIG_MAX_ATTEMPTS = "max_attempts"; | ||
export var NODE_MAX_ATTEMPT_CONFIG_OPTIONS = { | ||
environmentVariableSelector: function (env) { | ||
var value = env[ENV_MAX_ATTEMPTS]; | ||
if (!value) | ||
return undefined; | ||
const maxAttempt = parseInt(value); | ||
var maxAttempt = parseInt(value); | ||
if (Number.isNaN(maxAttempt)) { | ||
throw new Error(`Environment variable ${ENV_MAX_ATTEMPTS} mast be a number, got "${value}"`); | ||
throw new Error("Environment variable ".concat(ENV_MAX_ATTEMPTS, " mast be a number, got \"").concat(value, "\"")); | ||
} | ||
return maxAttempt; | ||
}, | ||
configFileSelector: (profile) => { | ||
const value = profile[CONFIG_MAX_ATTEMPTS]; | ||
configFileSelector: function (profile) { | ||
var value = profile[CONFIG_MAX_ATTEMPTS]; | ||
if (!value) | ||
return undefined; | ||
const maxAttempt = parseInt(value); | ||
var maxAttempt = parseInt(value); | ||
if (Number.isNaN(maxAttempt)) { | ||
throw new Error(`Shared config file entry ${CONFIG_MAX_ATTEMPTS} mast be a number, got "${value}"`); | ||
throw new Error("Shared config file entry ".concat(CONFIG_MAX_ATTEMPTS, " mast be a number, got \"").concat(value, "\"")); | ||
} | ||
@@ -31,25 +32,30 @@ return maxAttempt; | ||
}; | ||
export const resolveRetryConfig = (input) => { | ||
const maxAttempts = normalizeProvider(input.maxAttempts ?? DEFAULT_MAX_ATTEMPTS); | ||
return { | ||
...input, | ||
maxAttempts, | ||
retryStrategy: async () => { | ||
if (input.retryStrategy) { | ||
return input.retryStrategy; | ||
} | ||
const retryMode = await normalizeProvider(input.retryMode)(); | ||
if (retryMode === RETRY_MODES.ADAPTIVE) { | ||
return new AdaptiveRetryStrategy(maxAttempts); | ||
} | ||
return new StandardRetryStrategy(maxAttempts); | ||
}, | ||
}; | ||
export var resolveRetryConfig = function (input) { | ||
var _a; | ||
var maxAttempts = normalizeProvider((_a = input.maxAttempts) !== null && _a !== void 0 ? _a : DEFAULT_MAX_ATTEMPTS); | ||
return __assign(__assign({}, input), { maxAttempts: maxAttempts, retryStrategy: function () { return __awaiter(void 0, void 0, void 0, function () { | ||
var retryMode; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (input.retryStrategy) { | ||
return [2, input.retryStrategy]; | ||
} | ||
return [4, normalizeProvider(input.retryMode)()]; | ||
case 1: | ||
retryMode = _a.sent(); | ||
if (retryMode === RETRY_MODES.ADAPTIVE) { | ||
return [2, new AdaptiveRetryStrategy(maxAttempts)]; | ||
} | ||
return [2, new StandardRetryStrategy(maxAttempts)]; | ||
} | ||
}); | ||
}); } }); | ||
}; | ||
export const ENV_RETRY_MODE = "AWS_RETRY_MODE"; | ||
export const CONFIG_RETRY_MODE = "retry_mode"; | ||
export const NODE_RETRY_MODE_CONFIG_OPTIONS = { | ||
environmentVariableSelector: (env) => env[ENV_RETRY_MODE], | ||
configFileSelector: (profile) => profile[CONFIG_RETRY_MODE], | ||
export var ENV_RETRY_MODE = "AWS_RETRY_MODE"; | ||
export var CONFIG_RETRY_MODE = "retry_mode"; | ||
export var NODE_RETRY_MODE_CONFIG_OPTIONS = { | ||
environmentVariableSelector: function (env) { return env[ENV_RETRY_MODE]; }, | ||
configFileSelector: function (profile) { return profile[CONFIG_RETRY_MODE]; }, | ||
default: DEFAULT_RETRY_MODE, | ||
}; |
@@ -1,9 +0,9 @@ | ||
export const DEFAULT_RETRY_DELAY_BASE = 100; | ||
export const MAXIMUM_RETRY_DELAY = 20 * 1000; | ||
export const THROTTLING_RETRY_DELAY_BASE = 500; | ||
export const INITIAL_RETRY_TOKENS = 500; | ||
export const RETRY_COST = 5; | ||
export const TIMEOUT_RETRY_COST = 10; | ||
export const NO_RETRY_INCREMENT = 1; | ||
export const INVOCATION_ID_HEADER = "amz-sdk-invocation-id"; | ||
export const REQUEST_HEADER = "amz-sdk-request"; | ||
export var DEFAULT_RETRY_DELAY_BASE = 100; | ||
export var MAXIMUM_RETRY_DELAY = 20 * 1000; | ||
export var THROTTLING_RETRY_DELAY_BASE = 500; | ||
export var INITIAL_RETRY_TOKENS = 500; | ||
export var RETRY_COST = 5; | ||
export var TIMEOUT_RETRY_COST = 10; | ||
export var NO_RETRY_INCREMENT = 1; | ||
export var INVOCATION_ID_HEADER = "amz-sdk-invocation-id"; | ||
export var REQUEST_HEADER = "amz-sdk-request"; |
@@ -0,4 +1,6 @@ | ||
import { __awaiter, __generator } from "tslib"; | ||
import { isThrottlingError } from "@aws-sdk/service-error-classification"; | ||
export class DefaultRateLimiter { | ||
constructor(options) { | ||
var DefaultRateLimiter = (function () { | ||
function DefaultRateLimiter(options) { | ||
var _a, _b, _c, _d, _e; | ||
this.currentCapacity = 0; | ||
@@ -11,8 +13,8 @@ this.enabled = false; | ||
this.timeWindow = 0; | ||
this.beta = options?.beta ?? 0.7; | ||
this.minCapacity = options?.minCapacity ?? 1; | ||
this.minFillRate = options?.minFillRate ?? 0.5; | ||
this.scaleConstant = options?.scaleConstant ?? 0.4; | ||
this.smooth = options?.smooth ?? 0.8; | ||
const currentTimeInSeconds = this.getCurrentTimeInSeconds(); | ||
this.beta = (_a = options === null || options === void 0 ? void 0 : options.beta) !== null && _a !== void 0 ? _a : 0.7; | ||
this.minCapacity = (_b = options === null || options === void 0 ? void 0 : options.minCapacity) !== null && _b !== void 0 ? _b : 1; | ||
this.minFillRate = (_c = options === null || options === void 0 ? void 0 : options.minFillRate) !== null && _c !== void 0 ? _c : 0.5; | ||
this.scaleConstant = (_d = options === null || options === void 0 ? void 0 : options.scaleConstant) !== null && _d !== void 0 ? _d : 0.4; | ||
this.smooth = (_e = options === null || options === void 0 ? void 0 : options.smooth) !== null && _e !== void 0 ? _e : 0.8; | ||
var currentTimeInSeconds = this.getCurrentTimeInSeconds(); | ||
this.lastThrottleTime = currentTimeInSeconds; | ||
@@ -23,21 +25,37 @@ this.lastTxRateBucket = Math.floor(this.getCurrentTimeInSeconds()); | ||
} | ||
getCurrentTimeInSeconds() { | ||
DefaultRateLimiter.prototype.getCurrentTimeInSeconds = function () { | ||
return Date.now() / 1000; | ||
} | ||
async getSendToken() { | ||
return this.acquireTokenBucket(1); | ||
} | ||
async acquireTokenBucket(amount) { | ||
if (!this.enabled) { | ||
return; | ||
} | ||
this.refillTokenBucket(); | ||
if (amount > this.currentCapacity) { | ||
const delay = ((amount - this.currentCapacity) / this.fillRate) * 1000; | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
} | ||
this.currentCapacity = this.currentCapacity - amount; | ||
} | ||
refillTokenBucket() { | ||
const timestamp = this.getCurrentTimeInSeconds(); | ||
}; | ||
DefaultRateLimiter.prototype.getSendToken = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
return __generator(this, function (_a) { | ||
return [2, this.acquireTokenBucket(1)]; | ||
}); | ||
}); | ||
}; | ||
DefaultRateLimiter.prototype.acquireTokenBucket = function (amount) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var delay_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this.enabled) { | ||
return [2]; | ||
} | ||
this.refillTokenBucket(); | ||
if (!(amount > this.currentCapacity)) return [3, 2]; | ||
delay_1 = ((amount - this.currentCapacity) / this.fillRate) * 1000; | ||
return [4, new Promise(function (resolve) { return setTimeout(resolve, delay_1); })]; | ||
case 1: | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
this.currentCapacity = this.currentCapacity - amount; | ||
return [2]; | ||
} | ||
}); | ||
}); | ||
}; | ||
DefaultRateLimiter.prototype.refillTokenBucket = function () { | ||
var timestamp = this.getCurrentTimeInSeconds(); | ||
if (!this.lastTimestamp) { | ||
@@ -47,11 +65,11 @@ this.lastTimestamp = timestamp; | ||
} | ||
const fillAmount = (timestamp - this.lastTimestamp) * this.fillRate; | ||
var fillAmount = (timestamp - this.lastTimestamp) * this.fillRate; | ||
this.currentCapacity = Math.min(this.maxCapacity, this.currentCapacity + fillAmount); | ||
this.lastTimestamp = timestamp; | ||
} | ||
updateClientSendingRate(response) { | ||
let calculatedRate; | ||
}; | ||
DefaultRateLimiter.prototype.updateClientSendingRate = function (response) { | ||
var calculatedRate; | ||
this.updateMeasuredRate(); | ||
if (isThrottlingError(response)) { | ||
const rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate); | ||
var rateToUse = !this.enabled ? this.measuredTxRate : Math.min(this.measuredTxRate, this.fillRate); | ||
this.lastMaxRate = rateToUse; | ||
@@ -67,18 +85,18 @@ this.calculateTimeWindow(); | ||
} | ||
const newRate = Math.min(calculatedRate, 2 * this.measuredTxRate); | ||
var newRate = Math.min(calculatedRate, 2 * this.measuredTxRate); | ||
this.updateTokenBucketRate(newRate); | ||
} | ||
calculateTimeWindow() { | ||
}; | ||
DefaultRateLimiter.prototype.calculateTimeWindow = function () { | ||
this.timeWindow = this.getPrecise(Math.pow((this.lastMaxRate * (1 - this.beta)) / this.scaleConstant, 1 / 3)); | ||
} | ||
cubicThrottle(rateToUse) { | ||
}; | ||
DefaultRateLimiter.prototype.cubicThrottle = function (rateToUse) { | ||
return this.getPrecise(rateToUse * this.beta); | ||
} | ||
cubicSuccess(timestamp) { | ||
}; | ||
DefaultRateLimiter.prototype.cubicSuccess = function (timestamp) { | ||
return this.getPrecise(this.scaleConstant * Math.pow(timestamp - this.lastThrottleTime - this.timeWindow, 3) + this.lastMaxRate); | ||
} | ||
enableTokenBucket() { | ||
}; | ||
DefaultRateLimiter.prototype.enableTokenBucket = function () { | ||
this.enabled = true; | ||
} | ||
updateTokenBucketRate(newRate) { | ||
}; | ||
DefaultRateLimiter.prototype.updateTokenBucketRate = function (newRate) { | ||
this.refillTokenBucket(); | ||
@@ -88,9 +106,9 @@ this.fillRate = Math.max(newRate, this.minFillRate); | ||
this.currentCapacity = Math.min(this.currentCapacity, this.maxCapacity); | ||
} | ||
updateMeasuredRate() { | ||
const t = this.getCurrentTimeInSeconds(); | ||
const timeBucket = Math.floor(t * 2) / 2; | ||
}; | ||
DefaultRateLimiter.prototype.updateMeasuredRate = function () { | ||
var t = this.getCurrentTimeInSeconds(); | ||
var timeBucket = Math.floor(t * 2) / 2; | ||
this.requestCount++; | ||
if (timeBucket > this.lastTxRateBucket) { | ||
const currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket); | ||
var currentRate = this.requestCount / (timeBucket - this.lastTxRateBucket); | ||
this.measuredTxRate = this.getPrecise(currentRate * this.smooth + this.measuredTxRate * (1 - this.smooth)); | ||
@@ -100,6 +118,8 @@ this.requestCount = 0; | ||
} | ||
} | ||
getPrecise(num) { | ||
}; | ||
DefaultRateLimiter.prototype.getPrecise = function (num) { | ||
return parseFloat(num.toFixed(8)); | ||
} | ||
} | ||
}; | ||
return DefaultRateLimiter; | ||
}()); | ||
export { DefaultRateLimiter }; |
import { NO_RETRY_INCREMENT, RETRY_COST, TIMEOUT_RETRY_COST } from "./constants"; | ||
export const getDefaultRetryQuota = (initialRetryTokens, options) => { | ||
const MAX_CAPACITY = initialRetryTokens; | ||
const noRetryIncrement = options?.noRetryIncrement ?? NO_RETRY_INCREMENT; | ||
const retryCost = options?.retryCost ?? RETRY_COST; | ||
const timeoutRetryCost = options?.timeoutRetryCost ?? TIMEOUT_RETRY_COST; | ||
let availableCapacity = initialRetryTokens; | ||
const getCapacityAmount = (error) => (error.name === "TimeoutError" ? timeoutRetryCost : retryCost); | ||
const hasRetryTokens = (error) => getCapacityAmount(error) <= availableCapacity; | ||
const retrieveRetryTokens = (error) => { | ||
export var getDefaultRetryQuota = function (initialRetryTokens, options) { | ||
var _a, _b, _c; | ||
var MAX_CAPACITY = initialRetryTokens; | ||
var noRetryIncrement = (_a = options === null || options === void 0 ? void 0 : options.noRetryIncrement) !== null && _a !== void 0 ? _a : NO_RETRY_INCREMENT; | ||
var retryCost = (_b = options === null || options === void 0 ? void 0 : options.retryCost) !== null && _b !== void 0 ? _b : RETRY_COST; | ||
var timeoutRetryCost = (_c = options === null || options === void 0 ? void 0 : options.timeoutRetryCost) !== null && _c !== void 0 ? _c : TIMEOUT_RETRY_COST; | ||
var availableCapacity = initialRetryTokens; | ||
var getCapacityAmount = function (error) { return (error.name === "TimeoutError" ? timeoutRetryCost : retryCost); }; | ||
var hasRetryTokens = function (error) { return getCapacityAmount(error) <= availableCapacity; }; | ||
var retrieveRetryTokens = function (error) { | ||
if (!hasRetryTokens(error)) { | ||
throw new Error("No retry token available"); | ||
} | ||
const capacityAmount = getCapacityAmount(error); | ||
var capacityAmount = getCapacityAmount(error); | ||
availableCapacity -= capacityAmount; | ||
return capacityAmount; | ||
}; | ||
const releaseRetryTokens = (capacityReleaseAmount) => { | ||
availableCapacity += capacityReleaseAmount ?? noRetryIncrement; | ||
var releaseRetryTokens = function (capacityReleaseAmount) { | ||
availableCapacity += capacityReleaseAmount !== null && capacityReleaseAmount !== void 0 ? capacityReleaseAmount : noRetryIncrement; | ||
availableCapacity = Math.min(availableCapacity, MAX_CAPACITY); | ||
}; | ||
return Object.freeze({ | ||
hasRetryTokens, | ||
retrieveRetryTokens, | ||
releaseRetryTokens, | ||
hasRetryTokens: hasRetryTokens, | ||
retrieveRetryTokens: retrieveRetryTokens, | ||
releaseRetryTokens: releaseRetryTokens, | ||
}); | ||
}; |
import { MAXIMUM_RETRY_DELAY } from "./constants"; | ||
export const defaultDelayDecider = (delayBase, attempts) => Math.floor(Math.min(MAXIMUM_RETRY_DELAY, Math.random() * 2 ** attempts * delayBase)); | ||
export var defaultDelayDecider = function (delayBase, attempts) { | ||
return Math.floor(Math.min(MAXIMUM_RETRY_DELAY, Math.random() * Math.pow(2, attempts) * delayBase)); | ||
}; |
@@ -0,12 +1,20 @@ | ||
import { __awaiter, __generator } from "tslib"; | ||
import { HttpRequest } from "@aws-sdk/protocol-http"; | ||
import { INVOCATION_ID_HEADER, REQUEST_HEADER } from "./constants"; | ||
export const omitRetryHeadersMiddleware = () => (next) => async (args) => { | ||
const { request } = args; | ||
if (HttpRequest.isInstance(request)) { | ||
delete request.headers[INVOCATION_ID_HEADER]; | ||
delete request.headers[REQUEST_HEADER]; | ||
} | ||
return next(args); | ||
export var omitRetryHeadersMiddleware = function () { | ||
return function (next) { | ||
return function (args) { return __awaiter(void 0, void 0, void 0, function () { | ||
var request; | ||
return __generator(this, function (_a) { | ||
request = args.request; | ||
if (HttpRequest.isInstance(request)) { | ||
delete request.headers[INVOCATION_ID_HEADER]; | ||
delete request.headers[REQUEST_HEADER]; | ||
} | ||
return [2, next(args)]; | ||
}); | ||
}); }; | ||
}; | ||
}; | ||
export const omitRetryHeadersMiddlewareOptions = { | ||
export var omitRetryHeadersMiddlewareOptions = { | ||
name: "omitRetryHeadersMiddleware", | ||
@@ -18,6 +26,6 @@ tags: ["RETRY", "HEADERS", "OMIT_RETRY_HEADERS"], | ||
}; | ||
export const getOmitRetryHeadersPlugin = (options) => ({ | ||
applyToStack: (clientStack) => { | ||
export var getOmitRetryHeadersPlugin = function (options) { return ({ | ||
applyToStack: function (clientStack) { | ||
clientStack.addRelativeTo(omitRetryHeadersMiddleware(), omitRetryHeadersMiddlewareOptions); | ||
}, | ||
}); | ||
}); }; |
import { isClockSkewError, isRetryableByTrait, isThrottlingError, isTransientError, } from "@aws-sdk/service-error-classification"; | ||
export const defaultRetryDecider = (error) => { | ||
export var defaultRetryDecider = function (error) { | ||
if (!error) { | ||
@@ -4,0 +4,0 @@ return false; |
@@ -1,8 +0,20 @@ | ||
export const retryMiddleware = (options) => (next, context) => async (args) => { | ||
const retryStrategy = await options.retryStrategy(); | ||
if (retryStrategy?.mode) | ||
context.userAgent = [...(context.userAgent || []), ["cfg/retry-mode", retryStrategy.mode]]; | ||
return retryStrategy.retry(next, args); | ||
import { __awaiter, __generator, __read, __spreadArray } from "tslib"; | ||
export var retryMiddleware = function (options) { | ||
return function (next, context) { | ||
return function (args) { return __awaiter(void 0, void 0, void 0, function () { | ||
var retryStrategy; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4, options.retryStrategy()]; | ||
case 1: | ||
retryStrategy = _a.sent(); | ||
if (retryStrategy === null || retryStrategy === void 0 ? void 0 : retryStrategy.mode) | ||
context.userAgent = __spreadArray(__spreadArray([], __read((context.userAgent || [])), false), [["cfg/retry-mode", retryStrategy.mode]], false); | ||
return [2, retryStrategy.retry(next, args)]; | ||
} | ||
}); | ||
}); }; | ||
}; | ||
}; | ||
export const retryMiddlewareOptions = { | ||
export var retryMiddlewareOptions = { | ||
name: "retryMiddleware", | ||
@@ -14,6 +26,6 @@ tags: ["RETRY"], | ||
}; | ||
export const getRetryPlugin = (options) => ({ | ||
applyToStack: (clientStack) => { | ||
export var getRetryPlugin = function (options) { return ({ | ||
applyToStack: function (clientStack) { | ||
clientStack.add(retryMiddleware(options), retryMiddlewareOptions); | ||
}, | ||
}); | ||
}); }; |
@@ -0,1 +1,2 @@ | ||
import { __awaiter, __generator } from "tslib"; | ||
import { HttpRequest, HttpResponse } from "@aws-sdk/protocol-http"; | ||
@@ -9,85 +10,130 @@ import { isThrottlingError } from "@aws-sdk/service-error-classification"; | ||
import { defaultRetryDecider } from "./retryDecider"; | ||
export class StandardRetryStrategy { | ||
constructor(maxAttemptsProvider, options) { | ||
var StandardRetryStrategy = (function () { | ||
function StandardRetryStrategy(maxAttemptsProvider, options) { | ||
var _a, _b, _c; | ||
this.maxAttemptsProvider = maxAttemptsProvider; | ||
this.mode = RETRY_MODES.STANDARD; | ||
this.retryDecider = options?.retryDecider ?? defaultRetryDecider; | ||
this.delayDecider = options?.delayDecider ?? defaultDelayDecider; | ||
this.retryQuota = options?.retryQuota ?? getDefaultRetryQuota(INITIAL_RETRY_TOKENS); | ||
this.retryDecider = (_a = options === null || options === void 0 ? void 0 : options.retryDecider) !== null && _a !== void 0 ? _a : defaultRetryDecider; | ||
this.delayDecider = (_b = options === null || options === void 0 ? void 0 : options.delayDecider) !== null && _b !== void 0 ? _b : defaultDelayDecider; | ||
this.retryQuota = (_c = options === null || options === void 0 ? void 0 : options.retryQuota) !== null && _c !== void 0 ? _c : getDefaultRetryQuota(INITIAL_RETRY_TOKENS); | ||
} | ||
shouldRetry(error, attempts, maxAttempts) { | ||
StandardRetryStrategy.prototype.shouldRetry = function (error, attempts, maxAttempts) { | ||
return attempts < maxAttempts && this.retryDecider(error) && this.retryQuota.hasRetryTokens(error); | ||
} | ||
async getMaxAttempts() { | ||
let maxAttempts; | ||
try { | ||
maxAttempts = await this.maxAttemptsProvider(); | ||
} | ||
catch (error) { | ||
maxAttempts = DEFAULT_MAX_ATTEMPTS; | ||
} | ||
return maxAttempts; | ||
} | ||
async retry(next, args, options) { | ||
let retryTokenAmount; | ||
let attempts = 0; | ||
let totalDelay = 0; | ||
const maxAttempts = await this.getMaxAttempts(); | ||
const { request } = args; | ||
if (HttpRequest.isInstance(request)) { | ||
request.headers[INVOCATION_ID_HEADER] = v4(); | ||
} | ||
while (true) { | ||
try { | ||
if (HttpRequest.isInstance(request)) { | ||
request.headers[REQUEST_HEADER] = `attempt=${attempts + 1}; max=${maxAttempts}`; | ||
}; | ||
StandardRetryStrategy.prototype.getMaxAttempts = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var maxAttempts, error_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 2, , 3]); | ||
return [4, this.maxAttemptsProvider()]; | ||
case 1: | ||
maxAttempts = _a.sent(); | ||
return [3, 3]; | ||
case 2: | ||
error_1 = _a.sent(); | ||
maxAttempts = DEFAULT_MAX_ATTEMPTS; | ||
return [3, 3]; | ||
case 3: return [2, maxAttempts]; | ||
} | ||
if (options?.beforeRequest) { | ||
await options.beforeRequest(); | ||
}); | ||
}); | ||
}; | ||
StandardRetryStrategy.prototype.retry = function (next, args, options) { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var retryTokenAmount, attempts, totalDelay, maxAttempts, request, _loop_1, this_1, state_1; | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
attempts = 0; | ||
totalDelay = 0; | ||
return [4, this.getMaxAttempts()]; | ||
case 1: | ||
maxAttempts = _a.sent(); | ||
request = args.request; | ||
if (HttpRequest.isInstance(request)) { | ||
request.headers[INVOCATION_ID_HEADER] = v4(); | ||
} | ||
_loop_1 = function () { | ||
var _b, response, output, e_1, err, delayFromDecider, delayFromResponse, delay_1; | ||
return __generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: | ||
_c.trys.push([0, 4, , 7]); | ||
if (HttpRequest.isInstance(request)) { | ||
request.headers[REQUEST_HEADER] = "attempt=".concat(attempts + 1, "; max=").concat(maxAttempts); | ||
} | ||
if (!(options === null || options === void 0 ? void 0 : options.beforeRequest)) return [3, 2]; | ||
return [4, options.beforeRequest()]; | ||
case 1: | ||
_c.sent(); | ||
_c.label = 2; | ||
case 2: return [4, next(args)]; | ||
case 3: | ||
_b = _c.sent(), response = _b.response, output = _b.output; | ||
if (options === null || options === void 0 ? void 0 : options.afterRequest) { | ||
options.afterRequest(response); | ||
} | ||
this_1.retryQuota.releaseRetryTokens(retryTokenAmount); | ||
output.$metadata.attempts = attempts + 1; | ||
output.$metadata.totalRetryDelay = totalDelay; | ||
return [2, { value: { response: response, output: output } }]; | ||
case 4: | ||
e_1 = _c.sent(); | ||
err = asSdkError(e_1); | ||
attempts++; | ||
if (!this_1.shouldRetry(err, attempts, maxAttempts)) return [3, 6]; | ||
retryTokenAmount = this_1.retryQuota.retrieveRetryTokens(err); | ||
delayFromDecider = this_1.delayDecider(isThrottlingError(err) ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE, attempts); | ||
delayFromResponse = getDelayFromRetryAfterHeader(err.$response); | ||
delay_1 = Math.max(delayFromResponse || 0, delayFromDecider); | ||
totalDelay += delay_1; | ||
return [4, new Promise(function (resolve) { return setTimeout(resolve, delay_1); })]; | ||
case 5: | ||
_c.sent(); | ||
return [2, "continue"]; | ||
case 6: | ||
if (!err.$metadata) { | ||
err.$metadata = {}; | ||
} | ||
err.$metadata.attempts = attempts; | ||
err.$metadata.totalRetryDelay = totalDelay; | ||
throw err; | ||
case 7: return [2]; | ||
} | ||
}); | ||
}; | ||
this_1 = this; | ||
_a.label = 2; | ||
case 2: | ||
if (!true) return [3, 4]; | ||
return [5, _loop_1()]; | ||
case 3: | ||
state_1 = _a.sent(); | ||
if (typeof state_1 === "object") | ||
return [2, state_1.value]; | ||
return [3, 2]; | ||
case 4: return [2]; | ||
} | ||
const { response, output } = await next(args); | ||
if (options?.afterRequest) { | ||
options.afterRequest(response); | ||
} | ||
this.retryQuota.releaseRetryTokens(retryTokenAmount); | ||
output.$metadata.attempts = attempts + 1; | ||
output.$metadata.totalRetryDelay = totalDelay; | ||
return { response, output }; | ||
} | ||
catch (e) { | ||
const err = asSdkError(e); | ||
attempts++; | ||
if (this.shouldRetry(err, attempts, maxAttempts)) { | ||
retryTokenAmount = this.retryQuota.retrieveRetryTokens(err); | ||
const delayFromDecider = this.delayDecider(isThrottlingError(err) ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE, attempts); | ||
const delayFromResponse = getDelayFromRetryAfterHeader(err.$response); | ||
const delay = Math.max(delayFromResponse || 0, delayFromDecider); | ||
totalDelay += delay; | ||
await new Promise((resolve) => setTimeout(resolve, delay)); | ||
continue; | ||
} | ||
if (!err.$metadata) { | ||
err.$metadata = {}; | ||
} | ||
err.$metadata.attempts = attempts; | ||
err.$metadata.totalRetryDelay = totalDelay; | ||
throw err; | ||
} | ||
} | ||
} | ||
} | ||
const getDelayFromRetryAfterHeader = (response) => { | ||
}); | ||
}); | ||
}; | ||
return StandardRetryStrategy; | ||
}()); | ||
export { StandardRetryStrategy }; | ||
var getDelayFromRetryAfterHeader = function (response) { | ||
if (!HttpResponse.isInstance(response)) | ||
return; | ||
const retryAfterHeaderName = Object.keys(response.headers).find((key) => key.toLowerCase() === "retry-after"); | ||
var retryAfterHeaderName = Object.keys(response.headers).find(function (key) { return key.toLowerCase() === "retry-after"; }); | ||
if (!retryAfterHeaderName) | ||
return; | ||
const retryAfter = response.headers[retryAfterHeaderName]; | ||
const retryAfterSeconds = Number(retryAfter); | ||
var retryAfter = response.headers[retryAfterHeaderName]; | ||
var retryAfterSeconds = Number(retryAfter); | ||
if (!Number.isNaN(retryAfterSeconds)) | ||
return retryAfterSeconds * 1000; | ||
const retryAfterDate = new Date(retryAfter); | ||
var retryAfterDate = new Date(retryAfter); | ||
return retryAfterDate.getTime() - Date.now(); | ||
}; | ||
const asSdkError = (error) => { | ||
var asSdkError = function (error) { | ||
if (error instanceof Error) | ||
@@ -99,3 +145,3 @@ return error; | ||
return new Error(error); | ||
return new Error(`AWS SDK error wrapper for ${error}`); | ||
return new Error("AWS SDK error wrapper for ".concat(error)); | ||
}; |
{ | ||
"name": "@aws-sdk/middleware-retry", | ||
"version": "3.185.0", | ||
"version": "3.186.0", | ||
"scripts": { | ||
@@ -23,6 +23,6 @@ "build": "concurrently 'yarn:build:cjs' 'yarn:build:es' 'yarn:build:types'", | ||
"dependencies": { | ||
"@aws-sdk/protocol-http": "3.183.0", | ||
"@aws-sdk/service-error-classification": "3.185.0", | ||
"@aws-sdk/types": "3.183.0", | ||
"@aws-sdk/util-middleware": "3.183.0", | ||
"@aws-sdk/protocol-http": "3.186.0", | ||
"@aws-sdk/service-error-classification": "3.186.0", | ||
"@aws-sdk/types": "3.186.0", | ||
"@aws-sdk/util-middleware": "3.186.0", | ||
"tslib": "^2.3.1", | ||
@@ -32,3 +32,3 @@ "uuid": "^8.3.2" | ||
"devDependencies": { | ||
"@aws-sdk/node-config-provider": "3.183.0", | ||
"@aws-sdk/node-config-provider": "3.186.0", | ||
"@tsconfig/recommended": "1.0.1", | ||
@@ -35,0 +35,0 @@ "@types/uuid": "^8.3.0", |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
111376
1401
+ Added@aws-sdk/protocol-http@3.186.0(transitive)
+ Added@aws-sdk/service-error-classification@3.186.0(transitive)
+ Added@aws-sdk/types@3.186.0(transitive)
+ Added@aws-sdk/util-middleware@3.186.0(transitive)
- Removed@aws-sdk/protocol-http@3.183.0(transitive)
- Removed@aws-sdk/service-error-classification@3.185.0(transitive)
- Removed@aws-sdk/types@3.183.0(transitive)
- Removed@aws-sdk/util-middleware@3.183.0(transitive)
Updated@aws-sdk/types@3.186.0