decide the pg server-side stamp-arithmetic guard (review 004 item 5): the server expression owns it — the clamp domain is the arithmetic result, saturated to the i64 domain, no input clamped/validated at any seam; per-call args (heartbeat extend, retry override, lock ttl x2) have no enqueue to clamp at, QueueOpts verbatim-stamping is the review-002 posture, and the boundary row already pins argument totality — the annotation corrects the 'plain SQL integer add' form on pg (never total in the overflow domain), verified green against the Postgres 16 harness (both clamp edges, representable rides exact, now-MIN clamps to MAX); six sites of record (queue.rs:386/912/1000/715, lock.rs:92/217); window state verified open (no tag, no publish), dated class-1 annotation landed in ADR-023 §2; task -> completed
This commit is contained in:
1 parent
608104ab13
commit
60f7d02769
2 files changed
+138
-8
No files matched your search
@@ -170,7 +170,69 @@ offsets are total.** Each disposition answers to a decided principle:
|
||||
checkpoint are the [ADR-019](019-mechanism-handle-surfaces.md) §6
|
||||
monotone no-ops — rejections were never the behavior, so pinning
|
||||
the duration row's shape here would have contradicted all three
|
||||
engines.)*
|
||||
engines.)
|
||||
|
||||
*(Annotation, 2026-10-11 — the pg stamp-arithmetic guard decision
|
||||
([ADR-017](017-contract-versioning.md) §2 class 1, the window
|
||||
verified open — no tag exists, no `cargo publish` has occurred; the
|
||||
review-004 item-5 pre-fix precedent). Review 004's post-round finding:
|
||||
pg's server-side stamp arithmetic — the claim's
|
||||
`$2 + visibility_timeout_s` (claim deadline), the heartbeat's
|
||||
`$3 + extend`, the retry-reschedule's `$2 + delay_s`, the retention
|
||||
sweep's `$2 − dead_letter_retention_s`, and the lock stamp/renew's
|
||||
`$3 + ttl` (`lock.rs:92/217`) — overflow-failed on plain bigint
|
||||
("bigint out of range") and the failure is *sticky per row*, one
|
||||
extreme-stamped row poisoning every later claim/heartbeat/sweep that
|
||||
sums against it. The side question is here because the boundary row's
|
||||
totality lives on the *argument* values these sums consume, and the
|
||||
two candidate guards differ in what they touch: an enqueue-seam
|
||||
clamp/validate owns the guard at the entry of the consumer-supplied
|
||||
i64s; a server-expression guard clamps the arithmetic *result*.
|
||||
**Decided: the server expression owns it — the clamp domain is the
|
||||
sum's result, no input is clamped or validated at any seam.** The
|
||||
enqueue seam cannot own the guard: four of the six arguments are
|
||||
per-call (`heartbeat(extend)`, the `retry(err, Some(d))` override,
|
||||
`try_lock`/`renew(ttl)` ×2) and have no enqueue to clamp at; and
|
||||
where an enqueue does exist, a *result* saturation is what the
|
||||
pinned posture already computes, not a new input rule — enqueue's
|
||||
own stamps resolve in Rust `saturating_add` (`resolution.rs`, both
|
||||
engines), the boundary row pins argument totality ("any i64 is
|
||||
well-defined… no clamping, no rejection" — a caller's
|
||||
`heartbeat(i64::MAX)` or `retry(err, Some(i64::MAX))` is a value,
|
||||
not a violation), and `QueueOpts` stamps enter rows unvalidated
|
||||
verbatim by the recorded review-002 disposition, so validating them
|
||||
at entry would be a *posture change*, not a guard. The guard form,
|
||||
precise: **every server-side stamp expression saturates its result
|
||||
to the i64 domain** — `LEAST(GREATEST(<sum computed in numeric>,
|
||||
-9223372036854775808), 9223372036854775807)::bigint`, subtraction
|
||||
sites analogously — verified green against a Postgres 16 harness: an
|
||||
overflowing sum clamps at either edge, a representable result rides
|
||||
untouched (`1700000000 + i64::MIN` stays exact, matching Rust's
|
||||
`saturating_add`), `now − i64::MIN` clamps to `i64::MAX` (matching
|
||||
mem's retention `saturating_sub`). The extreme-stamped row's
|
||||
observable semantics, per site: a row stamped
|
||||
`visibility_timeout_s = i64::MAX` claims to the `i64::MAX` deadline
|
||||
(the claim never lapses through expiry; effectively-never, the same
|
||||
shape as sqlite's REAL-approximation and mem's saturation); a row
|
||||
stamped `dead_letter_retention_s = i64::MIN` sweeps every dead row
|
||||
(`sat(now − MIN) = i64::MAX`); `heartbeat(extend)` extremes renew to
|
||||
the saturated edge (a past saturated deadline voids through the
|
||||
validity predicate on the next op, the annotation above's negative
|
||||
semantics preserved); `retry(err, Some(i64::MAX))` reschedules to
|
||||
`i64::MAX` (ready ~never, the caller's value honored to the edge);
|
||||
`ttl = i64::MAX` stamps an `i64::MAX` expiry. No contract text
|
||||
changes: the table pins these dispositions already — what the
|
||||
annotation completes is the *arithmetic form*, correcting the
|
||||
"plain SQL integer add" parenthetical above: on pg the pinned
|
||||
saturating arithmetic is realized as the server-side guard, not a
|
||||
plain add, and the "bigint out of range" failure leaves the
|
||||
reachable surface (no opaque-Err arm covers arithmetic overflow;
|
||||
the duration row's rejection stays `ttl <= 0` only). Sites of
|
||||
record: pg `queue.rs:386/912/1000/715`, `lock.rs:92/217`; enqueue
|
||||
itself stays Rust-resolved (no SQL-side sum at enqueue). The
|
||||
implementation rides `tasks/pg-fix-bigint-stamp-overflow.md` —
|
||||
`cargo test -p alkstore-postgres` live-server legs pin the
|
||||
sticky-poison retirement.)*
|
||||
|
||||
This is a **core-contract amendment** (doc text on `claim_batch`,
|
||||
both `read` forms, `try_lock`/`renew`, and a shared domain-rule
|
||||
|
||||
Reference in new issue
Block a user