Fourth review round (ADR-023): encode_payload typed (Codec), numeric-argument domains pinned by kind, plain-path SQLite open (URI flag dropped, D-29), watcher cadence posture — waves-1-2 general-review findings
This commit is contained in:
1 parent
ee7871d25d
commit
44637eea5b
11 files changed
+502
-22
No files matched your search
@@ -1,6 +1,6 @@
|
||||
---
|
||||
status: draft
|
||||
last_updated: 2026-10-07 (ADR-021 + third-round follow-through: enqueue_tx stamp source, sweep_expired handle form, handle-op/ack_batch/opts-type pins, with_tx signature, payload-limit basis, per-stream offsets)
|
||||
last_updated: 2026-10-08 (ADR-023 — fourth review round: numeric-argument domains pinned by kind, encode_payload typed, plain-path SQLite open, watcher cadence posture)
|
||||
---
|
||||
|
||||
# Core contract
|
||||
@@ -107,11 +107,35 @@ point ([ADR-015](decisions/015-streams-depth.md) §2).
|
||||
|
||||
Mechanism handles come off the store (or, for transactional variants,
|
||||
off the handle). **Payload encoding** ([ADR-020](decisions/020-enqueue-opt-semantics-and-bridges.md)
|
||||
§4): the trait crosses `serde_json::Value`; engines serialize it with
|
||||
serde_json and store exactly those bytes — `payload_as<T>` decodes
|
||||
§4, signature typed by [ADR-023](decisions/023-fourth-review-round.md)
|
||||
§1): the trait crosses `serde_json::Value`; engines serialize it with
|
||||
serde_json via core's `encode_payload` (fallible — `Codec` on
|
||||
serialization failure, typed at the helper; never a silent fallback
|
||||
form) and store exactly those bytes — `payload_as<T>` decodes
|
||||
them, `Codec` on failure; round-trip (publish → read → decode →
|
||||
`Value` equality) is what the contract suite pins, on both engines.
|
||||
|
||||
**Numeric-argument domains** ([ADR-023](decisions/023-fourth-review-round.md)
|
||||
§2) — classified by argument kind, uniform on both engines (no engine
|
||||
may reinterpret a caller-supplied numeric argument via SQL-dialect
|
||||
semantics; validation happens at the trait-impl entry point, before
|
||||
the engine's storage layer):
|
||||
|
||||
- **Extents** ("at most this many": `claim_batch(n)`,
|
||||
`read_since(limit)`, `read_from_consumer(limit)`) — non-positive
|
||||
values yield the **empty value result** (a value, not an error —
|
||||
the no-work vocabulary); the SQLite `LIMIT -1` dialect artifact
|
||||
(a negative extent reaching the whole queue/stream) is dead by
|
||||
this rule.
|
||||
- **Durations** (`try_lock(ttl)`, `renew(ttl)`) — non-positive values
|
||||
are rejections (`Err`, opaque `Database`, detail in the source
|
||||
chain); an exclusion mechanism must not silently hand back a lease
|
||||
the caller believes it holds.
|
||||
- **Boundaries/offsets** (`trim_to(horizon)`, enqueue `delay`/
|
||||
`run_at`/`expires`) — total: any `i64` is well-defined (a negative
|
||||
horizon deletes nothing; a negative delay resolves to a ready
|
||||
time in the past, i.e. ready now).
|
||||
|
||||
```text
|
||||
store.notify(channel, payload) handle.notify_tx(channel, payload)
|
||||
store.stream(name) -> StreamHandle handle.publish_tx / publish_with_key_tx
|
||||
@@ -258,6 +282,9 @@ business-tx shape). Depth pinned by
|
||||
([ADR-015](decisions/015-streams-depth.md) §5) stays a valid
|
||||
position marker.
|
||||
- `trim_to(horizon) -> count` — delete events with `offset <= horizon`
|
||||
(exact-boundary; the horizon is a boundary argument, total per
|
||||
[ADR-023](decisions/023-fourth-review-round.md) §2 — a negative
|
||||
horizon deletes nothing, idempotently)
|
||||
([ADR-015](decisions/015-streams-depth.md) §5; the return is the
|
||||
deleted-row count, [ADR-019](decisions/019-mechanism-handle-surfaces.md)). The stream-side
|
||||
bounded-growth op, consumer-invoked, no engine-default
|
||||
@@ -336,6 +363,10 @@ obligations:
|
||||
ordering: priority DESC, then
|
||||
ready-time, then enqueue order (FIFO under equal priority). Claims
|
||||
return boxed `JobHandle`s — ops + row value (same ADR, §3).
|
||||
`claim_batch(n)`'s `n` is an extent
|
||||
([ADR-023](decisions/023-fourth-review-round.md) §2): `n <= 0`
|
||||
claims nothing (the empty `Vec`, a value — never an error, never
|
||||
the whole queue).
|
||||
- Job handle: `ack / retry / fail / heartbeat` on the boxed
|
||||
`JobHandle` a claim returns ([ADR-019](decisions/019-mechanism-handle-surfaces.md)
|
||||
§3 — `job(&self) -> &Job` beside the ops; one-shot `self: Box<Self>`
|
||||
@@ -392,7 +423,9 @@ release it — the TTL discipline governs).
|
||||
lock handle. Release on explicit unlock or TTL expiry.
|
||||
Re-acquirable after expiry (POC-pinned on Postgres; on SQLite it
|
||||
rests on the forked substrate's lock machinery — the SQLite-side pin
|
||||
is in the verification backlog below).
|
||||
is in the verification backlog below). `ttl <= 0` is a rejection
|
||||
(duration kind, [ADR-023](decisions/023-fourth-review-round.md) §2)
|
||||
— opaque `Err`, never a silent immediately-expired lease.
|
||||
- **Guarantee row** ([ADR-008](decisions/008-contract-v1-pinning.md)
|
||||
§7): mutual exclusion bounded by TTL + renewal — after TTL expiry
|
||||
exclusion lapses *silently* (no revocation event); holders must
|
||||
@@ -690,6 +723,31 @@ before the engine specs are called `stable`:
|
||||
SQLite's receiver closes on watcher death (terminal `None`); the
|
||||
pg receiver stays open across forwarder reconnects (reconnect-wakes
|
||||
arrive; no close) and closes only at engine shutdown.
|
||||
- **Numeric-domain semantics on both engines**
|
||||
([ADR-023](decisions/023-fourth-review-round.md) §2) — extents
|
||||
clamp to empty (`claim_batch(n <= 0)` claims nothing; stream reads
|
||||
with `limit <= 0` read nothing, never the whole queue/stream), so
|
||||
the SQLite `LIMIT -1` dialect artifact is dead on both engines;
|
||||
durations reject (`try_lock`/`renew` with `ttl <= 0` are opaque
|
||||
`Err`s on both engines); boundaries are total (a negative
|
||||
`trim_to` horizon deletes nothing, idempotently).
|
||||
- **`encode_payload` typed failure**
|
||||
([ADR-023](decisions/023-fourth-review-round.md) §1) — the helper
|
||||
returns `Result<Vec<u8>, Error::Codec>`; enqueue/publish store and
|
||||
decode the exact serialization of the input `Value` (byte-identical
|
||||
rows cross-engine, the §4 round-trip unchanged), and the failure
|
||||
arm is typed at the helper — no silent empty-byte fallback exists.
|
||||
- **Plain-path SQLite open** ([ADR-023](decisions/023-fourth-review-round.md)
|
||||
§3) — the SQLite engine's `open` path argument is a plain
|
||||
filesystem path: a string carrying a `?name=value` suffix is
|
||||
treated as a filename, no URI interpretation, no connection-
|
||||
semantics mutation outside the constructor's opts.
|
||||
- **Watcher cadence default + knob** ([ADR-023](decisions/023-fourth-review-round.md)
|
||||
§4) — the SQLite store's watcher runs at the inherited 1 ms
|
||||
default; `SqliteOpts::poll_interval` changes the configured
|
||||
cadence (config-surface accounting — wall-clock cadence
|
||||
measurements stay out of the contract suite's deterministic
|
||||
posture).
|
||||
|
||||
## Design Decisions
|
||||
|
||||
@@ -711,6 +769,7 @@ before the engine specs are called `stable`:
|
||||
| [019](decisions/019-mechanism-handle-surfaces.md) | Mechanism handles (amends 008 §1/§2/§8) | handle traits pinned (`Queue`/`StreamHandle`/`Outbox`/`Lock`/`JobHandle`); `worker_id` claimant identity; `Job`/`Schedule` struct shapes; core-owned `StopToken`; save-offset composition |
|
||||
| [020](decisions/020-enqueue-opt-semantics-and-bridges.md) | Enqueue options + bridges (amends 008 §1/§2, 009 §3) | `delay` wins over `run_at`; `expires` = relative seconds; scheduler stamps from derived queue defaults; serde_json byte-level payload encoding |
|
||||
| [021](decisions/021-tx-reads-and-value-shape-fixes.md) | Third review round (amends 008 §2/§8, 019 §1/§3, 009 §1) | tx-side read methods on `TxHandle` (read-your-own-writes); `Job.claimed_at` restored; `schedule()` queue argument validated; drop = rollback; receiver close/error arms pinned |
|
||||
| [023](decisions/023-fourth-review-round.md) | Fourth review round (amends 020 §4, 008 §5, 012 §4) | numeric-argument domains by kind (extents clamp empty, durations reject, boundaries total); `encode_payload` typed (`Codec`); plain-path SQLite open (URI flag dropped); watcher cadence 1 ms default + `SqliteOpts` knob |
|
||||
|
||||
## Open Questions
|
||||
|
||||
|
||||
Reference in new issue
Block a user