Files
alkstore/tasks/mem-engine-scheduler-outbox.md
T
glm-5.3-flash 5abd351c86 Wave-6 decomposition (mem engine): eleven tasks per the pg-wave rhythm — foundations (crate scaffold, MemStore::new isolation, guarded state core, wake routing, wasm32 gate from birth), seam-tx (the load-bearing tx overlay: eleven staged _tx methods, commit-merge + wake-at-commit — pg-fix-tx-wake's discipline structural from the start, drop = discard trivial, with_tx panic posture), mechanisms mutually parallel (notify-listen close-at-engine-drop/no-replay/never-coalesced; queues = ADR-010 machine driver-free with the equal-jitter curve and opts resolution as ADR-012 §2's third owner; streams log+offsets+trim; locks TTL registry, no busy-path analog), scheduler-outbox (tick/catch-up-64/leadership through the __alkstore_scheduler lock, engine-uniform per ADR-009 §4; outbox over queues with the 60/5/5 seam stamp), integration (suite third StoreFactory column green on host under a current-thread flavor, wasm32 acceptance items, engine-mem.md to implementation-complete), and the review-wave-6 gate (born-pinned audit: conformance read, discharge-map wiring, wasm items, three-way equivalence green, class-1 window discipline). Window riders: suite-harness-current-thread — the wave's one suite-side pre-task, past_stamp_sleep's blocking sleep goes async per review 003's properties.rs:1683 posture note, enabling the current-thread flavor; pg-fix-scheduler-tick-seam — review 003 Finding 1, the cfg(test) transient-fault seam driving the real tick_with_retries through retry-then-success and exhaustion arms (the last uncovered wave-4 hardening arm, sqlite-commit-error-arm pattern). Task hygiene: pg-fix-open-path frontmatter fixed (unquoted 'max_size: 0' colon-space inside the name scalar broke YAML parsing — taskgraph validate had been red one-error since the wave-4 fix-batch decomposition; also restores review-wave-4-fixes' dependency resolution). implementation.md: wave-6 table row → decomposed, task-set listing with the parallelism note (rider + pre-task independent of the mem chain; rider closes before the gate), wave-6 decomposition bullet in the review-rounds record. Docs-only change; verified taskgraph validate (60 tasks, no cycles), cargo fmt --check, cross-reference read
2026-10-10 09:57:01 +00:00

87 lines
3.8 KiB
Markdown

---
id: mem-engine-scheduler-outbox
name: Mem engine — scheduler (`schedule`/`unschedule`/`run_schedules`) and outbox helper
status: pending
depends_on: [mem-engine-queues, mem-engine-locks]
scope: moderate
risk: medium
impact: component
level: implementation
tags: [wave-6, mem-engine, scheduler, outbox]
---
## Description
The collapsed scheduler and the outbox helper
([ADR-009](../architecture/decisions/009-scheduler-collapse.md),
[ADR-014](../architecture/decisions/014-outbox-tx-enqueue.md)):
- **Scheduler surface**: `schedule(name, spec, queue, payload, opts)`
(upsert by name; *both* name-bearing arguments validated — schedule
name and queue argument identically, `InvalidName`/`ReservedName`),
`unschedule(name) -> bool`, and the opt-in
`run_schedules(stop: StopToken)` runner over a mem-owned tick task.
- **Tick machinery — engine-uniform** ([ADR-009](../architecture/decisions/009-scheduler-collapse.md)
§4's rule; single-instance leadership is trivially owned but the
machinery still runs): each boundary fire enqueues ordinary work
stamped with the schedule's `ScheduleOpts` resolved over the target
queue's derived engine defaults; fire+advance is atomic under the
guarded state (the no-double-fire floor); **bounded catch-up with
the fixed 64-boundary cap and skip-forward past the cap**
(mem-owned tick implementation — third owner per
[ADR-012](../architecture/decisions/012-forked-substrate-design.md)
§2); leadership rides the reserved `__alkstore_scheduler` lock
through the lock mechanism (so two runners on one instance contend
through the same machinery the other engines use; loss →
`Err(LeadershipLost)`, clean stop → `Ok(())`).
- **Spec grammar v1**: `@every <n><unit>` (`s|m|h|d`) only — mem owns
its parser (third copy, the grammar is small and pinned); cron
strings rejected (`InvalidSpec`). Boundary waits ride tokio time.
- **Outbox helper**: `Outbox { enqueue, run_once }` per
[ADR-019](../architecture/decisions/019-mechanism-handle-surfaces.md)
§5 — `run_once(worker_id, delivery) -> bool` is pull: claim one,
ack on `Ok`, `retry(err, None)` on `Err`; **no engine-issued
heartbeat inside delivery** (inherited posture);
`outbox_enqueue_tx` staged in the overlay by `mem-engine-seam-tx`
(the 60/5/5 stamp set); the derived backing queue
`__alkstore_outbox:{outbox}` unreachable by `enqueue_tx`
(validation already pins the reserved prefix), name format pinned
identical to the other engines (the suite's derived-queue-naming
row composes it).
Unit tests pin: boundary fires (deterministic, tokio-time driven),
bounded catch-up + skip-forward, leadership lock contention/loss
through two `run_schedules` calls, outbox run_once ack/retry shapes,
derived-name format.
## Acceptance Criteria
- [ ] Scheduler surface + tick: boundary fire, catch-up (≤64),
skip-forward — unit-pinned with tokio-time determinism
- [ ] Leadership contention through the `__alkstore_scheduler` lock;
`LeadershipLost` on loss, `Ok(())` on clean stop — pinned
- [ ] Schedule name + queue argument validation; `@every`-only parser
(cron → `InvalidSpec`) — pinned
- [ ] Outbox: `enqueue`/`run_once` semantics (no engine heartbeat);
derived backing-queue name format identical to both engines —
pinned
- [ ] `cargo check --target wasm32-unknown-unknown -p alkstore-mem`
clean; workspace gates green
## References
- docs/architecture/queues.md; core-contract.md §scheduler, §outbox
- docs/architecture/decisions/009-scheduler-collapse.md §1/§3/§4/§6;
014-outbox-tx-enqueue.md; 019 §4/§5; 020 §3
- docs/architecture/engine-mem.md §Mapping the contract
(scheduler/outbox row)
- tasks/pg-engine-scheduler-outbox.md (the structural twin's task of
record — tick/catch-up/leadership shapes)
## Notes
> To be filled by implementation agent
## Summary
> To be filled on completion