Search code examples
rustrust-dieselrust-rocketr2d2

Rust - Cannot Access r2d2 pool connection from Rocket State


I am currently learning Rust and Rocket

Using Rust 1.54.0+Rocket 0.5.0_rc1+ Diesel 1.4.7 + r2d2 0.8.9

I created a DB Postgres connection pool with r2d2. I want to share the connection pool between requests/Routes, to do that I am trying to use Rocket Managed State. https://rocket.rs/v0.5-rc/guide/state/#managed-state

I created a DB Connection Pool, saved it on the state, but when I tried to access that DB Connection Pool from the Route. I am getting 2 error on the same line

Cell<i32> cannot be shared between threads safely

RefCell<HashMap<StatementCacheKey<Pg>, pg::connection::stmt::Statement>> cannot be shared between threads safely

here my code

pub async fn establish_pooled_connection() -> Result<PooledConnection<ConnectionManager<PgConnection>>, r2d2_diesel::Error> {
    dotenv().ok();
    let database_url = env::var("DATABASE_URL")
        .expect("DATABASE_URL must be set");
    let manager = ConnectionManager::<PgConnection>::new(&database_url);
    let pool = r2d2::Pool::builder().build(manager).expect("Failed to create pool.");
    let conn = pool.clone().get().unwrap();
    Ok(conn)
}

struct DBPool{
    db_pool: PooledConnection<ConnectionManager<PgConnection>>
}

#[rocket::main]
async fn main() {
  
    let pool = establish_pooled_connection();    
    rocket::build()
        .mount("/",routes![callapi])
        .manage(DBPool{db_pool: pool})
        .launch()
        .await.ok();       
}

#[post("/callapi", data = "<request>")]
async fn callapi(request: RequestAPI<'_>, _dbpool: &State<DBPool>) -> Json<models::api_response::ApiResponse> {
......

The errors are for this parameter

_dbpool: &State<DBPool>

thanks in advance


Solution

  • Finally, I was able to make this work.

    I mostly used this GitHub repo as base https://github.com/practical-rust-web-development/mystore/tree/v1.1 The repo uses Actix but I am using Rocket.

    I think my main miss understanding is that the base PostgreSQL connection is a PGConnection and what you get from the Pool is a PGPooledConnection which at the end is the same.

    Here my code

    db_connection.rs

    use diesel::pg::PgConnection;
    use dotenv::dotenv;
    use std::env;
    use diesel::r2d2::{ Pool, PooledConnection, ConnectionManager, PoolError };
    
    pub type PgPool = Pool<ConnectionManager<PgConnection>>;
    pub type PgPooledConnection = PooledConnection<ConnectionManager<PgConnection>>;
    
    //Connects to Postgres and call init pool
    pub fn establish_connection() -> PgPool {
        dotenv().ok();
    
        let database_url = env::var("DATABASE_URL")
            .expect("DATABASE_URL must be set");
        init_pool(&database_url).expect("Failed to create pool")
    }
    
    
    //Creates a default R2D2 Postgres DB Pool
    fn init_pool(database_url: &str) -> Result<PgPool, PoolError> {
        let manager = ConnectionManager::<PgConnection>::new(database_url);
        Pool::builder().build(manager)
    }
    
    
    //this functions returns a connection from the Pool
    pub fn pg_pool_handler(pool: &PgPool) -> Result<PgPooledConnection, PoolError> {
        let _pool = pool.get().unwrap();
        Ok(_pool)
    }
    

    main.rs

    mod db_connection;
    use db_connection::{PgPool};
    
    #[rocket::main]
    async fn main() {
        rocket::build()
            .mount("/API",routes![demo])
            .manage(db_connection::establish_connection()) //here is where you pass the pool to Rocket state.
            .launch()
            .await.ok();       
    }
    
    #[post("/demo", data = "<request>")]
    async fn demo _dbpool: &State<PgPool>) -> Json<models::Response> {
    
    let connection = db_connection::pg_pool_handler(_dbpool).unwrap();
    
    let results = users.limit(1)
            .load::<User>(&connection)
            .expect("Error loading users");
    ........
    

    That is the basic code, the code can be improved to handle errors in a better way.