Rust中利用Drop trait执行DROP DATABASE时程序挂起问题求助
问题:PostgreSQL DROP DATABASE在Drop trait中执行挂起超时
我正在跟随《zero2prod Rust 实战手册》学习,目前处于第3章末尾,搭建集成测试时已经实现“每次测试运行创建独立数据库”的功能。现在希望扩展该功能,在每个测试结束后自动删除数据库,计划借助Drop trait执行DROP DATABASE命令(已存储数据库名称及连接/连接池用于后续访问)。但执行DROP DATABASE查询时程序会挂起,60秒后超时,且无法打印或调试连接状态。
现有可正常运行的代码
use futures::executor; use sqlx::{Connection, Executor, PgConnection, PgPool}; use std::net::TcpListener; use uuid::Uuid; use zero2prod::configuration::{get_configuration, DatabaseSettings}; const BASE_URL: &str = "127.0.0.1"; pub struct TestApp { db_name: String, connection_string: String, pub address: String, pub db_pool: PgPool, pub connection: PgConnection, } /** * We need to refactor our project into a library and a binary: all our logic will live in the library crate while the binary itself will be just an entrypoint with a very slim main function */ pub async fn init(url: &str) -> TestApp { let mut app = spawn_app().await; app.address = format!("{}{}", app.address, url); return app; } // Launch our application in the background ~somehow~ async fn spawn_app() -> TestApp { // We take the BASE_URL const and assign it a port 0. We then // pass the listener to the server let base_url = format!("{}:0", BASE_URL); let listener = TcpListener::bind(base_url).expect("Failed to bind random port"); // We retrieve the port assigned by the OS let port = listener.local_addr().unwrap().port(); let (connection, db_connection, db_name, connection_string) = init_db().await; // We pass the port now to our server let server = zero2prod::run(listener, db_connection.clone()).expect("Failed to bind address"); let _ = actix_web::rt::spawn(server); let address = format!("http://{}:{}", BASE_URL, port); TestApp { db_name: String::from(db_name), address, db_pool: db_connection, connection, connection_string, } } async fn init_db() -> (PgConnection, PgPool, String, String) { let mut configuration = get_configuration().expect("Failed to read configuration"); // We change the db name in each run as we need to run the test multiple times configuration.database.database_name = Uuid::new_v4().to_string(); let (connection, pool) = configure_database(&configuration.database).await; return ( connection, pool, String::from(&configuration.database.database_name), configuration.database.connection_string_without_db(), ); } async fn configure_database(config: &DatabaseSettings) -> (PgConnection, PgPool) { // The following returns: // format!( // "postgres://{}:{}@{}:{}", // self.username, self.password, self.host, self.port // ) let mut connection = PgConnection::connect(&config.connection_string_without_db()) .await .expect("Failed to connect to Postgres."); connection .execute(format!(r#"CREATE DATABASE "{}""#, config.database_name).as_str()) .await .expect("Failed to create the db"); // Migrate the database let connection_pool = PgPool::connect(&config.connection_string()) .await .expect("Failed to connect to Postgres"); sqlx::migrate!("./migrations") .run(&connection_pool) .await .expect("Failed to migrate db"); return (connection, connection_pool); }
入口函数为init(),返回TestApp结构体(原本仅包含db_pool和address字段),上述代码均可正常运行。
尝试过的方案(均出现挂起超时问题)
方案1:使用Smol runtime在drop中初始化新Postgres连接
impl Drop for TestApp { fn drop(&mut self) { smol::block_on(async { let mut connection = PgConnection::connect(&self.connection_string) .await .expect("Failed to connect to Postgres."); let result = connection .execute(format!(r#"DROP DATABASE "{}""#, self.db_name).as_str()) .await .expect("Error while querying the drop database"); println!("{:?}", result); }); } }
方案2:使用Smol runtime在drop中复用现有db_pool
impl Drop for TestApp { fn drop(&mut self) { smol::block_on(async { let result = self .db_pool .execute(format!(r#"DROP DATABASE "{}""#, self.db_name).as_str()) .await.expect("Error while querying"); println!("{:?}", result); }); } }
方案3:使用Futures crate executor复用现有db_pool
impl Drop for TestApp { fn drop(&mut self) { let result = executor::block_on( self.db_pool .execute(format!(r#"DROP DATABASE "{}""#, self.db_name).as_str()), ) .expect("Failed to drop database"); println!("{:?}", result); } }
方案4:使用Futures crate executor先获取连接再操作(挂起在db_pool.acquire处)
impl Drop for TestApp { fn drop(&mut self) { executor::block_on(self.db_pool.acquire()).expect("Failed to acquire pool"); let result = executor::block_on( self.db_pool .execute(format!(r#"DROP DATABASE "{}""#, self.db_name).as_str()), ) .expect("Failed to drop database"); println!("{:?}", result); } }
方案5:使用Futures crate executor复用现有connection
impl Drop for TestApp { fn drop(&mut self) { let result = executor::block_on( self.connection .execute(format!(r#"DROP DATABASE "{}""#, self.db_name).as_str()), ) .expect("Failed to drop database"); println!("{:?}", result); } }
目前代码仅为测试方案,尚未优化。没有报错信息,无法定位问题,恳请提供排查思路或解决方案。
内容的提问来源于stack exchange,提问作者Jose A
相关产品推荐
相关产品推荐

