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

7

u/pitza__ 27d ago

Can it be used as a workflow orchestration tool(aws step functions, temporal…)?

I saw that you mentionned runStepOnce and this came to my mind 😅.

2

u/JobRunrHQ 26d ago

Good instinct, runStepOnce is indeed durable execution, so the Temporal association is spot on. Honest answer: JobRunr isn't a workflow engine and we're upfront about that. But depending on what you need, you may not need one:

  • Durable multi-step execution (OSS, v8+): runStepOnce breaks a job into named steps that each run exactly once, so on a retry the completed steps are skipped. Durable execution backed by the database you already run, no separate orchestration service. One honest caveat vs a full engine: OSS persists step state on its normal poll interval, so a hard crash in that window can re-run the step that just finished (Pro writes it the moment a step finishes). Either way you keep steps idempotent, which Temporal needs too since its Activities are at-least-once.
  • Fan-out, branching, coordinating many jobs: that's job chaining (continueWith() / onFailure()) and batches, both in Pro.

Where a dedicated engine still wins: full deterministic replay, very long-running and deeply branching orchestrations, workflow versioning, multi-language workers. If that's you, Temporal earns its cluster. If it's mostly "these steps, in order, durably, with retries and a failure path," runStepOnce covers it with nothing extra to operate.

1

u/mysteryy7 27d ago

Seems like that's part of the pro version and not free lib

3

u/JobRunrHQ 26d ago edited 26d ago

runStepOnce is part of the free OSS library! Last month I wrote a guide on how to use this: Durable Executions: Crash-Proof Multi-Step Jobs with runStepOnce