4.6 KiB
4.6 KiB
id, name, status, depends_on, scope, risk, impact, level, tags
| id | name | status | depends_on | scope | risk | impact | level | tags | ||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| sqlite-engine-scheduler-outbox | SQLite engine — scheduler (`schedule`/`unschedule`/`run_schedules`) and outbox helper | pending |
|
broad | high | component | implementation |
|
Description
Implement the scheduler collapse (ADR-009) and the outbox helper (ADR-019 §1, ADR-014) — the last mechanisms, and the only ones with their own long-running machinery:
Scheduler:
schedule(name, spec, queue, payload, opts)— upsert by name via the substrate'sscheduler_register; validate name AND queue argument (shared-namespace rules, ADR-021 §3); validate the spec against the v1 grammar (@every <n><unit>, s|m|h|d) before storage —InvalidSpec, no round trip (the substrate'sparse_every_intervalis the parser; map its rejection toInvalidSpec). Returns theScheduleread-back (via the core constructor).unschedule(name)— substrate'sscheduler_unregister;true= removed,false= absent.run_schedules(stop)— the leader loop (ADR-009 §1), engine-side async over the sync substrate tick:- Acquire the leadership lock (
__alkstore_scheduler— the reserved-prefix derived name; use the engine's own lock machinery with a derived TTL, engine-internal detail). - Loop: renew the lock — on loss, return
Err(LeadershipLost)before ticking (never fire on a stolen lease); callscheduler_tick(fires due boundaries, catch-up-capped at 64, skip-forward — all substrate-side); sleep untilscheduler_soonestorstop. - A cancelled
StopTokenends the sleep and returns the cleanOk(()). The loop runs inspawn_blocking(it's a blocking sleep loop) with the stop token checked between iterations — or a tokio-select shape over a watch channel wrapping the token (implementer's choice; the token's flip-everywhere semantics are core's).
- Acquire the leadership lock (
- The runner must be honest about the wake path: after a tick fires jobs, queue claims proceed through the ordinary queue machinery — the scheduler adds no delivery machinery (ADR-009 §3).
Outbox:
outbox(name)— validated constructor; derives the backing queue name__alkstore_outbox:{name}(reserved prefix — unreachable byenqueue_txby design).Outbox::enqueue— auto-commit enqueue into the backing queue, stamping the outbox's derived 60/5/5 set (ADR-014 §1) — the one non-plain defaults set.Outbox::run_once(worker_id, delivery)— claim one job from the backing queue, rundelivery.deliver(job):Ok⇒ ack;Err(e)⇒retry(Some(e), None)(the queue's curve).true= claimed and processed;false= no work. No engine-issued heartbeat inside delivery (the boxedJobHandle's own heartbeat is the consumer's renewal path).
Acceptance Criteria
scheduleupserts; spec/name/queue validation all pinned (InvalidSpecpre-storage,InvalidName/ReservedNameon both name and queue argument);Scheduleread-back correctunschedulereturns true/false correctlyrun_schedules: fires due boundaries into the named queue (jobs claimable via the ordinary queue surface); leadership lock held; a second concurrent runner does not double-fire (one-firer-per-boundary — the writer serialization floor)- Leadership loss returns
Err(LeadershipLost)before any tick; clean stop returnsOk(()); catch-up cap + skip-forward observable through the substrate's pinned behavior - Scheduler-fired jobs stamp the target queue's derived defaults
(300/3/5/none) with
ScheduleOptsapplied over them (ADR-020 §3) —get_job-visible - Outbox: derived backing queue unreachable by
enqueue_tx(reserved-prefix rejection);outbox_enqueue_tx(from the seam task) commit-atomic;enqueuestamps 60/5/5;run_onceacks onOk, retries onErr, returns false on empty cargo test -p alkstore-sqlite, clippy-D warnings, fmt clean
References
- docs/architecture/decisions/009-scheduler-collapse.md
- docs/architecture/decisions/014-outbox-tx-enqueue.md
- docs/architecture/decisions/019-mechanism-handle-surfaces.md §1, §4, §5
- docs/architecture/decisions/020-enqueue-opt-semantics-and-bridges.md §3
- docs/architecture/decisions/021-tx-reads-and-value-shape-fixes.md §3
- docs/architecture/engine-sqlite.md (Mapping the contract: scheduler/outbox)
- alkstore-sqlite/src/substrate/queue_ops.rs (scheduler_* fns)
Notes
To be filled by implementation agent
Summary
To be filled on completion