152 lines
7.8 KiB
Markdown
152 lines
7.8 KiB
Markdown
---
|
||
id: suite-opts-backoff-rows
|
||
name: Contract-suite rows — backoff-curve equivalence + the deferred enqueue-opts clock legs
|
||
status: completed
|
||
depends_on: []
|
||
scope: narrow
|
||
risk: low
|
||
impact: phase
|
||
level: implementation
|
||
tags: [wave-5, contract-suite, queues]
|
||
---
|
||
|
||
## Description
|
||
|
||
Complete the arithmetic-equivalence backlog rows
|
||
(core-contract.md §Verification backlog): "Backoff-curve and
|
||
stamp-resolution equivalence across engines" and the two
|
||
wall-clock-adjacent legs the existing `enqueue_opts_resolution` row
|
||
explicitly deferred ("The `run_at`-alone-literal and
|
||
neither-field-resolves-to-now legs ... pin with wave 5's equivalence
|
||
row").
|
||
|
||
Work:
|
||
|
||
- **Extend `enqueue_opts_resolution` in place** (one owner per
|
||
property — the row's own doc comment names this wave-5 completion;
|
||
stamps accumulate per the convention): the `run_at`-alone literal
|
||
leg (a future `run_at` is the row's ready time verbatim; a past
|
||
`run_at` resolves to ready-now — the boundary-total rule, ADR-023
|
||
§2) and the neither-field leg (ready now — tolerance-bounded
|
||
proximity, never a tight timing assert).
|
||
- **Add `backoff_curve_equivalence`** — the equal-jitter exponential
|
||
curve (ADR-010 §3): `delay ∈ [base·2^(a−1)/2, base·2^(a−1)]` per
|
||
attempt, uniform jitter over the lower half — *the range, not a
|
||
jitter label, is the definition*. Drive claim → `retry(err, None)`
|
||
cycles on a queue with a small `backoff_base_s` stamp (2 s keeps
|
||
the integer-second row resolution honest: ranges [1,2], [2,4],
|
||
[4,8] across the first attempts; the inter-attempt waits stay
|
||
tolerance-bounded and sequential), reading the computed delay back
|
||
via `get_job`'s `run_at` minus now, asserting range membership per
|
||
attempt on both engines — identical bounds is the equivalence pin
|
||
(ADR-012 §2: engine-side arithmetic, suite-pinned identical
|
||
outputs). Include the explicit-delay override leg
|
||
(`retry(err, Some(d))` honored — a `Some(0)` resolves ready-now per
|
||
the boundary-total rule). **The 1-hour cap leg is not
|
||
suite-pinnable** (reaching capped attempts requires waiting out
|
||
minute-scale delays); it stays pinned engine-side (both engines'
|
||
unit tests already pin the curve against the ADR text — the
|
||
wave-3/4 reviews verified it) — record that disposition in Notes
|
||
for the review gate.
|
||
|
||
## Acceptance Criteria
|
||
|
||
- [ ] `enqueue_opts_resolution` extended with both clock legs; its
|
||
doc comment's deferral note updated (stamps accumulate)
|
||
- [ ] `backoff_curve_equivalence` exists, version-stamped (ADR-010 §3,
|
||
ADR-020 §1–§3, ADR-012 §2, ADR-023 §2)
|
||
- [ ] Rows wired into both engines' `contract_suite.rs` targets
|
||
- [ ] SQLite column green server-less; pg column green against the
|
||
harness server
|
||
- [ ] The cap-leg disposition recorded in Notes
|
||
- [ ] `cargo test -p alkstore-sqlite -p alkstore-postgres` green
|
||
(pg rows skip cleanly server-less); clippy `-D warnings`; fmt clean
|
||
|
||
## References
|
||
|
||
- docs/architecture/core-contract.md §Verification backlog (backoff-curve
|
||
and stamp-resolution equivalence; enqueue-opts resolution equivalence)
|
||
- docs/architecture/decisions/010-queue-semantics-depth.md §3
|
||
- docs/architecture/decisions/020-enqueue-opt-semantics-and-bridges.md §1–§3
|
||
- docs/architecture/decisions/012-forked-substrate-design.md §2
|
||
- alkstore-contract-suite/src/properties.rs (`enqueue_opts_resolution`'s
|
||
deferral note)
|
||
|
||
## Notes
|
||
|
||
|
||
- **The 1-hour cap leg is not suite-pinned** (as pre-decided): reaching
|
||
capped attempts requires waiting out minute-scale delays. The cap
|
||
stays pinned engine-side — both engines' unit tests pin the curve
|
||
against the ADR text including the cap (SQLite's
|
||
`backoff_curve_lands_in_the_pinned_range` and the pg twin, verified
|
||
in the wave-3/4 rounds); the disposition is recorded in the row's
|
||
doc comment for the review gate.
|
||
- **Observation tolerance convention for the curve row**: the
|
||
computed delay is read back as `get_job`'s `run_at` minus a clock
|
||
read taken *before* the `retry(err, None)` call. That direction is
|
||
deliberate and race-free on the lower side: the retry's internal
|
||
clock read (SQLite's `unixepoch()` in-statement, pg's Rust-side
|
||
engine read) cannot precede the suite's, so the observed value
|
||
*over-reads* the delay, never under-reads — the pinned lower bound
|
||
is exact. The upper bound carries a one-second straddle tolerance
|
||
(retry commit and suite read may cross a wall second, and the
|
||
engines' stamps are integer seconds, ADR-010 §3's integerized
|
||
resolution). Asserted as a `(lower..=upper+1)` range membership; the
|
||
*identical* assertion on both engines is the equivalence pin
|
||
(ADR-012 §2's identical-outputs rule) — the row body is shared, so
|
||
the bounds are identical by construction.
|
||
- **Base 2 s kept the integer-second row resolution honest** per the
|
||
task text: ranges [1,2], [2,4], [4,8] across attempts 1–3 (the a=1
|
||
pin is inherently weak at one-second resolution — lower bound 1 with
|
||
upper ±1 — but the attempt-2/3 ranges are genuinely discriminating,
|
||
and the explicit-delay legs pin the exact-literal resolution).
|
||
- **Past-run_at leg stores the literal too**: the pin on the past leg
|
||
is the *claim semantics* (ready-now through the `run_at <= now`
|
||
predicate, bounded-wait claimable) and the stored literal (no
|
||
arithmetic on any form) — "resolves to ready now" is the claim-
|
||
predicate reading of the boundary-total rule (ADR-023 §2), not a
|
||
clamping of the stored value; both engines store verbatim
|
||
(`resolution.rs` twins verified).
|
||
- **Claim-polling observation posture**: both rows' ready-now waits
|
||
are bounded `claim_one` poll loops (100/50 ms cadence), deadline
|
||
asserts only — no timing-value assertions, single-store sequential
|
||
drive within the module doc's determinism posture.
|
||
- **One unreproducibility note on the helper**: the first draft made
|
||
`await_claimable` re-claim after the bounded wait, which found
|
||
nothing (the bounded wait consumed the claim); the helper returns
|
||
the claimed handle. Found on the first SQLite run, no engine-side
|
||
hazard.
|
||
- No ADR amendments needed; no divergences found (both engines'
|
||
arithmetic agreed out of the box — the equivalence pin held on the
|
||
first full run pairs).
|
||
|
||
## Summary
|
||
|
||
`enqueue_opts_resolution` extended in place with the wave-5
|
||
completion legs (its deferral note replaced by the completion
|
||
statement, ADR-023 §2 stamp accumulated): the `run_at`-alone literal
|
||
leg (future `run_at` = the row's ready time verbatim — a
|
||
deterministic equality, no clock read involved; past `run_at` stored
|
||
verbatim too and *claimable now* within a bounded-wait claim, the
|
||
run_at-ASC ordering making the observation unambiguous among the
|
||
row's other legs) and the neither-field leg (ready now at the
|
||
enqueue instant, `abs_diff(now) <= 5` tolerance-bounded proximity —
|
||
never a tight timing assert). New `backoff_curve_equivalence` row:
|
||
claim → `retry(err, None)` cycles on a `backoff_base_s = 2` queue
|
||
(ranges [1,2], [2,4], [4,8] per attempt, asserted as `(lower..=upper+1)`
|
||
range membership of `run_at − now_pre` with the race-free lower
|
||
bound and one-second straddle tolerance documented), reading each
|
||
computed delay back through `get_job`'s resolved `run_at`; the
|
||
explicit-delay override legs ride the same row (`Some(5)` honored
|
||
verbatim in [5, 6]; `Some(0)` resolving ready-now per the
|
||
boundary-total rule, claimed within a bounded wait). Stamped ADR-010
|
||
§3, ADR-020 §1, ADR-012 §2, ADR-023 §2. Both rows wired into both
|
||
engines' `contract_suite.rs` targets (SQLite `#[tokio::test]`, pg
|
||
`harness_row!`) — 25 rows per column, up from 24. Verified: SQLite
|
||
column green server-less (25/25 suite, 189 lib), pg column green vs
|
||
the harness server (postgres/poc@:15432 — 25/25 suite, 121 lib + 9
|
||
schema), 3 solo re-runs of each new/extended row per engine
|
||
(determinism), server-less pg column skips cleanly (25 "passed" via
|
||
the reachability gate), `cargo test -p alkstore-sqlite -p
|
||
alkstore-postgres` green, clippy `-D warnings`, fmt clean. |