# pool-mod v0.9.0 — Hardening + Audit
**Feature freeze.** v0.9.0 lands the last feature — an opt-in background reaper —
and then submits the crate to the mandatory pre-1.0 audit. Everything else is
verification: code cleanliness, error hardening, supply-chain checks, and the full
cross-platform matrix on stable and the MSRV. No breaking changes to the existing
surface; one additive config field. The crate is now feature-complete.
## What is pool-mod?
A generic object and connection pool for Rust. Implement one trait describing how
to create, validate, and recycle a resource, and the pool handles sizing, blocking
acquisition with timeouts, validation-on-borrow, and idle/lifetime expiry. It is
runtime-agnostic and has zero runtime dependencies.
## What's new in 0.9.0
### Background reaper (opt-in)
Until now, an idle resource past its `idle_timeout` or `max_lifetime` was only
discarded when something tried to borrow it — fine for a busy pool, but a resource
that is never borrowed again could linger until the pool closed. That matters for
connection pools, where servers drop idle connections on their own schedule.
`PoolConfig::reap_interval` (and `Builder::reap_interval`) opts into a background
thread that prunes expired idle resources on a fixed cadence:
```rust
use std::time::Duration;
use pool_mod::{Manager, Pool};
# use std::convert::Infallible;
# struct M;
# impl Manager for M {
# type Resource = (); type Error = Infallible;
# fn create(&self) -> Result<(), Infallible> { Ok(()) }
# fn recycle(&self, _r: &mut ()) -> Result<(), Infallible> { Ok(()) }
# }
let pool = Pool::builder(M)
.max_size(16)
.idle_timeout(Some(Duration::from_secs(300)))
.reap_interval(Some(Duration::from_secs(30))) // prune every 30s
.build()
.expect("configuration is valid");
# let _ = pool;
```
Design notes:
- **Off by default.** `reap_interval` defaults to `None`; behavior and overhead are
unchanged unless you opt in. With it `None`, expiry stays lazy (applied on
checkout), which is a perfectly valid mode and still the default.
- **Prune-only.** The reaper never calls `create` — it only drops expired idle
resources (outside the pool lock). On-demand growth refills as needed.
- **Clean shutdown.** The reaper holds a *weak* reference to the pool, so it never
keeps the pool alive. It stops promptly when the pool is closed or its last
handle is dropped, via a shared shutdown signal. If the OS refuses a thread,
the pool falls back to lazy expiry rather than failing to build.
- **Shared expiry logic.** Checkout and the reaper now apply `idle_timeout` /
`max_lifetime` through one helper, so eager and lazy expiry agree exactly.
`reap_interval` is an additive field on the public `PoolConfig`. Code that builds
a config with `..PoolConfig::default()` is unaffected.
## Pre-1.0 audit
The full audit checklist from `.dev/ROADMAP.md`, with findings.
### Feature completeness
- Every advertised feature is implemented: generic resource management, min/max
sizing, blocking acquisition with timeouts, non-blocking `try_get`,
validation-on-borrow, idle/lifetime expiry (lazy and eager), and a status
snapshot. **No outstanding roadmap features.**
### Code cleanliness
- No `unwrap` / `expect` / `todo!` / `unimplemented!` / `unreachable!` / `dbg!` /
`print*` anywhere in shipping code (verified by scan). The crate-level lints deny
all of them.
- The single `#[allow]` in the source is `clippy::unwrap_used` / `expect_used` on
the pool's test module, justified inline — REPS permits `unwrap` in tests.
- No dead or commented-out code; no `TODO` / `FIXME`.
### Error hardening
- One `unsafe` block in the crate — `ManuallyDrop::take` in the guard's `Drop` —
carries a `// SAFETY:` proof and is exercised by the check-in tests.
- `Error<E>` is `#[non_exhaustive]`. Every variant (`Backend`, `Timeout`,
`Closed`, `InvalidConfig`) is documented with caller guidance and covered by a
test. `Display` is actionable; `source()` exposes the backend cause.
- The pool lock guards only counters and a queue, never user code; the lock helper
recovers from poisoning rather than propagating an unrelated panic as a
permanent outage.
### Supply chain
- **Zero runtime dependencies.** `cargo tree --edges normal` is just `pool-mod`.
- `cargo audit` — no advisories across the 80-crate dev-dependency tree.
- `cargo deny check` — advisories, bans, licenses, and sources all pass against
the committed `deny.toml` (permissive licenses only; wildcards and unknown
registries denied; yanked crates denied).
### Documentation
- Every public item has a rustdoc example; `cargo doc` is clean under
`RUSTDOCFLAGS=-D warnings`. `docs/API.md` documents the full surface, including
the new `reap_interval`.
### Tests
- 21 unit + 10 lifecycle + 3 concurrency + 4 property + 1 smoke integration tests,
plus 17 doctests. Property tests cover the sizing and close invariants; the
reaper has both deterministic unit coverage and timed integration coverage.
### Cross-platform
- Green on Linux, macOS, and Windows across stable and MSRV 1.75:
`fmt --check`, `clippy --all-targets --all-features -D warnings`,
`test --all-features`, and `doc` with `-D warnings`.
### Deferred to 1.x
- Tracked Criterion baselines with a regression gate (benchmarks exist; baseline
storage and the CI gate are a 1.0 task).
- Wiring `cargo audit` / `cargo deny` into the CI workflow (both pass locally
against `deny.toml`; the workflow itself is unchanged in this release).
No blocking findings. The API is considered frozen for 1.0.
## Breaking changes
**None to existing call sites.** `reap_interval` is an additive `PoolConfig`
field; the `..PoolConfig::default()` construction pattern and every existing
method are unchanged.
## Verification
Run on Windows x86_64 (Rust stable 1.95.0 and 1.75.0) and on Linux via WSL2
Ubuntu (Rust stable 1.95.0); these mirror the CI matrix.
```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all-features
RUSTDOCFLAGS="-D warnings" cargo doc --no-deps --all-features
cargo +1.75 build --all-targets --all-features
cargo +1.75 test --all-features
cargo audit
cargo deny check
```
All green. Counts at this tag: 21 unit + 18 integration (10 lifecycle + 3 pool +
4 property + 1 smoke) + 17 doctests.
## What's next
- **1.0.0 — Stable release.** Final API freeze, tracked benchmark baselines, and
the 1.0 tag.
## Installation
```toml
[dependencies]
pool-mod = "0.9"
```
MSRV: Rust 1.75. Edition 2021.
## Documentation
- [README](https://github.com/jamesgober/pool-mod/blob/main/README.md)
- [API Reference](https://github.com/jamesgober/pool-mod/blob/main/docs/API.md)
- [CHANGELOG](https://github.com/jamesgober/pool-mod/blob/main/CHANGELOG.md)
---
**Full diff:** [`v0.5.0...v0.9.0`](https://github.com/jamesgober/pool-mod/compare/v0.5.0...v0.9.0).
**Changelog:** [`CHANGELOG.md`](https://github.com/jamesgober/pool-mod/blob/main/CHANGELOG.md#090---2026-05-27).