Durable Object Alarm Scheduler
Per-entity durable timers, where the schedule lives with the thing being scheduled instead of in a table that a cron job sweeps. The serverless answer to send this in 36 hours has been a trigger every minute plus a due-at query, which runs 1,440 times a day whether or not anything is due, floors granularity at a minute, turns its LIMIT into a throughput ceiling under a spike, needs a lock so two overlapping sweeps do not double-send, and concentrates every tenant on one hot row range. An alarm is a timer the platform holds per object, so ten million pending reminders are ten million sleeping objects and zero running queries. The scheduler file is built around the constraint that there is exactly one alarm per object and setAlarm overwrites rather than enqueues: tasks live in the object's own SQLite table, every mutation routes through a single sync step that re-points the alarm at the earliest due row and deletes it when the queue empties, because a stale alarm bills a wake on every entity that ever had a task. The handler drains everything due rather than one row, counts the attempt before running it so a poison task gives up deliberately instead of exhausting the platform's retry budget and disappearing, uses alarmInfo.retryCount to reschedule rather than burn the last retry, anchors recurrence to now so a late wake does not fire every occurrence it slept through, and treats the caller-supplied task id as the idempotency key because a retry re-runs the side effect. The worker file makes the naming decision explicit, since getByName is the address and the grain chosen there is the one thing nothing downstream can fix, and it deliberately ships no cron trigger.
npx shadcn@latest add https://ui.aryank.space/r/durable-object-alarm-scheduler.jsonInstalls from ui.aryank.space. To add it by hand, copy the files in Files below, or register the @compronents namespace via the docs.
The timer lives with the entity as a DO alarm, firing exactly when due, instead of a per-minute cron sweeping a due-at query 1,440 times a day whether or not anything is due.
@cloudflare/[email protected]