For this tutorial, we're going to use apalis to run cron jobs in an async context. We will also look at how to decorate our jobs with tower middleware allowing us to unlock features like retries, prometheus, sentry etc
To begin, we will want to:
Setup a new project
cargo new my-cron-scheduler
cd my-cron-scheduler
Add to Cargo.toml
[dependencies]
apalis = { version = "0.3", features = ["cron"] }
serde = { version = "1.0", features = ["derive"] }
tower = 0.4
In this tutorial we will create a daily reminder example.
use apalis::prelude::*;
use serde::{Serialize,Deserialize};
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
struct Reminder;
// Define an apalis job
impl Job for Reminder {
const NAME: &'static str = "reminder::DailyReminder";
}
// Define a handler
async fn send_reminder(job: Reminder, ctx: JobContext) {
// Do reminder stuff
}
Lets define our entry point
use apalis::cron::{CronWorker, Schedule};
#[tokio::main]
async fn main() {
// any valid cron string that points to the future should work
let schedule = Schedule::from_str("@daily").unwrap();
let worker = CronWorker::new(schedule, job_fn(send_reminder));
Monitor::new()
.register(worker)
.run()
.await
.unwrap();
}
This works perfectly but we can add some more functionality eg retries and extensions to a job context.
To use these features by apalis
, you need to include the retry
and extension
features.
use apalis::layers::{Extension, DefaultRetryPolicy, RetryLayer};
#[derive(Clone)]
struct FakeService;
let service = ServiceBuilder::new()
// Will retry 25 times
.layer(RetryLayer::new(DefaultRetryPolicy))
.layer(Extension(FakeService))
.service(job_fn(send_reminder));
let worker = CronWorker::new(schedule, service);
Monitor::new()
.register(worker)
.run()
.await
.unwrap();
Now we can access our FakeService
in our handler
// Define a handler
async fn send_reminder(job: Reminder, ctx: JobContext) {
let fake_service = ctx.data_opt::<FakeService>();
// fake_service.fetch_from_db()
}
You can repeat the process for different cron jobs.
Monitor::new()
.register(daily_worker)
.register(weekly_worker)
.run()
.await
.unwrap();
Further reading:
Background job processing with rust using apalis, actix and redis
Apalis on Github
Top comments (2)
looks interesting, I've been trying out apalis recently with a custom
MemoryStorage
storage but I've been running into some issues: I can't schedule for a timeline such as 30 mins. It seems apalis uses a non-standard cron syntax.Consider using a backend with persistence such as sqlite, postgres or redis.