pg: server-side stamp sums saturate to the i64 domain — the review-004 item-5 sticky-poison class dies (pg-fix-bigint-stamp-overflow): every stamp sum/subtraction computes in numeric via one owner (resolution::saturating_stamp_expr, the ADR-023 §2 2026-10-11 annotation's form) called at all six sites — claim deadline (queue.rs:385), retention sweep (:721), retry reschedule (:927), heartbeat renewal (:1016), lock insert + renew (lock.rs:95/222); the realized ($N::bigint)::numeric chain pins the params' inferred bigint type (a bare $N::numeric fails the i64 binding with WrongType — found live, recorded in the owner's doc); behavior pinned against the harness server per site (i64::MAX stamp claims to the i64::MAX deadline with neighbors unaffected, heartbeat extremes renew to the saturated edge and now+MIN rides exact then voids through the validity predicate, retry Some(i64::MIN)/(i64::MAX) reschedule, retention i64::MIN sweep-all + i64::MAX delete-nothing, ttl i64::MAX insert+renew); two consecutive full-crate harness runs green (135+25+9), workspace gates + fuzz corpus gate green server-less; no re-deciding, no contract-text change (the failure-set change was the decision's own annotation); task -> completed
This commit is contained in:
1 parent
60f7d02769
commit
ce5da852ca
6 files changed
+400
-25
No files matched your search
@@ -1437,3 +1437,216 @@ fn backoff_formula_unit_pins() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Acceptance: the server-side stamp-arithmetic guard (ADR-023 §2's
|
||||
/// 2026-10-11 annotation) — extreme i64 stamps and per-call arguments
|
||||
/// are values whose server-side sums saturate to the i64 domain, never
|
||||
/// "bigint out of range" failures, and the sticky-poison property (one
|
||||
/// extreme-stamped row failing every later claim that touches it) is
|
||||
/// retired. Pinned per site against the live server: an
|
||||
/// `visibility_timeout_s = i64::MAX` row claims to the saturated
|
||||
/// `i64::MAX` deadline and neighbors claim unaffected; extreme
|
||||
/// heartbeat extends renew to the saturated edge and the representable
|
||||
/// `now + i64::MIN` sum rides exact (Rust `saturating_add` parity),
|
||||
/// the past deadline voiding through the validity predicate on the
|
||||
/// next op; the retry override reschedules `Some(i64::MAX)` to
|
||||
/// `i64::MAX` (and the negative-extreme delay rides exact to
|
||||
/// ready-now); the retention `i64::MIN` arm sweeps every dead row
|
||||
/// (`sat(now − MIN) = i64::MAX`) while the `Some(i64::MAX)` arm
|
||||
/// deletes nothing.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn extreme_stamps_saturate_and_no_longer_poison_queue_ops() {
|
||||
let Some(fx) = Fixture::open(&instance_namer("qext")).await else {
|
||||
eprintln!("skip: no harness server");
|
||||
return;
|
||||
};
|
||||
let now = now_unix();
|
||||
|
||||
// Claim + heartbeat over an i64::MAX-stamped row: the claim UPDATE
|
||||
// sums now + visibility_timeout_s server-side (pre-guard this
|
||||
// failed sticky per row) — it must land, deadline at the saturated
|
||||
// edge.
|
||||
let queue = fx
|
||||
.store
|
||||
.queue(
|
||||
"neverlaps",
|
||||
QueueOpts {
|
||||
visibility_timeout_s: i64::MAX,
|
||||
..opts()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let id = queue
|
||||
.enqueue(json!({"k": 1}), EnqueueOpts::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let h = queue
|
||||
.claim_one("w1")
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("the extreme-stamped row claims (no overflow failure)");
|
||||
assert_eq!(h.job().id, id);
|
||||
assert_eq!(
|
||||
h.job().claim_expires_at,
|
||||
Some(i64::MAX),
|
||||
"the claim deadline saturates to the i64::MAX edge"
|
||||
);
|
||||
|
||||
// A neighboring normal queue's row claims unaffected (the poison
|
||||
// was per-row, not per-store; the other-rows-unaffected arm).
|
||||
let normal = fx.store.queue("normal", opts()).await.unwrap();
|
||||
let normal_id = normal
|
||||
.enqueue(json!({"k": 2}), EnqueueOpts::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let nh = normal
|
||||
.claim_one("w1")
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("normal rows still claim");
|
||||
assert_eq!(nh.job().id, normal_id);
|
||||
assert!(
|
||||
nh.job().claim_expires_at.unwrap() >= now + 300,
|
||||
"normal rows still claim (unaffected by the extreme row)"
|
||||
);
|
||||
assert!(nh.ack().await.unwrap(), "the normal row acks");
|
||||
|
||||
// Heartbeats on the extreme row land: a normal renewal, then the
|
||||
// extreme extend renewing to the saturated edge.
|
||||
assert!(h.heartbeat(120).await.unwrap(), "heartbeat renews");
|
||||
let renewed = queue.get_job(id).await.unwrap().unwrap();
|
||||
assert!(
|
||||
renewed.claim_expires_at.unwrap() > now,
|
||||
"renewal is an absolute reset from now: {:?}",
|
||||
renewed.claim_expires_at
|
||||
);
|
||||
assert!(
|
||||
h.heartbeat(i64::MAX).await.unwrap(),
|
||||
"the i64::MAX extend renews (no overflow failure)"
|
||||
);
|
||||
let renewed = queue.get_job(id).await.unwrap().unwrap();
|
||||
assert_eq!(
|
||||
renewed.claim_expires_at,
|
||||
Some(i64::MAX),
|
||||
"sat(now + i64::MAX) = i64::MAX"
|
||||
);
|
||||
|
||||
// The negative extreme: now + i64::MIN is representable, so the
|
||||
// guard rides it exact (matching Rust `saturating_add` — no
|
||||
// gratuitous clamp) and the past deadline it stamps voids the
|
||||
// claim through the validity predicate on the next op.
|
||||
let before_min = now_unix();
|
||||
assert!(
|
||||
h.heartbeat(i64::MIN).await.unwrap(),
|
||||
"the i64::MIN extend renews"
|
||||
);
|
||||
let renewed = queue.get_job(id).await.unwrap().unwrap();
|
||||
let offset = renewed.claim_expires_at.unwrap().wrapping_sub(i64::MIN);
|
||||
assert!(
|
||||
(before_min..=before_min + 2).contains(&offset),
|
||||
"sat(now + i64::MIN) rides exact: deadline {:?} vs before {before_min}",
|
||||
renewed.claim_expires_at
|
||||
);
|
||||
assert!(
|
||||
!h.heartbeat(60).await.unwrap(),
|
||||
"the past saturated deadline voids the claim on the next op"
|
||||
);
|
||||
|
||||
// Retry override extremes: `Some(i64::MIN)` rides exact to
|
||||
// ready-now (the row reclaims immediately), `Some(i64::MAX)`
|
||||
// reschedules to the ready-~never saturated edge.
|
||||
let id2 = normal
|
||||
.enqueue(json!({"k": 5}), EnqueueOpts::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let h = normal.claim_one("w1").await.unwrap().unwrap();
|
||||
let before = now_unix();
|
||||
assert!(
|
||||
h.retry(Some("boom".to_string()), Some(i64::MIN))
|
||||
.await
|
||||
.unwrap(),
|
||||
"the negative-extreme delay retry lands"
|
||||
);
|
||||
let job = normal.get_job(id2).await.unwrap().unwrap();
|
||||
assert_eq!(job.state, JobState::Pending);
|
||||
let offset = job.run_at.wrapping_sub(i64::MIN);
|
||||
assert!(
|
||||
(before..=before + 2).contains(&offset),
|
||||
"sat(now + i64::MIN) rides exact: run_at {} vs now {before}",
|
||||
job.run_at
|
||||
);
|
||||
let h = normal
|
||||
.claim_one("w1")
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("the ready-now row reclaims");
|
||||
assert_eq!(h.job().id, id2);
|
||||
assert!(
|
||||
h.retry(Some("boom".to_string()), Some(i64::MAX))
|
||||
.await
|
||||
.unwrap(),
|
||||
"the positive-extreme delay retry lands"
|
||||
);
|
||||
let job = normal.get_job(id2).await.unwrap().unwrap();
|
||||
assert_eq!(job.run_at, i64::MAX, "sat(now + i64::MAX) = i64::MAX");
|
||||
|
||||
// Retention arms: `Some(i64::MIN)` sweeps every dead row
|
||||
// (sat(now − MIN) = i64::MAX, the sweep-all edge — pre-guard this
|
||||
// sweep failed sticky with the overflow error);
|
||||
// `Some(i64::MAX)` deletes nothing (sat(now − MAX) = i64::MIN).
|
||||
let sweepall = fx
|
||||
.store
|
||||
.queue(
|
||||
"sweepall",
|
||||
QueueOpts {
|
||||
dead_letter_retention_s: Some(i64::MIN),
|
||||
..opts()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let swept_id = sweepall
|
||||
.enqueue(json!({"k": 3}), EnqueueOpts::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let h = sweepall.claim_one("w1").await.unwrap().unwrap();
|
||||
assert!(h.fail(None).await.unwrap(), "the dead row exists");
|
||||
assert!(
|
||||
sweepall.sweep_expired().await.unwrap() >= 1,
|
||||
"the retention i64::MIN sweep lands (no overflow failure)"
|
||||
);
|
||||
assert!(
|
||||
sweepall.get_job(swept_id).await.unwrap().is_none(),
|
||||
"sat(now − i64::MIN) = i64::MAX sweeps the dead row"
|
||||
);
|
||||
|
||||
let sweepnone = fx
|
||||
.store
|
||||
.queue(
|
||||
"sweepnone",
|
||||
QueueOpts {
|
||||
dead_letter_retention_s: Some(i64::MAX),
|
||||
..opts()
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let kept_id = sweepnone
|
||||
.enqueue(json!({"k": 4}), EnqueueOpts::default())
|
||||
.await
|
||||
.unwrap();
|
||||
let h = sweepnone.claim_one("w1").await.unwrap().unwrap();
|
||||
assert!(h.fail(None).await.unwrap());
|
||||
assert_eq!(
|
||||
sweepnone.sweep_expired().await.unwrap(),
|
||||
0,
|
||||
"sat(now − i64::MAX) = i64::MIN deletes nothing"
|
||||
);
|
||||
assert!(
|
||||
sweepnone.get_job(kept_id).await.unwrap().is_some(),
|
||||
"the retention i64::MAX dead row survives"
|
||||
);
|
||||
|
||||
drop_schema(&fx.admin, &fx.schema).await;
|
||||
}
|
||||
Reference in new issue
Block a user