Skip to content

Commit

Permalink
feat(util-waiter): add jitter to waiters exponential backoff
Browse files Browse the repository at this point in the history
  • Loading branch information
AllanZhengYP committed Dec 7, 2020
1 parent 2b52619 commit 88ac270
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 16 deletions.
2 changes: 1 addition & 1 deletion packages/util-waiter/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Shared utilities for client waiters for the AWS SDK",
"dependencies": {
"tslib": "^1.8.0",
"@aws-sdk/abort-controller": "1.0.0-rc.7"
"@aws-sdk/abort-controller": "1.0.0-rc.8"
},
"devDependencies": {
"@types/jest": "^26.0.4",
Expand Down
16 changes: 9 additions & 7 deletions packages/util-waiter/src/poller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,12 @@ describe(runPolling.name, () => {

beforeEach(() => {
(sleep as jest.Mock).mockResolvedValueOnce("");
jest.spyOn(global.Math, "random").mockReturnValue(0.5);
});

afterEach(() => {
jest.clearAllMocks();
jest.spyOn(global.Math, "random").mockRestore();
});

it("should returns state in case of failure", async () => {
Expand Down Expand Up @@ -80,12 +82,12 @@ describe(runPolling.name, () => {

expect(sleep).toHaveBeenCalled();
expect(sleep).toHaveBeenCalledTimes(7);
expect(sleep).toHaveBeenNthCalledWith(1, 2);
expect(sleep).toHaveBeenNthCalledWith(2, 4);
expect(sleep).toHaveBeenNthCalledWith(3, 8);
expect(sleep).toHaveBeenNthCalledWith(4, 16);
expect(sleep).toHaveBeenNthCalledWith(5, 30);
expect(sleep).toHaveBeenNthCalledWith(6, 30);
expect(sleep).toHaveBeenNthCalledWith(7, 30);
expect(sleep).toHaveBeenNthCalledWith(1, 2); // min delay
expect(sleep).toHaveBeenNthCalledWith(2, 3); // +random() * 2
expect(sleep).toHaveBeenNthCalledWith(3, 5); // +random() * 4
expect(sleep).toHaveBeenNthCalledWith(4, 9); // +random() * 8
expect(sleep).toHaveBeenNthCalledWith(5, 17); // +random() * 16
expect(sleep).toHaveBeenNthCalledWith(6, 30); // max delay
expect(sleep).toHaveBeenNthCalledWith(7, 30); // max delay
});
});
18 changes: 10 additions & 8 deletions packages/util-waiter/src/poller.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { sleep } from "./utils/sleep";
import { WaiterOptions, WaiterResult, WaiterState } from "./waiter";

function exponentialBackoff(floor: number, ciel: number, attempt: number): number {
return Math.min(ciel, floor * 2 ** attempt);
}
/**
* Reference: https://github.com/awslabs/smithy/pull/656
* The theoretical limit to the attempt is max delay cannot be > Number.MAX_VALUE, but it's unlikely because of
* `maxWaitTime`
*/
const exponentialBackoff = (floor: number, ciel: number, attempt: number) =>
Math.floor(Math.min(ciel, randomInRange(floor, floor * 2 ** (attempt - 1))));
const randomInRange = (min: number, max: number) => min + Math.random() * (max - min);

/**
* Function that runs indefinite polling as part of waiters.
Expand All @@ -13,22 +18,19 @@ function exponentialBackoff(floor: number, ciel: number, attempt: number): numbe
* @param stateChecker function that checks the acceptor states on each poll.
*/
export const runPolling = async <T, S>(
params: WaiterOptions,
{ minDelay, maxDelay }: WaiterOptions,
client: T,
input: S,
acceptorChecks: (client: T, input: S) => Promise<WaiterResult>
): Promise<WaiterResult> => {
let currentAttempt = 1;
let currentDelay = params.minDelay;

while (true) {
await sleep(currentDelay);
await sleep(exponentialBackoff(minDelay, maxDelay, currentAttempt));
const { state } = await acceptorChecks(client, input);
if (state === WaiterState.SUCCESS || state === WaiterState.FAILURE) {
return { state };
}

currentDelay = exponentialBackoff(params.minDelay, params.maxDelay, currentAttempt);
currentAttempt += 1;
}
};

0 comments on commit 88ac270

Please sign in to comment.