Features
- Generic over any resource — pool connections, clients, threads, buffers, or anything else through one
Managertrait. - Min / max sizing —
min_idleresources are created up front and kept ready; the pool grows on demand up tomax_sizeand never beyond it. - Blocking acquisition with timeouts —
getwaits up to a configuredcreate_timeout;get_timeoutoverrides it per call, andtry_getnever blocks. - Validation-on-borrow — an optional
validatehook (a health-check callback) runs on checkout; a resource that fails is discarded and replaced transparently. - Idle & max-lifetime expiry — stale resources are dropped and replaced, bounded by
idle_timeoutandmax_lifetime. Applied lazily on checkout by default, or eagerly by an opt-in background reaper (reap_interval). - RAII return — the
Pooledguard recycles and returns its resource automatically on drop. There is noreleaseto forget and no way to leak a resource. - Thread-safe and cheap to share —
PoolisSend + Syncand clones into another handle onto the same pool. - Runtime-agnostic, zero-dependency — no async runtime, no third-party crates.
no_std-aware — the crate root compiles withoutstd; the pool itself is behind the defaultstdfeature.
Installation
[]
= "1.0"
MSRV is Rust 1.75. The crate is edition 2021 and builds on Linux, macOS, and Windows.
Quick Start
Implement Manager for your resource, then build a pool and borrow from it:
use ;
use Infallible;
// Describe how to create, reset, and (optionally) validate the resource.
let pool = builder
.max_size
.min_idle
.build
.expect;
// Borrow a buffer; it returns to the pool when `buf` is dropped.
let mut buf = pool.get.expect;
buf.extend_from_slice;
assert_eq!;
How It Works
A pool owns up to max_size resources. Each checkout:
- Reuses an idle resource if one is available — after applying
max_lifetime,idle_timeout, and thevalidatehealth check. A resource that is too old, too stale, or invalid is dropped and the pool moves on. - Creates a new resource if none is idle and the pool has not reached
max_size. - Waits if the pool is saturated, until a resource is returned or the timeout elapses.
When a Pooled guard is dropped, its resource is passed to recycle and returned to the idle set. If recycling fails — or the pool has been closed — the resource is dropped instead and its slot is freed for a replacement.
Resource construction, validation, and recycling all run without the pool's internal lock held, so a slow create (opening a socket, say) never blocks other threads from returning resources. The lock guards only a small queue and a couple of counters.
Configuration
Configure through the Builder, or build a PoolConfig directly (for example, from a settings file).
| Setting | Default | Meaning |
|---|---|---|
max_size |
10 |
Upper bound on resources owned at once (idle + checked out). |
min_idle |
0 |
Resources created up front and kept ready. Must be ≤ max_size. |
create_timeout |
30s |
How long get waits when saturated. None waits indefinitely. |
idle_timeout |
None |
Replace a resource unused for this long, checked on next borrow. |
max_lifetime |
None |
Replace a resource older than this, checked on next borrow. |
reap_interval |
None |
Background prune cadence. None applies expiry lazily on borrow. |
use Duration;
use ;
# use Infallible;
# ;
#
let pool = builder
.max_size
.min_idle
.create_timeout
.idle_timeout
.max_lifetime
.build
.expect;
# let _ = pool;
Using It From Async
The pool has no async dependency and get blocks the calling thread. In an async
context, acquire on a blocking-friendly executor thread; the returned guard is
Send, so it may be held across .await points:
let pool = pool.clone;
let mut conn = spawn_blocking.await??;
// `conn` is usable across awaits here.
A native non-blocking async acquisition API is on the roadmap (see below).
API Overview
For the complete reference — every public item, its parameters, return values, error semantics, and runnable examples — see docs/API.md.
Manager— the trait you implement:create,recycle, and the optionalvalidatehealth check.Pool—builder/new,get/get_timeout/try_get,status,close,is_closed.Builder— fluent configuration.PoolConfig— limits and lifecycle policy.Pooled— the RAII guard, deref-coercing to your resource.Status—size,idle,in_use,max_size.Error—Backend,Timeout,Closed,InvalidConfig.
Performance
The hot path is borrow-and-return against an available resource. The pool guards only a small queue and a few counters under a mutex; resource construction, validation, and recycling all run outside the lock, and an uncontended check-in/return touches the condition variable only when a thread is actually waiting.
Latest local Criterion means (cargo bench, Windows x86_64, Rust stable,
single-threaded, trivial resource):
| Benchmark | Time/op |
|---|---|
get + return (reuse) |
~98 ns |
try_get + return |
~97 ns |
status snapshot |
~8.5 ns |
These measure the pool machinery itself with a no-op resource; in real use the
checkout cost is dominated by the work the resource does (a query, a request),
which the pool exists to amortize. The steady-state checkout/return path performs
no heap allocation. Numbers vary by CPU and platform — run cargo bench on your
target for figures that matter to you.
Cross-Platform Support
- Linux (x86_64, aarch64)
- macOS (x86_64, Apple Silicon)
- Windows (x86_64)
CI runs formatting, lints, tests, and rustdoc with -D warnings on all three
operating systems, across both the stable toolchain and the MSRV (1.75).
Testing
The suite covers every lifecycle path with unit tests, an eight-thread
concurrency test, proptest properties for the pool's invariants (the max_size
ceiling, the size == idle + in_use identity, reuse, and close semantics), and
doctests on every public item.
# Full suite (unit, integration, property, and doctests)
# Microbenchmarks for the acquire/return hot path
# Lints and formatting, as enforced in CI
Stability
1.0.0 is the API freeze. Every public item is stable under semantic
versioning: nothing is removed, renamed, or changed in a breaking way within the
1.x series. New functionality is additive, and Error is #[non_exhaustive]
so it can grow without breaking matches. The MSRV (Rust 1.75) will not rise in a
patch release. See docs/API.md for the full
promise and CHANGELOG.md for the history.
Standards
- REPS governs every decision. See REPS.md.
- MSRV: Rust 1.75.
- Edition: 2021.
- Cross-platform: Linux, macOS, Windows.
License
Dual-licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE)
- MIT License (LICENSE-MIT)
at your option.