Skip to content

sqlite async API #54307

Description

@ronag

Provide an async API to our sqlite bindings. In order to avoid blocking the event loop on lookups that require IO.

Activity

  1. added
    sqliteIssues and PRs related to the SQLite subsystem.
    on Aug 10, 2024
  2. benjamingr commented on Aug 10, 2024

    @benjamingr
    Member

    @ronag can you add "why" to the issue? (You can copy it from the original discussion).

  3. geeksilva97 commented on Dec 16, 2024

    @geeksilva97
    Contributor

    Hey, @ronag . To make this happen, Would there be a DatabaseAsync - in contrast to the DatabaseSync - class?

  4. geeksilva97 commented on Jan 21, 2025

    @geeksilva97
    Contributor

    Hey, @ronag . To make this happen, Would there be a DatabaseAsync - in contrast to the DatabaseSync - class?

    This doesn't make so much sense. I think it would be better to have Database just like fs that has someFunction and someFunctionSync.

  5. cjihrig commented on Feb 4, 2025

    @cjihrig
    Contributor

    Just FYI, this appears to be the stance of better-sqlite3 on the topic - https://github.com/WiseLibs/better-sqlite3/blob/HEAD/docs/threads.md.

  6. BurningEnlightenment commented on Mar 16, 2025

    @BurningEnlightenment

    A detailed description of a use case which would benefit from offloading to a thread pool can be found here:

  7. geeksilva97 commented on May 12, 2025

    @geeksilva97
    Contributor

    A detailed description of a use case which would benefit from offloading to a thread pool can be found here:

    IMHO, this link clarifies the use cases and the necessity for an async API.

    That said, I'd love to start working on this. What do you say, @cjihrig?

  8. geeksilva97 commented on Jun 18, 2025

    @geeksilva97
    Contributor

    Heads up, I started working on it. I will open PR by next month to get some feedback.

  9. himself65 commented on Jun 25, 2025

    @himself65
    Member

    Heads up, I started working on it. I will open PR by next month to get some feedback.

    this is awesome. cant wait for that. let me know if you have the PR. i will defenitely test it

  10. geeksilva97 commented on Jun 26, 2025

    @geeksilva97
    Contributor

    Heads up, I started working on it. I will open PR by next month to get some feedback.

    this is awesome. cant wait for that. let me know if you have the PR. i will defenitely test it

    For sure, will defintely need help 😅

  11. 1 remaining item

  12. louwers commented on Jan 5, 2026

    @louwers
    Contributor

    Picking up the discussion from #61262.

    The reason I added the Sync on the class name is because I don't think we can design an efficient database connection that does both sync and async work

    @cjihrig Could you elaborate on that? Because as I see it, when in multi-thread mode, the main thread is 'just another thread', so I don't know what would be inefficient about it.

    Do you see any problems with this:

    const { Database } from 'node:sqlite';
    
    const db = new Database('...', {
      threadingMode: 'multiThread'
    });
    // will fail if threadingMode is 'singleThread'
    const dbPool = db.pool; // DatabasePool instance
    
    await db.pool.exec('...');  // async
    db.exec('...');  // sync

    And perhaps:

    const sql = db.createTagStore();
    
    const rows = sql.all`SELECT * FROM widgets`; // sync
    
    const rows = await sql.pool.all`SELECT * FROM widgets`;  // async
  13. cjihrig commented on Jan 5, 2026

    @cjihrig
    Contributor

    So, in this case, db and db.pool are basically separate database connections. It's not necessarily a bad API. But, what I was referring to was the fact that async access requires a different mode of execution for SQLite (and possibly extra synchronization in Node depending on the implementation) that is less efficient than the single threaded model. I just don't want to see the synchronous performance take a hit to accommodate an async API. Other than that, I'm sure whatever the maintainers come up with will be fine.

  14. louwers commented on Jan 5, 2026

    @louwers
    Contributor

    @cjihrig Technically db.pool would be an abstraction over the database connections on however many threads your pool has.

    less efficient than the single threaded model

    Yes! Hence the threadingMode flag on Database construction. I don't think allowing synchronous on a multi-thread enabled database handle is a performance issue, but defaulting to enabling multi-thread for all connections definitely is problematic for purely synchronous use cases.

  15. BurningEnlightenment commented on Jan 6, 2026

    @BurningEnlightenment

    Please read the official SQLite documentation regarding threading mode. A threadingMode constructor option is at least misleading, because single vs multi threading mode in the SQLite sense is a compile (and startup) time option which should always be set to multi-thread (not serialized) in nodejs, otherwise it would be unsafe to create database connections from nodejs worker threads.

    If one wants to provide an option to preclude the creation of a db connection pool with automatic thread pool dispatching, the option should be named to reflect exactly that.

  16. louwers commented on Jan 6, 2026

    @louwers
    Contributor

    @BurningEnlightenment Since you can override the compile time option at runtime, the way I imagine it is if you specify threadingMode: 'single', multi-threading is not available for that database connection. Whereas you might use threadingMode: 'searialized' if you want to build your own worker implementation and let SQLite handle the synchronization.

  17. BurningEnlightenment commented on Jan 6, 2026

    @BurningEnlightenment

    @louwers no, you can't. To quote the relevant part of the documentation:

    If single-thread mode has not been selected at compile-time or start-time, then individual database connections can be created as either multi-thread or serialized. It is not possible to downgrade an individual database connection to single-thread mode. Nor is it possible to escalate an individual database connection if the compile-time or start-time mode is single-thread.

    So either the whole process is in single-threaded mode or it isn't.

  18. louwers commented on Jan 6, 2026

    @louwers
    Contributor

    Alright! So we could use threadingMode, but then (by default) the only available options would be multiThread or searialized. I wonder if there is any performance penalty for single-threaded use cases when compiling SQLite with SQLITE_THREADSAFE=2. Since SQLite does not do synchronization I suppose not?

    Otherwise a Node.js runtime flag to could be introduced to choose single-threaded SQLite at start time. If enabled then threadingMode: single has to be chosen, and it is not available otherwise.

  19. BurningEnlightenment commented on Jan 6, 2026

    @BurningEnlightenment

    Serialized mode isn't really useful if one follows the pool model proposed above. Serialized mode is for cases where a single sqlite database connection is used from multiple threads without external synchronization. DatabaseSync objects currently can't (and shouldn't) be shared between main and worker threads, i.e. it's not useful in this case. (Is DatabaseSync even transferable btw?). And in case of the proposed .pool API one can design the API in a way to ensure that only ever one concurrent thread accesses a single database connection. External synchronization via API invariants is way more efficient than relying on the internal sqlite db connection mutex.

    Otherwise a Node.js runtime flag to could be introduced to choose single-threaded SQLite at start time. If enabled then threadingMode: single has to be chosen, and it is not available otherwise.

    @louwers Note that if single threaded mode is enabled via runtime flag, you'd also have to prevent worker threads from creating db connections.

  20. geeksilva97 commented on Jan 13, 2026

    @geeksilva97
    Contributor

    Alright! So we could use threadingMode, but then (by default) the only available options would be multiThread or searialized. I wonder if there is any performance penalty for single-threaded use cases when compiling SQLite with SQLITE_THREADSAFE=2. Since SQLite does not do synchronization I suppose not?

    Otherwise a Node.js runtime flag to could be introduced to choose single-threaded SQLite at start time. If enabled then threadingMode: single has to be chosen, and it is not available otherwise.

    I don't see a reason not to set SQLITE_THREADSAFE to 2. In the current implementation, there's no way to have multiple threads working on the same connection. That's how better-sqlite set it, by the way.

    For an async API, the serialization can be done in a queue per connection.

  21. BurningEnlightenment commented on Feb 18, 2026

    @BurningEnlightenment

    Given that @geeksilva97 stopped working on it, I'd like to pick up the work. Some use cases (as has already been pointed out) can be well served by delegating db accesses to worker threads, there are some scenarios where this either isn't tenable or has considerable downsides:

    1. Pooling many concurrent SQLite DB connections in a similar fashion to the libuv thread pool for IO bound queries can't be easily achieved with workers as it is expensive (and currently not implemented) to move DB connections between worker threads.
    2. Spawning a worker thread per DB connection incurs considerable memory overhead and setup latency penalties which makes it infeasible for opening hundreds of connections to different databases. A use case for this is described in the previously mentioned kysely issue.1

    Therefore, I still think it is well justified to have an async implementation which dispatches to the libuv thread pool. However, as the previous implementation attempt has shown that there is a feature set which can not (and should not) be shared with an asynchronous db connection, I'd like to go in a slightly different direction regarding the API, namely introducing separate Database and Statement classes with a comparatively slim and focused feature set. Whereas everything introducing user callbacks into sql queries (e.g. .function) is left out to avoid the complexity which arises from the then necessary synchronization. In contrast to node-sqlite3 I want to implement a batching mechanism and avoid the pseudo-parallelization non-sense, i.e. the implementation should behave similar to node-sqlite3's serial mode, but without the inefficiency of creating a thread pool work item per query.

    Footnotes

    1. https://github.com/kysely-org/kysely/issues/1385#issuecomment-2734843120 ↩

  22. BurningEnlightenment commented on Mar 10, 2026

    @BurningEnlightenment

    The implementation in #62015 has reached MVP state and I'd like to get some feedback on whether the implementation approach is deemed acceptable before investing time in writing the documentation.

  23. github-actions commented on Jul 20, 2026

    @github-actions
    Contributor

    This issue has been marked as stale due to 90 days of inactivity.
    It will be automatically closed in 30 days if no further activity occurs. If this is still relevant, please leave a comment or update it to keep it open.

  24. added
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 20, 2026
  25. BurningEnlightenment commented on Jul 20, 2026

    @BurningEnlightenment

    🪄 unstale

  26. removed
    staleIssues and PRs marked stale due to inactivity and scheduled for automatic closure.
    on Jul 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    feature requestIssues requesting new Node.js features.sqliteIssues and PRs related to the SQLite subsystem.

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions