Create new databases with auto_vacuum incremental (closes #43) #49

Merged
clawbot merged 1 commits from issue-43-auto-vacuum into next 2026-10-03 17:09:30 +02:00
Collaborator

New databases had auto_vacuum off, so the periodic incremental vacuum freed nothing and the file never shrank after routes were withdrawn (#43). SQLite only accepts auto_vacuum before the database file is first written. The connection switches to WAL as it opens, which writes the file, so the PRAGMA auto_vacuum=INCREMENTAL in Initialize came too late and SQLite ignored it without an error.

_auto_vacuum=incremental now goes in the connection string in internal/database/database.go, with the other connection settings, and the late PRAGMA is removed. The driver (mattn/go-sqlite3 v1.14.29) reads _auto_vacuum from the connection string and runs it right after opening, before _journal_mode, so it takes effect on a new file.

Vacuum now reads every row PRAGMA incremental_vacuum(1000) returns. SQLite frees one page each time the statement steps, and ExecContext steps it only once, so a run freed a single page; now one run frees up to 1000.

Tests: TestConnectionPoolPragmas now also checks that each held pooled connection sees auto_vacuum incremental on a new database, and the new TestVacuumReturnsFreePages writes and deletes 2,000 routes, which leaves fewer than 1000 free pages, and checks that one Vacuum call leaves none.

Disclosures:

  • Existing databases get no code at all, as the plan on the issue says (#43 (comment)): a file created before this change keeps auto_vacuum off, and incremental vacuum still frees nothing there until it is replaced by a new one.

Model: opus-5-5

New databases had `auto_vacuum` off, so the periodic incremental vacuum freed nothing and the file never shrank after routes were withdrawn (https://git.eeqj.de/sneak/routewatch/issues/43). SQLite only accepts `auto_vacuum` before the database file is first written. The connection switches to WAL as it opens, which writes the file, so the `PRAGMA auto_vacuum=INCREMENTAL` in `Initialize` came too late and SQLite ignored it without an error. `_auto_vacuum=incremental` now goes in the connection string in `internal/database/database.go`, with the other connection settings, and the late PRAGMA is removed. The driver (`mattn/go-sqlite3` v1.14.29) reads `_auto_vacuum` from the connection string and runs it right after opening, before `_journal_mode`, so it takes effect on a new file. `Vacuum` now reads every row `PRAGMA incremental_vacuum(1000)` returns. SQLite frees one page each time the statement steps, and `ExecContext` steps it only once, so a run freed a single page; now one run frees up to 1000. Tests: `TestConnectionPoolPragmas` now also checks that each held pooled connection sees `auto_vacuum` incremental on a new database, and the new `TestVacuumReturnsFreePages` writes and deletes 2,000 routes, which leaves fewer than 1000 free pages, and checks that one `Vacuum` call leaves none. Disclosures: - Existing databases get no code at all, as the plan on the issue says (https://git.eeqj.de/sneak/routewatch/issues/43#issuecomment-107029): a file created before this change keeps `auto_vacuum` off, and incremental vacuum still frees nothing there until it is replaced by a new one. Model: opus-5-5
clawbot added the needs-review label 2026-10-03 14:40:12 +02:00
clawbot self-assigned this 2026-10-03 14:40:16 +02:00
Author
Collaborator
  1. Vacuum in internal/database/database.go still frees only one page per call. It runs PRAGMA incremental_vacuum(1000) with ExecContext, which steps the statement once, and SQLite frees one page per step, so each ten-minute run returns a single 4 KiB page. The definition of done on #43 ("the periodic incremental vacuum returns free pages") is met in name only, and the new TODO.md line saying so overstates it. Acceptable: Vacuum reads every row the pragma returns, so one call frees up to its 1000-page limit.
  2. TestVacuumReturnsFreePages in internal/database/database_test.go passes when Vacuum frees one page of the many the deletes leave, so it does not catch item 1. Acceptable: the test checks that Vacuum frees every free page the deletes left (fewer than the 1000-page limit), so the free page count ends at zero.

Judgement call: I read "returns free pages" in the definition of done as the pages a run is meant to return, so the Vacuum fix belongs in this PR.

Model: opus-5-5

1. `Vacuum` in `internal/database/database.go` still frees only one page per call. It runs `PRAGMA incremental_vacuum(1000)` with `ExecContext`, which steps the statement once, and SQLite frees one page per step, so each ten-minute run returns a single 4 KiB page. The definition of done on https://git.eeqj.de/sneak/routewatch/issues/43 ("the periodic incremental vacuum returns free pages") is met in name only, and the new `TODO.md` line saying so overstates it. Acceptable: `Vacuum` reads every row the pragma returns, so one call frees up to its 1000-page limit. 2. `TestVacuumReturnsFreePages` in `internal/database/database_test.go` passes when `Vacuum` frees one page of the many the deletes leave, so it does not catch item 1. Acceptable: the test checks that `Vacuum` frees every free page the deletes left (fewer than the 1000-page limit), so the free page count ends at zero. Judgement call: I read "returns free pages" in the definition of done as the pages a run is meant to return, so the `Vacuum` fix belongs in this PR. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-03 16:01:53 +02:00
clawbot added 1 commit 2026-10-03 16:11:38 +02:00
SQLite only accepts auto_vacuum before the database file is first
written. The connection switched to WAL first, which writes the file,
so the PRAGMA auto_vacuum in Initialize came too late and was ignored.
The setting now goes in the connection string, which the driver applies
on open before the journal mode, and the late PRAGMA is removed.
Vacuum now reads every row PRAGMA incremental_vacuum returns: SQLite
frees one page per row, and the single step ExecContext takes freed
only one page per call. Tests check that every pooled connection sees
auto_vacuum incremental on a new database and that one Vacuum call
frees every page left by deleting routes.

Model: opus-5-5
clawbot force-pushed issue-43-auto-vacuum from 3e35fae822 to a2c9d68bfe 2026-10-03 16:11:38 +02:00 Compare
Author
Collaborator
  1. Vacuum now runs the pragma with QueryContext and reads every row through rows.Next(), then checks rows.Err(), with a comment saying why the rows must be read; one call frees up to 1000 pages. The TODO.md line now says so.
  2. TestVacuumReturnsFreePages now requires the free page count to be zero after one Vacuum call; it fails against the old one-step ExecContext.

Model: opus-5-5

1. `Vacuum` now runs the pragma with `QueryContext` and reads every row through `rows.Next()`, then checks `rows.Err()`, with a comment saying why the rows must be read; one call frees up to 1000 pages. The `TODO.md` line now says so. 2. `TestVacuumReturnsFreePages` now requires the free page count to be zero after one `Vacuum` call; it fails against the old one-step `ExecContext`. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-03 16:11:54 +02:00
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit 44a5f4cdca into next 2026-10-03 17:09:30 +02:00
clawbot deleted branch issue-43-auto-vacuum 2026-10-03 17:09:30 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/routewatch#49