Skip to content

Commit 8e4e1ea

Browse files
kuhetrivikr
andauthored
docs(util-retry): add readme for util-retry, add configurable backoff (#4702)
* docs(util-retry): add docs for util retry * docs(util-retry): add readme for util-retry, add configurable backoff * fix: typo getRetyErrorInfo to getRetryErrorInfo --------- Co-authored-by: Trivikram Kamat <[email protected]>
1 parent 5f4ac23 commit 8e4e1ea

File tree

12 files changed

+172
-59
lines changed

12 files changed

+172
-59
lines changed

packages/middleware-retry/src/retryMiddleware.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const retryMiddleware =
5353
output.$metadata.totalRetryDelay = totalRetryDelay;
5454
return { response, output };
5555
} catch (e) {
56-
const retryErrorInfo = getRetyErrorInto(e);
56+
const retryErrorInfo = getRetryErrorInfo(e);
5757
lastError = asSdkError(e);
5858
try {
5959
retryToken = await retryStrategy.refreshRetryTokenForRetry(retryToken, retryErrorInfo);
@@ -85,7 +85,7 @@ const isRetryStrategyV2 = (retryStrategy: RetryStrategy | RetryStrategyV2) =>
8585
typeof (retryStrategy as RetryStrategyV2).refreshRetryTokenForRetry !== "undefined" &&
8686
typeof (retryStrategy as RetryStrategyV2).recordSuccess !== "undefined";
8787

88-
const getRetyErrorInto = (error: SdkError): RetryErrorInfo => {
88+
const getRetryErrorInfo = (error: SdkError): RetryErrorInfo => {
8989
const errorInfo: RetryErrorInfo = {
9090
errorType: getRetryErrorType(error),
9191
};

packages/types/src/retry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export interface RetryStrategyV2 {
142142
refreshRetryTokenForRetry(tokenToRenew: RetryToken, errorInfo: RetryErrorInfo): Promise<RetryToken>;
143143

144144
/**
145-
* Upon successful completion of the operation, a user calls this function
145+
* Upon successful completion of the operation, this function is called
146146
* to record that the operation was successful.
147147
*/
148148
recordSuccess(token: RetryToken): void;

packages/util-retry/README.md

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,61 @@
33
[![NPM version](https://img.shields.io/npm/v/@aws-sdk/util-retry/latest.svg)](https://www.npmjs.com/package/@aws-sdk/util-retry)
44
[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/util-retry.svg)](https://www.npmjs.com/package/@aws-sdk/util-retry)
55

6-
> An internal package
7-
86
This package provides shared utilities for retries.
97

108
## Usage
119

12-
You probably shouldn't, at least directly.
10+
### Default
11+
12+
By default, each client already has a default retry strategy. The default retry count is 3, and
13+
only retryable errors will be retried.
14+
15+
[AWS Documentation: Retry behavior](https://docs.aws.amazon.com/sdkref/latest/guide/feature-retry-behavior.html).
16+
17+
```js
18+
import { S3Client } from "@aws-sdk/client-s3";
19+
20+
const client = new S3Client({}); // default retry strategy included.
21+
```
22+
23+
### MaxAttempts
24+
25+
If you want to change the number of attempts, you can use the `StandardRetryStrategy` from `@aws-sdk/util-retry`.
26+
27+
```js
28+
import { S3Client } from "@aws-sdk/client-s3";
29+
import { StandardRetryStrategy } from "@aws-sdk/util-retry";
30+
31+
const client = new S3Client({
32+
// custom max number of attempts.
33+
retryStrategy: new StandardRetryStrategy(4),
34+
});
35+
```
36+
37+
This is recommended because the `StandardRetryStrategy` includes backoff calculation,
38+
deciding whether an error should be retried, and a retry token counter.
39+
40+
### MaxAttempts and BackoffComputation
41+
42+
If you want to change the number of attempts and use a custom delay
43+
computation, you can use the `ConfiguredRetryStrategy` from `@aws-sdk/util-retry`.
44+
45+
```js
46+
import { S3Client } from "@aws-sdk/client-s3";
47+
import { ConfiguredRetryStrategy } from "@aws-sdk/util-retry";
48+
49+
const client = new S3Client({
50+
retryStrategy: new ConfiguredRetryStrategy(
51+
4, // max attempts.
52+
(attempt: number) => 100 + attempt * 1000 // backoff function.
53+
),
54+
});
55+
```
56+
57+
This example sets the backoff at 100ms plus 1s per attempt.
58+
59+
### Further customization
60+
61+
You can implement the `RetryStrategyV2` interface.
62+
63+
https://github.com/aws/aws-sdk-js-v3/blob/main/packages/types/src/retry.ts

packages/util-retry/src/AdaptiveRetryStrategy.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import { StandardRetryStrategy } from "./StandardRetryStrategy";
66
import { RateLimiter } from "./types";
77

88
/**
9-
* @internal
10-
*
9+
* @public
10+
*
1111
* Strategy options to be passed to AdaptiveRetryStrategy
1212
*/
1313
export interface AdaptiveRetryStrategyOptions {
1414
rateLimiter?: RateLimiter;
1515
}
1616

1717
/**
18-
* @internal
19-
*
18+
* @public
19+
*
2020
* The AdaptiveRetryStrategy is a retry strategy for executing against a very
2121
* resource constrained set of resources. Care should be taken when using this
2222
* retry strategy. By default, it uses a dynamic backoff delay based on load
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { ConfiguredRetryStrategy } from "./ConfiguredRetryStrategy";
2+
3+
describe(ConfiguredRetryStrategy.name, () => {
4+
it("allows setting a custom backoff function", async () => {
5+
const strategy = new ConfiguredRetryStrategy(5, (attempt) => attempt * 1000);
6+
7+
const token = await strategy.acquireInitialRetryToken("");
8+
token.getRetryCount = () => 4;
9+
10+
const retryToken = await strategy.refreshRetryTokenForRetry(token, {
11+
errorType: "TRANSIENT",
12+
});
13+
14+
expect(retryToken.getRetryCount()).toBe(4);
15+
expect(retryToken.getRetryDelay()).toBe(4000);
16+
});
17+
});
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type {
2+
Provider,
3+
RetryBackoffStrategy,
4+
RetryErrorInfo,
5+
RetryStrategyV2,
6+
StandardRetryToken,
7+
} from "@aws-sdk/types";
8+
9+
import { DEFAULT_RETRY_DELAY_BASE } from "./constants";
10+
import { StandardRetryStrategy } from "./StandardRetryStrategy";
11+
12+
/**
13+
* @public
14+
*
15+
* This extension of the StandardRetryStrategy allows customizing the
16+
* backoff computation.
17+
*/
18+
export class ConfiguredRetryStrategy extends StandardRetryStrategy implements RetryStrategyV2 {
19+
private readonly computeNextBackoffDelay: (attempt: number) => number;
20+
/**
21+
* @param maxAttempts - the maximum number of retry attempts allowed.
22+
* e.g., if set to 3, then 4 total requests are possible.
23+
* @param computeNextBackoffDelay - a millisecond delay for each retry or a function that takes the retry attempt
24+
* and returns the delay.
25+
*
26+
* @example exponential backoff.
27+
* ```js
28+
* new Client({
29+
* retryStrategy: new ConfiguredRetryStrategy(3, (attempt) => attempt ** 2)
30+
* });
31+
* ```
32+
* @example constant delay.
33+
* ```js
34+
* new Client({
35+
* retryStrategy: new ConfiguredRetryStrategy(3, 2000)
36+
* });
37+
* ```
38+
*/
39+
public constructor(
40+
maxAttempts: number | Provider<number>,
41+
computeNextBackoffDelay: number | RetryBackoffStrategy["computeNextBackoffDelay"] = DEFAULT_RETRY_DELAY_BASE
42+
) {
43+
super(typeof maxAttempts === "function" ? maxAttempts : async () => maxAttempts);
44+
if (typeof computeNextBackoffDelay === "number") {
45+
this.computeNextBackoffDelay = () => computeNextBackoffDelay;
46+
} else {
47+
this.computeNextBackoffDelay = computeNextBackoffDelay;
48+
}
49+
}
50+
51+
public async refreshRetryTokenForRetry(
52+
tokenToRenew: StandardRetryToken,
53+
errorInfo: RetryErrorInfo
54+
): Promise<StandardRetryToken> {
55+
const token = await super.refreshRetryTokenForRetry(tokenToRenew, errorInfo);
56+
token.getRetryDelay = () => this.computeNextBackoffDelay(token.getRetryCount());
57+
return token;
58+
}
59+
}

packages/util-retry/src/DefaultRateLimiter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { isThrottlingError } from "@aws-sdk/service-error-classification";
33
import { RateLimiter } from "./types";
44

55
/**
6-
* @internal
6+
* @public
77
*/
88
export interface DefaultRateLimiterOptions {
99
beta?: number;
@@ -14,7 +14,7 @@ export interface DefaultRateLimiterOptions {
1414
}
1515

1616
/**
17-
* @internal
17+
* @public
1818
*/
1919
export class DefaultRateLimiter implements RateLimiter {
2020
// User configurable constants

packages/util-retry/src/StandardRetryStrategy.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,18 @@ import { DEFAULT_RETRY_DELAY_BASE, INITIAL_RETRY_TOKENS } from "./constants";
55
import { getDefaultRetryToken } from "./defaultRetryToken";
66

77
/**
8-
* @internal
8+
* @public
99
*/
1010
export class StandardRetryStrategy implements RetryStrategyV2 {
11-
private retryToken: StandardRetryToken;
1211
public readonly mode: string = RETRY_MODES.STANDARD;
12+
private retryToken: StandardRetryToken;
13+
private readonly maxAttemptsProvider: Provider<number>;
1314

14-
constructor(private readonly maxAttemptsProvider: Provider<number>) {
15+
constructor(maxAttempts: number);
16+
constructor(maxAttemptsProvider: Provider<number>);
17+
constructor(private readonly maxAttempts: number | Provider<number>) {
1518
this.retryToken = getDefaultRetryToken(INITIAL_RETRY_TOKENS, DEFAULT_RETRY_DELAY_BASE);
16-
this.maxAttemptsProvider = maxAttemptsProvider;
19+
this.maxAttemptsProvider = typeof maxAttempts === "function" ? maxAttempts : async () => maxAttempts;
1720
}
1821

1922
public async acquireInitialRetryToken(retryTokenScope: string): Promise<StandardRetryToken> {

packages/util-retry/src/config.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
/**
2-
* @internal
2+
* @public
33
*/
44
export enum RETRY_MODES {
55
STANDARD = "standard",
66
ADAPTIVE = "adaptive",
77
}
88

99
/**
10-
* @internal
11-
*
10+
* @public
11+
*
1212
* The default value for how many HTTP requests an SDK should make for a
1313
* single SDK operation invocation before giving up
1414
*/
1515
export const DEFAULT_MAX_ATTEMPTS = 3;
1616

1717
/**
18-
* @internal
19-
*
18+
* @public
19+
*
2020
* The default retry algorithm to use.
2121
*/
22-
export const DEFAULT_RETRY_MODE = "STANDARD" as RETRY_MODES;
22+
export const DEFAULT_RETRY_MODE = RETRY_MODES.STANDARD;

packages/util-retry/src/constants.ts

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,67 @@
11
/**
2-
* @internal
3-
*
2+
* @public
3+
*
44
* The base number of milliseconds to use in calculating a suitable cool-down
55
* time when a retryable error is encountered.
66
*/
77
export const DEFAULT_RETRY_DELAY_BASE = 100;
88

99
/**
10-
* @internal
11-
*
10+
* @public
11+
*
1212
* The maximum amount of time (in milliseconds) that will be used as a delay
1313
* between retry attempts.
1414
*/
1515
export const MAXIMUM_RETRY_DELAY = 20 * 1000;
1616

1717
/**
18-
* @internal
19-
*
18+
* @public
19+
*
2020
* The retry delay base (in milliseconds) to use when a throttling error is
2121
* encountered.
2222
*/
2323
export const THROTTLING_RETRY_DELAY_BASE = 500;
2424

2525
/**
26-
* @internal
27-
*
26+
* @public
27+
*
2828
* Initial number of retry tokens in Retry Quota
2929
*/
3030
export const INITIAL_RETRY_TOKENS = 500;
3131

3232
/**
33-
* @internal
34-
*
33+
* @public
34+
*
3535
* The total amount of retry tokens to be decremented from retry token balance.
3636
*/
3737
export const RETRY_COST = 5;
3838

3939
/**
40-
* @internal
41-
*
40+
* @public
41+
*
4242
* The total amount of retry tokens to be decremented from retry token balance
4343
* when a throttling error is encountered.
4444
*/
4545
export const TIMEOUT_RETRY_COST = 10;
4646

4747
/**
48-
* @internal
49-
*
48+
* @public
49+
*
5050
* The total amount of retry token to be incremented from retry token balance
5151
* if an SDK operation invocation succeeds without requiring a retry request.
5252
*/
5353
export const NO_RETRY_INCREMENT = 1;
5454

5555
/**
56-
* @internal
57-
*
56+
* @public
57+
*
5858
* Header name for SDK invocation ID
5959
*/
6060
export const INVOCATION_ID_HEADER = "amz-sdk-invocation-id";
6161

6262
/**
63-
* @internal
64-
*
63+
* @public
64+
*
6565
* Header name for request retry information.
6666
*/
6767
export const REQUEST_HEADER = "amz-sdk-request";

packages/util-retry/src/defaultRetryToken.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
import { getDefaultRetryBackoffStrategy } from "./defaultRetryBackoffStrategy";
1212

1313
/**
14-
* @internal
14+
* @public
1515
*/
1616
export interface DefaultRetryTokenOptions {
1717
/**

packages/util-retry/src/index.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,7 @@
1-
/**
2-
* @internal
3-
*/
41
export * from "./AdaptiveRetryStrategy";
5-
/**
6-
* @internal
7-
*/
2+
export * from "./ConfiguredRetryStrategy";
83
export * from "./DefaultRateLimiter";
9-
/**
10-
* @internal
11-
*/
124
export * from "./StandardRetryStrategy";
13-
/**
14-
* @internal
15-
*/
165
export * from "./config";
17-
/**
18-
* @internal
19-
*/
206
export * from "./constants";
21-
/**
22-
* @internal
23-
*/
247
export * from "./types";

0 commit comments

Comments
 (0)