mem-engine-foundations — the mem engine's foundations land (alkstore-mem, wave 6 task 1 of the ADR-024 chain): fourth workspace member with the manifest surface ADR-024 §1 pins — alkstore versioned path pin (ADR-017 §4.1) plus tokio restricted to the wasm-supported subset (sync + time; dev-deps rt/macros for the current-thread-flavor tests), no driver deps, no parking_lot (the description's prose named it, but the ADR + acceptance criterion pin the manifest tighter — short-held internal guards are std::sync::Mutex behind a state::Guard trait that folds poisoning away structurally, guards never held across awaits or user code, tokio mpsc the cross-await primitive). Five modules re-exported from lib.rs whose crate docs carry the ADR-016 posture statement (honest-ephemeral, full contract v1, wasm32-compile-clean identity, architecture sketch with the wave-6 construction line). MemStore::new() is the engine-native isolated constructor (ADR-024 §4's documented open-prose exception): fresh guarded state core (state.rs — empty named per-mechanism sections QueueState/StreamState/LockState/ScheduleState, shapes owned by the mechanism tasks), fresh wake bus, fresh clock per open; teardown is drop driving WakeBus::close() — no explicit close form. Wake substrate (wakes.rs): WakeBus::attach → WakeFeed (one unbounded mpsc feed per subscriber, detach-on-drop, attach under the registry guard so attach-after-commit no-replay is structural), fire_commit_wakes(channels) as the commit-ordered wake source's entry point (guard makes watermark bump + per-channel fanout one operation — per-subscriber FIFO by commit order, never coalesced), close empties senders so engine drop disconnects every feed; commit_watermark is a cfg(test) ordering observable only. Clock (clock.rs): one accessor Clock::unix_now over SystemTime, freeze/advance/unfreeze as the cfg(test) deterministic-seam; MemStore clock/wakes cfg(test) accessors. Validation (validation.rs) carries no new rule — pin module only (empty/whitespace InvalidName, prefix ReservedName, RESERVED_LISTENER_RECONNECTED rejected though unused here); mechanisms route core's validate_shared_name/local_name directly. Targeted allow(dead_code) on not-yet-wired scaffold surfaces so clippy -D warnings stays green pre-consumer (mechanism tasks own exhausting the allows); tokio time pinned unexercised (lock-TTL/scheduler consumers arrive with their tasks). Unit pins: 17 mem tests — two-instances-share-nothing (Arc pointer identity + behavioral: frozen clock isolation, cross-instance wake silence), state section independence/relock, wake routing (channel-scoped + foreign silence, per-notify never coalesced, watermark-ordered FIFO, no-replay, close disconnects all + post-close inert, dropped feed detaches), engine-drop closes feeds, clock determinism, validation pin row. Task file: status completed, Notes (decisions of record: parking_lot rejection, empty sections, substrate API shape, no-close, scaffold allow posture, unexercised time feature) + Summary filled. Verified: cargo build; workspace cargo test green (pg 124/25/9, sqlite 191/25, core 25, suite 3, mem 17); clippy --all-targets -D warnings clean; cargo fmt --check clean; cargo check --target wasm32-unknown-unknown -p alkstore-mem clean — the wave-6 wasm gate starts green from this task onward
This commit is contained in:
1 parent
6f9cfb5778
commit
6632dcce1f
10 files changed
+938
-8
No files matched your search
@@ -0,0 +1,110 @@
|
||||
//! The engine's clock — every timestamp the mem engine produces (unix
|
||||
//! seconds stamps, visibility deadlines, TTLs, scheduler boundaries)
|
||||
//! reads this one accessor ([`Clock::unix_now`]); the process clock is
|
||||
//! all there is (engine-mem.md: no server clock to defer to).
|
||||
//!
|
||||
//! The single point exists so tests can observe timestamps
|
||||
//! deterministically the way the SQL engines observe row shapes: a
|
||||
//! `#[cfg(test)]` freeze/advance seam pins a wall value and every
|
||||
//! accessor call returns it (see the unit pins below). Production
|
||||
//! reads `std::time::SystemTime`.
|
||||
|
||||
#[cfg(test)]
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[cfg(test)]
|
||||
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 {
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|since| since.as_secs() as i64)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
/// The engine's single clock.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Clock {
|
||||
#[cfg(test)]
|
||||
frozen: Mutex<Option<i64>>,
|
||||
}
|
||||
|
||||
impl Default for Clock {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
pub(crate) fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(test)]
|
||||
frozen: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Unix-seconds "now" — the one timestamp source of the engine.
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn unix_now(&self) -> i64 {
|
||||
#[cfg(test)]
|
||||
let frozen = { *self.frozen.guard() };
|
||||
#[cfg(test)]
|
||||
if let Some(unix) = frozen {
|
||||
return unix;
|
||||
}
|
||||
process_unix_now()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn freeze(&self, at_unix: i64) {
|
||||
*self.frozen.guard() = Some(at_unix);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn advance(&self, seconds: i64) {
|
||||
let mut guard = self.frozen.guard();
|
||||
*guard = Some(frozen_now(&guard).saturating_add(seconds));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn unfreeze(&self) {
|
||||
*self.frozen.guard() = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn frozen_now(frozen: &Option<i64>) -> i64 {
|
||||
frozen.unwrap_or_else(process_unix_now)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn unfrozen_reads_a_plausible_process_time() {
|
||||
let clock = Clock::new();
|
||||
let now = clock.unix_now();
|
||||
assert!(
|
||||
now > 1_700_000_000,
|
||||
"the accessor reads the process clock when unfrozen"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn frozen_reads_deterministically_and_advances() {
|
||||
let clock = Clock::new();
|
||||
clock.freeze(1_234_567);
|
||||
assert_eq!(clock.unix_now(), 1_234_567);
|
||||
clock.advance(10);
|
||||
assert_eq!(clock.unix_now(), 1_234_577);
|
||||
clock.unfreeze();
|
||||
assert!(
|
||||
clock.unix_now() > 1_700_000_000,
|
||||
"unfreeze restores the process clock"
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user