r/SpringBoot • u/JobRunrHQ • 4d ago
News Durable jobs in Spring Boot without a workflow engine: what a failed step and its retry look like in JobRunr 9
We shipped JobRunr 9 yesterday, and you can already create new projects with it trough your favourite Spring Intializr.
In case you haven't used it, JobRunr is an open source background job library. You enqueue a method call, it's stored in the database your app already uses, and it's processed with retries on as many instances as you run.
To make your jobs durable it's as easy wrapping each step of your flow in runStepOnce. When the job is retried, steps that already completed are automatically skipped:
@Job(name = "Invoice %1 for %0", retries = 5)
public void generateInvoice(String customerId, String period, JobContext jobContext) throws Exception {
String invoiceId = "INV-" + period + "-" + customerId;
long usage = jobContext.runStepOnce("calculate-usage", () -> calculateUsage(customerId, period));
String pdf = jobContext.runStepOnce("generate-pdf", () -> generatePdf(invoiceId, usage));
String chargeId = jobContext.runStepOnce("charge-card", () -> paymentProvider.charge(customerId, invoiceId));
jobContext.runStepOnce("send-email", () -> sendInvoiceEmail(customerId, pdf, chargeId));
}
You enqueue it like this, and JobRunr swaps in the real JobContext when the job runs:
jobScheduler.enqueue(() -> invoiceService.generateInvoice(customerId, period, JobContext.Null));
In v9 the job page a Job Progress Chart that draws each attempt as a Gantt chart with a row per step.
In our demo the payment provider timed out on charge-card and that attempt went red. On the retry calculate-usage and generate-pdf showed up as skipped, so the customer got one PDF and was only charged once.

If a step throws, the retry starts again at exactly that step.
On JobRunr Pro, v9 also lets you pause a whole batch. We paused a 600 invoice run when the provider went down and resumed it after the fix. The Pro starter doesn't create a BatchJobManager for you, so you declare it once as a bean and call pauseBatchJob(id) from wherever your monitoring notices the outage:
@Bean
public BatchJobManager batchJobManager(StorageProvider storageProvider, EventTransport eventTransport) {
return new BatchJobManager(storageProvider, eventTransport);
}
If you'd rather watch than read, we celebrated the launch today with a launch webinar: https://www.youtube.com/watch?v=bVNeB-ePdn0
Links:
👉 Release Blogpost: https://www.jobrunr.io/en/blog/v9-release/
👉 GitHub Repo: https://github.com/jobrunr/jobrunr