Skip to content

Conversation

@trueqbit
Copy link
Collaborator

@trueqbit trueqbit commented Feb 4, 2025

With this PR a storage instance can be safely shared, i.e. opening, closing or retaining database connections are thread-safe.

Previously this was only the case if the database was opened permanently ("forever").
Note that other shared configuration settings or objects, such as limits or application-defined functions, can still only be managed in a single-thread context, which makes sense anyway.

Establishing a connection should be performant in all variants:

  1. single-threaded use
  2. opened permanently (open forever)
  3. concurrent open/close

I tested the new core logic with a test example and also checked my thoughts this time with a Claude conversation.

Note: One important thing necessary to discuss is the phase of setting up a database connection within a user-provided on_open callback that captures the storage instance and in turn access the freshly opened database - like creating application-defined functions, setting pragma and limit values or querying database properties:

  1. Whether we allow recursively "retaining" a connection.
  2. Whether we explicitly discourage accessing the "storage" instance in the , but would need to live with potential deadlocks in the wild if users don't adhere to it.

Currently option 1. is implemented, which involves checking the thread id of the recursively executing thread.

Additionally I'd like to extend the wiki or documentation somewhere about
thread-safety.md.

This PR would resolve the following issues:

#207
#1053 [With test example that I verified!]

This PR would partially resolve the following issues, due to internal connection handling now being thread-safe:

#163
#707
#804
#875
#1274

The connection holder should be performant in all variants:
1. single-threaded use
2. opened once (open forever)
3. concurrent open/close

Hence, a light-weight binary semaphore is used to synchronize opening and closing a database connection.
A user-provided 'on open' handler and connection control options can now be specified in a declarative way when making the 'storage' object.
@trueqbit trueqbit force-pushed the experimental/threadsafe_connection branch from 564025d to 14a2aa0 Compare February 10, 2025 19:03
@trueqbit trueqbit force-pushed the experimental/threadsafe_connection branch from f82faa4 to 3ed7507 Compare February 16, 2025 12:32
@trueqbit trueqbit requested a review from fnc12 October 24, 2025 08:43
@trueqbit trueqbit marked this pull request as ready for review October 24, 2025 16:14
@trueqbit trueqbit marked this pull request as draft October 25, 2025 14:05
* Use a `std::mutex` in favor of `std::binary_semaphore` as it is most likely the better choice for the purpose of mutually exclusive locking. While it is much bigger in size and provides less `noexcept` guarantees, I didn't notice any difference in performance and it is readily available. Bigger performance gains can be achieved by other means.
* This version of the connection holder properly handles recursive calls while the connection setup lock is held.
…ling

* The variable storing the initializing thread id must be atomic.
* Introduced a connection pointer in order to be able to handle both, connected and unconnected states.
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.

3 participants