Skip to main content

ConnectionPool

Struct ConnectionPool 

Source
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

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn available_readers(&self) -> usize

Get the current number of available reader connections.

Source

pub fn max_readers(&self) -> usize

Get the total number of reader connections in the pool.

Source

pub fn config(&self) -> &PoolConfig

Return the pool configuration.

Source

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) no matter how many stores or namespaces are constructed over it: the OnceLock runs its init closure at most once, so concurrent callers either race to run it once or block on the in-flight init and then all receive a clone of the same resulting handle. This is what makes the write queue an actual single-writer core rather than one writer task per store — a per-store writer task would let concurrent migrated stores over the same pool spawn independent writer connections that contend with each other at BEGIN IMMEDIATE, defeating the point of Component A.

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.

Source

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().

Source

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.

Source

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§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more