如何创建兼容sqlx连接池与事务的actix-web服务器?编译报错求助
解决Actix-web + SQLx测试时的Executor trait约束问题
咱们先拆解下你遇到的两个编译错误:
PgPool没实现Executor<'_>:SQLx 0.4里,只有连接池的引用&PgPool才实现了Executortrait,直接传PgPool实例肯定不满足约束。PgPool没实现Copy:连接池内部是带状态的结构,根本没法复制,Copy只给简单值类型用的,这个约束完全没必要加。
另外测试里传的&mut PgTransaction虽然符合Executor,但可变引用不能克隆,而Actix的HttpServer会给每个worker线程执行一次初始化闭包,这也会导致问题。下面一步步来修复:
第一步:修改create_server的泛型约束
我们调整泛型参数,去掉不合理的Copy,改用Clone配合Arc实现安全共享,同时适配Executor的正确实现方式:
use std::sync::Arc; use std::net::TcpListener; use actix_web::dev::Server; use actix_web::{web, App, HttpServer, Responder}; use sqlx::{PgPool, Postgres, Executor}; // 保留你的create_pool、index函数不变 pub fn create_server<E>( listener: TcpListener, executor: E, ) -> Result<Server, std::io::Error> where // 核心约束:任意生命周期下,E的引用都能当Executor用 for<'a> &'a E: Executor<'a, Database = Postgres>, // 要求E能克隆、且是线程安全的静态类型 E: Clone + 'static + Send + Sync, { let executor = Arc::new(executor); let server = HttpServer::new(move || { App::new() // 每个worker克隆一份Arc,共享底层的executor .data(Arc::clone(&executor)) .route("/", web::get().to(index)) }) .listen(listener)? .run(); Ok(server) } pub async fn server(pool: PgPool) -> std::io::Result<()> { const PORT: usize = 8088; let listener = TcpListener::bind(format!("0.0.0.0:{}", PORT)).expect("Failed to create listener"); println!("Running on port {}", PORT); // 克隆连接池(内部是Arc,开销极小) create_server(listener, pool.clone()).unwrap().await } // 保留main函数不变
第二步:调整测试代码
测试用的事务是可变的,没法直接克隆,所以用Arc<Mutex>包裹它,保证线程安全的可变访问:
#[cfg(test)] pub mod tests { use super::*; use std::net::TcpListener; use std::sync::{Arc, Mutex}; #[actix_rt::test] async fn test_foo() { let pool = create_pool().await; let transaction = pool.begin().await.expect("Failed to create transaction"); // 用Arc<Mutex>把事务包起来,实现线程安全的共享可变 let shared_tx = Arc::new(Mutex::new(transaction)); let listener = TcpListener::bind("0.0.0.0:0").expect("Failed to create listener"); let server = create_server(listener, shared_tx).expect("Failed to create server"); tokio::spawn(server); // 这里可以加测试逻辑,比如发请求到测试服务器 // 测试完记得回滚事务:shared_tx.lock().unwrap().rollback().await.unwrap(); } }
如果你的业务handler需要用这个事务,记得先锁定Mutex再获取引用:
async fn get_user_count(data: web::Data<Arc<Mutex<PgTransaction>>>) -> impl Responder { let mut tx = data.lock().unwrap(); let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut *tx) .await .unwrap(); format!("Total users: {}", count) }
第三步:验证编译
修改完之后,不管是生产环境用连接池,还是测试环境用事务,都能满足Executor的约束了。Cargo.toml的依赖版本不用改,Actix-web 3.x和SQLx 0.4.x是兼容的。
内容的提问来源于stack exchange,提问作者Oskar Persson
相关产品推荐
相关产品推荐

