无法安全地在线程之间发送具有lazy_static的 SQLx MySQL 连接

SQLx MySQL connection with lazy_static cannot be sent between threads safely

提问人:Abdelaziz Said 提问时间:6/20/2022 最后编辑:smitopAbdelaziz Said 更新时间:9/3/2022 访问量:1232

问:

我使用 sqlx 初始化 mysql 连接(异步),但发生了奇怪的错误。lazy_static

这是我写的代码:

use actix_web::middleware::Logger;
use actix_web::web::{Data, JsonConfig};
use actix_web::{App, HttpServer};

lazy_static::lazy_static! {
    static ref MYSQL_DB: async_once::AsyncOnce<sqlx::Pool<sqlx::MySql>> = async_once::AsyncOnce::new(async {
        dotenv::dotenv().expect("Failed to read .env file");
        let uri = std::env::var("DATABASE_URL").unwrap();
        sqlx::MySqlPool::connect(uri.as_str()).await.unwrap()
    });
    static ref DB_CLIENT : DBClient = DBClient{};
}

#[derive(Clone, Copy)]
pub struct DBClient;

impl DBClient {
    pub fn get() -> &'static DBClient {
        &DB_CLIENT
    }
    pub async fn mysql_pool(self) -> &'static sqlx::Pool<sqlx::MySql> {
        MYSQL_DB.get().await
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // start_tracing();
    let db_client = Data::new(DBClient::get());
    dotenv::dotenv().expect("Failed to read .env file");

    HttpServer::new(move || {
        App::new()
            .wrap(Logger::default())
            .app_data(Data::new(JsonConfig::default().limit(4096)))
            .app_data(db_client.clone())
    })
    .bind(format!(
        "{}:{}",
        std::env::var("HOST").unwrap(),
        std::env::var("PORT").unwrap()
    ))
    .expect("Server binding exception")
    .run()
    .await
}

这是我的 Cargo.toml 文件:

