… blips
- store: switch SQLite DSN to synchronous=FULL + fullfsync=true. NORMAL only
protects against process crashes; FULL protects against OS/power crashes.
Matters for `msgvault serve` on laptops (sleep/wake, forced reboots, OOM)
where torn writes have been corrupting sync_runs — the most write-heavy
table. Write volume is tiny, so the durability cost is negligible.
- store: add OpenForTest with synchronous=OFF for fast test DBs.
synchronous=FULL adds an fsync per commit, which on Windows CI runners
pushed bulk-import tests like TestImportDYI_TimingTripwire past their
30-second timing tripwires (~50s on the Windows job). Tests use ephemeral
t.TempDir() databases that get discarded at exit, so OS-crash durability
is irrelevant. openSQLite now takes a params string so the test path can
opt into a fast DSN; testutil.NewTestStore calls store.OpenForTest while
production callers continue through store.Open with the durable DSN.
- serve: distinguish transient network errors (DNS lookup timeout, dial
timeout, no route) from real auth errors when refreshing tokens. The old
message blamed the token on every failure, telling users to re-authorize
perfectly valid tokens after a Wi-Fi flap.
- scheduler: downgrade transient-network sync failures from ERROR to WARN
so a laptop wake-up doesn't paint dashboards red.
- syncerr: new shared package classifying transient network errors (typed
net.DNSError/net.OpError plus substring fallback for libraries that wrap
with %v instead of %w).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
What changed
store: SQLite DSN switched fromsynchronous=NORMALtosynchronous=FULL+fullfsync=true.NORMALonly protects against process crashes;FULLprotects against OS/power crashes too.fullfsyncis the macOS-only fcntl that forces drive cache flush; no-op elsewhere.serve:runScheduledSyncnow distinguishes transient network errors (DNS lookup timeout, dial timeout, no route) from real auth errors when refreshing tokens. Old code suggested re-authorization on every failure.scheduler: Transient-network sync failures are logged at WARN instead of ERROR.syncerr: New shared package withIsTransientNetwork(error) bool. Checks typed*net.DNSError/*net.OpError/net.Error(Timeout()) and falls back to substring matching for libraries that wrap with%vinstead of%w(notablygolang.org/x/oauth2).Why
Log analysis of a 24/7 laptop
msgvault serveshowed ~1,200 sync failures attributed to "token may be expired" — but only 14 of those were actualoauth2: invalid_grant. The other ~98% were DNS lookup timeouts after sleep/wake or Wi-Fi flaps. The misleading error message sent the user to re-authorize valid tokens.The same laptop was hitting recurring
database disk image is malformederrors insync_runs(the most write-heavy table).NORMAL+ WAL is durable against process crashes but not OS-level crashes / power loss, which a laptop daemon experiences regularly.How to use
No flags. Behavior changes automatically: