Point-in-Time Incremental Backup
Forward-incremental
.packbackups scoped to changed pages; restore reassembles a base and heals it through crash recovery's RecoveryDriver.
Status: ๐ Planned ยท Visibility: Public ยท Level: ๐ฃ Advanced ยท Category: Durability Assumes: Crash Recovery (RecoveryDriver)
๐ฏ What it solves
A database that only ever recovers to "last checkpoint plus WAL replay" has no answer for disk failure, accidental mass-delete, or wanting a snapshot from four hours ago โ the WAL itself is recycled after each checkpoint (ADR-014), so nothing but the live data file survives past that point. A naive backup that copies the whole database every time doesn't scale: I/O cost is proportional to database size, not to how much actually changed. PIT Backup gives Typhon an external, self-contained recovery point whose cost tracks the change rate, without taxing the live transaction path to get it.
โ๏ธ How it works (in brief)
The checkpoint pipeline already iterates dirty pages every cycle; it OR's each flushed page's index into a
persistent dirty bitmap at near-zero cost. A backup point forces a checkpoint, reads that bitmap as the
changed-page set since the last backup, copies just those pages from the data file, and writes them โ LZ4-compressed,
checksummed, page-indexed โ into a self-contained .pack file. Successive .pack files chain forward; restoring a
point walks the chain backward, taking the newest copy of each page (first-hit-wins) to assemble a single backup
base. That base is then handed to the same RecoveryDriver used for crash recovery โ it validates pages and
rebuilds every derived structure (indexes, EntityMap, occupancy bitmap, spatial/statistics) from scratch, exactly as
it does after a crash. Backups therefore never capture derived-structure pages at all, and restore carries no
backup-specific replay code. Periodic compaction collapses the chain into a fresh base to bound restore time; a
retention policy prunes old points.
Design note: the original draft (6-part series) protected the live capture window with a scoped Copy-on-Write shadow buffer. That mechanism was dropped in the 2026-06-11 redesign (MinimalWal README ยงP3, decision D11) in favor of plain post-checkpoint reads โ consistency is restored at restore time by RecoveryDriver instead of guaranteed at capture time, which is simpler and reuses an already-proven healing path.
๐ป Usage
Not implemented โ there is no API or CLI to create, restore, or manage backups today. The sketch below shows the intended shape (a CLI tool plus an in-process retention policy) for planning purposes only; none of this exists yet:
// Illustrative only โ not a real/current Typhon API.
// Planned: configure automatic compaction/pruning alongside the engine.
// var policy = new BackupRetentionPolicy
// {
// MaxAge = TimeSpan.FromDays(7),
// MaxTotalSize = 20L * 1024 * 1024 * 1024,
// CompactThreshold = 42, // compact after 42 incrementals (~1 week at 4h intervals)
// MinKeep = 5,
// };
# Planned CLI surface (separate process / offline โ not engine-embedded)
typhon-backup create --db <path> --dest <backup-dir>
typhon-backup restore --source <backup-dir> --target <db-path> [--point <id|datetime>]
typhon-backup compact --source <backup-dir>
typhon-backup prune --source <backup-dir> --before <id|datetime>
typhon-backup verify --source <backup-dir> [--point <id|datetime>]
โ ๏ธ Guarantees & limits
- Not implemented. No code exists under
src/Typhon.Engine/Backuptoday; everything above is a design sketch, subject to change. - I/O proportional to change, not size โ a backup point costs O(changed pages); only periodic compaction costs O(database size), amortized over the chain (design target: ~15 GB per incremental vs. ~205 GB for a naive full-copy scheme, at 100 GB DB / 10% churn).
- Self-contained per chain โ each
.packfile is independently checksummed (header, footer, and per-page CRC32C); the base point needs no live WAL to restore. - Zero transaction-path overhead when idle โ the only steady-state cost is the checkpoint pipeline's one bit-OR per flushed page.
- Restore heals, it doesn't trust โ the assembled base is run through
RecoveryDriver, the identical mechanism crash recovery uses; derived structures are always rebuilt, never restored byte-for-byte. - One backup at a time โ compaction and pruning run offline against
.packfiles only and never touch the live engine. - Fine-grained point-in-time restore is a separate, optional layer โ retaining WAL segments past checkpoint (revising ADR-014) is planned but not part of the base design; without it, restore granularity is "whichever backup point you took."
๐ Related
- Sibling: Checkpoint v2 (SnapshotStore pipeline) โ a backup point forces a checkpoint cycle and reads its dirty-page bitmap
- Sibling: Pluggable WAL I/O Backend (IWalFileIO seam) โ the kind of file-system access seam backup tooling would need for its own I/O