[package]
name = "untitled"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
actix-web = "4.0.1"
sqlx = { version = "0.5.*", features = ["mysql", "runtime-async-std-native-tls"] }
actix-rt = "2.*"
juniper = { version = "0.15.9", features = ["chrono"] }
uuid = { version = "=0.8", features = ["serde", "v4"] }
chrono = { version = "0.4", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
dotenv = "0.15.0"
lazy_static = "1.4.*"
async_once = "0.2.6"
validator = { version = "0.12", features = ["derive"] }

这是我的.env文件:

DATABASE_URL=mysql://user_name:password@localhost:3306/db_name
HOST=127.0.0.1
PORT=3000

这些是我的错误:

error[E0277]: `*const Pool<MySql>` cannot be sent between threads safely
  --> src/main.rs:6:1
   |
6  | / lazy_static::lazy_static! {
7  | |     static ref MYSQL_DB: async_once::AsyncOnce<sqlx::Pool<sqlx::MySql>> = async_once::AsyncOnce::new(async {
8  | |         dotenv::dotenv().expect("Failed to read .env file");
9  | |         let uri = env::var("DATABASE_URL").unwrap();
...  |
12 | |     static ref DB_CLIENT : DBClient = DBClient{};
13 | | }
   | |_^ `*const Pool<MySql>` cannot be sent between threads safely
   |
   = help: the trait `Send` is not implemented for `*const Pool<MySql>`
   = note: required because of the requirements on the impl of `Send` for `Cell<*const Pool<MySql>>`
   = note: required because it appears within the type `AsyncOnce<Pool<MySql>>`
   = note: required because of the requirements on the impl of `Sync` for `spin::once::Once<AsyncOnce<Pool<MySql>>>`
   = note: required because it appears within the type `lazy_static::lazy::Lazy<AsyncOnce<Pool<MySql>>>`
   = note: shared static variables must have a type that implements `Sync`
   = note: this error originates in the macro `__lazy_static_create` (in Nightly builds, run with -Z macro-backtrace for more info)


error[E0277]: `(dyn std::future::Future<Output = Pool<MySql>> + 'static)` cannot be sent between threads safely
  --> src/main.rs:6:1
   |
6  | / lazy_static::lazy_static! {
7  | |     static ref MYSQL_DB: async_once::AsyncOnce<sqlx::Pool<sqlx::MySql>> = async_once::AsyncOnce::new(async {
8  | |         dotenv::dotenv().expect("Failed to read .env file");
9  | |         let uri = env::var("DATABASE_URL").unwrap();
...  |
12 | |     static ref DB_CLIENT : DBClient = DBClient{};
13 | | }
   | |_^ `(dyn std::future::Future<Output = Pool<MySql>> + 'static)` cannot be sent between threads safely
   |
   = help: the trait `Send` is not implemented for `(dyn std::future::Future<Output = Pool<MySql>> + 'static)`
   = note: required because of the requirements on the impl of `Send` for `Unique<(dyn std::future::Future<Output = Pool<MySql>> + 'static)>`
   = note: required because it appears within the type `Box<(dyn std::future::Future<Output = Pool<MySql>> + 'static)>`
   = note: required because it appears within the type `Pin<Box<(dyn std::future::Future<Output = Pool<MySql>> + 'static)>>`
   = note: required because it appears within the type `Result<Pool<MySql>, Pin<Box<(dyn std::future::Future<Output = Pool<MySql>> + 'static)>>>`
   = note: required because of the requirements on the impl of `Send` for `Mutex<Result<Pool<MySql>, Pin<Box<(dyn std::future::Future<Output = Pool<MySql>> + 'static)>>>>`
   = note: required because it appears within the type `AsyncOnce<Pool<MySql>>`
   = note: required because of the requirements on the impl of `Sync` for `spin::once::Once<AsyncOnce<Pool<MySql>>>`
   = note: required because it appears within the type `lazy_static::lazy::Lazy<AsyncOnce<Pool<MySql>>>`
   = note: shared static variables must have a type that implements `Sync`
   = note: this error originates in the macro `__lazy_static_create` (in Nightly builds, run with -Z macro-backtrace for more info)

I don't know the reason of the errors, but when I do the following it builds successfully:

  1. Replace the connection with Pool<Postgres>

  2. Change the to in sqlx's section in Cargo.toml.mysqlpostgresfeatures

  3. Change in the .env file to Postgres URIDATABASE_URL

I got help from this question in stackoverflow: Rust lazy_static with async/await?

MySQL的 锈-sqlx 惰性静态

评论

2赞 Chayim Friedman 6/20/2022
吹毛求疵:不要使用,使用,它的 API 会集成到 std 中。lazy_staticonce_cell
0赞 Jeremy Meadows 6/20/2022
如果某些东西不是,通常可以通过将其包装在 .如果用 ?SendArcMYSQL_DB: AsyncOnce<Pool<MySql>>MYSQL_DB: Arc<AsyncOnce<Pool<MySql>>>
0赞 Abdelaziz Said 6/21/2022
@JeremyMeadows Arc 包装器给了我同样的错误!!
0赞 Abdelaziz Said 6/21/2022
@ChayimFriedman 你能给我举个例子吗?
1赞 Chayim Friedman 6/22/2022
@JeremyMeadows .where T: Send + Sync

答:

0赞 Jeremy Meadows 6/21/2022 #1

正如评论中所讨论的那样,我用(同步)OnceCell 替换了 /。通过这些更改,您的程序能够编译:lazy_static!AsyncOnce

use actix_web::middleware::Logger;
use actix_web::web::{Data, JsonConfig};
use actix_web::{App, HttpServer};
use once_cell::sync::OnceCell;
use sqlx::{Pool, MySql};

static MYSQL_DB: OnceCell<Pool<MySql>> = OnceCell::new();
static DB_CLIENT: DBClient = DBClient {};

#[derive(Clone, Copy)]
pub struct DBClient;

impl DBClient {
    pub fn get() -> &'static DBClient {
        &DB_CLIENT
    }

    pub async fn mysql_pool(self) -> &'static sqlx::Pool<sqlx::MySql> {
        MYSQL_DB.get().unwrap()
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    MYSQL_DB.set({
        dotenv::dotenv().expect("Failed to read .env file");
        let uri = std::env::var("DATABASE_URL").unwrap();
        sqlx::MySqlPool::connect(uri.as_str()).await.unwrap()
    }).unwrap();

    // start_tracing();
    let db_client = Data::new(DBClient::get());
    dotenv::dotenv().expect("Failed to read .env file");

    HttpServer::new(move || {
        App::new()
            .wrap(Logger::default())
            .app_data(Data::new(JsonConfig::default().limit(4096)))
            .app_data(db_client.clone())
    })
    .bind(format!(
        "{}:{}",
        std::env::var("HOST").unwrap(),
        std::env::var("PORT").unwrap()
    ))
    .expect("Server binding exception")
    .run()
    .await
}

我不认为我能很好地解释为什么你所拥有的东西不起作用。

评论

0赞 Abdelaziz Said 6/21/2022
相同的错误错误[E0277]:无法安全地在线程之间共享 --> rest-service/src/main.rs:16:18 |16 |静态MYSQL_DB: OnceCell<MysqlPool> = OnceCell::new(); |^^^^^^^^^^^^^^^^^^^ 无法安全地在线程之间共享 |= help: within ,该特征未实现 = 注意:必需,因为它出现在 type = 注意:共享静态变量必须具有实现UnsafeCell<Option<Pool<MySql>>>UnsafeCell<Option<Pool<MySql>>>OnceCell<Pool<MySql>>SyncUnsafeCell<Option<Pool<MySql>>>OnceCell<Pool<MySql>>Sync
0赞 Jeremy Meadows 6/21/2022
你在用@AbdelazizSaid?该错误消息与我得到的错误消息相匹配(注意同步与不同步)。我把它复制粘贴回我的编辑器中,它仍然对我有用once_cell::sync::OnceCellonce_cell::unsync::OnceCell
0赞 Abdelaziz Said 7/4/2022
当我需要其他数据库时,我发现服务器抛出此错误!!线程“main”在“建立 MySql 登录连接时出错!!: 池 { 大小:1,num_idle:1,is_closed:false,选项:PoolOptions { max_connections:10,min_connections:0,connect_timeout:30s,max_lifetime:Some(1800s),idle_timeout:Some(600s),test_before_acquire:true } }',
0赞 Abdelaziz Said 7/4/2022
我在main中添加了这个函数,就像你所做的一样,并抛出了一个异常。LoginDBClient::one_cell_mysql_pool().set({ dotenv::d otenv().expect(“无法读取 .env 文件”); let uri = env::var(“LOGIN_DATABASE_URL”).unwrap(); println!(”login_uri: {}“, uri);MysqlPool::connect(uri.as_str()).await.unwrap() }).expect(“建立MySql登录连接时出错!!”);