如何在 rocket.rs 的启动阶段正确执行 long-运行 数据库操作?

How to properly exec a long-running database operation during the launching phase in rocket.rs?

我正在尝试进行一个长 运行(~1 分钟)的函数调用,该函数调用查询数据库并构造一些数据对象以供将来使用。但是如果我在启动函数中创建一个 postgres::Client,它会给我一个运行时恐慌。

thread 'main' panicked at 'Cannot start a runtime from within a runtime. This happens because a function (like `block_on`) attempted to block the current thread while the thread is being used to drive asynchronous tasks.'

最小的示例代码片段如下所示:

use postgres::{Client, NoTls};
use my_crate::my_libs::fill_data_model_from_db;

#[macro_use] extern crate rocket;

#[get("/world")]
fn world() -> &'static str {
    "Hello, world!"
}

#[launch]
fn rocket() -> _ {
    let mut client: Client = Client::connect("host=localhost dbname=testdb", NoTls).unwrap();
    // long running db queries for data preparation
    // let d = fill_data_model_from_db(&mut client);
    // will use State to encapsulate `d`
    rocket::build().mount("/hello", routes![world])
}

我有点奇怪,虽然postgres::Client实际上是一个synchronous PostgreSQL client,正如它的文档所说,为什么创建它会导致嵌套的tokio运行时问题?我已经看过 rocket_db_pools,但无法弄清楚数据库池在这里有何帮助。我对 Rust 和 Rocket 还是很陌生。任何提示或帮助都将非常受欢迎!非常感谢!

postgres::Client 从调用者的角度来看是同步的,但它通过创建运行时并在内部调用异步 API 来工作。

在这种情况下,解决方案非常简单:只需将 rocket 函数设为 async 并使用异步 Postgres API:

use tokio_postgres::{Client, NoTls};

#[macro_use] extern crate rocket;

#[get("/world")]
fn world() -> &'static str {
    "Hello, world!"
}

#[launch]
async fn rocket() -> _ {
    let (client, connection) = tokio_postgres::connect("host=localhost dbname=testdb", NoTls).await.unwrap();
    tokio::spawn(async move {
        if let Err(e) = connection.await {
            eprintln!("connection error: {}", e);
        }
    });
    // use `client` to make asynchronous DB requests
    rocket::build().mount("/hello", routes![world])
}