Skip to content

Enable safe concurrent SQLite reads and writes - #3

Merged
glani merged 1 commit into
mainfrom
1.5.0-sqlite
May 2, 2026
Merged

Enable safe concurrent SQLite reads and writes#3
glani merged 1 commit into
mainfrom
1.5.0-sqlite

Conversation

@glani

@glani glani commented May 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Reconfigure the SQLite backend (NewSQLiteRepo) to apply journal_mode=WAL, busy_timeout=5000, foreign_keys=1, synchronous=NORMAL, and _txlock=immediate on every pooled connection via a URI DSN — fixing instant SQLITE_BUSY, busy-snapshot in read-then-write transactions, and the latent FK-only-on-one-conn bug.
  • Bound the connection pool from CPU count; pin :memory: paths to a single connection.
  • Add concurrent test coverage at the repository layer (per-conn pragma verification, 32×25 concurrent writes, 8-reader/4-writer mixed load, 16 concurrent read-then-write transactions, expiration sweeper racing with writes) and at the service layer (24 goroutines hitting StoreEntry on the same word — all succeed with unique resolved words; 32×10 distinct concurrent writes).
  • Run go test -race in CI and add a dedicated concurrency-stress job (-count=5).
  • Document SQLite concurrency configuration and the steps to switch an existing deployment from PostgreSQL to SQLite (config flip, Docker adjustments, manual data dump path).

Test plan

  • go vet ./...
  • go build ./...
  • go test -race -timeout 5m ./...
  • go test -race -count=5 -run='Concurrent|PragmasOnEveryPoolConnection' ./internal/repository/...
  • go test -race -count=5 -run='Concurrent' ./internal/service/...
  • Manually verify a WORDSTORE_DATABASE_DRIVER=sqlite boot creates ./data/word-store.db, runs migrations, and serves an MCP request.

Reconfigure the SQLite backend so multiple goroutines can read and write
without hitting SQLITE_BUSY or busy-snapshot errors:

- Build a URI DSN that applies journal_mode=WAL, busy_timeout=5000,
  foreign_keys=1, and synchronous=NORMAL on every pooled connection
  (the previous one-shot db.Exec only configured one pool member).
- Force _txlock=immediate so every BeginTx acquires the writer lock up
  front, eliminating busy-snapshot in the read-then-write transactions
  used for collision resolution and file ops.
- Bound the connection pool from CPU count; pin :memory: paths to a
  single connection since each new conn opens a separate in-memory DB.

Add concurrent test coverage for the repository (per-conn pragma check,
distinct concurrent writes, mixed reader/writer load, concurrent
read-then-write transactions, expiration sweeper racing with writes)
and the service layer (concurrent collision resolution producing
unique resolved words, distinct concurrent writes via StoreEntry).

Run go test with -race in CI and add a dedicated concurrency stress
job that runs the concurrent suites with -count=5.

Document the SQLite concurrency configuration and the steps to switch
an existing deployment from PostgreSQL to SQLite.
@glani
glani merged commit 1746f83 into main May 2, 2026
10 checks passed
@glani
glani deleted the 1.5.0-sqlite branch May 2, 2026 11:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant