blackbeam / mysql_async

Asyncronous Rust Mysql driver based on Tokio.
Apache License 2.0
372 stars 113 forks source link

Is pools close if their ownership moves? #282

Closed Necoo33 closed 6 months ago

Necoo33 commented 6 months ago

Hello, i'm currently trying to use your package but i have some issues.

Here is my code:


#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    let ip_address_and_port = match env::var("IP_ADDRESS_AND_PORT") {
        Ok(variable) => variable,
        Err(_) => panic!("Cannot found 'IP_ADDRESS_AND_PORT' env variable."),
    };

    let db_conn_string = match env::var("DB_CONN_URL") {
        Ok(variable) => variable,
        Err(_) => panic!("Cannot found 'DB_CONN_STRING' env variable."),
    };

    let pool = database::create_db_pool(db_conn_string.as_str()).await;

    let server = HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .wrap(from_fn(authlogic::refresh_cookies))
            .service(navctrl::home_controller)
            .service(navctrl::about_us_controller)
            .service(navctrl::contact_controller)
            .service(routers::admin_panel_router())
            .service(routers::posts_router())
    });

    server.bind((ip_address_and_port.as_str()))?.run().await?;

    Ok(())
}

in this code, the pool variable is released when the server variable ends because of the move semantics and the pool variable freed from memory. In this case, is connection closed or not in the end of that code?

blackbeam commented 6 months ago

Hi.

In this case, is connection closed or not in the end of that code?

Which one? I mean that i see no code here that creates a connection so it's hard to answer the question.

in this code, the pool variable is released when the server variable ends because of the move semantics and the pool variable freed from memory

Note that Conn if created from a pool holds a copy of this pool so it won't vanish while Conns exists. Also I believe that the copy is also alive within the factory closure

Necoo33 commented 6 months ago
let pool = database::create_db_pool(db_conn_string.as_str()).await;

obviously the pool variable, just this code: let pool = database::create_db_pool(db_conn_string.as_str()).await;. I don't think this isn't obvious, the source code is this:


pub async fn create_db_pool(url: &str) -> mysql_async::Pool {
    return mysql_async::Pool::new(url);
}

Also i don't create a standalone connection, i create a connection pool.

If i pass that pool value with web::Data::new() code that inside in the "server" variable takes the ownership of that pool variable, even that value is a clone. So i'm curious about if that variable freed from memory due to move of ownership, is that connection pool closed?

blackbeam commented 6 months ago

Oh, I see - pool is a reference-counted smart pointer. Every clone of a pool instance points to the same pool instance (see corresponding section in the docs)

Practically this means that pool won't close as long as there exist a clone.

Necoo33 commented 6 months ago

Oh, I see - pool is a reference-counted smart pointer. Every clone of a pool instance points to the same pool instance (see corresponding section in the docs)

Practically this means that pool won't close as long as there exist a clone.

This is so abstract answer, because of that i'm writing this for make things Clear: my assumption is if there is no unclosed single connection and if ownership moves into another data structure that means pool were closed automatically. Is that right?

I mean, there is no way to explicitly disconnect that pool after that value passed on server variable, i tried many things and also there is no way to reach that connection later than that server variable. This should make it closed after moving.

blackbeam commented 6 months ago

Hmm..

First let me mention that there is no such thing as "async destructor" in Rust, that's why you have to explicitly call pool.disconnect() to disconnect it gracefully. Now if pool instance is moved (say in a way it is moved into a closure in your example) than it won't be disconnected nor deallocated even if it is the only instance of this pool.

If you want to gracefully close the pool you need to keep a clone somewhere so that you can call disconnect (see docs):

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    dotenv().ok();

    let ip_address_and_port = match env::var("IP_ADDRESS_AND_PORT") {
        Ok(variable) => variable,
        Err(_) => panic!("Cannot found 'IP_ADDRESS_AND_PORT' env variable."),
    };

    let db_conn_string = match env::var("DB_CONN_URL") {
        Ok(variable) => variable,
        Err(_) => panic!("Cannot found 'DB_CONN_STRING' env variable."),
    };

    let pool = database::create_db_pool(db_conn_string.as_str()).await;
    let pool_clone = pool.clone();

    let server = HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(pool.clone()))
            .wrap(from_fn(authlogic::refresh_cookies))
            .service(navctrl::home_controller)
            .service(navctrl::about_us_controller)
            .service(navctrl::contact_controller)
            .service(routers::admin_panel_router())
            .service(routers::posts_router())
    });

    server.bind((ip_address_and_port.as_str()))?.run().await?;

    pool_clone.disconnect().await?;

    Ok(())
}
Necoo33 commented 6 months ago

Understand. Because it's an Arc type, that means all the clones are reference to the same pool if we close one of them we'll be close every one of them. Thanks.