Connection Pool

RecipeCratesCategories
Create a Connection Pooldeadpoolcat-database

Create a Connection Pool

deadpool deadpool-crates.io deadpool-github deadpool-lib.rs

deadpool is a simple async pool for connections and objects of any type.

use deadpool::managed;

#[derive(Debug)]
enum Error {
    Fail,
}

struct Server;

impl Server {
    async fn get_answer(&self) -> i32 {
        42
    }
}

struct Manager;

impl managed::Manager for Manager {
    type Error = Error;
    type Type = Server;

    async fn create(&self) -> Result<Server, Error> {
        Ok(Server)
    }

    async fn recycle(
        &self,
        _: &mut Server,
        _: &managed::Metrics,
    ) -> managed::RecycleResult<Error> {
        Ok(())
    }
}

type Pool = managed::Pool<Manager>;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let mgr = Manager;
    let pool = Pool::builder(mgr).build()?;

    let conn = pool.get().await.map_err(|err| {
        anyhow::anyhow!("Could not retrieve from the Pool: {:?}", err)
    })?;
    let answer = conn.get_answer().await;
    assert_eq!(answer, 42);
    println!("The answer is {}", answer);
    Ok(())
}

Here is an example demonstrating the use of deadpool to connect to a Postgres database:

use deadpool_postgres::Runtime;
use dotenvy::dotenv;
use tokio_postgres::NoTls;

// Deadpool + Postgres example

// `deadpool` is an async pool for connections and objects of any type.
// `deadpool_postgres` implements a `deadpool` manager for `tokio-postgres`
// and also provides a statement cache by wrapping `tokio_postgres::Client` and
// `tokio_postgres::Transaction`.

// Add to your `Config.toml`:
// deadpool = "0.12.1" # or latest version
// deadpool-postgres = { version = "0.14.1", features = ["serde"] }

// Add you database's configuration to your `.env` file, for example:
// PG__HOST=pg.example.com
// PG__USER=john_doe
// PG__PASSWORD=topsecret
// PG__DBNAME=example
// PG__POOL__MAX_SIZE=16
// PG__POOL__TIMEOUTS__WAIT__SECS=5
// PG__POOL__TIMEOUTS__WAIT__NANOS=0

#[derive(Debug, serde::Deserialize)]
struct Config {
    pub pg: deadpool_postgres::Config,
}

impl Config {
    // With the `serde` feature enabled, we can read the configuration using the
    // `config` crate.
    pub fn from_env() -> Result<Self, config::ConfigError> {
        config::Config::builder()
            .add_source(config::Environment::default().separator("__"))
            .build()?
            .try_deserialize()
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Loads the `.env` file from the current directory or parents.
    dotenv().ok();
    // Hydrate the configuration from environment variables.
    let cfg = Config::from_env()?;
    // Creates a new `Pool` using the `deadpool_postgres::Config`.
    let pool = cfg.pg.create_pool(Some(Runtime::Tokio1), NoTls)?;
    // Retrieves an Object from this Pool or waits for one to become available
    let client = pool.get().await?;
    // Like `tokio_postgres::Client::prepare()`, but uses an existing
    // `Statement` from the `StatementCache` if possible
    let stmt = client.prepare_cached("SELECT version()").await?;
    let rows = client.query(&stmt, &[]).await?;

    for row in rows {
        let version: &str = row.get(0);
        println!("PostgreSQL version: {}", version);
    }

    Ok(())
}