startWALCheckpointer runs PRAGMA wal_checkpoint(TRUNCATE) on a timer so the WAL file can't grow without bound. SQLite's automatic wal_autocheckpoint is PASSIVE: it can only checkpoint frames no reader still needs and never truncates the file. Pad's web UI keeps a steady stream of pollers (dashboard/
()
| 265 | // checkpoint (readers active) just retries next tick. SQLite-only — |
| 266 | // Close() stops it; the Postgres constructor never calls this. |
| 267 | func (s *Store) startWALCheckpointer() { |
| 268 | s.stopMaint = make(chan struct{}) |
| 269 | s.maintDone = make(chan struct{}) |
| 270 | go func() { |
| 271 | defer close(s.maintDone) |
| 272 | ticker := time.NewTicker(walCheckpointInterval) |
| 273 | defer ticker.Stop() |
| 274 | for { |
| 275 | select { |
| 276 | case <-s.stopMaint: |
| 277 | return |
| 278 | case <-ticker.C: |
| 279 | if _, err := s.db.Exec("PRAGMA wal_checkpoint(TRUNCATE)"); err != nil { |
| 280 | slog.Warn("wal checkpoint failed", "error", err) |
| 281 | } |
| 282 | } |
| 283 | } |
| 284 | }() |
| 285 | } |
| 286 | |
| 287 | // openPostgresDB opens and configures a PostgreSQL connection pool. |
| 288 | func openPostgresDB(connStr string) (*sql.DB, error) { |