Skip to content
This repository was archived by the owner on Jan 24, 2023. It is now read-only.

Commit

Permalink
upgrade retry, lose lift
Browse files Browse the repository at this point in the history
  • Loading branch information
Elmer Bulthuis committed Oct 28, 2019
1 parent 8d1fb3a commit ef4023c
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 182 deletions.
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from "./delay";
export * from "./retry";
export * from "./defer";
export * from "./random";
27 changes: 0 additions & 27 deletions src/lift.spec.ts

This file was deleted.

78 changes: 0 additions & 78 deletions src/lift.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/main.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from "./lift";
export * from "./delay";
export * from "./retry";
export * from "./defer";
3 changes: 3 additions & 0 deletions src/random.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function randomBetween(min: number, max: number) {
return min + (max - min) * Math.random();
}
71 changes: 23 additions & 48 deletions src/retry.spec.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,30 @@
import * as test from "blue-tape";
import { retry } from "./retry";

test("retry", async (t) => {

{
let callCount = 0;
const offset = new Date().valueOf();
const result = await retry((attempt) => {
callCount++;
return true;
});

const time = new Date().valueOf() - offset;
t.ok(result);
t.equal(callCount, 1);
t.ok(time >= 0 && time < 1000, `time should be 0ms`);
}

{
let callCount = 0;
const offset = new Date().valueOf();
const result = await retry((attempt) => {
callCount++;
if (attempt === 2) return true;
throw new Error("cancel");
}, { intervalBase: 1000, intervalIncrement: 1000 });

const time = new Date().valueOf() - offset;
t.ok(result);
t.equal(callCount, 3);
t.ok(time >= 3000 && time < 4000, `time should be 3000ms`);
}

{
let callCount = 0;
const offset = new Date().valueOf();

try {
const result = await retry((attempt) => {
callCount++;
throw new Error("cancel");
}, { intervalBase: 1000, intervalIncrement: 0, retryLimit: 4 });
t.fail("should throw");
}
catch (err) {
t.pass("should throw");
test("does not retry when told not to", async (t) => {
let triesLeft = 3;
const retryLogic = async () => await retry(() => {
if (triesLeft > 0) {
t.comment("Retry!");
throw new Error("Error");
}
},
{},
_ => (triesLeft-- > 0),
);

const time = new Date().valueOf() - offset;
t.equal(callCount, 4);
t.ok(time >= 4000 && time < 5000, `time should be 3000ms`);
}
t.doesNotThrow(retryLogic, "did not retry when no tries were left");
});

test("forwards error to caller", async (t) => {
try {
await retry(() => {
throw new Error("InnerError");
},
{},
_ => false,
);
} catch (err) {
t.equal(err.message, "InnerError", "error messages match");
}
});
56 changes: 32 additions & 24 deletions src/retry.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,47 @@
import { delay } from "./delay";
import { delay, randomBetween } from ".";

export interface RetryConfig {
retryLimit: number;
intervalBase: number;
intervalIncrement: number;
retryLimit?: number;
intervalCap?: number;
intervalBase?: number;
}

export const defaultRetryConfig = Object.freeze<RetryConfig>({
retryLimit: 3,
intervalBase: 10 * 1000, // 10 seconds
intervalIncrement: 10 * 1000, // 10 seconds,
});
export const defaultRetryConfig = {
retryLimit: 10,
intervalCap: 5000,
intervalBase: 100,
};

export async function retry<T>(
job: (attempt: number) => PromiseLike<T> | T,
config: Partial<RetryConfig> = {},
config: RetryConfig = {},
shouldTryAgain = (error: any) => true,
): Promise<T> {
const {
retryLimit, intervalBase, intervalIncrement,
} = { ...defaultRetryConfig, ...config } as RetryConfig;

let lastError: any;

for (let attempt = 0; attempt < retryLimit; attempt++) {
retryLimit,
intervalBase,
intervalCap,
} = { ...defaultRetryConfig, ...config };
let retryAttempt = 0;
let intervalCurrent = intervalBase;
while (true) {
try {
const result: T = await job(attempt);
const result = await job(retryAttempt);
return result;
}
catch (err) {
lastError = err;
await delay(
intervalBase + intervalIncrement * attempt,
);
catch (error) {
if (
retryAttempt < retryLimit &&
shouldTryAgain(error)
) {
error = null;
}
if (error) throw error;
}
}
// https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
intervalCurrent = Math.min(intervalCap, randomBetween(intervalBase, intervalCurrent * 3));
await delay(intervalCurrent);

throw lastError;
retryAttempt++;
}
}
5 changes: 1 addition & 4 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,7 @@
"one-line": false,
"arrow-parens": false,
"curly": false,
"object-literal-sort-keys": [
true,
"declaration-order"
]
"object-literal-sort-keys": false
},
"rulesDirectory": []
}

0 comments on commit ef4023c

Please sign in to comment.