Skip to content

Commit

Permalink
Add limited retries to lock acquisition (#49)
Browse files Browse the repository at this point in the history
The lock acquisition could continue forever. This ensures there is a hard limit on the number of times its attempted.
  • Loading branch information
jphastings authored Jul 10, 2023
1 parent c93893a commit 77906b2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 3 deletions.
40 changes: 40 additions & 0 deletions __tests__/retry.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { retry } from "../src/retry";

describe("retry", () => {
it("returns function output on success", async () => {
const result = await retry(() => Promise.resolve(true));
expect(result).toBe(true);
});

it("throws error after exactly specified number of times", async () => {
const maxRetries = 5;
let timesCalled = 0;
const result = await retry(
async () => {
timesCalled++;
return Promise.resolve(timesCalled === maxRetries);
},
maxRetries,
"did not execute enough times",
0
);

expect(timesCalled).toBe(maxRetries);
expect(result).toBe(true);
});

it("pauses between executions", async () => {
const waitTimeMs = 1234;
const perform = async () =>
await retry(() => Promise.resolve(false), 2, "intentional final failure", waitTimeMs);

const start = Date.now();
await expect(perform()).rejects.toThrow("intentional final failure");
const execTimeMs = Date.now() - start;

// Must wait at least this long
expect(execTimeMs).toBeGreaterThanOrEqual(waitTimeMs);
// Will be some delay for the expectation, but more than a second out causes unpredictability
expect(execTimeMs).toBeLessThan(waitTimeMs + 1000);
});
});
6 changes: 3 additions & 3 deletions src/main.run.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { GithubApiManager } from "./GithubApiManager";
import { mergeDeployablePullRequests, getBaseBranch } from "./mergeDeployablePullRequests";
import { GitCommandManager } from "./GitCommandManager";
import { promises } from "fs";
import { retry } from "./retry";
import { acquireLock, removeLock } from "./acquireLock";
const { mkdtemp } = promises;

Expand Down Expand Up @@ -47,9 +48,8 @@ export async function run() {
const github = new GithubApiManager(token, owner, repo);
const lockBranchName = getInput(lockBranchNameInputName);
const lockCheckIntervalInMs = Number(getInput(lockCheckIntervalInputName));
while (!(await acquireLock(github, lockBranchName, baseBranch))) {
await new Promise(resolve => setTimeout(resolve, lockCheckIntervalInMs));
}
const acquireThisLock = () => acquireLock(github, lockBranchName, baseBranch);
await retry(acquireThisLock, 5, "Could not acquire lock", lockCheckIntervalInMs);
const workingDirectory = await mkdtemp("git-workspace");
const git = new GitCommandManager(workingDirectory, user, token);
await mergeDeployablePullRequests(
Expand Down
18 changes: 18 additions & 0 deletions src/retry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export const retry = async (
fn: () => Promise<boolean>,
retries = 3,
finalError = `Retried ${retries} times without success`,
waitBetweenMs = 1000
): Promise<boolean> => {
for (let i = 0; i < retries; i++) {
const success = await fn();
if (success) {
return Promise.resolve(true);
}

if (i < retries - 1) {
await new Promise(resolve => setTimeout(resolve, waitBetweenMs));
}
}
throw new Error(finalError);
};

0 comments on commit 77906b2

Please sign in to comment.