Commit Graph

4 Commits

Author SHA1 Message Date
will.anderson 02dc12d785 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.
2026-08-12 15:42:49 -05:00
will.anderson a72145b44e engram tiered storage M3: wire store behind ENGRAM_STORE (default off) + .egm rename
Caller-side shim in el_runtime.c maps EngramNode/Edge <-> StoreNode/Edge; engine
keeps zero soul deps (libengram boundary, design §10). Flag off = today's JSON
path byte-for-byte (proven: no neuron.egm created, graph identical). Flag on =
engram_open (import snapshot.json once into neuron.egm, else WAL-replay) +
resident load; node/edge create + forget dual-write via guarded hooks. Files
renamed engram.store->neuron.egm, engram.wal->neuron.wal.

Gate: M3 parity PASS (graph on==off byte-exact modulo ordering; snapshot round-trip;
reboot-from-egm with snapshot.json deleted; activation set+sequence identical;
ASan/UBSan clean). M1 33/33 + M2 36/36 green post-rename.

Known gap (pre-flip): in-place hebb/WM/activation_count updates during activation
are not yet persisted to the store (create/connect/forget are). Must close before
live flip so learned edges survive restart.
2026-08-11 23:21:21 -05:00
will.anderson 8affb1d6e0 engram tiered storage M2: WAL + checkpoint + crash recovery + legacy import
Write-back no-steal buffer pool makes the fsync'd WAL load-bearing (M1 was
write-through). Logical WAL with record-granularity page-LSN redo idempotency.
Checkpoint = flush dirty pages, fsync store, advance last_checkpoint_lsn,
reclaim WAL prefix. One-time snapshot.json import only when store absent;
JSON never read as the ongoing store thereafter.

Gates: 33/33 M1 (no regression) + 36/36 M2 — replay parity, torn-tail fuzz
(every byte offset), checkpoint-crash at all 5 phases, torn-page+WAL redo,
legacy-import parity, hebb-survives-crash.
2026-08-11 23:00:20 -05:00
will.anderson fa47b98d18 engram tiered storage M1: on-disk paged store format + round-trip tests
Self-contained paged store (lang/runtime/engram_store.{c,h}): 16KiB slotted pages,
u32 TLV self-describing records (forward-compatible), overflow chains, B+-tree
id-index + from/to adjacency, page free-list, tombstones, double superblock + crc
recovery. Not yet wired to activation (M3). 33/33 tests pass (ASan/UBSan clean);
5k nodes/20k edges round-trip bit-exact incl 768xf32 emb + hebb; store 25MB vs 64MB
JSON. Format is final — see design §2.4.
2026-08-11 22:26:05 -05:00