sqlite: run_schedules' failed-renewal arm classifies as Database, never LeadershipLost (review 004 item 9) — a lost lease (Ok(false)) keeps the respawn arm unchanged; a failed renewal (closed store / storage failure) propagates its opaque classification, so respawning misdirects at a dead store no more; dated (2026-10-11) ADR-017 §2 class-1 annotation of the loss-vs-failure arm set in ADR-009 §1, core-contract §Errors, queues.md, engine-sqlite.md, core+sqlite doc comments; two tests pin both arms; pg twin recorded for follow-up; task -> completed
This commit is contained in:
1 parent
acdef94058
commit
9a9ccf26e0
8 files changed
+281
-21
No files matched your search
@@ -605,7 +605,15 @@ and `LeadershipLost` (`run_schedules` return on leadership loss) —
|
||||
both from [ADR-009](decisions/009-scheduler-collapse.md) §6 — are
|
||||
part of contract v1's initial text (pre-release additions per
|
||||
[ADR-017](decisions/017-contract-versioning.md) class 1; the
|
||||
"post-v1 additions" framing an earlier draft used is retired). Pinning rule: a variant exists only when
|
||||
"post-v1 additions" framing an earlier draft used is retired).
|
||||
`LeadershipLost` is the *loss* arm only — the lease expired and was
|
||||
re-acquired elsewhere; a *failed* renewal is not a loss and never
|
||||
returns `LeadershipLost`: a closed store (respawning a leader
|
||||
against a dead store is misdirected — the engine-wide closed-store
|
||||
posture) and storage failure both surface the opaque `Database`
|
||||
fallback with the engine detail preserved via the source chain
|
||||
(annotated 2026-10-11 — review 004 item 9; ADR-009 §1's dated
|
||||
classification note). Pinning rule: a variant exists only when
|
||||
callers can act differently on it. Queue claim "no work" and job/lock
|
||||
boolean results are values, not errors; dead-letter is observable
|
||||
state (`get_job`), not an error.
|
||||
|
||||
@@ -97,6 +97,19 @@ store.run_schedules(stop) -> Result<()> // runs until `stop`
|
||||
on it. (One taxonomy note: `LeadershipLost` is a *return-shape*
|
||||
variant of the runner, pinned by the same act-differently rule as
|
||||
ADR-008 §5 — the caller acts on it by respawning.)
|
||||
*(Annotated 2026-10-11 — review 004 item 9; amend-in-place per
|
||||
ADR-017 §2 class 1, no publish tag exists.)* The loss arm is the
|
||||
refused renewal (`renew` → `Ok(false)`): the lease expired and was
|
||||
re-acquired elsewhere. A *failed* renewal is not a loss and never
|
||||
returns `LeadershipLost` — the lock semantics were never reached:
|
||||
a closed store (respawning a leader against a dead store is
|
||||
misdirected; the engine-wide closed-store posture applies) and
|
||||
storage failure both surface the opaque `Database` fallback with
|
||||
the engine detail preserved via the source chain. Engines whose
|
||||
renew is infallible (`Ok(bool)` only) have no failure arm to
|
||||
classify; engines whose renew can fail must propagate the `Err`
|
||||
unchanged (the SQLite engine's pre-annotation flattening —
|
||||
`unwrap_or(false)` — was the finding).
|
||||
- Schedules do not fire until a consumer runs `run_schedules` — the
|
||||
opt-in is explicit, the no-ambient-timers posture holds,
|
||||
[ADR-002](002-feature-scope.md)'s scheduler evidence is served, and
|
||||
|
||||
@@ -83,7 +83,7 @@ per-contract obligations live in the core spec and are not restated here.
|
||||
| streams | the forked substrate's stream machinery (inherited near-verbatim — the `__alkstore_stream` table's field set is already the contract's shape (its `topic` column carries the contract's `stream` field — the one name delta, ADR-012 §3's fidelity posture), the offset-ASC read path, keyed publish's nullable key column, monotone offset saves ([ADR-015](decisions/015-streams-depth.md)); explicit offset saves through the tx seam; `trim_to` is a `DELETE FROM … WHERE offset <= ?` inside the writer-slot lease) |
|
||||
| queues | owned queue machinery (forked substrate re-derived on [ADR-010](decisions/010-queue-semantics-depth.md) — stamps, no-stranded-rows sweep, dead-visible `get_job`); ADR-003's ride posture superseded on ownership by [ADR-011](decisions/011-sqlite-substrate-fork.md) |
|
||||
| named locks | the substrate's lock machinery (`lock_renew` carries the renew semantics the guarantee row pins; re-acquire does not refresh TTL — inherited deliberately, [ADR-012](decisions/012-forked-substrate-design.md) §4) |
|
||||
| scheduler / outbox | collapse shape ([ADR-009](decisions/009-scheduler-collapse.md)): owned scheduler storage (`__alkstore_scheduler_tasks`, forked substrate) carrying `@every` specs (cron machinery not ported); the leader loop pattern (TTL lock, heartbeat, exit-before-tick-on-loss); the leadership lock is `__alkstore_scheduler`; outbox = helper over queues with the derived backing-queue name ([ADR-008](decisions/008-contract-v1-pinning.md) §4) |
|
||||
| scheduler / outbox | collapse shape ([ADR-009](decisions/009-scheduler-collapse.md)): owned scheduler storage (`__alkstore_scheduler_tasks`, forked substrate) carrying `@every` specs (cron machinery not ported); the leader loop pattern (TTL lock, heartbeat, exit-before-tick-on-loss); the leadership lock is `__alkstore_scheduler`; outbox = helper over queues with the derived backing-queue name ([ADR-008](decisions/008-contract-v1-pinning.md) §4). The loss decision is one owner — the loop's top-of-iteration renew: a refused renewal (`Ok(false)`, lease expired and re-acquired elsewhere) exits with `LeadershipLost`; a *failed* renewal is not a loss and propagates its opaque `Database` classification unchanged — the closed-store posture (a dead store must not misdirect the respawn recipe, review 004 item 9, annotated 2026-10-11 per [ADR-009](decisions/009-scheduler-collapse.md) §1) and every other renewal failure |
|
||||
| begin_tx | acquires the writer slot, opens `BEGIN IMMEDIATE`, returns the caller-held handle ([ADR-007](decisions/007-transactional-seam.md)) |
|
||||
| handle ops | each `*_tx` op round-trips `spawn_blocking` to the same connection (thread-affinity note in [ADR-007](decisions/007-transactional-seam.md)) |
|
||||
| commit/rollback | releases the writer slot |
|
||||
|
||||
@@ -207,8 +207,13 @@ Collapsed into queues: no `Scheduler` handle, no schedule objects.
|
||||
the ADR; extension path: a consumer-inventory row first).
|
||||
- `run_schedules` is opt-in: schedules never fire without a runner
|
||||
(the no-ambient-timers posture). The runner: leadership lock →
|
||||
{renew (on loss, return before ticking — `Err(LeadershipLost)`,
|
||||
distinct from clean `Ok(())` stop) → fire due boundaries → sleep}.
|
||||
{renew (an actual lease loss — the refused renewal — returns before
|
||||
ticking with `Err(LeadershipLost)`, distinct from clean `Ok(())`
|
||||
stop; a *failed* renewal is not a loss — closed store and storage
|
||||
failure surface the opaque `Err(Database)` classification, a dead
|
||||
store must not misdirect the respawn recipe, annotated 2026-10-11
|
||||
— [ADR-009](decisions/009-scheduler-collapse.md) §1, review 004
|
||||
item 9) → fire due boundaries → sleep}.
|
||||
The `stop` parameter is a core-crate cloneable `StopToken`
|
||||
([ADR-019](decisions/019-mechanism-handle-surfaces.md) §4).
|
||||
Leadership lock: reserved name `__alkstore_scheduler`
|
||||
|
||||
Reference in new issue
Block a user