pool only.Expand description
Runtime-agnostic object pools for async Rust.
This module provides a manager-created bounded pool and an unbounded pool that can also accept objects supplied by callers.
Both implementations provide resource reuse without taking ownership of runtime policy. They do
not start maintenance tasks or install timers. Callers decide how to schedule maintenance and
can wrap operations such as bounded::Pool::get in the deadline mechanism of their runtime.
§Bounded pool
A bounded pool uses a ManageObject implementation to create, validate, and detach objects.
Objects cannot be inserted manually.
The pool is bounded by the max_size config option of PoolConfig. If
the pool reaches the maximum size, additional Pool::get calls wait until
an object is returned to or detached from the pool.
Bounded pools are useful for pooling database connections.
§Examples
The following example shows the core managed-pool workflow.
use asyncband::pool::ManageObject;
use asyncband::pool::ObjectStatus;
use asyncband::pool::bounded::Pool;
use asyncband::pool::bounded::PoolConfig;
struct Compute;
impl Compute {
async fn do_work(&self) -> i32 {
42
}
}
struct Manager;
impl ManageObject for Manager {
type Object = Compute;
type Error = ();
async fn create(&self) -> Result<Self::Object, Self::Error> {
Ok(Compute)
}
async fn is_recyclable(
&self,
o: &mut Self::Object,
status: &ObjectStatus,
) -> Result<(), Self::Error> {
Ok(())
}
}
let pool = Pool::new(PoolConfig::new(16), Manager);
let o = pool.get().await.unwrap();
assert_eq!(o.do_work().await, 42);§Unbounded pool
An unbounded pool accepts manually supplied objects and can be used like Go’s
sync.Pool.
To configure a factory for creating objects when the pool is empty, like sync.Pool’s New,
you can create the unbounded pool via Pool::new with an
implementation of ManageObject.
§Examples
The following example shows a manually populated unbounded pool.
use asyncband::pool::unbounded::Pool;
use asyncband::pool::unbounded::PoolConfig;
let pool = Pool::<Vec<u8>>::never_manage(PoolConfig::default());
assert!(pool.try_get().is_none());
pool.extend_one(Vec::with_capacity(1024));
let o = pool.try_get().unwrap();
assert_eq!(o.capacity(), 1024);§FAQ
§Why does the caller control timeouts?
A timer inside the pool would couple it to a runtime or require one adapter per timer ecosystem. Separate wait, create, and recycle timeouts also do not necessarily express the caller’s actual deadline for the complete checkout operation. Asyncband therefore returns an ordinary future so the caller can apply one end-to-end deadline with its chosen timer:
use std::sync::Arc;
use std::time::Duration;
use asyncband::pool::ManageObject;
use asyncband::pool::ObjectStatus;
use asyncband::pool::bounded::Object;
use asyncband::pool::bounded::Pool;
#[derive(Clone)]
struct ConnectionPool {
pool: Arc<Pool<ManageConnection>>,
}
impl ConnectionPool {
async fn acquire(&self) -> Result<Object<ManageConnection>, AcquireError> {
const ACQUIRE_TIMEOUT: Duration = Duration::from_secs(60);
// Callers can use the timer implementation of their runtime.
match tokio::time::timeout(ACQUIRE_TIMEOUT, self.pool.get()).await {
Ok(result) => result.map_err(AcquireError::Create),
Err(_) => Err(AcquireError::Timeout),
}
}
}§Why are general before/after hooks outside the pool?
Before/after behavior is application policy. Small operations are clearer at the call site, while larger behavior can live in the manager or an application wrapper without forcing a general closure and error model into the pool.
For example, create and recycle behavior can be expressed directly by ManageObject:
use asyncband::pool::ManageObject;
use asyncband::pool::ObjectStatus;
struct Manager;
impl ManageObject for Manager {
type Object = i32;
type Error = std::convert::Infallible;
async fn create(&self) -> Result<Self::Object, Self::Error> {
let o = 42;
// any post-create hooks
Ok(o)
}
async fn is_recyclable(
&self,
object: &mut Self::Object,
status: &ObjectStatus,
) -> Result<(), Self::Error> {
// Validate or refresh `object`, using `status` when useful.
Ok(())
}
}Modules§
Structs§
- Object
Status - Lifecycle metadata for a pooled object.
- Retain
Result - The result of a pool’s
retaincall.
Enums§
- Queue
Strategy - Strategy for dequeuing objects from the object pool.
- Recycle
Cancelled Strategy - Strategy to apply when object recycling is cancelled.
Traits§
- Manage
Object - A trait whose instance creates new objects and recycles existing ones.