doctor: open the state DB read-only so inspection never mutates it
`banger doctor` used to call store.Open, which unconditionally runs migrations on the way up. Diagnostics mutating persistent state is a surprise — particularly now that migration 2 drops a column, so a plain `doctor` invocation against an old DB would silently schema- evolve it. Add store.OpenReadOnly: separate DSN builder with mode=ro and a minimal pragma set (foreign_keys, busy_timeout — no journal_mode=WAL, no wal_autocheckpoint), skips runMigrations, and pings on open so a missing DB fails up front rather than at first query. doctor.go now uses OpenReadOnly; the existing storeErr fallback path surfaces any failure as a failing check, unchanged. Tests pin two invariants: - OpenReadOnly against a DB whose migration 2 marker was removed and packages_path re-added must leave both alone (i.e. no drift is applied behind the user's back). - Any write attempted through the read-only handle is rejected at the driver layer (belt-and-braces for future refactors). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
129475be20
commit
2685bc73f8
3 changed files with 141 additions and 1 deletions
|
|
@ -24,7 +24,12 @@ func Doctor(ctx context.Context) (system.Report, error) {
|
|||
if err != nil {
|
||||
return system.Report{}, err
|
||||
}
|
||||
db, storeErr := store.Open(layout.DBPath)
|
||||
// Doctor must be read-only: running it should never mutate the
|
||||
// state DB (no migrations, no WAL checkpoint, no pragma writes).
|
||||
// If the DB is missing or unreadable the storeErr path surfaces
|
||||
// it as a failing check rather than half-opening a writable
|
||||
// handle.
|
||||
db, storeErr := store.OpenReadOnly(layout.DBPath)
|
||||
d := &Daemon{
|
||||
layout: layout,
|
||||
config: cfg,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue