Skip to content

Commit c85ee19

Browse files
dilyevskyclaude
andcommitted
[apiserver] fix lost_and_found cleanup skipped when auto_vacuum already enabled
The lost_and_found drop code was inside the mode != 2 branch, so when auto_vacuum was already set to incremental the function returned early and never dropped the tables. Move cleanup before the mode check so it always runs, and VACUUM to reclaim space when tables were dropped. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 9445e52 commit c85ee19

File tree

2 files changed

+116
-16
lines changed

2 files changed

+116
-16
lines changed

pkg/apiserver/storage.go

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ import (
2626
"sigs.k8s.io/controller-runtime/pkg/metrics"
2727
)
2828

29+
// SQLite auto_vacuum modes: https://www.sqlite.org/pragma.html#pragma_auto_vacuum
30+
const sqliteAutoVacuumIncremental = 2
31+
2932
// encodeSQLiteConnArgs encodes connection arguments as a query string.
3033
func encodeSQLiteConnArgs(args map[string]string) string {
3134
var buf strings.Builder
@@ -51,21 +54,8 @@ func enableAutoVacuum(path string) error {
5154
}
5255
defer db.Close()
5356

54-
var mode int
55-
if err := db.QueryRow("PRAGMA auto_vacuum").Scan(&mode); err != nil {
56-
return fmt.Errorf("querying auto_vacuum mode: %w", err)
57-
}
58-
if mode == 2 {
59-
slog.Info("SQLite auto_vacuum already set to incremental")
60-
return nil
61-
}
62-
63-
slog.Info("Setting SQLite auto_vacuum to incremental", "current_mode", mode)
64-
if _, err := db.Exec("PRAGMA auto_vacuum = INCREMENTAL"); err != nil {
65-
return fmt.Errorf("setting auto_vacuum: %w", err)
66-
}
67-
6857
// Drop litestream recovery artifacts (lost_and_found tables from sqlite3 .recover).
58+
// This runs unconditionally so cleanup happens even when auto_vacuum is already set.
6959
rows, err := db.Query("SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'lost_and_found%'")
7060
if err != nil {
7161
return fmt.Errorf("querying lost_and_found tables: %w", err)
@@ -87,6 +77,29 @@ func enableAutoVacuum(path string) error {
8777
}
8878
}
8979

80+
var mode int
81+
if err := db.QueryRow("PRAGMA auto_vacuum").Scan(&mode); err != nil {
82+
return fmt.Errorf("querying auto_vacuum mode: %w", err)
83+
}
84+
85+
if mode == sqliteAutoVacuumIncremental {
86+
if len(toDrop) == 0 {
87+
slog.Info("SQLite auto_vacuum already set to incremental")
88+
return nil
89+
}
90+
// Tables were dropped; VACUUM to reclaim space.
91+
slog.Info("SQLite auto_vacuum already set, vacuuming to reclaim dropped tables", "dropped", len(toDrop))
92+
if _, err := db.Exec("VACUUM"); err != nil {
93+
return fmt.Errorf("vacuuming database: %w", err)
94+
}
95+
return nil
96+
}
97+
98+
slog.Info("Setting SQLite auto_vacuum to incremental", "current_mode", mode)
99+
if _, err := db.Exec("PRAGMA auto_vacuum = INCREMENTAL"); err != nil {
100+
return fmt.Errorf("setting auto_vacuum: %w", err)
101+
}
102+
90103
// VACUUM is required to convert the database to the new auto_vacuum mode.
91104
if _, err := db.Exec("VACUUM"); err != nil {
92105
return fmt.Errorf("vacuuming database: %w", err)

pkg/apiserver/storage_test.go

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,93 @@ func createBloatedDB(t *testing.T, path string) int64 {
5252
return fi.Size()
5353
}
5454

55+
func TestEnableAutoVacuum_AlreadyEnabled(t *testing.T) {
56+
dbPath := filepath.Join(t.TempDir(), "test.db")
57+
58+
// Create a DB with auto_vacuum already set to incremental, then add lost_and_found tables.
59+
db, err := sql.Open("sqlite3", dbPath)
60+
if err != nil {
61+
t.Fatal(err)
62+
}
63+
if _, err := db.Exec("PRAGMA auto_vacuum = INCREMENTAL"); err != nil {
64+
t.Fatal(err)
65+
}
66+
if _, err := db.Exec("VACUUM"); err != nil {
67+
t.Fatal(err)
68+
}
69+
// Verify auto_vacuum is set.
70+
var mode int
71+
if err := db.QueryRow("PRAGMA auto_vacuum").Scan(&mode); err != nil {
72+
t.Fatal(err)
73+
}
74+
if mode != sqliteAutoVacuumIncremental {
75+
t.Fatalf("setup: auto_vacuum = %d, want %d", mode, sqliteAutoVacuumIncremental)
76+
}
77+
78+
// Add data and lost_and_found artifacts.
79+
if _, err := db.Exec("CREATE TABLE t(k TEXT, v BLOB)"); err != nil {
80+
t.Fatal(err)
81+
}
82+
if _, err := db.Exec("CREATE TABLE lost_and_found(rootpgno INT, pgno INT, nfield INT, id INT, c0, c1)"); err != nil {
83+
t.Fatal(err)
84+
}
85+
if _, err := db.Exec("CREATE TABLE lost_and_found_0(rootpgno INT, pgno INT, nfield INT, id INT, c0)"); err != nil {
86+
t.Fatal(err)
87+
}
88+
for i := 0; i < 500; i++ {
89+
if _, err := db.Exec("INSERT INTO lost_and_found VALUES (?, ?, 2, ?, randomblob(1024), randomblob(1024))", i, i, i); err != nil {
90+
t.Fatal(err)
91+
}
92+
if _, err := db.Exec("INSERT INTO lost_and_found_0 VALUES (?, ?, 1, ?, randomblob(2048))", i, i, i); err != nil {
93+
t.Fatal(err)
94+
}
95+
}
96+
db.Close()
97+
98+
before, err := os.Stat(dbPath)
99+
if err != nil {
100+
t.Fatal(err)
101+
}
102+
t.Logf("before: %d bytes", before.Size())
103+
104+
if err := enableAutoVacuum(dbPath); err != nil {
105+
t.Fatalf("enableAutoVacuum: %v", err)
106+
}
107+
108+
db, err = sql.Open("sqlite3", dbPath)
109+
if err != nil {
110+
t.Fatal(err)
111+
}
112+
defer db.Close()
113+
114+
// Verify auto_vacuum is still set.
115+
if err := db.QueryRow("PRAGMA auto_vacuum").Scan(&mode); err != nil {
116+
t.Fatal(err)
117+
}
118+
if mode != sqliteAutoVacuumIncremental {
119+
t.Errorf("auto_vacuum = %d, want %d", mode, sqliteAutoVacuumIncremental)
120+
}
121+
122+
// Verify lost_and_found tables were dropped.
123+
var lostCount int
124+
if err := db.QueryRow("SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name LIKE 'lost_and_found%'").Scan(&lostCount); err != nil {
125+
t.Fatal(err)
126+
}
127+
if lostCount != 0 {
128+
t.Errorf("lost_and_found tables remain: %d", lostCount)
129+
}
130+
131+
after, err := os.Stat(dbPath)
132+
if err != nil {
133+
t.Fatal(err)
134+
}
135+
t.Logf("after: %d bytes", after.Size())
136+
137+
if after.Size() >= before.Size() {
138+
t.Errorf("DB did not shrink: before=%d after=%d", before.Size(), after.Size())
139+
}
140+
}
141+
55142
func TestEnableAutoVacuum(t *testing.T) {
56143
dbPath := filepath.Join(t.TempDir(), "test.db")
57144
before := createBloatedDB(t, dbPath)
@@ -71,8 +158,8 @@ func TestEnableAutoVacuum(t *testing.T) {
71158
if err := db.QueryRow("PRAGMA auto_vacuum").Scan(&mode); err != nil {
72159
t.Fatal(err)
73160
}
74-
if mode != 2 {
75-
t.Errorf("auto_vacuum = %d, want 2", mode)
161+
if mode != sqliteAutoVacuumIncremental {
162+
t.Errorf("auto_vacuum = %d, want %d", mode, sqliteAutoVacuumIncremental)
76163
}
77164

78165
// Verify lost_and_found tables were dropped.

0 commit comments

Comments
 (0)