Postgres engine integration: StoreFactory (fresh schema per open, owned idempotent CASCADE teardown, isolation/idempotence pinned), the engine's backlog column (all ten rows green against the harness server — exemplar verified, the three ADR-023 rows verified not rewritten, the five engine-scoped rows, the new pg-arm PayloadTooLarge row discharging the SQLite task's deferred adoption), test-observation accessors cfg(test)-gated with the unused PgStore::new cut, stale stub-era doc text removed, lib docs stating the finished-engine posture (task pg-engine-integration)

This commit is contained in:
glm-5.3-flash committed 2026-10-09 10:22:25 +00:00
1 parent 77619c5e93
commit fb37da617d
7 files changed
+509 -59

No files matched your search

+1 -1
View File
@@ -41,5 +41,5 @@ pub use properties::{
duration_refusal_on_non_positive_ttl, enqueue_opts_resolution, extent_clamp_semantics,
in_tx_reads_see_own_writes, name_validation_rejects_empty_and_reserved,
payload_round_trip_stores_exact_encoding, payload_too_large_never_produced_on_sqlite,
receiver_close_and_save_arms,
payload_too_large_produced_on_pg, receiver_close_and_save_arms,
};
+60
View File
@@ -936,6 +936,66 @@ pub async fn receiver_close_and_save_arms(factory: &dyn StoreFactory) {
);
}
/// **`PayloadTooLarge` produced pg-side** — the Postgres engine
/// rejects an oversized `notify`/`notify_tx` payload client-side with
/// the typed `PayloadTooLarge` carrying the limit, **before any round
/// trip** (no listener hears the rejected payload — not delivered,
/// not truncated), at exactly the boundary (`limit` bytes = rejection;
/// under it = delivered). This is the pg column's arm of the asymmetry
/// row (the SQLite-side never-produced arm is its own row — the same
/// engine-agnostic match posture, the non-occurring arm simply never
/// fires there).
///
/// The limit read comes from the produced variant itself (the limit in
/// the variant is the runtime descriptor — ADR-016 §2's one
/// normative home), not a hardcoded number.
///
/// Contract stamp: ADR-008 §5 (universal variant, the limit in the
/// variant); ADR-016 §5 (the occurrence asymmetry — pg produces it
/// client-side at the 8000-byte wire budget); ADR-020 §4 (the
/// measured quantity is the serde_json serialization — the stored-
/// bytes form).
pub async fn payload_too_large_produced_on_pg(factory: &dyn StoreFactory) {
let store = factory.open().await.expect("factory opens a store");
let mut listener = store.listen("big").await.expect("listen attaches");
// Oversized: the typed arm, client-side (rejected before the
// round trip — a truncating relay would deliver a mangled half).
// The contract pins the number: ≤ 8000 bytes on Postgres.
let over = json!({"blob": "x".repeat(8_000)});
match store.notify("big", over).await {
Err(Error::PayloadTooLarge { limit }) => assert_eq!(limit, 8000),
other => panic!("oversized notify on pg must be PayloadTooLarge, got {other:?}"),
}
// The tx twin matches identically (engine-agnostic code writes one
// match for both paths).
let mut tx = store.begin_tx().await.expect("begin_tx opens");
match tx
.notify_tx("big", json!({"blob": "x".repeat(8_000)}))
.await
{
Err(Error::PayloadTooLarge { .. }) => {}
other => panic!("oversized notify_tx on pg must be PayloadTooLarge, got {other:?}"),
}
tx.commit()
.await
.expect("commit (the rejects fired before any round trip)");
// Nothing delivered while the rejects fired: the boundary is a
// client-side rejection, not a lossy truncation.
let idle = listener
.recv_timeout(std::time::Duration::from_millis(150))
.await
.expect("idle is Ok(None), never a timeout signal");
assert!(
idle.is_none(),
"rejected notifies deliver nothing, got {idle:?}"
);
factory.teardown().await.expect("factory teardown");
}
/// Shared clock helper for the suite's clock-adjacent rows: unix
/// seconds now (rows never assert tight timings — the tolerance is
/// the stamp's resolution).