Skip to content

Commit 6421b78

Browse files
committed
Cron should handle its own retries
1 parent 6b63ebd commit 6421b78

File tree

1 file changed

+29
-5
lines changed

1 file changed

+29
-5
lines changed

src/cron.ts

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,27 @@ import {
1717
WorkerEvents,
1818
} from "./interfaces";
1919
import {
20+
calculateDelay,
2021
coerceError,
2122
CompiledOptions,
2223
CompiledSharedOptions,
2324
Releasers,
25+
RetryOptions,
26+
sleep,
2427
} from "./lib";
2528

2629
interface CronRequirements {
2730
pgPool: Pool;
2831
events: WorkerEvents;
2932
}
3033

34+
const CRON_RETRY: RetryOptions = {
35+
maxAttempts: Infinity,
36+
minDelay: 200,
37+
maxDelay: 60_000,
38+
multiplier: 1.5,
39+
};
40+
3141
/**
3242
* This function looks through all the cron items we have (e.g. from our
3343
* crontab file) and compares them to the items we already know about. If the
@@ -347,7 +357,7 @@ export const runCron = (
347357
if (!stopCalled) {
348358
stopCalled = true;
349359
if (e) {
350-
promise.reject(e);
360+
promise.reject(coerceError(e));
351361
} else {
352362
promise.resolve();
353363
}
@@ -358,6 +368,17 @@ export const runCron = (
358368
}
359369
}
360370

371+
let attempts = 0;
372+
function restartCronAfterDelay(error: Error) {
373+
++attempts;
374+
const delay = calculateDelay(attempts - 1, CRON_RETRY);
375+
logger.error(
376+
`Cron hit an error; restarting in ${delay}ms (attempt ${attempts}): ${error}`,
377+
{ error, attempts },
378+
);
379+
sleep(delay).then(cronMain).catch(restartCronAfterDelay);
380+
}
381+
361382
async function cronMain() {
362383
if (!cron._active) {
363384
return stop();
@@ -411,6 +432,8 @@ export const runCron = (
411432
if (!cron._active) {
412433
return stop();
413434
}
435+
// Healthy!
436+
attempts = 0;
414437

415438
// THIS MUST COME BEFORE nextTimestamp IS MUTATED
416439
const digest = digestTimestamp(nextTimestamp);
@@ -516,9 +539,10 @@ export const runCron = (
516539
// timestamps on error).
517540
scheduleNextLoop();
518541
} catch (e) {
519-
// If something goes wrong; abort. The calling code should re-schedule
520-
// which will re-trigger the backfilling code.
521-
return stop(coerceError(e));
542+
// If something goes wrong; abort the current loop and restart cron
543+
// after an exponential back-off. This is essential because we need to
544+
// re-trigger the backfilling code.
545+
return restartCronAfterDelay(coerceError(e));
522546
}
523547
}
524548

@@ -540,7 +564,7 @@ export const runCron = (
540564
promise,
541565
};
542566

543-
cronMain().catch(stop);
567+
cronMain().catch(restartCronAfterDelay);
544568

545569
return cron;
546570
};

0 commit comments

Comments
 (0)