Switch to incremental vacuum for non-blocking space reclamation

- Use PRAGMA incremental_vacuum instead of full VACUUM
- Frees ~1000 pages (~4MB) per run without blocking writes
- Run every 10 minutes instead of 6 hours since it's lightweight
- Set auto_vacuum=INCREMENTAL pragma for new databases
- Remove blocking VACUUM on startup
This commit is contained in:
2025-12-29 16:00:33 +07:00
parent da6d605e4d
commit d7e6f46320
2 changed files with 23 additions and 19 deletions

View File

@@ -12,14 +12,19 @@ import (
// Database maintenance configuration constants.
const (
// vacuumInterval is how often to run VACUUM.
vacuumInterval = 6 * time.Hour
// vacuumInterval is how often to run incremental vacuum.
// Since incremental vacuum only frees ~1000 pages (~4MB) per run,
// we run it frequently to keep up with deletions.
vacuumInterval = 10 * time.Minute
// analyzeInterval is how often to run ANALYZE.
analyzeInterval = 1 * time.Hour
// maintenanceTimeout is the max time for a maintenance operation.
maintenanceTimeout = 5 * time.Minute
// vacuumTimeout is the max time for incremental vacuum (should be quick).
vacuumTimeout = 30 * time.Second
// analyzeTimeout is the max time for ANALYZE.
analyzeTimeout = 5 * time.Minute
)
// DBMaintainer handles background database maintenance tasks.
@@ -91,12 +96,12 @@ func (m *DBMaintainer) run() {
}
}
// runVacuum performs a VACUUM operation on the database.
// runVacuum performs an incremental vacuum operation on the database.
func (m *DBMaintainer) runVacuum() {
ctx, cancel := context.WithTimeout(context.Background(), maintenanceTimeout)
ctx, cancel := context.WithTimeout(context.Background(), vacuumTimeout)
defer cancel()
m.logger.Info("Starting database VACUUM")
m.logger.Debug("Running incremental vacuum")
startTime := time.Now()
err := m.db.Vacuum(ctx)
@@ -110,15 +115,15 @@ func (m *DBMaintainer) runVacuum() {
m.statsMu.Unlock()
if err != nil {
m.logger.Error("VACUUM failed", "error", err, "duration", time.Since(startTime))
m.logger.Error("Incremental vacuum failed", "error", err, "duration", time.Since(startTime))
} else {
m.logger.Info("VACUUM completed", "duration", time.Since(startTime))
m.logger.Debug("Incremental vacuum completed", "duration", time.Since(startTime))
}
}
// runAnalyze performs an ANALYZE operation on the database.
func (m *DBMaintainer) runAnalyze() {
ctx, cancel := context.WithTimeout(context.Background(), maintenanceTimeout)
ctx, cancel := context.WithTimeout(context.Background(), analyzeTimeout)
defer cancel()
m.logger.Info("Starting database ANALYZE")