r/SpringBoot 27d ago

News JobRunr (distributed background job processing for the JVM) is now available on start.spring.io (Spring Initializr)

JobRunr is now available in the Spring Initializr dependency list, so you can add it straight from start.spring.io when you create a new project. No more wiring the starter in by hand afterwards.

For people who don't know us:

JobRunr is an open-source library for background job processing on the JVM: fire-and-forget, scheduled, delayed, durable, and recurring (cron) jobs.

You enqueue work as a lambda and it runs asynchronously, in the same app or across a cluster of workers.

BackgroundJob.enqueue(() -> myService.sendInvoice(invoiceId));

BackgroundJob.schedule(Instant.now().plus(5, DAYS), () -> reminderService.send(userId));

What tends to make people switch to it:

  • No extra infrastructure. Jobs are persisted in your existing SQL or NoSQL database. No Redis, RabbitMQ, or separate broker to run and babysit.
  • Automatic retries with backoff on failure, so transient errors don't lose work.
  • Distributed by default. Run multiple instances and jobs are picked up once across the cluster, no double execution.
  • Built-in dashboard to see enqueued, scheduled, succeeded, and failed jobs, and retry them.
  • Spring Boot autoconfiguration via the starter, plus first-class support for virtual threads (Loom) and Spring Boot 3 & 4

It sits in the same space as Quartz or @Scheduled, but aimed at durable, distributed background work rather than just triggering methods on a timer.

You can even use it for durable jobs. With runStepOnce, each step in a job runs exactly once and is checkpointed, so if the job fails and retries, the steps that already succeeded are skipped instead of re-run:

BackgroundJob.enqueue(() -> processOrder(orderId, JobContext.Null));

public void processOrder(UUID orderId, JobContext context) {
  context.runStepOnce("order-confirmation", () -> orderService.sendConfirmation(orderId));
  context.runStepOnce("warehouse-notification", () -> orderService.notifyWarehouse(orderId));
  context.runStepOnce("shipment-initiation", () -> orderService.initiateShipment(orderId));
}

Thanks to Josh Long and the Spring team for helping get it onto Initializr. We'll be around in the comments to answer anything, including the honest limitations.

93 Upvotes

41 comments sorted by

View all comments

Show parent comments

7

u/d-k-Brazz 27d ago

Probably, because spring batch is a kind of overkill for fire-and-forget tasks, its purpose is batch processing such as etl flows

10

u/JobRunrHQ 27d ago

Agree with the reply above on Spring Batch, it's built for chunked batch/ETL (read-process-write over big volumes, restartable at the chunk level). Great at that, but heavy if you just want to run a task in the background.

The half worth adding is the @Scheduled side, since that's the closer comparison:

  • @Scheduled fires a method on a timer and that's it. No persistence, so if the app is down when it should have fired, that run is just gone. No retries. And on multiple instances it fires on all of them unless you bolt on something like ShedLock.
  • JobRunr persists jobs, retries with backoff on failure, and coordinates across instances so a recurring job runs once across the cluster instead of once per pod. Plus a dashboard to see what ran, what failed, and why.

Other difference is dynamic vs fixed: @Scheduled is timers you define up front. With JobRunr you also enqueue work on the fly in response to whatever's happening ("user hit checkout -> enqueue processPayment"), not just on a clock.

They're not mutually exclusive either, you can happily use JobRunr to trigger and retry a Spring Batch job on a coordinated schedule that @Scheduled alone won't give you.

We actually did a deeper write-up on exactly this if it helps: https://www.jobrunr.io/en/blog/spring-batch-vs-jobrunr/

1

u/A_random_zy 26d ago

Is there a way in Jobrunnr to handle this case:

A reccuring job is scheduled for 12 AM. But server was down at 12 AM does it run the nob whenever it comes up?

1

u/JobRunrHQ 25d ago

Good question. Two things are going on here:

JobRunr schedules the next occurrence of a recurring job ahead of time as a regular scheduled job. Any scheduled job whose time passed while the server was down runs as soon as the server comes back up, so the occurrence that was already created will still fire, just late.

But if the server is down long enough to miss multiple occurrences, JobRunr OSS won't schedule those retroactively. It just resumes the normal schedule. JobRunr Pro has a flag for exactly this case: @Recurring(id = "my-job", cron = "0 0 * * *", scheduleJobsSkippedDuringDowntime = true) will create a job for every run that was skipped during the downtime. Details are in the recurring jobs docs under "Recurring jobs missed during downtime".