Skip to content

Harden default SQLite durability: change _synchronous from NORMAL to FULL #289

@wesm

Description

@wesm

Context

The main SQLite DSN currently sets _synchronous=NORMAL (internal/store/store.go:37):

const defaultSQLiteParams = "?_journal_mode=WAL&_busy_timeout=30000&_synchronous=NORMAL&_foreign_keys=ON"

With WAL + NORMAL, SQLite fsyncs the WAL at checkpoints but not on every commit. Per the SQLite pragma docs and how-to-corrupt guide:

  • NORMAL is durability-risky (a committed transaction can be rolled back on power loss) but generally not corruption-risky in normal operation.
  • FULL adds a WAL fsync after each commit, closing the narrow checkpoint-sync failure window that can cause corruption.
  • Maximum-reliability recommendation is FULL.

Proposal

Change the default to _synchronous=FULL for the main Open() path, framed as archival-durability hardening.

Rationale:

  • msgvault is an archive, not a latency-sensitive OLTP workload. One extra fsync per commit is imperceptible against Gmail sync throughput.
  • The name "vault" implies durability over speed.
  • Defense-in-depth against the one corruption window that NORMAL does leave open.

This is not framed as the fix for #287 — that incident's root cause is not established (could be hardware, WAL sidecar copies, iCloud Drive, kernel panic, etc.). This is independent hardening.

Scope

  • Change: default _synchronous=FULL in defaultSQLiteParams (internal/store/store.go:37).
  • Config escape hatch: support [sync] synchronous = \"normal\" in config.toml for users who prefer the older behavior.
  • Release note: document the perf tradeoff.
  • Do not change: OpenReadOnly (synchronous is meaningless on read-only connections) and subset.go (create-subset is the disposable-rebuild case — interrupted runs are re-run, target file is not a long-lived archive).

Out of scope

Any other durability/WAL changes — keep the diff narrow.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions