Skip to content

Commit 6f95bfb

Browse files
committed
Warn about the importance of catching exceptions in DO alarm handlers
Since otherwise a series of repeated uncaught exceptions can cause alarms to stop getting retried. That behavior was documented, but it isn't obvious that that means you may want to be careful about catching exceptions and scheduling your own new alarms.
1 parent 8fb697a commit 6f95bfb

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

src/content/docs/durable-objects/api/alarms.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ This is due to the fact that, if the Durable Object wakes up after being inactiv
123123

124124
- This method can be `async`.
125125

126+
:::note[Catching exceptions in alarm handlers]
127+
128+
Because alarms are only retried up to 6 times on error, it's recommended to catch any exceptions inside your `alarm()` handler and schedule a new alarm before returning if you want to make sure your alarm handler will be retried indefinitely. Otherwise, a sufficiently long outage in a downstream service that you depend on or a bug in your code that goes unfixed for hours can exhaust the limited number of retries, causing the alarm to not be re-run in the future until the next time you call `setAlarm`.
129+
130+
:::
131+
126132
## Example
127133

128134
This example shows how to both set alarms with the `setAlarm(timestamp)` method and handle alarms with the `alarm()` handler within your Durable Object.

src/content/docs/durable-objects/best-practices/rules-of-durable-objects.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1318,7 +1318,7 @@ export class GameMatch extends DurableObject<Env> {
13181318
}
13191319

13201320
// Called when the alarm fires
1321-
async alarm() {
1321+
async alarm(alarmInfo) {
13221322
const isActive = await this.ctx.storage.get<boolean>("gameActive");
13231323

13241324
if (!isActive) {
@@ -1330,7 +1330,17 @@ export class GameMatch extends DurableObject<Env> {
13301330
await this.ctx.storage.put("gameEnded", Date.now());
13311331

13321332
// Calculate final scores, notify players, etc.
1333-
await this.calculateFinalScores();
1333+
try {
1334+
await this.calculateFinalScores();
1335+
} catch (err) {
1336+
// If we're almost out of retries but still have work to do, schedule a new alarm
1337+
// rather than letting our retries run out to ensure we keep getting invoked.
1338+
if (alarmInfo.retryCount >= 5) {
1339+
await this.ctx.storage.setAlarm(Date.now() + 30 * 1000);
1340+
return;
1341+
}
1342+
throw err;
1343+
}
13341344

13351345
// Schedule the next alarm only if there's more work to do
13361346
// In this case, schedule cleanup in 24 hours

0 commit comments

Comments
 (0)