Pluggable Storage Backend (Persistent vs Transient)
One set of segment/index code, JIT-specialized per backend, so
Transientcomponents get heap speed for free.
Status: โ Implemented ยท Visibility: Internal ยท Category: Storage
๐ฏ What it solves
Every Typhon data structure โ segments, B+Trees, hash maps, chunk accessors โ needs to read and write 8 KiB
pages. For durable components those pages live in the memory-mapped file and carry dirty tracking, eviction,
and CRC protection; for StorageMode.Transient components (see Storage Modes)
none of that applies โ the data is heap-only and gone on restart by design. Forking the segment/B+Tree/hash-map
implementations to get a fast heap-only path would double the maintenance surface and risk the two copies
drifting. The pluggable storage backend lets one implementation serve both cases at full speed for each.
โ๏ธ How it works (in brief)
IPageStore is the abstraction every generic storage type is built against (where TStore : struct, IPageStore)
โ never as a runtime-polymorphic field. Because the constraint is a struct, not a class, the JIT compiles a
fully specialized copy of LogicalSegment<TStore>, ChunkBasedSegment<TStore>, ChunkAccessor<TStore>,
BTree<TKey, TStore>, etc. per concrete backend, with zero virtual dispatch. Two backends ship in-tree:
PersistentStore, a one-line forwarding wrapper around the memory-mapped page cache, and TransientStore,
a heap-only store backed by pinned memory blocks. typeof(TStore) checks in hot paths (e.g. dirty tracking)
are constant-folded per specialization, so a TransientStore build of ChunkAccessor literally has no dirty-
tracking instructions left in it โ not a cheap no-op call, an absent one.
Sub-features
| Sub-feature | Backs | Use it for |
|---|---|---|
| Persistent Store (MMF-backed) | StorageMode.Versioned / SingleVersion |
Any durable component โ the default path, selected automatically |
| Transient Store (heap-backed) | StorageMode.Transient |
Scratch data with StorageMode.Transient โ tune via TransientOptions |
โ ๏ธ Guarantees & limits
IPageStoreis[PublicAPI]for type-visibility reasons only โ it is not meant for external implementation; the two stores ship in-tree and selection is automatic fromStorageMode, not something application code chooses directly.- Backend selection is per-component, decided at
[Component(StorageMode = ...)]registration โ never mixed within a single segment. PersistentStoreisreadonly struct(8 bytes, one pointer) with every methodAggressiveInlining-forwarded to the page cache โ the abstraction costs nothing over calling the page cache directly.TransientStoretrades the persistence guarantees away entirely in exchange for the no-op dirty-tracking path โ see the Transient Store page for what that costs you.
๐งช Tests
- StorageModeInfrastructureTests โ per-component backend selection via
[Component(StorageMode = ...)]; confirms Versioned/SV/Transient never share a segment
๐ Related
- Related feature: Storage Modes โ the component-level decision this backend split exists to serve
- Sub-features: Persistent Store (MMF-backed), Transient Store (heap-backed)