Skip to content

Roadmap: portable durable scheduling #37

Description

@cardmagic

PRD: portable durable scheduling — remaining work

Status: Partially implemented
Owner: Solid Objects maintainers

Reminders now cover most of the original scope in both runtimes. This description has been rewritten to the parts that are not done, so nobody re-implements what shipped. The shipped portion is recorded at the bottom.

Remaining requirements

  • SCHED-003 (partial): Cancellation shall report whether a schedule was removed. unschedule and unscheduleAll stage an intent and return nothing, so a caller cannot tell a cancellation that removed a schedule from one that found nothing. Cancellation is already idempotent; only the report is missing.
  • SCHED-005 (partial): A skip missed-run policy shall exist, dropping missed occurrences and waiting for the next scheduled time. Only latest (run once for a missed window) and all (catch up on every missed occurrence) exist. Decide at the same time whether to keep those names or adopt the PRD's run_once / catch_up, and document the mapping either way.
  • SCHED-007 (partial): Actor-facing inspection shall return last run, attempt, and failure information.
    • Last run is not stored. Neither schema has a column for it. This is the only item here that needs a migration.
    • Attempt and failure are stored but not reachable from an actor. occurrence and error are on the row and are exposed on the operator-facing ReminderRecord, while the actor-facing ScheduledReminder carries only name, operation, key, next run, interval, missed policy, status, and handle.

Remaining acceptance criteria

  • One conformance suite across adapters. Create, replace, cancel, recurring execution, each missed-run policy, restart recovery, authorization failure, and duplicate delivery are covered today, but by separate SQL and Durable Objects tests rather than one suite both adapters run. test/support/portable-runtime-contract.ts exercises no scheduling at all. Repeated parity audits on this project have found gaps that only a shared contract catches, so this is the highest-value item left.
  • Document the timing contract. Time zone, clock skew, retention, and retry behaviour for scheduled work are undocumented in both runtimes.

Not planned

  • reschedule(handle, { runAt }) as a distinct method. SCHED-002 is satisfied: scheduling an existing name replaces its next occurrence atomically, and the repository reports the replacement with its previous run time. A second spelling of that would add surface without adding capability. Reopen if the ergonomics prove otherwise.

Already implemented, for reference

  • SCHED-001 schedule({ at:, every:, missed:, key: }) returns a durable handle in both runtimes.
  • SCHED-002 A name identifies at most one active schedule per actor, and rescheduling replaces the next occurrence atomically.
  • SCHED-004 One-shot and recurring schedules, cancellable by operation, by key, or by handle.
  • SCHED-006 A fired reminder enqueues an ordinary message carrying reminder:<id>:<occurrence> as its idempotency key, so it inherits mailbox ordering, authorization, retry, dead letters, and deduplication.
  • SCHED-008 Restart recovery on every adapter: Durable Objects re-arm their alarm on construction, and the SQL scheduler reclaims from the reminders table.
  • The portability outcome holds. The same API runs over SQL polling, PostgreSQL notifications, and Durable Object alarms, and no caller names the mechanism.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions