1 | import { __awaiter, __generator } from "tslib";
|
2 | import { HttpRequest } from "@aws-sdk/protocol-http";
|
3 | import { isThrottlingError } from "@aws-sdk/service-error-classification";
|
4 | import { v4 } from "uuid";
|
5 | import { DEFAULT_MAX_ATTEMPTS, RETRY_MODES } from "./config";
|
6 | import { DEFAULT_RETRY_DELAY_BASE, INITIAL_RETRY_TOKENS, INVOCATION_ID_HEADER, REQUEST_HEADER, THROTTLING_RETRY_DELAY_BASE, } from "./constants";
|
7 | import { getDefaultRetryQuota } from "./defaultRetryQuota";
|
8 | import { defaultDelayDecider } from "./delayDecider";
|
9 | import { defaultRetryDecider } from "./retryDecider";
|
10 | var StandardRetryStrategy = (function () {
|
11 | function StandardRetryStrategy(maxAttemptsProvider, options) {
|
12 | var _a, _b, _c;
|
13 | this.maxAttemptsProvider = maxAttemptsProvider;
|
14 | this.mode = RETRY_MODES.STANDARD;
|
15 | this.retryDecider = (_a = options === null || options === void 0 ? void 0 : options.retryDecider) !== null && _a !== void 0 ? _a : defaultRetryDecider;
|
16 | this.delayDecider = (_b = options === null || options === void 0 ? void 0 : options.delayDecider) !== null && _b !== void 0 ? _b : defaultDelayDecider;
|
17 | this.retryQuota = (_c = options === null || options === void 0 ? void 0 : options.retryQuota) !== null && _c !== void 0 ? _c : getDefaultRetryQuota(INITIAL_RETRY_TOKENS);
|
18 | }
|
19 | StandardRetryStrategy.prototype.shouldRetry = function (error, attempts, maxAttempts) {
|
20 | return attempts < maxAttempts && this.retryDecider(error) && this.retryQuota.hasRetryTokens(error);
|
21 | };
|
22 | StandardRetryStrategy.prototype.getMaxAttempts = function () {
|
23 | return __awaiter(this, void 0, void 0, function () {
|
24 | var maxAttempts, error_1;
|
25 | return __generator(this, function (_a) {
|
26 | switch (_a.label) {
|
27 | case 0:
|
28 | _a.trys.push([0, 2, , 3]);
|
29 | return [4, this.maxAttemptsProvider()];
|
30 | case 1:
|
31 | maxAttempts = _a.sent();
|
32 | return [3, 3];
|
33 | case 2:
|
34 | error_1 = _a.sent();
|
35 | maxAttempts = DEFAULT_MAX_ATTEMPTS;
|
36 | return [3, 3];
|
37 | case 3: return [2, maxAttempts];
|
38 | }
|
39 | });
|
40 | });
|
41 | };
|
42 | StandardRetryStrategy.prototype.retry = function (next, args, options) {
|
43 | return __awaiter(this, void 0, void 0, function () {
|
44 | var retryTokenAmount, attempts, totalDelay, maxAttempts, request, _loop_1, this_1, state_1;
|
45 | return __generator(this, function (_a) {
|
46 | switch (_a.label) {
|
47 | case 0:
|
48 | attempts = 0;
|
49 | totalDelay = 0;
|
50 | return [4, this.getMaxAttempts()];
|
51 | case 1:
|
52 | maxAttempts = _a.sent();
|
53 | request = args.request;
|
54 | if (HttpRequest.isInstance(request)) {
|
55 | request.headers[INVOCATION_ID_HEADER] = v4();
|
56 | }
|
57 | _loop_1 = function () {
|
58 | var _b, response, output, e_1, err, delay_1;
|
59 | return __generator(this, function (_c) {
|
60 | switch (_c.label) {
|
61 | case 0:
|
62 | _c.trys.push([0, 4, , 7]);
|
63 | if (HttpRequest.isInstance(request)) {
|
64 | request.headers[REQUEST_HEADER] = "attempt=".concat(attempts + 1, "; max=").concat(maxAttempts);
|
65 | }
|
66 | if (!(options === null || options === void 0 ? void 0 : options.beforeRequest)) return [3, 2];
|
67 | return [4, options.beforeRequest()];
|
68 | case 1:
|
69 | _c.sent();
|
70 | _c.label = 2;
|
71 | case 2: return [4, next(args)];
|
72 | case 3:
|
73 | _b = _c.sent(), response = _b.response, output = _b.output;
|
74 | if (options === null || options === void 0 ? void 0 : options.afterRequest) {
|
75 | options.afterRequest(response);
|
76 | }
|
77 | this_1.retryQuota.releaseRetryTokens(retryTokenAmount);
|
78 | output.$metadata.attempts = attempts + 1;
|
79 | output.$metadata.totalRetryDelay = totalDelay;
|
80 | return [2, { value: { response: response, output: output } }];
|
81 | case 4:
|
82 | e_1 = _c.sent();
|
83 | err = asSdkError(e_1);
|
84 | attempts++;
|
85 | if (!this_1.shouldRetry(err, attempts, maxAttempts)) return [3, 6];
|
86 | retryTokenAmount = this_1.retryQuota.retrieveRetryTokens(err);
|
87 | delay_1 = this_1.delayDecider(isThrottlingError(err) ? THROTTLING_RETRY_DELAY_BASE : DEFAULT_RETRY_DELAY_BASE, attempts);
|
88 | totalDelay += delay_1;
|
89 | return [4, new Promise(function (resolve) { return setTimeout(resolve, delay_1); })];
|
90 | case 5:
|
91 | _c.sent();
|
92 | return [2, "continue"];
|
93 | case 6:
|
94 | if (!err.$metadata) {
|
95 | err.$metadata = {};
|
96 | }
|
97 | err.$metadata.attempts = attempts;
|
98 | err.$metadata.totalRetryDelay = totalDelay;
|
99 | throw err;
|
100 | case 7: return [2];
|
101 | }
|
102 | });
|
103 | };
|
104 | this_1 = this;
|
105 | _a.label = 2;
|
106 | case 2:
|
107 | if (!true) return [3, 4];
|
108 | return [5, _loop_1()];
|
109 | case 3:
|
110 | state_1 = _a.sent();
|
111 | if (typeof state_1 === "object")
|
112 | return [2, state_1.value];
|
113 | return [3, 2];
|
114 | case 4: return [2];
|
115 | }
|
116 | });
|
117 | });
|
118 | };
|
119 | return StandardRetryStrategy;
|
120 | }());
|
121 | export { StandardRetryStrategy };
|
122 | var asSdkError = function (error) {
|
123 | if (error instanceof Error)
|
124 | return error;
|
125 | if (error instanceof Object)
|
126 | return Object.assign(new Error(), error);
|
127 | if (typeof error === "string")
|
128 | return new Error(error);
|
129 | return new Error("AWS SDK error wrapper for ".concat(error));
|
130 | };
|