-
-
Notifications
You must be signed in to change notification settings - Fork 50
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BoxDynError in worker fn #470
Comments
Is there a reason for using BoxDynError?
I recommend using the concrete `Error` type provided in apalis like in `email_service` example or define your error type.
I will investigate a little more.
|
A lot of my services have tower layers applied. Some layers convert error into Box<dyn Error+...> exampleuse apalis::prelude::*;
use apalis_sql::{context::SqlContext, sqlite::SqliteStorage};
use futures::future::BoxFuture;
use sqlx::SqlitePool;
use tower::{Layer, Service};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt::init();
let pool = SqlitePool::connect("sqlite::memory:").await?;
// Do migrations: Mainly for "sqlite::memory:"
SqliteStorage::setup(&pool)
.await
.expect("unable to run migrations for sqlite");
let storage: SqliteStorage<String> = SqliteStorage::new(pool.clone());
Monitor::new()
.register({
WorkerBuilder::new("tasty-mango")
.layer(DynLayer)
.backend(storage)
.build_fn(do_job)
})
.run()
.await?;
Ok(())
}
async fn do_job(job: String) -> Result<(), CustomError> {
tracing::info!("job: {job}");
Ok(())
}
#[derive(Debug)]
enum CustomError {}
impl std::error::Error for CustomError {}
impl std::fmt::Display for CustomError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
todo!()
}
}
#[derive(Debug)]
struct DynLayerService<S> {
inner: S,
}
impl<R, S> Service<R> for DynLayerService<S>
where
S: Service<R>,
{
type Response = S::Response;
type Error = Box<dyn std::error::Error + Send + Sync>;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
todo!()
}
fn call(&mut self, req: R) -> Self::Future {
todo!()
}
}
struct DynLayer;
impl<S> Layer<S> for DynLayer {
type Service = DynLayerService<S>;
fn layer(&self, inner: S) -> Self::Service {
DynLayerService { inner }
}
} this gives a possible hint why original build_fn is not allowed errorerror[E0277]: the size for values of type `(dyn std::error::Error + std::marker::Send + Sync + 'static)` cannot be known at compilation time
--> src/main.rs:21:10
|
21 | .register({
| ^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + std::marker::Send + Sync + 'static)`
= help: the trait `tower::Layer<S>` is implemented for `apalis_core::layers::AckLayer<A, Req, Ctx, Res>`
= note: required for `Box<(dyn std::error::Error + std::marker::Send + Sync + 'static)>` to implement `std::error::Error`
= note: required for `apalis_core::layers::AckLayer<SqliteStorage<std::string::String>, std::string::String, SqlContext, ()>` to implement `tower::Layer<DynLayerService<apalis::prelude::ServiceFn<fn(std::string::String) -> impl futures::Future<Output = Result<(), CustomError>> {do_job}, std::string::String, SqlContext, ()>>>` |
Some services do not need to pass real error, basic info is enough so boxed error suites well |
Oh I see what you are looking for:
Its the ErrorHandlerLayer.
This layer should be the top most layer. Could you check if that fixes your
issue?
|
kinda exampleuse apalis::{layers::ErrorHandlingLayer, prelude::*};
use apalis_sql::{context::SqlContext, sqlite::SqliteStorage};
use futures::future::BoxFuture;
use sqlx::SqlitePool;
use tower::Service;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt().init();
let pool = SqlitePool::connect("sqlite::memory:").await?;
SqliteStorage::setup(&pool)
.await
.expect("unable to run migrations for sqlite");
let storage: SqliteStorage<String> = SqliteStorage::new(pool.clone());
let service_worker = WorkerBuilder::new("do_job")
.layer(ErrorHandlingLayer::new())
.backend(storage.clone())
// fixed with layer
.build(ServiceWithBoxedError);
Monitor::new().register(service_worker).run().await?;
let fn_worker = WorkerBuilder::new("do_job")
.layer(ErrorHandlingLayer::new())
.backend(storage.clone())
// errors
.build_fn(do_job);
Monitor::new().register(fn_worker).run().await?;
Ok(())
}
async fn do_job(job: String) -> Result<(), Box<dyn std::error::Error + Sync + Send + 'static>> {
tracing::info!("job: {job}");
Ok(())
}
struct ServiceWithBoxedError;
impl Service<Request<String, SqlContext>> for ServiceWithBoxedError {
type Response = ();
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(
&mut self,
cx: &mut std::task::Context<'_>,
) -> std::task::Poll<Result<(), Self::Error>> {
todo!()
}
fn call(&mut self, req: Request<String, SqlContext>) -> Self::Future {
todo!()
}
} the fn case fails with errors errors
|
I have replicated your issue and ack that it is because of what constraints that |
For this, the |
Hi,
I'm using boxed error in worker function.
example
But i'm getting errors
errors
Replacing boxed error with concrete enum fixes errors
apalis v0.6.0
The text was updated successfully, but these errors were encountered: