Bound SQLite memory across the whole connection pool (closes #8) #15

Merged
clawbot merged 1 commits from issue-8-bound-sqlite-memory into next 2026-09-21 15:46:35 +02:00
Collaborator

Part of #3 (unit U2); closes #8.

The 3 GiB page cache and temp_store=MEMORY were applied once in Initialize, so only one pooled connection carried them and the other nine got no busy_timeout — the likely source of the many database is locked errors. That memory is C heap the Go runtime cannot see.

What changed, in internal/database/database.go:

  • The per-connection settings now live in the DSN, so every one of the ten pooled connections gets them: _cache_size=-65536 (64 MiB each, 640 MiB worst case for the pool), _synchronous=OFF, _busy_timeout=5000, _journal_mode=WAL.
  • cache_size, temp_store=MEMORY, synchronous and busy_timeout are gone from the Initialize pragma list. With temp_store back at its default, the DISTINCT temp B-trees spill to disk instead of growing the C heap.
  • Two process-wide pragmas are set once: soft_heap_limit 1 GiB and hard_heap_limit 1.5 GiB. At the hard limit a statement returns SQLITE_NOMEM; the batch write paths already log the error and drop the batch, so nothing panics or exits.
  • The four numbers are named constants, each with a one-line comment.

What the diff does not show: I confirmed the drop-on-error behaviour by reading the batch callers (internal/routewatch/prefixhandler.go and the ASN/peer handlers) — each logs and continues.

The test holds five pooled connections open at once and asserts on each: cache_size -65536, busy_timeout 5000, temp_store not 2, and the process-wide hard_heap_limit.

Disclosure: host make lint/make check cannot run here — the host Go is 1.26 and the pinned linter is built for 1.25, so it panics on load; the authoritative gate is the Docker build (script/cibuild), whose lint, fmt-check and test stages ran fresh and green.

Model: opus-4-8

Part of https://git.eeqj.de/sneak/routewatch/issues/3 (unit U2); closes https://git.eeqj.de/sneak/routewatch/issues/8. The 3 GiB page cache and `temp_store=MEMORY` were applied once in `Initialize`, so only one pooled connection carried them and the other nine got no `busy_timeout` — the likely source of the many `database is locked` errors. That memory is C heap the Go runtime cannot see. What changed, in `internal/database/database.go`: - The per-connection settings now live in the DSN, so every one of the ten pooled connections gets them: `_cache_size=-65536` (64 MiB each, 640 MiB worst case for the pool), `_synchronous=OFF`, `_busy_timeout=5000`, `_journal_mode=WAL`. - `cache_size`, `temp_store=MEMORY`, `synchronous` and `busy_timeout` are gone from the `Initialize` pragma list. With `temp_store` back at its default, the `DISTINCT` temp B-trees spill to disk instead of growing the C heap. - Two process-wide pragmas are set once: `soft_heap_limit` 1 GiB and `hard_heap_limit` 1.5 GiB. At the hard limit a statement returns `SQLITE_NOMEM`; the batch write paths already log the error and drop the batch, so nothing panics or exits. - The four numbers are named constants, each with a one-line comment. What the diff does not show: I confirmed the drop-on-error behaviour by reading the batch callers (`internal/routewatch/prefixhandler.go` and the ASN/peer handlers) — each logs and continues. The test holds five pooled connections open at once and asserts on each: `cache_size` -65536, `busy_timeout` 5000, `temp_store` not 2, and the process-wide `hard_heap_limit`. Disclosure: host `make lint`/`make check` cannot run here — the host Go is 1.26 and the pinned linter is built for 1.25, so it panics on load; the authoritative gate is the Docker build (`script/cibuild`), whose lint, fmt-check and test stages ran fresh and green. Model: opus-4-8
clawbot added 1 commit 2026-09-21 15:04:33 +02:00
The 3 GiB page cache and temp_store=MEMORY were set once in Initialize, so
only one pooled connection carried them and the other nine got no busy_timeout,
which drove many "database is locked" errors. None of that memory was visible
to the Go runtime.

Move the per-connection settings into the DSN so every pooled connection gets a
64 MiB cache (640 MiB worst case over ten connections), synchronous OFF, a
5 s busy_timeout and WAL. Drop cache_size, temp_store, synchronous and
busy_timeout from the Initialize pragmas; DISTINCT temp B-trees now spill to
disk. Add process-wide soft (1 GiB) and hard (1.5 GiB) heap limits; at the hard
limit a statement returns SQLITE_NOMEM and the existing batch paths already log
and drop, so nothing panics or exits.

Model: opus-4-8
clawbot added the needs-review label 2026-09-21 15:04:41 +02:00
clawbot self-assigned this 2026-09-21 15:04:41 +02:00
Author
Collaborator

PASS — this bounds the SQLite page cache and heap across the entire ten-connection pool exactly as #8 requires (per-connection settings moved to the DSN, -3145728 gone, cache_size/temp_store/synchronous/busy_timeout dropped from Initialize, process-wide soft_heap_limit and hard_heap_limit added as named constants), the pragma test holds five pooled connections open and meaningfully asserts each setting, and the authoritative Docker gate is green.

Disclosure: PR body (271 words) and commit body (124 words) sit just over the ~250/~120 guides; judged non-blocking.

Model: opus-4-8

PASS — this bounds the SQLite page cache and heap across the entire ten-connection pool exactly as https://git.eeqj.de/sneak/routewatch/issues/8 requires (per-connection settings moved to the DSN, `-3145728` gone, `cache_size`/`temp_store`/`synchronous`/`busy_timeout` dropped from `Initialize`, process-wide `soft_heap_limit` and `hard_heap_limit` added as named constants), the pragma test holds five pooled connections open and meaningfully asserts each setting, and the authoritative Docker gate is green. Disclosure: PR body (271 words) and commit body (124 words) sit just over the ~250/~120 guides; judged non-blocking. Model: opus-4-8
clawbot removed the needs-review label 2026-09-21 15:42:07 +02:00
clawbot merged commit 594e7a504b into next 2026-09-21 15:46:35 +02:00
clawbot deleted branch issue-8-bound-sqlite-memory 2026-09-21 15:46:35 +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#15