sfackler / rust-postgres

Native PostgreSQL driver for the Rust programming language
Apache License 2.0
3.44k stars 436 forks source link

How to use Transaction in Arc<Mutex> #968

Closed veerapatyok closed 1 year ago

veerapatyok commented 1 year ago

I use tokio Mutex and Arc for share Client for multi thread but I have a problem when I use Transaction in Arc<Mutex<Client>>

My code

use std::sync::Arc;
use tokio::sync::Mutex;
use tokio_postgres::{Client, Connection, Error, NoTls, Socket};
use tokio_postgres::tls::NoTlsStream;

pub async fn create_pg_client(
    config: &str,
) -> Result<(Arc<Mutex<Client>>, Connection<Socket, NoTlsStream>), Error> {
    let (c, conn) = tokio_postgres::connect(config, NoTls).await?;
    let connect_result = Arc::new(Mutex::new(c));

    Ok((connect_result, conn))
}

async fn some_task(c: Arc<Mutex<Client>>) -> Result<(), Error> {
    let t = c.lock().await.transaction().await.unwrap();

    // move data from table to other table
    let data = t.query("", &[]).await.unwrap();
    t.execute("", &[]).await.unwrap();
    t.commit();
    data.len();
    Ok(())
}

#[tokio::main]
async fn main() -> Result<(), Error> {
    let (c, conn) = create_pg_client("postgres://postgres:root@localhost:5432").await?;
    let h = tokio::spawn(async move {
        if let Err(e) = conn.await {
            eprintln!("An error occured while trying to connect to the database: {}", e);
        }
    });

    let a: tokio::task::JoinHandle<Result<(), Error>> = tokio::spawn(some_task(c.clone()));
    let b: tokio::task::JoinHandle<Result<(), Error>> = tokio::spawn(some_task(c));

    a.abort();
    b.abort();
    h.abort();
    Ok(())
}

and this is Error I got

error[E0716]: temporary value dropped while borrowed
  --> src\main.rs:16:13
   |
16 |     let t = c.lock().await.transaction().await.unwrap();
   |             ^^^^^^^^^^^^^^                             - temporary value is freed at the end of this statement
   |             |
   |             creates a temporary which is freed while still in use
...
24 | }
   | - borrow might be used here, when `t` is dropped and runs the `Drop` code for type `Transaction`
   |
   = note: consider using a `let` binding to create a longer lived value

I think a problem is transaction take &mut self and my code cannot guarantee life time

How to fix this problem or suggest a new solution

Thank you

sfackler commented 1 year ago

You would need to hold the lock guard across the transaction lifetime:

let mut conn = c.lock().await;
let t = conn.transaction().await.unwrap();
// ...
veerapatyok commented 1 year ago

I forgot, thank you very much