Repo: /home/dylon/Workspace/f1r3fly.io/libdictenstein · Scope: a REVERSIBLE, bench-internals-gated
extension making the immutable-overlay checkpoint support eviction-ON, + a pre-registered benchmark section
appended to the frozen ledger. NOT the production flip (checkpoint() untouched; owner-gated; out of
scope). ZERO new unsafe. Persisted from the Plan-agent design (2026-06-02).
FEASIBLE, reversibly, small surface. The hard part (watermark-bounded WAL reclaim under lock-free out-of-order commit) is ALREADY implemented + proven for the retain-WAL variant; the new work is purely additive (publish the eviction registry that capture already builds).
capture_snapshot_immutable ALREADY populates the registry. persist.rs:346-349 builds
eviction_registry = self.eviction_coordinator.as_ref().map(|_| DiskLocationRegistry::new()); :426-430
threads eviction_registry.as_mut() into the SAME serialize_char_node_to_disk the owned path uses, which
registers every node via reg.register_char(path, ptr, len, depth, node_type) (:919-927). Registry-over-
immutable-snapshot is already correct by construction; carried in CheckpointSnapshot.eviction_registry
(:500). Requirement #1 already satisfied.publish_immutable_snapshot_retaining_wal REFUSES a registry:
debug_assert!(snapshot.eviction_registry.is_none(), …) (persist.rs:560-565) + deliberately doesn't
publish it (:554-559). The bench shim bench_immutable_checkpoint (:622-626) calls it → benchmark runs
eviction-OFF (ledger §3). This debug_assert! is the one-line block.publish_durable_and_reclaim (:108-179) publishes via
coordinator.update_disk_registry(registry) (:123-127). But its reclaim is lock-free-incompatible: reclaims
by next_lsn (:153) which the lock-free path never advances (:405-411), and debug_assert_eq!s next_lsn
unchanged (:140-146) which a concurrent insert_cas_durable violates.publish_immutable_snapshot_retaining_wal writes
Checkpoint{checkpoint_lsn = committed_watermark_at_capture} (:567-598), no truncate (retain-WAL); no-lost-
write proven (:524-536 doc + the multi-writer soak :1407-1494).
$\Rightarrow$ the new component is ONE new publisher = retain-WAL publisher + registry publication, via a sibling bench
shim. Destructive truncation stays owner-gated. Stale-comment note: :557-558 claims the registry "is not
Clone" — FALSE (disk_registry.rs derives Clone); design moves the registry, doesn't need Clone; don't
repeat the claim.No new build code (already correct). New publisher beside publish_immutable_snapshot_retaining_wal:
#[cfg(any(test, feature = "bench-internals"))]
pub(crate) fn publish_immutable_snapshot_retaining_wal_with_eviction(
&self, snapshot: CheckpointSnapshot, // BY VALUE — moves the registry out
) -> Result<()> {
let checkpoint_lsn = snapshot.committed_watermark_at_capture.ok_or_else(|| /* internal err */)?;
self.publish_snapshot(&snapshot)?; // (1) durable descriptor publish (lin. point) + verify
self.verify_checkpoint()?;
if let Some(registry) = snapshot.eviction_registry { // (2) publish ONLY AFTER verify proves durable
if let Some(ref coordinator) = self.eviction_coordinator {
coordinator.update_disk_registry(registry); // coordinator.rs:379 (in-memory swap, no fsync)
}
}
if let Some(ref wal_writer) = self.wal_writer { // (3) record checkpoint_lsn = watermark; sync;
wal_writer.append(WalRecord::Checkpoint { checkpoint_lsn, timestamp })?; // RETAIN WAL (no rotate)
wal_writer.sync()?;
}
Ok(())
}
By-value because update_disk_registry consumes the registry; mirrors owned publish_durable_and_reclaim(snapshot);
only caller is the new shim. publish_snapshot(&snapshot) borrows before the move.
Invalidation contract PRESERVED (the subtlety): every durable mutation flows through append_to_wal_inner
(wal_helpers.rs:78) whose first act is invalidate_eviction_registry() (:86). The lock-free durable writer
ALREADY honors this: insert_cas_durable step 1 = append_to_wal_returning_lsn → append_to_wal_inner →
invalidate, BEFORE its visibility CAS (lockfree_cas.rs:214,232); likewise try_increment_cas_durable (:784).
So a concurrent writer invalidates the published registry before its write is visible; select/perform/force_ eviction_char gate on is_valid() (disk_registry.rs:325,370; coordinator.rs:332,592,636) → a dirtied
registry yields ZERO evictions, never a stale-pointer eviction. Risk = eviction liveness, not safety.
Load-bearing decision: RECORD checkpoint_lsn = committed watermark + RETAIN WAL — NO destructive truncate
(truncation = owner-gated flip, out of scope per persist.rs:512-514). Identical to the proven eviction-OFF
treatment. The single most dangerous line is the SAME line already shipped + proven; eviction-ON does not move
it.
Proof sketch: let w = committed_watermark_at_capture (captured Acquire STRICTLY before the root load —
persist.rs:403<:420, "DO NOT REORDER" :351-402); S = terms in the captured snapshot; recovery yields
$image(S) \oplus replay{lsn > w}$ (the Checkpoint{checkpoint_lsn=w} gates replay to tail >w; TLA RecoveredSet
LockFreeDurableCheckpoint.tla:164-165). For any visible write LSN ℓ:
\le$ w: watermark contract $\Rightarrow$ ℓ committed $\Rightarrow$ (Order A) WAL-synced-durable before its visibility CAS, which
linearized $\le$ the snapshot root load (watermark read first $\Rightarrow$ loaded root $\supseteq$ all ℓ$\le$w) $\Rightarrow$ $ℓ \in S \subseteq image(S)$. Preserved in image.ℓ > checkpoint_lsn $\Rightarrow$ recovery replays its (durable, retained) WAL record. Preserved via replay.
Exhaustive on ℓ ⪋ w; no double-count (membership idempotent; counter deltas: Checkpoint{=w} makes recovery
SKIP image-folded $\le$w, SUM only retained tail >w — the c0=115-vs-60 bug :524-530). Registry is invisible to
recovery (EvictionRegistryPublication.tla JustRecoveredMatchesDurable; recovery_independent_of_registry
test persistent_char_eviction_registry_correspondence.rs:133-159) $\Rightarrow$ eviction-ON cannot change the conclusion.
Capture-ordering assert debug_assert!(watermark ≤ synced_frontier) (:464-471) inherited verbatim.#[cfg(feature = "bench-internals")]
pub fn bench_immutable_checkpoint_with_eviction(&self) -> Result<()> {
let snapshot = self.capture_snapshot_immutable()?; // builds the registry when eviction on
self.publish_immutable_snapshot_retaining_wal_with_eviction(snapshot)
}
Reachability: eviction_coordinator is pub(crate) (mod.rs:438), so the bench binary needs a gated
enabler #[cfg(feature="bench-internals")] pub fn bench_enable_eviction(&mut self, config: EvictionConfig) on
PersistentARTrieChar that constructs the coordinator exactly as SharedCharARTrie::enable_eviction
(mod.rs:1452-1507). (TREATMENT can't run over SharedCharARTrie because bench_immutable_checkpoint* are
PersistentARTrieChar methods.) Rollback (one edit each): delete the 2 shims; remove the bench-internals
disjunct from the publisher (→ cfg(test)-only); revert ledger §E + bench arm. checkpoint() + production
untouched. ZERO new unsafe (only safe APIs) $\Rightarrow$ verify-unsafe-boundary-inventory.sh stays exit-0.
Reclaim under lock-free commit ALREADY covered: LockFreeDurableCheckpoint.tla has ReclaimWal (:152-160)
CrashRecover (:167-171), proves NoLostWriteUnderLockFreeCommit (:200-201) + CaptureEqualsPublishFrontier
(:195-196) under USE_WATERMARK=TRUE; _Unsafe.cfg = losing negative control. The bench reclaim (record-
watermark+retain) is a SUBSET of ReclaimWal. No new TLA for the reclaim. NOT captured: the registry
interaction. Minimal NEW spec LockFreeDurableCheckpointEviction.tla (do NOT mutate the frozen base spec),
reusing the base + adding: registryDurableUpTo: Nat, registryValid: BOOLEAN; PublishCheckpoint also sets
registryDurableUpTo'=ckptTarget, registryValid'=TRUE (after Verified→Publish); Commit(w) sets
registryValid'=FALSE (invalidation under lock-free writers); EvictUnderRegistry enabled only when
registryValid, evicts only entries $\le registryDurableUpTo$. Invariants: NoLostWriteUnderLockFreeCommit
(re-derived under reclaim+eviction — headline); RegistryPointsAtDurableWatermark == registryValid => registryDurableUpTo <= Watermark; EvictionTouchesOnlyDurable (evicted ⊆ durableCkpt); keep
CaptureEqualsPublishFrontier/RecoveredNeverInventsState/ImmutableSnapshotIsClosed/DurablePrefix.
CONSTANTS: Writers={w1,w2}, Lsns={1,2,3}, NoLsn=0, USE_WATERMARK=TRUE, CHECK_DEADLOCK FALSE; a
_Unsafe.cfg (USE_WATERMARK=FALSE) re-confirms the losing trace. Register both in
verify-formal-correspondence.sh SANY + RUN_TLC lists (beside :235-236,283-284); script stays exit-0.Rust correspondence tests (#[cfg(test)] mod immutable_eviction_checkpoint_correspondence in persist.rs beside
:1061):
immutable_eviction_checkpoint_reopens_losing_nothing: eviction-enabled overlay trie, Immediate,
enable_lockfree; insert_cas_durable a tier-spanning set; capture_snapshot_immutable (assert
snapshot.eviction_registry.char_len() > 0 — GAP closed); publish_*_with_eviction (assert
evictable_node_count() > 0); force an eviction (every term still resolves); drop WITHOUT destructive reclaim;
reopen; assert EVERY acknowledged term present.writers_concurrent_with_eviction_checkpointer_all_survive_reopen: N insert_cas_durable writers ‖ a
checkpointer looping capture + publish_*_with_eviction (retain) + a racing force_eviction; reopen $\Rightarrow$ exact
acknowledged set survives (counters CAPTURE-only like :1516).Both arms eviction ENABLED. CONTROL = owned tree + publish_durable_and_reclaim (publishes registry :123-127);
TREATMENT = overlay + bench_immutable_checkpoint_with_eviction.
\land$ significant $\land$ d $\ge$ 0.8. Expectation: eviction publication is OFF the timed writer path (checkpointer's
update_disk_registry = one RwLock::write swap), so track the eviction-OFF result (+312%); registry build cost
is in BOTH arms (same serializer). Secondaries (vetoes): SE1 pause T $\le$ C; SE2 tails $\le$1.10$\times$; SE3 RSS $\le$1.25$\times$;
SE4 contended not sig worse; SE5 (NEW correctness veto): post-checkpoint force_eviction + reload returns exact
values in BOTH arms — fail $\Rightarrow$ ABORT (bug, not perf).update_disk_registry adds ZERO fsync to either $\Rightarrow$ per-checkpoint fsync count
identical; no NEW asymmetry (only the truncate-vs-retain already logged §2.2/C3). Record round_dir_bytes +
evictable_node_count() per round.\ge$0.8)+Mann-Whitney, interleave+randomize (C9),
single-arm-per-process RSS (C10), real-disk target/bench-scratch never tmpfs, 5 GiB ceiling,
systemd 32G + taskset -c 0-15, both arms EvictionConfig::without_memory_monitor() (deterministic). §8 rule
1-4 verbatim, gated first by SE5.--eviction flag enabling eviction on both arms + routing TREATMENT to
bench_immutable_checkpoint_with_eviction; emit evictable_node_count. No Cargo change ([[bench]] already has
the features).\ge$2474 + verify-formal-correspondence exit 0)LockFreeDurableCheckpointEviction.tla+.cfg+_Unsafe.cfg; register in the verify
script. Gate: SANY passes, RUN_TLC holds invariants, exit 0. Rollback: delete 3 files + 4 script lines.cfg(test)): add publish_immutable_snapshot_retaining_wal_with_eviction
(cfg(any(test, bench-internals))) + T1/T2 in a #[cfg(test)] mod. Gate: nextest $\ge$2476; verify exit 0; T1
asserts registry char_len>0 + reopen-loses-nothing. Rollback: delete method + test mod.bench-internals): add bench_immutable_checkpoint_with_eviction +
bench_enable_eviction; add the bench-internals disjunct to Phase-2 publisher. Gate: default nextest $\ge$2474
(shims compiled out) + cargo build --benches --features persistent-artrie,bench-internals OK + verify exit 0.
Rollback: delete 2 shims + the disjunct.benches/lockfree_flip_benchmark.rs (--eviction arm); append
frozen §E. Gate: bench smoke (run_smoke) 1 round/arm; default nextest unaffected. Rollback: revert arm + §E.--eviction). Not part of the merge gate.RecoveredSet; the watermark/synced_lsn-domain bug is caught by the inherited debug_assert! (:464-471).
Lowest residual (dangerous line unmoved).is_valid() $\Rightarrow$ a dirtied registry yields zero evictions (liveness, not safety).eviction_primitive_tests
lockfree_cas.rs:1168-1260, EvictionWalkEBR.tla); the new publisher only publishes the registry. NEW combo
(force_eviction ‖ live insert_cas_durable) $\Rightarrow$ T2 is the runtime witness; if it flakes, surface it.update_disk_registry adds zero fsync; per-checkpoint count identical;
only truncate-vs-retain (already logged). Neutralized.bench_enable_eviction coupling: duplicates SharedCharARTrie::enable_eviction construction — maintenance
coupling (same crate), flagged.:557 "not Clone") — false; don't propagate.src/persistent_artrie_char/persist.rs (publisher beside :548; shim beside :622; T1/T2 beside :1061)src/persistent_artrie_char/mod.rs (gated bench_enable_eviction; invalidation contract :1581-1619)formal-verification/tla+/LockFreeDurableCheckpointEviction.tla (+ .cfg/_Unsafe.cfg)benches/lockfree_flip_benchmark.rs (eviction-ON arm; --eviction; evictable_node_count column)docs/experiments/lockfree-flip-benchmark-ledger.md (append-only frozen §E)Can you improve this documentation?Edit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |