pub struct ConnectionPool { /* private fields */ }Expand description
A read-write connection pool for SQLite.
Architecture:
- 1 writer connection protected by a Mutex (exclusive access)
- N reader connections in a lock-free queue (concurrent access)
- All connections share the same database file in WAL mode
For in-memory databases, or when WAL mode is disabled/unavailable, the pool degrades to single-connection mode and routes all operations through the writer connection.
Implementations§
Source§impl ConnectionPool
impl ConnectionPool
Sourcepub fn new(config: PoolConfig) -> Result<ConnectionPool, SqliteError>
pub fn new(config: PoolConfig) -> Result<ConnectionPool, SqliteError>
Create a new connection pool.
Opens 1 writer + N reader connections to the same database when pooling is enabled. All connections are configured consistently (busy timeout, foreign keys, cache, mmap, temp store). For in-memory databases, or when WAL is disabled or unavailable, the pool falls back to single-connection mode.
Sourcepub fn reader(&self) -> Result<ReaderGuard<'_>, SqliteError>
pub fn reader(&self) -> Result<ReaderGuard<'_>, SqliteError>
Check out a reader connection.
Tries to pop from the lock-free queue. If empty, spins briefly then
waits with exponential backoff up to checkout_timeout.
§Deadlock Warning
In degraded mode (WAL unavailable, max_readers == 0), this method locks
the writer mutex. If the calling thread already holds a WriterGuard,
this will deadlock (parking_lot Mutex is not reentrant). Never call
reader() while holding a WriterGuard on the same pool.
Sourcepub fn writer(&self) -> Result<WriterGuard<'_>, SqliteError>
pub fn writer(&self) -> Result<WriterGuard<'_>, SqliteError>
Check out the writer connection.
Waits up to checkout_timeout for the writer Mutex and returns
Err(SqliteError::InvalidData) if the timeout is exceeded.
Sourcepub fn try_writer(&self) -> Result<WriterGuard<'_>, SqliteError>
pub fn try_writer(&self) -> Result<WriterGuard<'_>, SqliteError>
Non-panicking writer checkout.
Returns Err on timeout instead of panicking. Use this in request
handlers where a 500 is preferable to crashing the process.
Sourcepub fn try_writer_nowait(&self) -> Result<WriterGuard<'_>, SqliteError>
pub fn try_writer_nowait(&self) -> Result<WriterGuard<'_>, SqliteError>
Zero-wait writer checkout for background tasks.
Uses try_lock() (no timeout, no spin) — returns Err immediately when
any other caller holds the writer Mutex. Background tasks (e.g. the WAL
checkpoint task) MUST use this instead of try_writer so that a busy
writer causes the background task to skip its current tick rather than
stalling for up to checkout_timeout (default 5s) while write traffic
is in progress.
Sourcepub fn available_readers(&self) -> usize
pub fn available_readers(&self) -> usize
Get the current number of available reader connections.
Sourcepub fn max_readers(&self) -> usize
pub fn max_readers(&self) -> usize
Get the total number of reader connections in the pool.
Sourcepub fn config(&self) -> &PoolConfig
pub fn config(&self) -> &PoolConfig
Return the pool configuration.
Sourcepub fn writer_task_handle(
&self,
) -> Result<Option<WriterTaskHandle>, StorageError>
pub fn writer_task_handle( &self, ) -> Result<Option<WriterTaskHandle>, StorageError>
Return the pool-wide ADR-067 Component A writer task, spawning it
lazily on first access if PoolConfig::write_queue_enabled is set.
Exactly one writer task exists per ConnectionPool (per DB file); see
crates/khive-db/docs/api/pool.md#connectionpoolwriter_task_handle–single-writer-task-rationale
for why a per-store writer task would defeat the single-writer
guarantee.
Returns Ok(None) if the flag is off, or if the writer task failed to
spawn for a reason other than a missing runtime (for example, an
in-memory pool has no standalone-connection support) — callers fall
back to the legacy pool-mutex write path in either case. A spawn
failure is logged once here (at first access), not once per store.
Returns Err(StorageError::WriterTaskNoRuntime) instead of panicking
when write_queue_enabled is set but this is the first access and no
Tokio runtime is available on the calling thread (checked via
tokio::runtime::Handle::try_current) — spawning the writer task
requires tokio::spawn, which panics outside a runtime. Callers that
already treat a missing writer task as best-effort (construction-time
degrade to the legacy path, matching slice 1’s documented policy) can
collapse this into None with .ok().flatten(); callers that need to
fail loud on a genuine misconfiguration (write queue requested but no
runtime to run it on) can propagate the Err directly.
Sourcepub fn legacy_conn(&self) -> Arc<Mutex<RawMutex, Connection>> ⓘ
pub fn legacy_conn(&self) -> Arc<Mutex<RawMutex, Connection>> ⓘ
Compatibility method: returns the writer connection wrapped in Arc<Mutex>.
WARNING: This exists only for backward compatibility with code that
calls store.conn(). New code should use reader() and writer().
Sourcepub fn open_standalone_writer(&self) -> Result<Connection, SqliteError>
pub fn open_standalone_writer(&self) -> Result<Connection, SqliteError>
Open a standalone read-write connection to the same file-backed database.
Stores whose trait methods take Send + 'static closures (executed via
spawn_blocking) cannot hold the pooled WriterGuard’s MutexGuard
across the call — it opens an independent connection instead. This
must still honor PoolConfig::read_only: opening
SQLITE_OPEN_READ_WRITE unconditionally here would let a read-only
backend’s graph/event/text stores bypass the flag that the pooled
writer enforces via query_only.
Sourcepub fn open_standalone_reader(&self) -> Result<Connection, SqliteError>
pub fn open_standalone_reader(&self) -> Result<Connection, SqliteError>
Open a standalone read-only connection to the same file-backed database.
Companion to open_standalone_writer for stores that also need an
independent reader connection outside the pooled reader queue.
Auto Trait Implementations§
impl !Freeze for ConnectionPool
impl !RefUnwindSafe for ConnectionPool
impl !UnwindSafe for ConnectionPool
impl Send for ConnectionPool
impl Sync for ConnectionPool
impl Unpin for ConnectionPool
impl UnsafeUnpin for ConnectionPool
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more