carlos / platform Public

registry: SnapshotTo — consistent VACUUM INTO copy for the snapshot pass

Paul Campbell b09e973d16c3068d7f4816fa282d86bc24ff0f78 parent a73539f
2 files changed, +78 −0
  • internal/registry/registry.go +25 −0
  • internal/registry/snapshot_test.go +53 −0
diff --git a/internal/registry/registry.go b/internal/registry/registry.go
index a0778d0b8..e62115a8a 100644
--- a/internal/registry/registry.go
+++ b/internal/registry/registry.go
@@ -13,6 +13,7 @@ import (
"crypto/sha256"
"database/sql"
"fmt"
+ "os"
"regexp"
"strings"
"time"
@@ -1295,3 +1296,27 @@ FROM fleet_boxes ORDER BY fleet, label`)
return fmt.Sprintf("%d:%x", n, h.Sum(nil)), nil
}
+
+// SnapshotTo writes a consistent single-file copy of the registry to path
+// via VACUUM INTO on a fresh connection — same fresh-connection recipe as
+// Fingerprint (modernc WAL visibility). path must not already exist:
+// modernc's VACUUM INTO silently overwrites an existing file rather than
+// refusing it (unlike upstream SQLite's CLI), so we check first — callers
+// stage into a temp dir. The bound-parameter form (not fmt.Sprintf/%q) is
+// required: %q is Go string escaping, not SQL escaping, so a path
+// containing a backslash gets VACUUM INTO writing to a different on-disk
+// path than the one os.Stat just checked, silently defeating the guard.
+func (r *Registry) SnapshotTo(path string) error {
+ if _, err := os.Stat(path); err == nil {
+ return fmt.Errorf("registry: snapshot target already exists: %s", path)
+ } else if !os.IsNotExist(err) {
+ return err
+ }
+ db, err := sql.Open("sqlite", r.path)
+ if err != nil {
+ return err
+ }
+ defer db.Close()
+ _, err = db.Exec("VACUUM INTO ?", path)
+ return err
+}
diff --git a/internal/registry/snapshot_test.go b/internal/registry/snapshot_test.go
new file mode 100644
index 000000000..65d73c1e2
--- /dev/null
+++ b/internal/registry/snapshot_test.go
@@ -0,0 +1,53 @@
+package registry
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+)
+
+func TestSnapshotToProducesOpenableCopy(t *testing.T) {
+ dir := t.TempDir()
+ reg, err := Open(filepath.Join(dir, "registry.db"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer reg.Close()
+ if err := reg.Upsert(Route{Host: "a.example.com", Kind: "service", Addr: "10.0.0.1:3000", Status: "ready"}); err != nil {
+ t.Fatal(err)
+ }
+
+ out := filepath.Join(dir, "snap.db")
+ if err := reg.SnapshotTo(out); err != nil {
+ t.Fatal(err)
+ }
+
+ copyReg, err := Open(out)
+ if err != nil {
+ t.Fatalf("open snapshot: %v", err)
+ }
+ defer copyReg.Close()
+ rt, err := copyReg.Get("a.example.com")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if rt.Addr != "10.0.0.1:3000" {
+ t.Fatalf("snapshot route addr = %q, want 10.0.0.1:3000", rt.Addr)
+ }
+}
+
+func TestSnapshotToRefusesExistingTarget(t *testing.T) {
+ dir := t.TempDir()
+ reg, err := Open(filepath.Join(dir, "registry.db"))
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer reg.Close()
+ out := filepath.Join(dir, "snap.db")
+ if err := os.WriteFile(out, []byte("x"), 0o644); err != nil {
+ t.Fatal(err)
+ }
+ if err := reg.SnapshotTo(out); err == nil {
+ t.Fatal("SnapshotTo over an existing file: want error, got nil")
+ }
+}