DEV Community

Njuguna Mureithi
Njuguna Mureithi

Posted on

2 1 1

How to schedule and run cron jobs in Rust using apalis

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
Enter fullscreen mode Exit fullscreen mode

Add to Cargo.toml

[dependencies]
apalis = { version = "0.3", features = ["cron"] }
serde = { version = "1.0", features = ["derive"] }
tower = 0.4
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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();

Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

You can repeat the process for different cron jobs.

Monitor::new()
    .register(daily_worker)
    .register(weekly_worker)
    .run()
    .await
    .unwrap();
Enter fullscreen mode Exit fullscreen mode

Further reading:
Background job processing with rust using apalis, actix and redis
Apalis on Github

Heroku

Build apps, not infrastructure.

Dealing with servers, hardware, and infrastructure can take up your valuable time. Discover the benefits of Heroku, the PaaS of choice for developers since 2007.

Visit Site

Top comments (2)

Collapse
 
adophilus profile image
Adophilus

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.

Collapse
 
njugunamureithi profile image
Njuguna Mureithi

Consider using a backend with persistence such as sqlite, postgres or redis.

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Dive into an ocean of knowledge with this thought-provoking post, revered deeply within the supportive DEV Community. Developers of all levels are welcome to join and enhance our collective intelligence.

Saying a simple "thank you" can brighten someone's day. Share your gratitude in the comments below!

On DEV, sharing ideas eases our path and fortifies our community connections. Found this helpful? Sending a quick thanks to the author can be profoundly valued.

Okay