Files
風雪and風雪 d73613387d fix(backup): snapshot the live DB with VACUUM INTO instead of a racy file copy (#92)
The in-app "Download backup" button exported the SQLite file with a plain
`os.Open` + `io.Copy` while the panel's GORM pool was still open and writing.
That copy races with concurrent writes (torn pages) and, even after the prior
WAL checkpoint, could leave committed frames in -wal that the copy never
replayed — an inconsistent or partially-lost backup.

Replace the checkpoint + file copy with SQLite's online backup primitive
VACUUM INTO, which reads through the WAL and in-flight transactions to produce
a self-contained, fully-replayed snapshot without stopping the panel.

Also stop bundling 3m-ui.db-wal / 3m-ui.db-shm into the zip. The VACUUM INTO
result is already complete (WAL merged in), so the sidecars were dead weight —
and they were never restored on import anyway, which silently dropped
un-checkpointed writes on restore.

Tests:
- TestWriteZipSnapshotIsConsistentAndSelfContained: a WAL-mode db with 200 rows
  held open by a live connection; WriteZip produces a zip whose 3m-ui.db opens
  standalone with all 200 rows and contains no -wal/-shm entries.
- go vet + go test -tags sqlite_modernc ./... green.

Co-authored-by: 風雪 <kazeyukiro@users.noreply.github.com>
2026-10-05 17:17:27 +08:00
..