Pluggable WAL I/O Backend (IWalFileIO seam)
Swap the WAL's disk backend for an in-memory one and run the full WAL + checkpoint pipeline with zero disk I/O.
Status: โ Implemented ยท Visibility: Internal ยท Category: Hosting
๐ฏ What it solves
WAL and checkpoint are mandatory in Typhon โ there is no "no-WAL" engine mode (ADR-054). That's correct for production, but a fast inner loop (unit tests, benchmarks, throwaway shell sessions) still needs to avoid touching disk on every WAL segment write. This seam lets a host replace the low-level file-IO backend the WAL talks to, so tests exercise the exact same durability pipeline as production without a divergent "fast but different" code path.
โ๏ธ How it works (in brief)
AddDatabaseEngine's factory resolves an optional IWalFileIO from the DI container before
constructing the engine. If nothing is registered โ the production case โ GetService returns
null and the engine builds its own disk-backed backend. If a host registers one, that instance
is used instead; every other code path (WAL records, checkpoints, recovery) is unchanged.
IWalFileIO and its implementations are internal to Typhon.Engine: only assemblies granted
[InternalsVisibleTo] (Typhon's own test, benchmark, and shell assemblies) can see the interface
or register a backend โ this is not a seam for arbitrary application code.
๐ป Usage
// Host assembly with InternalsVisibleTo on Typhon.Engine (test/benchmark/shell code)
using Typhon.Engine.Internals;
services
.AddSingleton<IWalFileIO>(_ => new InMemoryWalFileIO()) // or AddScoped for per-scope isolation
.AddDatabaseEngine(o =>
{
o.Wal = new WalWriterOptions { UseFUA = false }; // FUA is moot with no real disk
o.Resources.CheckpointIntervalMs = int.MaxValue; // keep the checkpoint thread idle
});
var engine = serviceProvider.GetRequiredService<DatabaseEngine>();
// Full WAL + checkpoint pipeline runs; no segment files are created on disk.
| Registration lifetime | When to use |
|---|---|
AddScoped<IWalFileIO> |
One engine per DI scope (e.g. a test fixture that opens/reopens within one run) โ each scope gets its own isolated in-memory backend. |
AddSingleton<IWalFileIO> |
One engine per process lifetime (benchmark harness). |
To exercise the real disk-backed path instead (crash/replay fixtures that must survive a process
restart), register new WalFileIO() โ the same internal type the engine falls back to when
nothing is injected.
โ ๏ธ Guarantees & limits
- Production is unaffected. No production DI setup registers
IWalFileIO, so the engine always resolvesnulland builds its own disk-backedWalFileIO. - Internal seam, not public API.
IWalFileIO,WalFileIO, andInMemoryWalFileIOare allinternal; only friend assemblies (via[InternalsVisibleTo]) can reference or register them. - Same pipeline, different medium. Injecting
InMemoryWalFileIOdoes not skip WAL records, checkpoints, or recovery โ only where the bytes land changes. Behavior under test matches production storage semantics. WalManagernever disposes an injected backend. Its lifetime is owned by whichever DI scope/container registered it.InMemoryWalFileIO.Disposeis idempotent, so double-dispose across recovery + steady-state is safe.- The supported replacement for the removed no-WAL mode. Durability stays a real, always-on knob (WAL always runs); only the storage medium is pluggable.
๐งช Tests
- WalIntegrationTests โ registers
AddSingleton<IWalFileIO>(new WalFileIO())to swap in the real disk-backed implementation and runs the full WAL/checkpoint/reopen/crash-recovery pipeline against it โ the documented alternative to the default in-memory backend
๐ Related
- Source:
TyphonBuilderExtensions.cs(CreateDatabaseEngine),IWalFileIO.cs,InMemoryWalFileIO.cs,WalFileIO.cs - Sibling: Write-Ahead Log (WAL v2 logical records) โ the durability pipeline this seam swaps the disk backend under, unchanged.
- Sibling: Pluggable Storage Backend (Persistent vs Transient) โ the analogous backend-injection seam for page storage instead of WAL.