glimr_sqlite/db/pool

SQLite Connection Pool

Opening a new SQLite connection per query adds file I/O overhead and risks hitting OS file descriptor limits under load. A pool keeps a fixed set of connections open and lends them out on demand, so queries execute against already-open handles and callers never need to manage connection lifecycle themselves.

Types

Re-exporting sqlight.Connection under a local alias lets the rest of the codebase reference Connection without depending on sqlight directly, so swapping the underlying driver only requires changes here.

pub type Connection =
  sqlight.Connection

Re-exporting DbError here keeps downstream modules from importing db just for the error type, reducing coupling to the framework internals.

pub type DbError =
  db.DbError

The pool must be opaque so callers can’t bypass the checkout/checkin protocol by accessing the raw connections directly. Storing closures rather than an Erlang PID keeps the Erlang pool internals hidden from Gleam code.

pub opaque type Pool

The Erlang FFI returns closures that capture the pool handle internally, so a named record type is needed to receive them across the FFI boundary. This stays public so the FFI module can construct it.

pub type PoolOps {
  PoolOps(
    checkout: fn() -> Result(
      #(sqlight.Connection, fn() -> Nil),
      String,
    ),
    stop: fn() -> Nil,
  )
}

Constructors

  • PoolOps(
      checkout: fn() -> Result(
        #(sqlight.Connection, fn() -> Nil),
        String,
      ),
      stop: fn() -> Nil,
    )

Values

pub fn get_connection(
  pool: Pool,
  f: fn(sqlight.Connection) -> a,
) -> a

A callback-based API guarantees the connection is returned to the pool even if the function crashes, preventing connection leaks that would eventually exhaust the pool under sustained traffic.

pub fn raw_checkout(
  pool: Pool,
) -> #(
  fn() -> Result(#(sqlight.Connection, fn() -> Nil), String),
  fn() -> Nil,
)

The framework’s driver-agnostic Pool vtable needs the raw checkout and stop closures to wire SQLite into the shared db interface. Returning a tuple avoids exposing the opaque Pool internals.

pub fn start_pool(config: db.Config) -> Result(Pool, db.DbError)

Wrapping the FFI result in the opaque Pool type ensures callers interact with the pool only through the public API. The Config is validated inside start, so errors from misconfiguration surface here rather than later.

pub fn stop_pool(pool: Pool) -> Nil

SQLite file locks aren’t released until connections close, so stopping the pool explicitly prevents lock contention if another process needs the database file after this application is done with it.

Search Document