chore: import upstream snapshot with attribution
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
package store_sqlite
|
||||
|
||||
// One-time boot compaction support.
|
||||
//
|
||||
// The graph store only ever grows on disk: deleted rows (a purged repo, the
|
||||
// duplicate-collapse migration, resolver cleanups) return their pages to
|
||||
// SQLite's freelist, where future writes reuse them — but nothing short of
|
||||
// VACUUM returns them to the filesystem. A long-lived store that shed a large
|
||||
// fraction of its rows can therefore pin gigabytes of dead file forever (a
|
||||
// live store sat at 64% freelist — 4.4 GB reclaimable in a 6.8 GB file).
|
||||
// These methods give the daemon the numbers to decide whether that one-time
|
||||
// rewrite is worth it, and the lever to run it. The policy (thresholds, disk
|
||||
// headroom, kill-switch) deliberately lives with the caller: the store cannot
|
||||
// know whether minutes of exclusive I/O are acceptable right now.
|
||||
|
||||
// Path returns the on-disk database file path. Empty when the store was not
|
||||
// opened from a file — callers using it to reason about the underlying
|
||||
// filesystem (disk-headroom checks) must treat "" as "unknown, don't".
|
||||
func (s *Store) Path() string {
|
||||
return s.dbPath
|
||||
}
|
||||
|
||||
// CompactStats reports how much of the database file is reclaimable dead
|
||||
// space: freeBytes is the freelist (freelist_count × page_size), totalBytes
|
||||
// the whole main file (page_count × page_size). Zeros on any pragma error —
|
||||
// a read failing here is the same teardown race panicOnFatal swallows, and
|
||||
// "nothing reclaimable" is the answer that makes every caller do nothing.
|
||||
// The -wal file is excluded on purpose: the checkpoint loop already bounds
|
||||
// it, and VACUUM only rewrites the main file.
|
||||
func (s *Store) CompactStats() (freeBytes, totalBytes int64) {
|
||||
var pageSize, pageCount, freePages int64
|
||||
if err := s.db.QueryRow(`PRAGMA page_size`).Scan(&pageSize); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
if err := s.db.QueryRow(`PRAGMA page_count`).Scan(&pageCount); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
if err := s.db.QueryRow(`PRAGMA freelist_count`).Scan(&freePages); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
return freePages * pageSize, pageCount * pageSize
|
||||
}
|
||||
|
||||
// Compact rewrites the database file (VACUUM), returning freelist pages to
|
||||
// the filesystem, then drains the write-ahead log with a TRUNCATE checkpoint
|
||||
// so the rewrite's WAL traffic doesn't linger as a second oversized file.
|
||||
//
|
||||
// Cost model callers must respect: VACUUM copies the live content into a
|
||||
// temporary database (up to a full extra copy on the same filesystem) and
|
||||
// needs exclusive access — it blocks Go-side writers via writeMu here, and a
|
||||
// concurrent reader on another pooled connection makes SQLite wait out
|
||||
// busy_timeout and then fail. That failure is clean (the store is untouched,
|
||||
// freelist pages remain reusable), which is why the daemon treats a Compact
|
||||
// error as skip-and-continue rather than fatal.
|
||||
func (s *Store) Compact() error {
|
||||
s.writeMu.Lock()
|
||||
_, err := s.db.Exec(`VACUUM`)
|
||||
s.writeMu.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.CheckpointWAL()
|
||||
}
|
||||
Reference in New Issue
Block a user