Xuanwo/backon

Unable to get Retry to work

Closed this issue · 7 comments

I'm sure I'm doing something completely stupid, but just can't work it out. In my reproduction of what I'm experiencing in my real project, I just can't get backon working with my future.

use backon::ConstantBackoff;
use backon::Retryable;
use std::error::Error;
use std::thread;
use std::time::Duration;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn Error>> {
    let _ = foo().retry(&ConstantBackoff::default()).await?;
    Ok(())
}

async fn foo() -> Result<(), Box<dyn Error + Send + Sync>> {
    async { thread::sleep(Duration::from_secs(1)) }.await;
    Ok(())
}

results in the following error

error[E0599]: the method `retry` exists for opaque type `impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>`, but its trait bounds were not satisfied
 --> src/main.rs:9:19
  |
9 |     let _ = foo().retry(&ConstantBackoff::default()).await?;
  |                   ^^^^^ method cannot be called on `impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>` due to unsatisfied trait bounds
  |
  = note: the following trait bounds were not satisfied:
          `<impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>> as FnOnce<()>>::Output = _`
          which is required by `impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`
          `impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: FnMut<()>`
          which is required by `impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`
          `<&impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>> as FnOnce<()>>::Output = _`
          which is required by `&impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, &impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`
          `&impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: FnMut<()>`
          which is required by `&impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, &impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`
          `<&mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>> as FnOnce<()>>::Output = _`
          which is required by `&mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, &mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`
          `&mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: FnMut<()>`
          which is required by `&mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>: Retryable<_, _, _, _, &mut impl Future<Output = Result<(), Box<(dyn std::error::Error + Send + Sync + 'static)>>>>`

warning: unused import: `backon::Retryable`
 --> src/main.rs:2:5
  |
2 | use backon::Retryable;
  |     ^^^^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

For more information about this error, try `rustc --explain E0599`.
warning: `mvr` (bin "mvr") generated 1 warning
error: could not compile `mvr` due to previous error; 1 warning emitted

I'm completely baffled. I've tried adding in anyhow, thinking it may be doing some magic, but get the same error. What am I doing wrong?

Thanks for reaching out!

Here are some problems here:

  • retry is implement on a future generator instead future it self (becuase we need to build the new future every time we need to retry it)
  • The returning error's type is not match.

I have tested the following code locally, may worth a look:

use backon::ConstantBuilder;
use backon::Retryable;
use std::error::Error;
use std::thread;
use std::time::Duration;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn Error + Send + Sync>> {
    // Note here, use `foo` instead of `foo()`
    let _ = foo.retry(&ConstantBuilder::default()).await?;
    Ok(())
}

async fn foo() -> Result<(), Box<dyn Error + Send + Sync>> {
    async { thread::sleep(Duration::from_secs(1)) }.await;
    Ok(())
}

Ah, Ok. thanks. And what if foo had arguments? like async foo(some_var: &str) -> Result<(), Box<dyn Error + Send + Sync>>

like async foo(some_var: &str) -> Result<(), Box<dyn Error + Send + Sync>>

We need to capture it in an async closure, something like:

let f = || async { foo(xxx).await }
f.retry().await

Magnificent. You're a star 🌟 I had tried some of that; missed out the await inside the closure. Couldn't see the woods for the trees.

Thank you again.

Thanks for posting this, I have to use it like this

let upsert_res = (|| async {
    current_epoch_store
        .clone()
        .upsert_current_epoch(epoch_info.epoch)
        .await
})
.retry(&ConstantBuilder::default())
.await;

@Xuanwo maybe this recipe could be added to the rustdocs? I also was confused by a similar compiler error .

@Xuanwo maybe this recipe could be added to the rustdocs? I also was confused by a similar compiler error .

Makes sense, let me add them.