Repository navigation
test(db): migrate OpenMemory's schema once per process and copy it - #2983
Conversation
Every OpenMemory call ran all 95 migrations through modernc's transpiled SQLite, which the race detector instruments heavily: a trivial internal/api test took 2.85s under -race, ~69% of it in migrate. The race shards ran at 840-847s against a 900s budget on CI and timed out outright on Apple Silicon. The first call now migrates a template database and serializes it; each call after deserializes a copy onto a fresh single-connection :memory: database. Per-test isolation is unchanged: every caller still gets its own database, pinned to one connection, holding exactly the state migrate produces. Production Open is untouched, and tests that re-run migrate on an OpenMemory database still exercise the real path. go test -race -run '^TestDownloadClient' ./internal/api: 157s -> 6.6s. make test-race shards: >15m timeout -> 30-78s each. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Francis Rath <fr@ncis.no>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
A failed template build and a template that cannot be loaded both fail the call, a closed database fails withRawConn, and a driver connection that cannot serialize is an error rather than a panic. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Francis Rath <fr@ncis.no>
vavallee
left a comment
There was a problem hiding this comment.
Thanks Francis, nice find. The race shards timing out had been a pain for a while and this is a small, tidy fix.
I merged current main into it (migrations 099 and 100 included) and checked a few things. A freshly migrated database and an OpenMemory copy have identical schema and rows, 32 parallel copies under race stay isolated, and 20 opens under race went from 259s to about 16s on my loaded box. Full suite passes.
One small nit, not blocking: synchronous and cache_size are per schema, so they reset on deserialize (they come back as 2 and -2000). Harmless for in memory tests, but the doc comment says the pragmas carry over. Happy to take a follow up that reruns those two after Deserialize or softens the comment.
Merging this one now, thanks again!
Summary
db.OpenMemory()ran all 95 migrations for every test. Under-race, modernc's transpiled SQLite made that ~2.5s of setup per test, which is why the race shards were at 840–847s of a 900s budget on CI and timed out outright on Apple Silicon.OpenMemorynow migrates a template once per process, serializes it, and deserializes a copy into each new single-connection:memory:database. ProductionOpenis unchanged.Closes #2982
Implementation notes
memoryTemplate()runs inside async.Once: open,migrate,Serialize(). Any error is cached and returned to every caller, the same as a migration failure today.openMemoryConn()is the old open path (DSN pragmas,SetMaxOpenConns(1),setPragmas) pulled out so the template and the copies are opened the same way.Serialize/Deserializeare reached viasql.Conn.Rawthrough a two-method interface. No new dependency, andgo.modis untouched.foreign_keys,busy_timeout, …) belong to the connection, not the database image, so they survive the deserialize. The new test checksforeign_keys.Scope decisions
migrateproduces (schema, seed rows,schema_migrations).schema_migrationsrow and callmigrate()on theOpenMemorydatabase, which still runs the real code. No test swapsmigrationsFSor any migration input.How it was verified
The change is about speed, so the evidence is timings rather than a test that fails on
main. All runs useGOTOOLCHAIN=go1.26.8on an Apple Silicon Mac:maingo test -race -run '^TestDownloadClient' ./internal/apimake test-race,internal/api^Test[A-B]make test-race,internal/api^Test[C-K]make test-race,internal/api^Test[L-Q]make test-race,internal/apirestmake test-race,internal/dbOn CI, the
validate (Go race)shards of this PR's run compared with a recent successful CI run on another PR (37187253010):The non-api shard drops too, because other packages also call
OpenMemory.codecov/patchreports 54%: the uncovered lines are the error branches (template migration, serialize/deserialize, and a driver conn withoutSerialize), which would need a fake driver to reach.TestOpenMemory_CopiesAreIndependentchecks the copy semantics: a row written to oneOpenMemorydatabase doesn't appear in the next,foreign_keysis still on, and every migration version is recorded.Checklist
Signed-off-bythat matches its author (git commit -s)docs/DEPLOYMENT.md: N/ATest plan
make testmake test-race(all six shards green, no races reported)go vet ./internal/db🤖 Generated with Claude Code