Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/skade

.nornir/read-path-reasoning.md

The read path — reasoning

Class: reasoning · Function: read-path. The descriptive what is in read-path-design.md.

Why three layers instead of one cache

Each layer removes a different, measured cost, so collapsing them would leave one of those costs on the warm path:

Three separate costs → three separate layers, each keyed so it's coherence-free.

Why no TTL, no invalidation

The tempting simplification — one cache with a TTL — is wrong here, and worse than the layered design, because it would reintroduce a staleness window that the keys make impossible. Every layer is keyed by the immutable, content-addressed metadata location (or an immutable snapshot id). A commit that changes a table writes a new file at a new location; the old key is simply never requested again. So an entry can only be evicted, never stale. A TTL would add latency (re-read after expiry) and a correctness cliff (a window where you might serve old-then-new inconsistently) to buy nothing.

Why the pointer mirror can be lock-free (and why that's safe)

Correctness rests on single-process ownership: redb takes an exclusive file lock, so every mutation of the tables keyspace flows through this process. Each write path updates the mirror write-through after its redb commit while still holding the redb write lock — so mirror updates are serialised with each other and ordered after the durable commit. The mirror is therefore always complete and never stale; a key absent from it means the table genuinely does not exist. The redb fallback on a miss is a belt-and-braces safety net that never runs on the warm path. If skade were multi-process this reasoning collapses — which is exactly why multi-process is a non-goal (see reasoning.md).

Why foldhash, why imbl