You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建兼容sqlx连接池与事务的actix-web服务器?编译报错求助

解决Actix-web + SQLx测试时的Executor trait约束问题

咱们先拆解下你遇到的两个编译错误:

  1. PgPool没实现Executor<'_>:SQLx 0.4里,只有连接池的引用&PgPool才实现了Executor trait,直接传PgPool实例肯定不满足约束。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 08:07:50