Skip to content

Named alarms

A Durable Object has one physical alarm. Durability lets operations and multiple named logical alarms share it without replacing one another’s wake-ups.

import { createDurability } from 'durability';
import { exponential, jitter } from 'durability/utils';
export class ImageJobs extends DurableObject<Env> {
private readonly durability = createDurability(
this.ctx,
{
resizeImage: async ({ payload }) =>
this.env.IMAGES.resize(payload.imageId),
},
{
alarms: {
cleanup: async ({
scheduledTime,
attempt,
idempotencyKey,
signal,
platform,
}) => {
await this.env.CLEANUP.fetch('https://cleanup.internal/run', {
method: 'POST',
headers: { 'Idempotency-Key': idempotencyKey },
signal,
});
console.log({ scheduledTime, attempt, platform });
},
},
alarmMethods: {
cleanup: {
attemptTimeoutMs: 60_000,
retries: {
maxAttempts: 5,
delay: (attempt) => jitter(exponential(attempt)),
},
retryTimeouts: true,
},
},
}
);
scheduleCleanup() {
return this.durability.alarm.cleanup(Date.now() + 5_000);
}
alarm(info?: AlarmInvocationInfo) {
return this.durability.alarm(info);
}
}

Each key in alarms becomes a typed scheduler method on durability.alarm.

Scheduling the same name again replaces its pending occurrence. If that name is already running:

  • The active handler continues.
  • The replacement remains pending and runs afterward.
  • A single name never has overlapping handlers within one Durable Object instance.
  • Different names can run concurrently.

A successful handler deletes only the occurrence it executed, so it cannot erase a replacement scheduled while it was running.

FieldMeaning
nameConfigured logical alarm name
scheduledTimeOriginal timestamp supplied to the scheduler
attemptOne-based attempt number
isRetryWhether this occurrence ran before
retryCountNumber of completed previous attempts
idempotencyKeyStable key for every attempt of this occurrence
signalAttempt-scoped timeout signal
platformCloudflare AlarmInvocationInfo, when supplied

Each schedule gets a new internal occurrence ID and idempotency key.

Named alarm timeouts are terminal by default because an external side effect may have completed before the caller stopped waiting. Set retryTimeouts: true only when the handler passes idempotencyKey to its side effects or can reconcile their outcome first.

If a timed-out handler ignores its signal, Durability holds the per-name execution lock until it actually settles. That prevents an overlapping retry.