puddle

A resource pool manager for Gleam. Manages a fixed pool of reusable resources with automatic crash recovery and backpressure.

Package Version Hex Docs

Installation

gleam add puddle

Quick Start

Basic usage

import gleam/int
import gleam/io
import puddle

pub fn main() {
  let assert Ok(manager) =
    puddle.new(fn() { Ok(int.random(8192)) })
    |> puddle.size(4)
    |> puddle.start(1000)

  let result = {
    use r <- puddle.apply(manager, fn(n) { puddle.keep(n * 2) }, 1000)
    r
  }
  io.debug(result)

  puddle.shutdown(manager)
}

Resource discard

When a resource becomes invalid, signal the pool to destroy it and create a replacement:

let result = {
  use r <- puddle.apply(manager, fn(conn) {
    case db.query(conn, "SELECT 1") {
      Ok(rows) -> puddle.keep(rows)
      Error(_) -> puddle.discard([])
    }
  }, 1000)
  r
}

Blocking checkout

When all resources are busy, apply_blocking queues the request instead of returning an error:

let result = {
  use r <- puddle.apply_blocking(manager, fn(n) { puddle.keep(n) }, 5000)
  r
}

Named pools

Give a pool a name to access it globally without passing the manager reference:

import gleam/erlang/process

let pool_name = process.new_name("my_db_pool")

let assert Ok(manager) =
  puddle.new(create_connection)
  |> puddle.size(10)
  |> puddle.name(pool_name)
  |> puddle.start(5000)

// Access from anywhere using the name
let named = process.named_subject(pool_name)
let result = {
  use r <- puddle.apply(named, fn(conn) { puddle.keep(conn) }, 1000)
  r
}

Configuration options

let assert Ok(manager) =
  puddle.new(create_connection)
  |> puddle.size(10)
  |> puddle.checkout_strategy(puddle.LIFO)
  |> puddle.creation_strategy(puddle.Lazy)
  |> puddle.on_shutdown(fn(conn) { db.close(conn) })
  |> puddle.start(5000)

Pool size

puddle.size(n) — maximum number of resources in the pool (default: 10).

Checkout strategy

puddle.checkout_strategy(strategy) — how idle resources are selected:

Creation strategy

puddle.creation_strategy(strategy) — when resources are created:

Shutdown callback

puddle.on_shutdown(fn(resource) { ... }) — called for each resource when the pool shuts down.

Pool name

puddle.name(process.new_name("my_pool")) — register the pool under a globally accessible name.

Pool status

let status = puddle.status(manager, 1000)
// status.state: Ready | Full | Overloaded
// status.size, status.available, status.busy, status.waiting

Pool states:

OTP supervision

import gleam/otp/static_supervisor

let child_spec =
  puddle.new(create_resource)
  |> puddle.size(5)
  |> puddle.supervised(5000)

let assert Ok(_supervisor) =
  static_supervisor.new(static_supervisor.OneForOne)
  |> static_supervisor.add(child_spec)
  |> static_supervisor.start

Named supervised pools

Combine name with supervised for globally accessible supervised pools:

let pool_name = process.new_name("my_pool")

let child_spec =
  puddle.new(create_resource)
  |> puddle.size(5)
  |> puddle.name(pool_name)
  |> puddle.supervised(5000)

// Start under supervisor, then access anywhere via:
let manager = process.named_subject(pool_name)
✨ Search Document