review-wave-6 — the wave-6 gate passes, engine-mem.md flipped to stable
The born-pinned gate audited the mem engine against the contract and ADR-024: the conformance read clean in mechanism substance (queue state machine, scheduler collapse, outbox, streams, locks, clock, validation, tx overlay, wake substrate — incl. a full guard-order and panic-hygiene audit); the suite's third column read row-by-row and 25/25 green under the per-test current-thread flavor (24 engine-shared rows + mem's payload_too_large non-occurrence arm + the factory-shape test); three-way arithmetic equivalence green on all columns; the wasm32 acceptance items verified (compile-clean, tokio sync+time, no blocking, flavor enforced); the rider's two arms verified (Finding 1 discharged; Findings 2-3 stay parked for wave 7); workspace gates, taskgraph validate green. Three edge-domain fixes landed inline: the stream cursor read's i64::MAX overflow (Bound::Excluded), trim_to's i64::MAX horizon endpoint (Bound::Included), and the scheduler register path now rejecting a parseable interval whose first boundary overflows with InvalidSpec — the one engine-vs-engine divergence the read found, aligned into the class-1 window — plus a pinning test and scaffold- residue doc/comment cleanups (clock allows, tx per-stream wake comment, wakes/stream module docs). Recorded findings with dispositions in the task's Notes for the wave-7 pre-release pass: the multi-thread publish-to-wake ordering tolerance, the heartbeat/ save_offset/EventReceiver-try_recv ADR-023 §2 classification gaps, and the mem arithmetic being a suite-pinned transplant. Verified: cargo build; cargo test --workspace green (mem 101 lib + 25 suite, sqlite 191+25, pg 124+25+9 server-less, core 25, harness 3); clippy --all-targets -D warnings clean; fmt clean; taskgraph validate (61 tasks). Gate record: tasks/review-wave-6.md (Notes + Summary), implementation.md wave-6 row + review-rounds entry.
This commit is contained in:
1 parent
76c06e42a9
commit
7e8bfde12d
8 files changed
+282
-37
No files matched your search
@@ -15,9 +15,6 @@ use std::sync::Mutex;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use crate::state::Guard;
|
use crate::state::Guard;
|
||||||
|
|
||||||
// Mechanism task code stamps every timestamp through this accessor —
|
|
||||||
// scaffolding reads as dead until the wave-6 mechanism tasks wire it.
|
|
||||||
#[allow(dead_code)]
|
|
||||||
fn process_unix_now() -> i64 {
|
fn process_unix_now() -> i64 {
|
||||||
std::time::SystemTime::now()
|
std::time::SystemTime::now()
|
||||||
.duration_since(std::time::UNIX_EPOCH)
|
.duration_since(std::time::UNIX_EPOCH)
|
||||||
@@ -47,7 +44,6 @@ impl Clock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Unix-seconds "now" — the one timestamp source of the engine.
|
/// Unix-seconds "now" — the one timestamp source of the engine.
|
||||||
#[allow(dead_code)]
|
|
||||||
pub(crate) fn unix_now(&self) -> i64 {
|
pub(crate) fn unix_now(&self) -> i64 {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
let frozen = { *self.frozen.guard() };
|
let frozen = { *self.frozen.guard() };
|
||||||
|
|||||||
@@ -165,7 +165,14 @@ pub(crate) fn schedule(
|
|||||||
visibility_timeout_s: stamps.visibility_timeout_s,
|
visibility_timeout_s: stamps.visibility_timeout_s,
|
||||||
backoff_base_s: stamps.backoff_base_s,
|
backoff_base_s: stamps.backoff_base_s,
|
||||||
dead_letter_retention_s: stamps.dead_letter_retention_s,
|
dead_letter_retention_s: stamps.dead_letter_retention_s,
|
||||||
next_fire_at: now.saturating_add(interval),
|
// A parseable interval whose first boundary overflows the
|
||||||
|
// clock registers nothing: the SQLite register path
|
||||||
|
// (substrate/queue_ops.rs) rejects the same spec with
|
||||||
|
// InvalidSpec — one register-time semantics, engines
|
||||||
|
// aligned (the tick's i64::MAX sentinel stays identical).
|
||||||
|
next_fire_at: now
|
||||||
|
.checked_add(interval)
|
||||||
|
.ok_or_else(|| invalid_spec(&spec))?,
|
||||||
};
|
};
|
||||||
{
|
{
|
||||||
let mut sched = state.schedules.guard();
|
let mut sched = state.schedules.guard();
|
||||||
@@ -453,6 +460,28 @@ mod tests {
|
|||||||
assert_eq!(row.expires_s, Some(120));
|
assert_eq!(row.expires_s, Some(120));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "current_thread")]
|
||||||
|
async fn register_rejects_a_parseable_interval_whose_first_boundary_overflows() {
|
||||||
|
let store = store();
|
||||||
|
// Parseable (n · 1s fits i64) but now + interval overflows the
|
||||||
|
// clock — the register path rejects with InvalidSpec, no row
|
||||||
|
// stored (aligned with the SQLite register semantics; distinct
|
||||||
|
// from the unparsable-too-large spec the parser refuses).
|
||||||
|
let err = store
|
||||||
|
.schedule(
|
||||||
|
"overflow",
|
||||||
|
"@every 9223372036854775807s",
|
||||||
|
"q",
|
||||||
|
serde_json::json!(1),
|
||||||
|
ScheduleOpts::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert!(matches!(err, Error::InvalidSpec { .. }), "{err:?}");
|
||||||
|
let sched = store.state.schedules.guard();
|
||||||
|
assert!(sched.rows.is_empty(), "nothing registered");
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "current_thread")]
|
#[tokio::test(flavor = "current_thread")]
|
||||||
async fn register_rejects_bad_names_and_specs_pre_storage() {
|
async fn register_rejects_bad_names_and_specs_pre_storage() {
|
||||||
let store = store();
|
let store = store();
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
//! FIFO by monotone offset; keyed publish rides the overlay).
|
//! FIFO by monotone offset; keyed publish rides the overlay).
|
||||||
//!
|
//!
|
||||||
//! - Publishes append to the per-stream log under its section guard:
|
//! - Publishes append to the per-stream log under its section guard:
|
||||||
//! engine-assigned offsets (the engine-wide allocator — pg-bigserial
|
//! engine-assigned offsets (the engine-wide allocator — monotone,
|
||||||
//! shaped, monotone, immutable, never renumbered; a fresh stream's
|
//! immutable, never renumbered; the concrete numbering is engine-
|
||||||
//! first event does not carry offset 1, the ADR-015 §4 note), keys
|
//! internal and never compared cross-engine — ADR-015 §4: mem's
|
||||||
|
//! fresh-engine count starts at 1, the SQL engines' counters may
|
||||||
|
//! not), keys
|
||||||
//! are carried metadata (a present key must be non-empty —
|
//! are carried metadata (a present key must be non-empty —
|
||||||
//! `InvalidName`, ADR-015 §1), and the commit boundary fires one
|
//! `InvalidName`, ADR-015 §1), and the commit boundary fires one
|
||||||
//! wake per event on the stream's engine-internal wake channel.
|
//! wake per event on the stream's engine-internal wake channel.
|
||||||
@@ -30,6 +32,7 @@
|
|||||||
//! (ADR-021 §5).
|
//! (ADR-021 §5).
|
||||||
|
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::ops::Bound;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@@ -72,7 +75,7 @@ pub(crate) fn read_events(s: &StreamState, stream: &str, from: i64, limit: i64)
|
|||||||
s.logs
|
s.logs
|
||||||
.get(stream)
|
.get(stream)
|
||||||
.map(|log| {
|
.map(|log| {
|
||||||
log.range((from + 1)..)
|
log.range((Bound::Excluded(from), Bound::Unbounded))
|
||||||
.take(limit.max(0) as usize)
|
.take(limit.max(0) as usize)
|
||||||
.map(|(_, row)| row.clone())
|
.map(|(_, row)| row.clone())
|
||||||
.collect()
|
.collect()
|
||||||
@@ -107,8 +110,10 @@ pub(crate) fn trim_events(s: &mut StreamState, stream: &str, horizon: i64) -> i6
|
|||||||
let Some(log) = s.logs.get_mut(stream) else {
|
let Some(log) = s.logs.get_mut(stream) else {
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
let horizon_next = horizon.saturating_add(1);
|
let removed: Vec<i64> = log
|
||||||
let removed: Vec<i64> = log.range(..horizon_next).map(|(k, _)| *k).collect();
|
.range((Bound::Unbounded, Bound::Included(horizon)))
|
||||||
|
.map(|(k, _)| *k)
|
||||||
|
.collect();
|
||||||
let count = removed.len();
|
let count = removed.len();
|
||||||
for offset in removed {
|
for offset in removed {
|
||||||
log.remove(&offset);
|
log.remove(&offset);
|
||||||
|
|||||||
@@ -443,8 +443,10 @@ fn merge_and_wake(state: Arc<State>, wakes: Arc<WakeBus>, mut overlay: Overlay)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Wake-at-commit: staged notifies in stage order (duplicates ride —
|
// Wake-at-commit: staged notifies in stage order (duplicates ride —
|
||||||
// per-notify), plus one wake per staged event on its stream's
|
// per-notify), plus one wake per staged stream on its engine-internal
|
||||||
// engine-internal feed (per event — never coalesced).
|
// feed (receivers drain by cursor, so one wake per stream is enough —
|
||||||
|
// the feed is plumbing, not contract surface; never coalesced across
|
||||||
|
// commits).
|
||||||
let mut fired: VecDeque<String> = VecDeque::with_capacity(overlay.notifies.len());
|
let mut fired: VecDeque<String> = VecDeque::with_capacity(overlay.notifies.len());
|
||||||
fired.extend(overlay.notifies.iter().cloned());
|
fired.extend(overlay.notifies.iter().cloned());
|
||||||
for stream in overlay.events.keys() {
|
for stream in overlay.events.keys() {
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
//! commit order; coalescing is not applicable. The substrate realizes
|
//! commit order; coalescing is not applicable. The substrate realizes
|
||||||
//! the shape structurally:
|
//! the shape structurally:
|
||||||
//!
|
//!
|
||||||
//! - **Commit-ordered source**: a mechanism (notify now — streams and
|
//! - **Commit-ordered source**: a mechanism (notify, stream publishes,
|
||||||
//! queue wakes arrive with their tasks) fires its commit's wakes
|
//! the tx commit-merge's staged notifies and publishes) fires its
|
||||||
|
//! commit's wakes
|
||||||
//! through [`WakeBus::fire_commit_wakes`], under one guard: the
|
//! through [`WakeBus::fire_commit_wakes`], under one guard: the
|
||||||
//! commit sequence bumps and the per-channel fanout completes as a
|
//! commit sequence bumps and the per-channel fanout completes as a
|
||||||
//! single operation, so every subscriber's receipt order is exactly
|
//! single operation, so every subscriber's receipt order is exactly
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
---
|
---
|
||||||
status: implementation-complete
|
status: stable
|
||||||
last_updated: 2026-10-10 (the wave-6 wave landed: the full surface
|
last_updated: 2026-10-10 (the wave-6 review gate passed: the
|
||||||
wired over guarded state, the suite's third `StoreFactory` column
|
conformance read clean — three edge-domain fixes landed inline (the
|
||||||
green on the host under the current-thread flavor, the wasm32
|
stream cursor read's `i64::MAX` overflow, `trim_to`'s horizon
|
||||||
compile gate clean — the review gate holds the stable flip)
|
endpoint, the scheduler register-time interval overflow now rejecting
|
||||||
|
with `InvalidSpec` like the SQLite register) plus scaffold-residue
|
||||||
|
cleanup; the third column's 25-row set green under the current-thread
|
||||||
|
flavor; the wasm32 gate + feature subset verified; the rider's arms
|
||||||
|
verified)
|
||||||
---
|
---
|
||||||
|
|
||||||
# Mem engine
|
# Mem engine
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
---
|
---
|
||||||
status: draft
|
status: draft
|
||||||
last_updated: 2026-10-10 (ADR-024 — the mem engine adopted as wave 6,
|
last_updated: 2026-10-10 (wave 6 implemented and reviewed — the mem
|
||||||
release readiness renumbered to wave 7; wave 6 decomposed — eleven
|
engine landed, the suite's third column green under the current-thread
|
||||||
tasks per the pg-wave rhythm with the tx overlay load-bearing. Waves
|
flavor, the wasm32 gate clean, the wave-6 gate passed with three
|
||||||
1–5 implemented and reviewed; engine specs stable. Wave-5 review
|
inline edge-domain fixes; Findings 2–3 and the flake watch carry to
|
||||||
residue decomposed — pg-fix-scheduler-fire-wake; flake-watch carried
|
wave 7.)
|
||||||
to wave 7.)
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# alkstore — Implementation plan
|
# alkstore — Implementation plan
|
||||||
@@ -44,7 +43,7 @@ core crate plus the finished suite (wave 5) — born pinned, per
|
|||||||
| [3](#wave-3--sqlite-engine) | SQLite engine: connection architecture, re-derived queue ops on contract v1, scheduler/outbox, tx seam, SQLite backlog column | waves 1 + 2 | implemented + reviewed (2026-10-08) |
|
| [3](#wave-3--sqlite-engine) | SQLite engine: connection architecture, re-derived queue ops on contract v1, scheduler/outbox, tx seam, SQLite backlog column | waves 1 + 2 | implemented + reviewed (2026-10-08) |
|
||||||
| 4 | [Postgres engine](#wave-4--postgres-engine): schema bootstrap, pool/open, listener/forwarder, all mechanisms, tx seam, pg backlog column | wave 1 | implemented + reviewed (2026-10-08) |
|
| 4 | [Postgres engine](#wave-4--postgres-engine): schema bootstrap, pool/open, listener/forwarder, all mechanisms, tx seam, pg backlog column | wave 1 | implemented + reviewed (2026-10-08) |
|
||||||
| 5 | [Contract suite](#wave-5--contract-suite): the cross-engine equivalence properties (core-contract.md §Verification backlog), version-stamped per ADR-017 | waves 3 + 4 | implemented + reviewed (2026-10-10) |
|
| 5 | [Contract suite](#wave-5--contract-suite): the cross-engine equivalence properties (core-contract.md §Verification backlog), version-stamped per ADR-017 | waves 3 + 4 | implemented + reviewed (2026-10-10) |
|
||||||
| 6 | [Mem engine](#wave-6--mem-engine): separate `alkstore-mem` crate, full contract v1 over in-memory mechanisms (tx overlay, queue/scheduler/streams/locks/wakes), the suite's third factory column (born pinned), wasm32 compile-clean acceptance | waves 1 + 5 | decomposed (2026-10-10) |
|
| 6 | [Mem engine](#wave-6--mem-engine): separate `alkstore-mem` crate, full contract v1 over in-memory mechanisms (tx overlay, queue/scheduler/streams/locks/wakes), the suite's third factory column (born pinned), wasm32 compile-clean acceptance | waves 1 + 5 | implemented + reviewed (2026-10-10) |
|
||||||
| 7 | [Release readiness](#wave-7--release-readiness): crate docs, deployment matrix final pass, README (written last, honestly), publish prep; fuzzing decision | wave 6 | not yet decomposed |
|
| 7 | [Release readiness](#wave-7--release-readiness): crate docs, deployment matrix final pass, README (written last, honestly), publish prep; fuzzing decision | wave 6 | not yet decomposed |
|
||||||
|
|
||||||
## Wave 1 — Foundations
|
## Wave 1 — Foundations
|
||||||
@@ -496,7 +495,34 @@ decomposes. Specific gates:
|
|||||||
structural twin; wave-3 outcomes absorbed (constructors exist;
|
structural twin; wave-3 outcomes absorbed (constructors exist;
|
||||||
arithmetic re-owned per engine; per-engine `@every` parser;
|
arithmetic re-owned per engine; per-engine `@every` parser;
|
||||||
SQLite-scoped deferred notes stay put). See the Wave 4 section.
|
SQLite-scoped deferred notes stay put). See the Wave 4 section.
|
||||||
- **Wave 6 decomposition** (2026-10-10) — ADR-024 adopted the mem
|
- **Wave 6 review gate** (`review-wave-6`, 2026-10-10) — the
|
||||||
|
born-pinned gate passed: the conformance read clean (queue state
|
||||||
|
machine, scheduler collapse, outbox, streams, locks, clock,
|
||||||
|
validation, tx overlay, wake substrate each verified against the
|
||||||
|
contract and the pinned ADRs; guard-order discipline audited across
|
||||||
|
every section-guard site); the suite's third column read row-by-row
|
||||||
|
and green (the 24 engine-shared rows + mem's
|
||||||
|
`payload_too_large_never_produced_on_mem` arm + the factory-shape
|
||||||
|
test, 25 green under the per-test current-thread flavor);
|
||||||
|
three-way arithmetic equivalence green (backoff curve, opts
|
||||||
|
resolution on all three columns); the wasm32 acceptance items
|
||||||
|
verified (compile-clean, tokio sync+time subset, no blocking calls,
|
||||||
|
flavor enforced); the rider's arms verified (Finding 1 discharged;
|
||||||
|
Findings 2–3 confirmed still parked for wave 7). Three edge-domain
|
||||||
|
fixes landed inline at the gate: the stream cursor read's `i64::MAX`
|
||||||
|
overflow (`Bound::Excluded`), `trim_to`'s `i64::MAX` horizon
|
||||||
|
endpoint (`Bound::Included`), and the scheduler register path
|
||||||
|
aligning with SQLite on rejecting a parseable interval whose first
|
||||||
|
boundary overflows (`InvalidSpec`). Unpinned contract-adjacent
|
||||||
|
observations recorded in the task's Notes for the wave-7 pre-release
|
||||||
|
pass: the multi-thread publish-to-wake ordering tolerance (same
|
||||||
|
shape implied for the SQL engines; nothing pins wake-fire order vs
|
||||||
|
offset order), the `heartbeat(extend)`/`save_offset`/
|
||||||
|
`EventReceiver::try_recv`-closed classifications missing from the
|
||||||
|
ADR-023 §2 table, and the mem arithmetic being a transplant of the
|
||||||
|
SQLite implementations (equivalence suite-pinned; ownership letter
|
||||||
|
satisfied). `engine-mem.md` flipped to `stable`.
|
||||||
|
- **Wave-6 decomposition** (2026-10-10) — ADR-024 adopted the mem
|
||||||
engine the same day the wave-5 gate closed; the task set follows the
|
engine the same day the wave-5 gate closed; the task set follows the
|
||||||
pg wave's rhythm with the tx overlay as the load-bearing task, the
|
pg wave's rhythm with the tx overlay as the load-bearing task, the
|
||||||
suite's third column as the born-pinned deliverable, the harness's
|
suite's third column as the born-pinned deliverable, the harness's
|
||||||
|
|||||||
+191
-9
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
id: review-wave-6
|
id: review-wave-6
|
||||||
name: Review gate — wave 6 (mem engine)
|
name: Review gate — wave 6 (mem engine)
|
||||||
status: pending
|
status: completed
|
||||||
depends_on: [mem-engine-integration, pg-fix-scheduler-tick-seam]
|
depends_on: [mem-engine-integration, pg-fix-scheduler-tick-seam]
|
||||||
scope: moderate
|
scope: moderate
|
||||||
risk: low
|
risk: low
|
||||||
@@ -50,16 +50,16 @@ contract and the pinned ADR-024 text, and audit:
|
|||||||
|
|
||||||
## Acceptance Criteria
|
## Acceptance Criteria
|
||||||
|
|
||||||
- [ ] Full conformance code-read recorded (findings + dispositions
|
- [x] Full conformance code-read recorded (findings + dispositions
|
||||||
inline in this task's Notes, mirroring review-wave-3/4 style)
|
inline in this task's Notes, mirroring review-wave-3/4 style)
|
||||||
- [ ] Suite mem column green on host, 25-row set wired per the
|
- [x] Suite mem column green on host, 25-row set wired per the
|
||||||
classification; three-way equivalence rows green
|
classification; three-way equivalence rows green
|
||||||
- [ ] wasm32 compile gate + feature-subset + current-thread flavor
|
- [x] wasm32 compile gate + feature-subset + current-thread flavor
|
||||||
verified, recorded
|
verified, recorded
|
||||||
- [ ] The rider's arms verified; Finding 1 disposition recorded
|
- [x] The rider's arms verified; Finding 1 disposition recorded
|
||||||
- [ ] Workspace gates re-run green this session (`cargo build`,
|
- [x] Workspace gates re-run green this session (`cargo build`,
|
||||||
`cargo test`, clippy `-D warnings`, fmt)
|
`cargo test`, clippy `-D warnings`, fmt)
|
||||||
- [ ] `engine-mem.md` disposition decided (stable flip or follow-up)
|
- [x] `engine-mem.md` disposition decided (stable flip or follow-up)
|
||||||
and `implementation.md`'s wave-6 row updated
|
and `implementation.md`'s wave-6 row updated
|
||||||
|
|
||||||
## References
|
## References
|
||||||
@@ -74,8 +74,190 @@ contract and the pinned ADR-024 text, and audit:
|
|||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
> To be filled by implementation agent
|
Gate verdict: **passed**. No blocker, no major live defect; three
|
||||||
|
edge-domain fixes landed inline (below); the remaining observations
|
||||||
|
are recorded with dispositions, none mem-divergent from the engines.
|
||||||
|
|
||||||
|
### Fixed inline at the gate (this session, verified clippy/fmt/test green after each)
|
||||||
|
|
||||||
|
1. **Stream cursor read overflow** (`stream.rs` `read_events`): the
|
||||||
|
cursor advanced by plain `from + 1`, panicking on debug build for a
|
||||||
|
caller-supplied `offset == i64::MAX` (direct `read_since`, or
|
||||||
|
indirect via `save_offset(i64::MAX)` + `read_from_consumer`) and
|
||||||
|
wrapping to a from-the-beginning read in release. Fixed with
|
||||||
|
`Bound::Excluded(from)` — boundary-total at every i64, the
|
||||||
|
ADR-023 §2 read-cursor classification this fix encodes.
|
||||||
|
2. **`trim_to` horizon endpoint**: `horizon.saturating_add(1)` clipped
|
||||||
|
`i64::MAX` and left a row at offset `i64::MAX` undeleted — the
|
||||||
|
exact-`<=` boundary broke at the one unreachable endpoint anyway.
|
||||||
|
Fixed with `Bound::Included(horizon)`; unreachable in practice
|
||||||
|
(2⁶³ publishes), fixed because the template cost nothing.
|
||||||
|
3. **Scheduler register overflow alignment** (conformance divergence
|
||||||
|
found by the read): mem registered a *parseable* interval whose
|
||||||
|
first boundary overflows (`@every 9223372036854775807s`) with a
|
||||||
|
saturating `next_fire_at` — accepted-never-fires — where the SQLite
|
||||||
|
register path rejects the same spec with `InvalidSpec`.
|
||||||
|
Class-1-window divergence between engines on a register-time
|
||||||
|
semantics; aligned mem to reject (`checked_add` → `InvalidSpec`, no
|
||||||
|
row stored) with a new pin test distinguishing it from the
|
||||||
|
unparsable-too-large spec the parser already refuses. The tick's
|
||||||
|
`i64::MAX` overflow sentinel stays identical in both engines.
|
||||||
|
4. **Scaffold-residue cleanup**: `clock.rs`'s stale
|
||||||
|
`#![allow(dead_code)]` on the now-wired accessor + scaffolding
|
||||||
|
comment removed; `tx.rs`'s commit-wake comment corrected to what
|
||||||
|
the code does (one wake per staged *stream* — receivers drain by
|
||||||
|
cursor, the feed is plumbing); `wakes.rs` module doc's "mechanisms
|
||||||
|
arrive with their tasks" updated to the wired mechanisms;
|
||||||
|
`stream.rs` module doc no longer claims "a fresh stream's first
|
||||||
|
event does not carry offset 1" for mem (mem's fresh count *is* 1;
|
||||||
|
the ADR-015 §4 note is engine-internal and never compared
|
||||||
|
cross-engine — the doc now says exactly that).
|
||||||
|
|
||||||
|
### Recorded findings, no code change (dispositions)
|
||||||
|
|
||||||
|
- **Multi-thread publish→wake ordering tolerance** (streams): the
|
||||||
|
publish path allocates the offset, then inserts under the guard,
|
||||||
|
then fires the wake after the guard drops — on a multi-thread
|
||||||
|
runtime two concurrent publishes can complete insert+wake out of
|
||||||
|
offset-allocation order, and a subscriber's cursor can skip an
|
||||||
|
event whose wake fires later (the event is never lost from the log,
|
||||||
|
but a pre-insert drain's checkpoint passes it). The SQL engines
|
||||||
|
share the shape in principle (offset assigned pre-commit, wake
|
||||||
|
post-commit — the "pg-shaped story" the mem docs cite), and nothing
|
||||||
|
contract-side pins wake-fire order vs offset order. Disposition:
|
||||||
|
this is a contract-level tolerance nobody pinned, not a mem-local
|
||||||
|
divergence — recorded for the wave-7 pre-release pass to either
|
||||||
|
document as tolerated or amend as ADR text (wake order per stream =
|
||||||
|
offset order). Mem's declared posture is current-thread-clean
|
||||||
|
(engine-mem.md) and the suite's mem column proves it under the
|
||||||
|
current-thread flavor, which is why nothing here fails today.
|
||||||
|
- **Mem arithmetic is a transplant, not an independent derivation**
|
||||||
|
(one-owner-rule note): `resolution.rs`'s backoff curve and opts
|
||||||
|
resolution, `scheduler.rs`'s `@every` parser and `skip_forward`, are
|
||||||
|
byte-level copies of the SQLite-side implementations (semantics
|
||||||
|
re-verified against the ADR text during this read). ADR-012 §2's
|
||||||
|
letter — each engine owns its implementation, equivalence
|
||||||
|
suite-pinned — is satisfied (mem owns its modules; the
|
||||||
|
`backoff_curve_equivalence` and `enqueue_opts_resolution` rows run
|
||||||
|
green on all three columns). Recorded so the gate's record doesn't
|
||||||
|
overstate the independent-signal value of the third copy; the suite
|
||||||
|
*is* the honesty instrument.
|
||||||
|
- **`heartbeat(extend)` unclassified** (ADR-023 §2): the domain table
|
||||||
|
classifies `try_lock`/`renew` durations but not the queue
|
||||||
|
heartbeat's seconds-shaped `extend` argument. Both engines treat it
|
||||||
|
boundary-total (`saturating_add`/SQL integer add — a negative
|
||||||
|
shortens/voids the claim). Recorded as a class-1-window doc
|
||||||
|
annotation candidate for wave 7's pre-release pass; no engine
|
||||||
|
diverges.
|
||||||
|
- **`save_offset`'s argument unclassified**: negatives accepted (stored,
|
||||||
|
reads resume forward), monotone no-op below the checkpoint — both
|
||||||
|
engines, contract pins monotonicity only (ADR-019 §6). Same
|
||||||
|
annotation candidate.
|
||||||
|
- **`EventReceiver::try_recv`'s closed shape unpinned**:
|
||||||
|
`Err(Closed)` (mirrors `WakeReceiver::try_recv`, ADR-008 §3);
|
||||||
|
taxonomy-consistent, mem-test-pinned, but ADR-021 §5 pins only
|
||||||
|
`recv()`'s arms. Same annotation candidate.
|
||||||
|
- **`get_job` is id-global, not queue-filtered** — uniform on all
|
||||||
|
three engines (the handle carries the queue, the lookup the id;
|
||||||
|
`get_job_tx` *is* queue-filtered on both engines with tx). Recorded
|
||||||
|
so the record doesn't misread core-contract's "queue-scoped" handle
|
||||||
|
phrasing as a filter; the ambiguity is contract-prose, not code.
|
||||||
|
- **Mem outbox `run_once` ignores a trailing ack/retry refusal**
|
||||||
|
(deadline lapsed mid-delivery): identical to SQLite; the uniform
|
||||||
|
validity predicate makes the refusal the at-least-once honesty —
|
||||||
|
conformant, noted for the boolean's silent discard.
|
||||||
|
- **Wake-bus attach-vs-close race (defense-in-depth note)**:
|
||||||
|
`WakeBus::attach`/`fire_commit_wakes` check `closed` before taking
|
||||||
|
the registry guard, so a listen racing the engine's drop could in
|
||||||
|
principle mint a feed whose sender post-dates the registry clear.
|
||||||
|
Unreachable through safe APIs (a live `&MemStore` borrow pins the
|
||||||
|
last reference — borrowck; the bus is reachable only through the
|
||||||
|
store), so no change; recorded as a hardening candidate if the bus
|
||||||
|
ever gains a second entry path.
|
||||||
|
|
||||||
|
### Audit record (verified items)
|
||||||
|
|
||||||
|
- **Conformance read**: queue state machine (claim predicate/ordering,
|
||||||
|
visibility, reclaim-eats-attempt, uniform validity predicate,
|
||||||
|
heartbeat, retry/fail/cancel/ack_batch, dead-letter + no-stranded
|
||||||
|
sweep, queue-scoped get_job incl. dead rows); scheduler (grammar
|
||||||
|
parser, register-time stamp resolution, one-guard fire-and-advance
|
||||||
|
tick in the schedules→queues order, 64-cap catch-up + grid-aligned
|
||||||
|
skip-forward, leadership acquire/renew/loss/clean-stop on the
|
||||||
|
reserved lock with runner-unique owner); outbox (derived reserved
|
||||||
|
backing queue unreachable by every consumer entry point, 60/5/5
|
||||||
|
stamps, run_once ack/retry curve, tx half stamping identically);
|
||||||
|
streams (offset-ASC reads, absent-consumer 0 head read, monotone
|
||||||
|
saves, trim exact-`<=` never-renumbering and waking nothing,
|
||||||
|
attach-before-read subscribe, engine-drop close arms); locks
|
||||||
|
(contention loser `Ok(None)`, `ttl <= 0` refused on try + renew,
|
||||||
|
silent lapse, non-owner release = false, renew-past-expiry refused,
|
||||||
|
no `lock_tx`); clock (one accessor, all stamps read it, freeze
|
||||||
|
seam cfg(test) only); validation (every entry routed through core's
|
||||||
|
`validate_shared_name`/`validate_local_name`, no engine-local
|
||||||
|
rules); tx overlay (eleven `*_tx` staged with entry validation
|
||||||
|
before effect, one clock read, ADR-020 §3 stamps incl. per-job
|
||||||
|
override, merged reads queue-scoped + dead-visible + offset-ASC +
|
||||||
|
extent-clamped, one-op commit merge + wake-at-commit, drop =
|
||||||
|
discard through unwind, stage-time id/offset allocation with legal
|
||||||
|
gaps); wake substrate (channel-scoped, per-notify never coalesced,
|
||||||
|
attach-after-commit no-replay, close disconnects all, dropped-feed
|
||||||
|
detach, commit-ordered FIFO). Guard discipline audited: canonical
|
||||||
|
order (schedules → queues → streams) holds at every two-guard site;
|
||||||
|
no guard across an `.await` or user code; zero panics/unwrap/
|
||||||
|
expect in library code.
|
||||||
|
- **Third column as compatibility instrument**: the mem target wires
|
||||||
|
exactly the 24 engine-shared rows + mem's
|
||||||
|
`payload_too_large_never_produced_on_mem` (stamped ADR-016 §5 +
|
||||||
|
ADR-024 §3) + the factory-shape test; the reconnection-shaped rows
|
||||||
|
stay per-engine wired (absent here, per ADR-024 §3's
|
||||||
|
classification); the three-way equivalence (`backoff_curve_equivalence`,
|
||||||
|
`enqueue_opts_resolution`) green on all three columns; 25/25 green
|
||||||
|
on the host.
|
||||||
|
- **wasm32 acceptance**: `cargo check --target wasm32-unknown-unknown
|
||||||
|
-p alkstore-mem` clean; manifest tokio subset sync+time (dev-deps
|
||||||
|
rt/macros only); no `spawn_blocking`/blocking-call in mem source;
|
||||||
|
current-thread flavor enforced on every mem-column test (a blocking
|
||||||
|
call would hang, not pass); on-target suite execution stays scoped
|
||||||
|
out behind the named adapter-crate collapse condition (ADR-024 §5 —
|
||||||
|
recorded, not an OQ).
|
||||||
|
- **Spec + matrix**: `engine-mem.md` read against the implemented
|
||||||
|
engine (tx overlay, asymmetry classification, isolation, wasm
|
||||||
|
identity — all true as landed; the stable flip records the gate in
|
||||||
|
its frontmatter); `deployment.md`'s mem rows read true as
|
||||||
|
implemented.
|
||||||
|
- **Rider closed**: `pg-fix-scheduler-tick-seam`'s two arms
|
||||||
|
(`one_faulted_tick_is_retried_and_the_job_fires`,
|
||||||
|
`tick_retry_exhaustion_exits_typed_and_leaves_the_lock_to_lapse`)
|
||||||
|
verified present against the real retry loop; review 003 Finding 1
|
||||||
|
discharged; Findings 2–3 confirmed still parked for wave 7
|
||||||
|
(implementation.md §Wave 7); the fire-wake gap
|
||||||
|
(`pg-fix-scheduler-fire-wake`) closed pre-wave and its retired gap
|
||||||
|
records verified; the wave-5 flake ledger's §6 update recorded.
|
||||||
|
- **Workspace gates re-run green this session**: `cargo build`;
|
||||||
|
`cargo test --workspace` green (core 25, suite harness 3, mem 101
|
||||||
|
lib + 25 suite, SQLite 191 + 25, pg 124 + 25 + 9, pg column
|
||||||
|
server-less per the SKIP posture — the pg-side verification was the
|
||||||
|
rider tasks' own live runs); clippy `--all-targets -D warnings`
|
||||||
|
clean; `cargo fmt --check` clean; `taskgraph validate` green
|
||||||
|
(61 tasks).
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
> To be filled on completion
|
The wave-6 review gate passed and flipped `engine-mem.md` to
|
||||||
|
`stable`. The conformance read was clean in mechanism substance; the
|
||||||
|
findings class was edge-domain and hygiene: three inline fixes (the
|
||||||
|
stream cursor's `i64::MAX` overflow, `trim_to`'s horizon endpoint,
|
||||||
|
and the scheduler register-overflow alignment with SQLite's
|
||||||
|
`InvalidSpec` — the one true engine-vs-engine divergence the read
|
||||||
|
found, resolved into the class-1 window) and the expected residue
|
||||||
|
cleanups. The suite's third column is born pinned as ADR-024
|
||||||
|
required — 25 green under the current-thread flavor, the three-way
|
||||||
|
arithmetic equivalence actually asserted — and the wasm32 acceptance
|
||||||
|
items verified. The unpinned contract-adjacent observations (the
|
||||||
|
multi-thread wake-ordering tolerance, three ADR-023 §2 table
|
||||||
|
classifications, the receiver try_recv closed shape) are recorded
|
||||||
|
above with their dispositions for the wave-7 pre-release pass; none
|
||||||
|
blocks. The rider (`pg-fix-scheduler-tick-seam`) closed Finding 1.
|
||||||
|
Task files audited: all eleven dependency tasks completed with
|
||||||
|
Notes/Summary filled.
|
||||||
Reference in new issue
Block a user