| name | durable-timers |
| description | Expert knowledge on durable timer patterns for recurring tasks, polling, and scheduled actions. |
Durable Timer Patterns
You are running in a durable execution environment with wait, cron, and cron_at tools that survive process restarts and node migrations.
Patterns
Recurring Task
1. cron(interval_seconds, reason="...")
2. Do work
3. Finish the turn normally
4. The orchestration wakes you again on the next interval
5. On each wake-up, perform the scheduled work before responding
Wall-Clock Schedule
1. cron_at(minute=M, hour=H, tz="Area/City", reason="...")
2. Finish the turn normally
3. The orchestration wakes you at the next matching calendar instant
4. On each wake-up, perform the scheduled work described by the reason before responding
Use cron_at for named calendar times such as daily at 02:00 UTC, hourly at HH:05, Mondays at 09:00 America/New_York, or monthly on day 1 at 04:00. Use max_fires: 1 for a one-shot scheduled-at-time action.
Polling with Backoff
loop:
1. Check condition
2. If met → done
3. wait(backoff_seconds) // increase each iteration
4. goto loop
Scheduled One-Shot
1. wait(delay_seconds)
2. Do the scheduled work
Rules
- Use
cron for recurring or periodic work
- Use
cron_at for wall-clock schedules; do not wake every N minutes just to check the current time
- When a cron or cron_at wake-up resumes you, do the scheduled work immediately; do not merely say the schedule is active or resumed
- Use
wait for one-shot delays, polling backoff, or short pauses inside a turn
- NEVER use
setTimeout, sleep, or other external timing mechanisms
- Timer tools are durable: they persist across pod restarts and worker migrations
- The wait and cron tools accept seconds. For minutes: multiply by 60. The cron_at tool accepts explicit calendar fields and a required IANA
tz
- By default, after a long wait you resume on potentially a different worker node — don't rely on in-memory state
- If the wait depends on this specific worker's local state (for example a local process, file, or socket), call
wait(..., preserveWorkerAffinity: true)
preserveWorkerAffinity: true is best-effort affinity preservation, not a hard same-node guarantee