engram tiered storage M4: demand-paging buffer pool (Phase 2, additive)
Turn M2's write-back/no-steal cache into a bounded, demand-paged buffer pool so
the paged store can exceed RAM while keeping only hot pages resident. On-disk
format UNCHANGED (additive residency only; no migration). Default budget is large
enough that today's store stays fully resident, so default behaviour == Phase 1.
- Frame table capped at `cap` frames (env ENGRAM_POOL_FRAMES; 0 = unlimited;
default 1<<20). Not-resident access faults in from neuron.egm.
- LRU eviction of CLEAN, unpinned frames only. Dirty frames are never stolen
(M2 no-steal / WAL durability preserved) — turned evictable by a checkpoint's
pc_flush, which then trims the pool back to budget.
- Pinning: superblocks (0,1) + index root/interior pages auto-pinned; explicit
store_pin_page/unpin and store_pin_layer/unpin (hot WM/core layers).
- Bounded sequential read-ahead on scans (env ENGRAM_PREFETCH, default 8).
- Correctness rests on callers copying page bytes into local buffers and never
retaining a frame pointer across another access, so evict+re-fault is safe.
Gates (plain gcc, ASan/UBSan clean):
M4 run_bufpool_tests.sh ...... 37 passed, 0 failed (+ ASan/UBSan: 37/0)
small-pool round-trip (cap=32 vs 1599 pages, 2708 evictions): 5000 nodes +
4000 sampled edges bit-exact, crc clean, pool bounded to cap.
eviction: hot set 0 re-faults, cold evicted, hit-rate 0.989; no-steal burst
(cap=8) holds 309 dirty frames > cap, reads correct from dirty pages.
pinning: superblocks/roots/explicit page/hot-layer(19 pages) stay resident;
unpin makes them evictable.
prefetch: sequential scan 511 demand-faults OFF -> 4 ON.
crash-under-paging (ENGRAM_POOL_FRAMES=16): WAL replay + checkpoint-crash
phases 0-4 all recover bit-exact.
default pool: 0 evictions, whole store resident (== Phase 1).
No regression: M1 33/0, M2 36/0, M3 parity PASS, M3.5 PASS.
This commit is contained in:
@@ -209,6 +209,41 @@ int store_scan_edges(EngramPagedStore* s, StoreEdgeScanCb cb, void* ctx);
|
||||
uint64_t engram_wal_next_lsn(const EngramPagedStore* s);
|
||||
uint64_t engram_last_checkpoint_lsn(const EngramPagedStore* s);
|
||||
|
||||
/* ── M4: demand-paging buffer pool (additive residency; on-disk format UNCHANGED) ──
|
||||
*
|
||||
* The write-back, no-steal cache of M2 becomes a bounded, demand-paged buffer
|
||||
* pool. A fixed frame budget (env ENGRAM_POOL_FRAMES; 0 = unlimited; default
|
||||
* large ⇒ whole store resident ⇒ identical to Phase 1) keeps only hot pages in
|
||||
* RAM; a page access that is not resident faults in from neuron.egm, and under
|
||||
* pressure a CLEAN, unpinned frame is evicted (LRU). Dirty frames are never
|
||||
* stolen (M2 no-steal / WAL durability), and superblocks + index root/interior
|
||||
* pages are auto-pinned. Prefetch (env ENGRAM_PREFETCH) reads ahead on scans. */
|
||||
|
||||
/* Pin / unpin an individual page (faults it in and keeps it resident until
|
||||
* unpinned). Pin a hot layer's pages (WM/core) as a set. Idempotent counts. */
|
||||
int store_pin_page(EngramPagedStore* s, uint64_t page_id);
|
||||
int store_unpin_page(EngramPagedStore* s, uint64_t page_id);
|
||||
int store_pin_layer(EngramPagedStore* s, uint32_t layer); /* returns #pages pinned */
|
||||
int store_unpin_layer(EngramPagedStore* s, uint32_t layer);
|
||||
|
||||
/* Buffer-pool introspection. */
|
||||
typedef struct StorePoolStats {
|
||||
size_t cap; /* frame budget (0 = unlimited) */
|
||||
size_t resident; /* frames currently resident */
|
||||
size_t pinned; /* frames that cannot be evicted (dirty/pinned/structural) */
|
||||
size_t dirty; /* dirty (un-checkpointed) frames */
|
||||
unsigned prefetch; /* read-ahead window */
|
||||
uint64_t hits, misses; /* page_read cache hits / demand faults */
|
||||
uint64_t evictions; /* clean frames reclaimed */
|
||||
uint64_t prefetch_reads; /* pages brought in by read-ahead */
|
||||
} StorePoolStats;
|
||||
void store_pool_stats(const EngramPagedStore* s, StorePoolStats* out);
|
||||
int store_pool_resident(const EngramPagedStore* s, uint64_t page_id);
|
||||
|
||||
/* Test hooks: set the frame budget / prefetch window at runtime (NOT format). */
|
||||
void store__set_pool_frames(EngramPagedStore* s, size_t frames);
|
||||
void store__set_prefetch(EngramPagedStore* s, unsigned window);
|
||||
|
||||
/* Crash-test hooks (writes only under a throwaway dir).
|
||||
* store__crash — abandon all RAM state without flush/fsync (power loss).
|
||||
* store__flush_pages — pwrite dirty pages to disk WITHOUT a checkpoint (steal).
|
||||
|
||||
Reference in New Issue
Block a user