fix(db): give DbClient a close() so test teardown releases the handle - #327
Open
lovepixel-git wants to merge 1 commit into
Open
fix(db): give DbClient a close() so test teardown releases the handle#327lovepixel-git wants to merge 1 commit into
lovepixel-git wants to merge 1 commit into
Conversation
`DbClient` had no way to release its backing resource, so a file-backed SQLite database stayed open for the life of the process. Test teardown removes the temp directory holding that file, and Windows refuses to unlink a file whose handle is still open, so the suite fails there with EBUSY. `createTestDb` is imported by 26 test files, which accounts for the bulk of the failures in CoreBunch#284. The helper's own comments already documented the gap: createTestDb said "bun:sqlite doesn't expose a close() method on our DbClient interface; on macOS/Linux the file can still be deleted while the handle is open", and its Postgres branch carried a TODO asking for pool termination. Both are now resolved rather than restated. `close()` is required rather than optional, per the pre-1.0 rule against compat shims: the interface is the source of truth and both adapters implement it in the same change. SQLite closes the database, releasing the WAL and SHM siblings. Postgres closes the pool, but only on the client that owns it — the transaction-scoped client handed to a `.transaction()` callback borrows the pool, so closing it there would tear the connection out from under the enclosing `sql.begin()`. Teardown now closes before unlinking at all five sites that open a file-backed database: the shared helper plus pluginScheduler, postTypeBuiltInFields, importEndpointGuidance and createDbClient. Every other test database in the repo is `:memory:`. `createFakeDb` gains a no-op `close`, so the `as DbClient` fakes satisfy the interface at runtime and not only through the type assertion. The EBUSY symptom is not portable, so the new tests assert the invariant instead: after close() the handle is released, later queries reject, and the directory is removable. That reproduces cross-platform without a Windows machine. Refs CoreBunch#284
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Refs #284. Addresses the
DbClient/SQLite half of that report, which accounts for the bulk of the Windows failures. The path-as-URL bugs and the rest are separate changes, as the reporter proposed.Root cause
DbClientexposed no way to release its backing resource, so a file-backed SQLite database stayed open for the life of the process. Test teardown then removes the temp directory holding that file. POSIX tolerates unlinking an open file; Windows returns EBUSY.src/__tests__/helpers/createTestDb.tsis imported by 26 test files, which is where most of the reported failures come from.The helper's own comments already described the defect:
and the Postgres branch carried a matching
TODO: extend DbClient with a close() method to properly terminate the Postgres connection pool. Both are resolved here rather than restated.CI is
ubuntu-lateston every job, so nothing upstream exercises this.Change
close(): Promise<void>on the interface, implemented by both adapters. SQLite closes the database, which also releases the WAL and SHM siblings created byPRAGMA journal_mode = WAL. Postgres closes the pool.Teardown closes before unlinking at all five sites that open a file-backed database: the shared helper, plus
pluginScheduler,postTypeBuiltInFields,importEndpointGuidanceandcreateDbClient. Every other test database in the repo is:memory:, so that is the complete set.Two decisions worth your call
Required, not optional. I made
close()required rather thanclose?(). CONTRIBUTING says to update the source of truth and all callers in the same change rather than add a compat path, and the Postgres TODO explicitly asked for pool termination, so implementing both adapters seemed closer to intent than a SQLite-only optional method. Happy to switch it to optional if you would rather keep the interface minimal.Transaction-scoped clients.
wrapSqlbacks both the owning client and thetxhanded to a.transaction()callback. Closing the pool from inside a transaction would tear the connection out from under the enclosingsql.begin(), so I gated it: only the client returned bycreatePostgresClientcloses the pool, andclose()on a tx client is a documented no-op. Nothing calls it there today, but the shared wrapper made it reachable, so I would rather it be deliberate than latent.createFakeDbalso gains a two-line no-opclose. Theas DbClientfakes typecheck through assertion widening, so without it they would satisfy the type while throwing at runtime.Tests
New:
src/__tests__/db/dbClientClose.test.ts, four cases. The EBUSY symptom is not portable, so these assert the invariant instead: afterclose()the handle is released and later queries reject,close()is idempotent, the directory is removable once closed, andcreateTestDb'scleanup()closes before removing. That reproduces the regression cross-platform with no Windows machine.bunx tsc -bexit 0bun run lintcleanbun test6534 pass, 1 failThe one failure is pre-existing
Bundle size budgets > ContentPage-*.js stays under 87.9 kB, at 90043 B against a 90000 B budget. Not from this change: I stashed the whole diff, rebuilt cleanmain, and got the byte-identical 90043. It only appears oncedist/exists, so a run on a fresh clone will not show it. Flagging rather than folding it in, since it is a separate problem.Overlap with #194
That PR contains a version of this change inside an 89-file feature branch, currently
CONFLICTINGand untouched since 2026-07-12. This is the focused version, mirroring how #192/#193/#202 landed separately. Glad to close this if you would rather take it there.