[storage]

The [storage] table — WAL fsync policy, checkpoint interval, and compaction, with their durability tradeoffs.

The [storage] table holds generate-time storage and durability knobs. They are schema-blind and baked into database.rs when you generate — change one and run forgedb generate. Defaults are byte-identical to the prior output.

[storage]
fsync = "always"             # Tier B — WAL fsync policy
wal_checkpoint_interval = 1000 # Tier B — mutations between WAL checkpoints
compaction = true            # Tier A — emit the auto-compaction trigger
compaction_threshold = 1000  # Tier B — dead versions before auto-compaction

Keys#

KeyTypeDefaultTierMeaning
fsyncstring "always" | "never""always"BWAL fsync policy for the durable write path.
wal_checkpoint_intervalu641000BMutations per collection between WAL checkpoints.
compactionbooltrueAEmit the in-process auto-compaction trigger.
compaction_thresholdu641000BDead row versions per model before an auto-compaction fires.

An unknown fsync value (anything but "always" or "never") is a config error.

fsync (Tier B, default always)#

Controls whether the WAL is fsynced on every commit.

  • "always" (default) — fsync every commit. On macOS File::sync_all() is an F_FULLFSYNC device barrier (~3.5 ms), so acked writes survive power loss with zero loss. This is the v1 crash-safe default.
  • "never" — never fsync; the OS flushes on its own schedule. This is a durability-weakening opt-in with a real data-loss window on power loss. It is the single biggest write-throughput lever (~80–110× faster single writes in benchmarks, because the barrier is essentially the entire write latency). Choose it only when you can tolerate losing recently-acked writes on a crash.

fsync is Tier B, not yet Tier A

"never" bakes the FsyncPolicy value as a const, but the substrate still matches that value per-write at runtime — the sync_all call is still compiled into the substrate binary as a dead, value-known branch rather than being removed. True Tier-A elimination (barrier gone from the binary) is a documented follow-up. The durability behavior is correct today; only the "the syscall is physically absent" optimization is pending.

See durability for the full crash-safety model and benchmarks for the fair barrier-vs-relaxed comparison.

wal_checkpoint_interval (Tier B, default 1000)#

Bakes const WAL_CHECKPOINT_INTERVAL. After this many mutations to a collection, the generated code checkpoints: it fsyncs every column and tombstone file, then truncates the WAL. Columns reach disk before the WAL is discarded, so a crash between the two loses no committed row. This bounds WAL growth and reopen-replay cost. A lower value keeps the WAL smaller at the cost of more frequent checkpoints.

compaction (Tier A, default on)#

When true, the generated code emits the auto-compaction trigger — the dead_since_compaction >= COMPACTION_DEAD_THRESHOLD check. When false, that check is omitted entirely and storage is reclaimed only when you call Database::compact() explicitly.

Because updates and deletes append superseding/tombstoned row versions (append-only storage), dead versions accumulate; compaction reclaims them. Turning the auto-trigger off does not disable compaction — it moves the decision to your code.

compaction_threshold (Tier B, default 1000)#

Bakes const COMPACTION_DEAD_THRESHOLD. When a model accumulates this many dead row versions, an in-process compaction is due. Crossing the soft threshold sets a flag rather than stalling the triggering write; the reclaim runs off the hot write turn (with a hard ceiling as a growth-bounding safety net). A lower threshold keeps storage tighter at a small throughput cost.

  • [runtime] — replication and changefeed knobs.
  • durability — the WAL, checkpoint, and single-writer contract.

Search documentation

Find pages across the ForgeDB docs