Skip to content

Commit 030dc74

Browse files
committed
fix: perpetually retry persistent tasks
1 parent d9fca04 commit 030dc74

File tree

3 files changed

+54
-8
lines changed

3 files changed

+54
-8
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ Retries are configured by passing a `retry` property to the trigger configuratio
401401
```ts
402402
/**
403403
* @property factor The exponential factor to use. Default is 2.
404-
* @property maxTimeout The maximum number of milliseconds between two retries. Default is Infinity.
404+
* @property maxTimeout The maximum number of milliseconds between two retries. Default is 30,000.
405405
* @property minTimeout The number of milliseconds before starting the first retry. Default is 1000.
406406
* @property retries The maximum amount of times to retry the operation. Default is 0. Seting this to 1 means do it once, then retry it once.
407407
*/

src/subscribe.ts

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import {
77
type Subscription,
88
type Trigger,
99
} from './types';
10+
import { setTimeout } from 'node:timers/promises';
11+
import { serializeError } from 'serialize-error';
1012

1113
const log = Logger.child({
1214
namespace: 'subscribe',
@@ -67,11 +69,21 @@ const runTask = async ({
6769

6870
let attempt = -1;
6971

70-
while (attempt++ < trigger.retry.retries) {
71-
const retriesLeft = trigger.retry.retries - attempt;
72+
while (true) {
73+
attempt++;
7274

73-
if (retriesLeft < 0) {
74-
throw new Error('Expected retries left to be greater than or equal to 0');
75+
if (attempt > 0) {
76+
const retryFactor = trigger.retry.factor ?? 2;
77+
const minTimeout = trigger.retry.minTimeout ?? 1_000;
78+
const maxTimeout = trigger.retry.maxTimeout ?? 30_000;
79+
const delay = Math.min(
80+
attempt * retryFactor * minTimeout,
81+
trigger.retry.maxTimeout ?? maxTimeout,
82+
);
83+
84+
log.debug('delaying retry by %dms...', delay);
85+
86+
await setTimeout(delay);
7587
}
7688

7789
try {
@@ -92,13 +104,40 @@ const runTask = async ({
92104
}),
93105
taskId,
94106
});
107+
108+
return;
95109
} catch (error) {
96110
if (error.name === 'AbortError') {
97111
log.warn('%s (%s): task aborted', trigger.name, taskId);
98112

99113
return;
100114
}
101115

116+
log.warn(
117+
{
118+
error: serializeError(error),
119+
},
120+
'routine produced an error',
121+
);
122+
123+
if (trigger.persistent) {
124+
log.warn(
125+
'%s (%s): retrying because the trigger is persistent',
126+
trigger.name,
127+
taskId,
128+
);
129+
130+
continue;
131+
}
132+
133+
const retriesLeft = trigger.retry.retries - attempt;
134+
135+
if (retriesLeft < 0) {
136+
throw new Error(
137+
'Expected retries left to be greater than or equal to 0',
138+
);
139+
}
140+
102141
if (retriesLeft === 0) {
103142
log.warn(
104143
'%s (%s): task will not be retried; attempts exhausted',
@@ -229,8 +268,15 @@ export const subscribe = (trigger: Trigger): Subscription => {
229268
}
230269
})
231270
// eslint-disable-next-line promise/prefer-await-to-then
232-
.catch(() => {
233-
log.warn('%s (%s): task failed', trigger.name, taskId);
271+
.catch((error) => {
272+
log.warn(
273+
{
274+
error: serializeError(error),
275+
},
276+
'%s (%s): task failed',
277+
trigger.name,
278+
taskId,
279+
);
234280
});
235281

236282
log.debug('%s (%s): started task', trigger.name, taskId);

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ type OnTeardownEventHandler = (event: TeardownEvent) => Promise<void>;
7676

7777
/**
7878
* @property factor The exponential factor to use. Default is 2.
79-
* @property maxTimeout The maximum number of milliseconds between two retries. Default is Infinity.
79+
* @property maxTimeout The maximum number of milliseconds between two retries. Default is 30,000.
8080
* @property minTimeout The number of milliseconds before starting the first retry. Default is 1000.
8181
* @property retries The maximum amount of times to retry the operation. Default is 10. Seting this to 1 means do it once, then retry it once.
8282
*/

0 commit comments

Comments
 (0)