feat: support independent SQLite connections - #364
Open
chrispader wants to merge 3 commits into
Open
chrispader wants to merge 3 commits into
chrispader wants to merge 3 commits into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
chrispader
marked this pull request as ready for review
September 18, 2026 13:29
This was referenced Sep 18, 2026
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.
NitroSQLite currently identifies each connection by its database name, so applications cannot open independent reader and writer handles to the same file. Reads on that connection wait behind its active transaction. This adds explicit independent connections while preserving the existing default connection API and transaction ordering.
open({ name, connection: 'independent', readOnly: true })creates a separate native handle and JavaScript queue. Name-based global methods continue to address the default connection. Read-only connections require an existing file, reject writes and attachments, and cannot delete the database.The native registry tracks connection identity separately from the physical file. Closing one handle leaves its peers open, deletion checks live connections and raw SQL attachments before closing anything, and migration reuses an already-open database path. Closed session objects cannot target a replacement connection. Independent opens reject SQLite builds with thread safety disabled.
Compatibility
Existing JavaScript and TypeScript calls retain their default connection, duplicate-open behavior, transaction ordering, and promise rejection behavior. The new options are opt-in. Each connection still uses the JavaScript queue to reserve it for an entire transaction callback, including awaits.
There are deliberate lifecycle changes. Deletion now rejects files still used by another handle or attachment and reports filesystem removal failures. An old session object cannot operate on a replacement opened under the same name. Apps must rebuild their native binaries to include the new bindings; custom native integrations may need to adapt to the changed C++ signatures.
Using parallel reads and writes
Apps can retain their current connection as the writer, initialize the schema before opening readers, and pass the reader into read helpers. Transaction helpers must keep using the callback's
txobject, including reads that need uncommitted changes. Await the writer's commit before starting a read that depends on it.Each connection still serializes its own work, and SQLite still permits one writer per file. Configure connection-local settings such as busy timeouts on each handle. Write bursts should use batches where appropriate. Rebuild both native apps after upgrading the package.
The migration guide covers setup, snapshots, contention, connection lifetimes, TypeORM, and the thread-safety settings. Existing single-connection apps need no API changes.
Reproduction
The example's connection suite opens a WAL writer transaction, holds its callback at a gate, and checks that a separate reader completes with only committed data. It also checks a read-only transaction's snapshot across a writer commit, name-based routing, independent close, writer contention, and deletion with open peers or attachments. Both platform test workflows include these cases.
Standalone native tests exercise a query blocked inside SQLite while another handle completes a query, WAL snapshots, concurrent writes, read-only restrictions, path aliases, and deletion safety. A separate build verifies rejection with
SQLITE_THREADSAFE=0. These tests and the existing migration tests pass locally. The changed native implementation and generated bindings also pass an iOS simulator target syntax check. Device integration tests have been added but have not been run locally.