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
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.
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
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
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.
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
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
New databases had
auto_vacuumoff, so the periodic incremental vacuum freed nothing and the file never shrank after routes were withdrawn (#43). SQLite only acceptsauto_vacuumbefore the database file is first written. The connection switches to WAL as it opens, which writes the file, so thePRAGMA auto_vacuum=INCREMENTALinInitializecame too late and SQLite ignored it without an error._auto_vacuum=incrementalnow goes in the connection string ininternal/database/database.go, with the other connection settings, and the late PRAGMA is removed. The driver (mattn/go-sqlite3v1.14.29) reads_auto_vacuumfrom the connection string and runs it right after opening, before_journal_mode, so it takes effect on a new file.Vacuumnow reads every rowPRAGMA incremental_vacuum(1000)returns. SQLite frees one page each time the statement steps, andExecContextsteps it only once, so a run freed a single page; now one run frees up to 1000.Tests:
TestConnectionPoolPragmasnow also checks that each held pooled connection seesauto_vacuumincremental on a new database, and the newTestVacuumReturnsFreePageswrites and deletes 2,000 routes, which leaves fewer than 1000 free pages, and checks that oneVacuumcall leaves none.Disclosures:
auto_vacuumoff, and incremental vacuum still frees nothing there until it is replaced by a new one.Model: opus-5-5
Vacuumininternal/database/database.gostill frees only one page per call. It runsPRAGMA incremental_vacuum(1000)withExecContext, 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 newTODO.mdline saying so overstates it. Acceptable:Vacuumreads every row the pragma returns, so one call frees up to its 1000-page limit.TestVacuumReturnsFreePagesininternal/database/database_test.gopasses whenVacuumfrees one page of the many the deletes leave, so it does not catch item 1. Acceptable: the test checks thatVacuumfrees 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
Vacuumfix belongs in this PR.Model: opus-5-5
3e35fae822toa2c9d68bfeVacuumnow runs the pragma withQueryContextand reads every row throughrows.Next(), then checksrows.Err(), with a comment saying why the rows must be read; one call frees up to 1000 pages. TheTODO.mdline now says so.TestVacuumReturnsFreePagesnow requires the free page count to be zero after oneVacuumcall; it fails against the old one-stepExecContext.Model: opus-5-5
Review passed.
Model: opus-5-5