Skip to main content

Crate compio_pool

Crate compio_pool 

Source
Expand description

A connection pool for compio, the completion-based, thread-per-core Rust runtime.

§Why compio needs its own pool

General-purpose pools (bb8, deadpool, r2d2) assume connections are Send: one shared Mutex<Vec<Conn>>, any worker takes any connection. compio breaks that assumption. Each thread runs its own driver (io_uring, IOCP or poll), IO handles are bound to the driver that created them, and buffers are handed to the kernel by ownership — so [compio::net::TcpStream] is !Send and cannot be put behind an Arc<Mutex<_>> at all.

So “share one connection across threads simultaneously” is not the thing to build. Two different things hide behind that phrase:

  • Concurrent use of a single connection — only meaningful for protocols that multiplex (HTTP/2, Redis pipelining). Otherwise a connection is an exclusive resource and sharing means taking turns.
  • A pool shared by every thread — any thread can check out some connection. That is what this crate provides.

§Design

Pool is Send + Sync and cheap to clone, but holds no connections. The connections live in a per-thread shard stored in a thread-local, so:

  • the acquire fast path touches no atomics and takes no lock;
  • Manage::Connection never needs to be Send;
  • a connection is always driven by the compio driver that created it;
  • at thread exit, that thread’s connections close on their own thread.

Config::max_size is therefore per shard, not per process. A global cap would need a cross-thread semaphore on the hot path, which is the exact contention thread-per-core exists to avoid.

§Letting connections migrate

Pure sharding wastes connections when load is skewed: a quiet thread holds idle sockets a busy thread could use. The optional Reservoir fixes that with a bounded, lock-free ArrayQueue shared by every thread: a shard whose free list is over min_idle detaches the surplus into it, and a shard whose free list is empty pops from it before paying for a handshake. The claiming thread re-wraps the socket in its own runtime via Detach::attach.

It requires Detach, which is platform-dependent and deliberately opt-in:

driverattachconnections may change threads
io_uring (Linux)no-opyes
poll (Unix fallback)no-opyes
IOCP (Windows)CreateIoCompletionPort, once per handleno

§Cancellation is the sharp edge

With completion-based IO, dropping a future mid-operation does not undo the operation. The connection’s protocol state is then unknown and it must be destroyed rather than returned. See Pooled::begin_op.

§Example

use std::time::Duration;
use compio_pool::{Config, Manage, Pool, SlotMeta};

struct Tcp(String);

impl Manage for Tcp {
    type Connection = compio::net::TcpStream;
    type Error = std::io::Error;

    async fn connect(&self) -> std::io::Result<Self::Connection> {
        compio::net::TcpStream::connect(&self.0).await
    }

    async fn recycle(
        &self,
        conn: &mut Self::Connection,
        _meta: &SlotMeta,
    ) -> std::io::Result<()> {
        conn.peer_addr().map(|_| ())
    }
}

let pool = Pool::builder(Tcp("127.0.0.1:6379".into()))
    .max_size(8)                                   // per thread
    .min_idle(2)
    .acquire_timeout(Duration::from_secs(5))
    .build();

// Clone `pool` onto every compio thread; each gets its own shard.
let mut conn = pool.acquire().await?;

let op = conn.begin_op();
// ... use `conn` ...
op.complete_op();

Structs§

Builder
Fluent constructor for Pool.
Config
Tunables for a Pool.
Metrics
A point-in-time snapshot of pool activity, from Pool::metrics.
NoExchange
The default: shards never share. Zero-sized, zero-cost.
OpGuard
Poisons its connection unless complete is called.
Pool
A sharded connection pool for compio.
Pooled
A connection checked out of the pool.
Reservoir
A bounded, lock-free pool of detached connections shared by every thread.
SlotMeta
Lifecycle metadata the pool tracks for every connection.

Enums§

Error
Failure modes of Pool::acquire.
Parked
What an Exchange did with a connection offered to Exchange::park.
Unparked
What an Exchange produced for Exchange::unpark.

Traits§

Detach
Opt-in marker for connections that can legally change threads.
Exchange
Strategy for moving idle connections between per-thread shards.
Manage
Creates and validates connections for a Pool.