3

I am trying to migrate an application from rocket 0.4 to rocket 0.5 Before it was using rocket_contrib feature diesel_postgres_pool and I though rocket_sync_db_pools would be the best fit for migration (but I am open to alternatives here)

I tried to change the code like this:

-        let quality = subjects::table
-            .find(&sub)
-            .select(subjects::quality)
-            .first::<i16>(&conn.0)
-            .ok();
+        let quality = conn.run( move |conn| {
+            subjects::table
+                .find(&sub)
+                .select(subjects::quality)
+                .first::<i16>(conn)
+                .ok();
+        });

But now I am not getting the actual result but a Promise.

The documentation suggests to use .await which seems to cause that the calling func needs to be async, too. But I cannot do this, because the code is also used in traits, which cannot be async easily.

I am pretty new to rust and don't understand two (or more) things:

  1. Why does my function have to be async if we are waiting for the result (with await)?
  2. Why does the sync DB pool return async promises / need async functions.

Is this the right approach?

What are the alternatives to fix this without rebuilding the full application?

EDIT

This is a step forward, after importing async_trait.


#[async_trait]
impl Statistic for Subject {
    async fn compute(conn: &DbConn, sub: String) -> Result<Self, Error> {


[...]

        let quality = conn.run( move |conn| {
            return subjects::table
                .find(&sub)
                .select(subjects::quality)
                .first::<i16>(conn)
                .ok();
        }).await;

But it seems to bring problems on the callee of compute:

        identifiers
            .map(|identifier| {
                Self::compute(&conn, identifier.clone()).map(|statistic| (identifier, statistic))
            })
            .collect()

Causes an error:

23  |                 Self::compute(&conn, identifier.clone()).map(|statistic| (identifier, statistic))
    |                                                          ^^^ `Pin<Box<dyn std::future::Future<Output = Result<Self, error::Error>> + std::marker::Send>>` is not an iterator

EDIT2: I might not need the async_trait crate, as rocket already supplies such a solution: https://rocket.rs/master/guide/upgrading-from-0.4/#async-traits

EDIT3: So I tried:

        identifiers
            .map(async move |identifier| {
                join_all(Self::compute(&conn, identifier.clone())).await.map(|statistic| (identifier, statistic))
            })
            .collect()

This leads to a new error :-)

error[E0658]: async closures are unstable
  --> src/aggregator.rs:23:18
   |
23 |             .map(async move |identifier| {
   |                  ^^^^^
   |
   = note: see issue #62290 <https://github.com/rust-lang/rust/issues/62290> for more information
   = help: to use an async block, remove the `||`: `async {`

EDIT4:

After trying pigeonhands answer with this code:

        let quality = Handle::current().block_on(async move {
            conn.run(move |conn| {
                subjects::table
                    .find(&sub)
                    .select(subjects::quality)
                    .first::<i16>(conn)
                    .ok();
            })
        });
        Ok(Subject {
            sub,
            quality,
            count,
            opinion_count,
            positive_count,
            confirmed_count,
        })

I am getting

error[E0308]: mismatched types
  --> src/aggregator.rs:84:13
   |
74 |             conn.run(move |conn| {
   |                      ----------- the found closure
...
84 |             quality,
   |             ^^^^^^^ expected enum `std::option::Option`, found opaque type
Alex
  • 32,506
  • 16
  • 106
  • 171
  • 1
    1 is just the design of async/await. – cafce25 Jan 22 '23 at 11:24
  • Is there a way to wait for the query's result and return it synchronously? – Alex Jan 22 '23 at 14:23
  • Found this:. https://stackoverflow.com/a/66280983/288568 so the question is also why and how this was working in rocket 0.4 – Alex Jan 22 '23 at 14:27
  • I have to say that I'm trying to switch from a 2 years-old nightly to stable rust. Maybe this is also part of the problem. – Alex Jan 22 '23 at 14:34
  • This is also interesting: https://github.com/SergioBenitez/Rocket/issues/1910 – Alex Jan 22 '23 at 15:34

1 Answers1

2

You could use tokio's block_on to resolve the future in a non-async function

let quality = Handle::current().block_on(async move {
    conn.run( move |conn| {
        subjects::table
            .find(&sub)
            .select(subjects::quality)
            .first::<i16>(conn)
            .ok()
    }).await
});.

Or you could use the diesel crate directly with the r2d2 feature for pooling, witch does not require using await. (this is what crates.io does with axum)

pigeonhands
  • 3,066
  • 15
  • 26
  • I used `use rocket::tokio::runtime::Handle;` and got `the found closure... quality ^^^^^^^ expected enum \`std::option::Option\`, found opaque type` – Alex Jan 27 '23 at 07:05
  • 1
    @Alex can you give more context? Where is it saying it is expecting `Option`? – pigeonhands Jan 27 '23 at 07:44
  • See EDIT4. The full code would be here https://gitlab.com/open-reviews/mangrove/-/merge_requests/113/diffs?commit_id=bb1c0ae5f3dc4782032712a6953a837d9c2bec37 by the way – Alex Jan 27 '23 at 08:19
  • @Alex whoops you need to await the `conn.run` future, or possibly calling `block_on` with the future returned by `conn.run` may work too. Ive updated my answer. – pigeonhands Jan 27 '23 at 08:26
  • @Alex you also may need to remove the `;` after `ok()`. – pigeonhands Jan 27 '23 at 08:31
  • Great that works. I tried .wait before, but the `;` was also important. – Alex Jan 28 '23 at 08:40