JobRunr with Spring Boot: Guide with & without DB

JobRunr is a powerful, distributed background job processing library for Java and Kotlin. It integrates seamlessly with Spring Boot, allowing developers to execute jobs asynchronously with minimal configuration. A key feature of JobRunr is its ability to function with or without a database, making it highly flexible depending on project requirements.

This article will explore how to use JobRunr in Spring Boot in both modes: with a database for persistence and durability, and without a database for in-memory job execution. It covers only the free feature of JobRunr.

1. What is JobRunr?

JobRunr is a Java library for background processing and job scheduling. It allows developers to offload long-running or time-consuming tasks to a separate worker thread, ensuring the main application remains responsive.

Key Features of JobRunr

  • Asynchronous job execution
  • Supports Spring Boot out of the box
  • Can be used with or without a database
  • Distributed job processing
  • Retry mechanism for failed jobs
  • User-friendly job dashboard

2. Setting Up JobRunr in Spring Boot

To use JobRunr in a Spring Boot application, developers must add the required dependencies.

Add JobRunr Dependency

Include the JobRunr starter dependency in your pom.xml:

<!-- https://mvnrepository.com/artifact/org.jobrunr/jobrunr -->
<dependency>
    <groupId>org.jobrunr</groupId>
    <artifactId>jobrunr</artifactId>
    <version>7.4.1</version>
</dependency>

3. Using JobRunr Without a Database (In-Memory Mode)

JobRunr supports an in-memory mode, which means jobs are not persisted and are lost when the application restarts. This mode is helpful for development, testing, or lightweight applications.

Configuring JobRunr Without a Database

By default, if no database is configured, JobRunr runs in-memory.

import org.jobrunr.configuration.JobRunr;
import org.jobrunr.scheduling.JobScheduler;
import org.jobrunr.storage.InMemoryStorageProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JobRunrConfig {
    @Bean
    public JobScheduler initJobRunr(InMemoryStorageProvider storageProvider) {
        return JobRunr.configure()
                .useStorageProvider(storageProvider)
                .useBackgroundJobServer()
                .useDashboard()
                .initialize()
                .getJobScheduler();
    }

    @Bean
    public InMemoryStorageProvider storageProvider() {
        return new InMemoryStorageProvider();
    }

}

Creating and Enqueuing a Job

import lombok.extern.slf4j.Slf4j;
import org.jobrunr.scheduling.JobScheduler;
import org.springframework.stereotype.Service;

// Example service with a background job
@Service
@Slf4j
public class ExampleJobService {

    public void performBackgroundTask(String taskData) {
        log.info("Performing background task with data: {}", taskData);
        // Simulate some work
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        log.info("Background task completed for: {}", taskData);
    }

    public void scheduleRecurringJob(JobScheduler jobScheduler) {
        // Schedule a recurring job that runs every minute
        jobScheduler.scheduleRecurrently(
                "*/1 * * * *", // Cron expression (every minute for demonstration)
                () -> performBackgroundTask("Recurring Task")
        );
    }

    public void enqueueSimpleJob(JobScheduler jobScheduler) {
        // Enqueue a one-time background job
        jobScheduler.enqueue(() -> performBackgroundTask("One-time Task"));
    }
}

Creating a RESTful controller

import com.example.jobrunr.service.ExampleJobService;
import org.jobrunr.scheduling.JobScheduler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class JobController {
    private final JobScheduler jobScheduler;
    private final ExampleJobService jobService;

    @Autowired
    public JobController(JobScheduler jobScheduler, ExampleJobService jobService) {
        this.jobScheduler = jobScheduler;
        this.jobService = jobService;
    }

    @PostMapping("/trigger-job")
    public ResponseEntity<String> triggerJob() {
        // Enqueue a job
        jobService.enqueueSimpleJob(jobScheduler);
        return ResponseEntity.ok("Job enqueued successfully");
    }

    @PostMapping("/schedule-recurring-job")
    public ResponseEntity<String> scheduleRecurringJob() {
        // Schedule a recurring job
        jobService.scheduleRecurringJob(jobScheduler);
        return ResponseEntity.ok("Recurring job scheduled successfully");
    }
}

Pros and Cons of In-Memory Mode

In-Memory Mode

4. Using JobRunr with a Database (Persistent Mode)

Database persistence is recommended for production applications to ensure jobs are not lost upon application restarts or crashes.

Supported Databases

JobRunr supports multiple databases, including:

  • PostgreSQL
  • MySQL
  • MariaDB
  • SQL Server
  • H2 (for testing)

Set up PostgreSQL Database Server

Database Configuration in application.properties

For PostgreSQL:

spring.datasource.url=jdbc:postgresql://localhost:5432/jobrunrdb
spring.datasource.username=postgres
spring.datasource.password=secret
spring.datasource.driver-class-name=org.postgresql.Driver

Include the starter dependency in your pom.xml:

  <!-- https://mvnrepository.com/artifact/org.jobrunr/jobrunr-spring-boot-3-starter -->
  <dependency>
   <groupId>org.jobrunr</groupId>
   <artifactId>jobrunr-spring-boot-3-starter</artifactId>
   <version>7.4.1</version>
  </dependency>

  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>

  <dependency>
   <groupId>org.postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <scope>runtime</scope>
  </dependency>

Configuring JobRunr to Use the Database

@Configuration
public class JobRunrConfig {

    @Bean
    public JobScheduler initJobRunr(DataSource dataSource) {
        return JobRunr.configure()
                .useStorageProvider(new PostgresStorageProvider(dataSource, StorageProviderUtils.DatabaseOptions.CREATE))
                .useBackgroundJobServer()
                .useDashboard()
                .initialize()
                .getJobScheduler();
    }
}

Pros and Cons of Database Mode

Database Mode

Table Jobrunr in Database

Table of Jobrunr

Example Query

-- Check background job servers
SELECT * FROM public.jobrunr_backgroundjobservers;

-- Check job status
SELECT id, state, createdAt, scheduledAt 
FROM public.jobrunr_jobs 
ORDER BY createdAt DESC;

-- Check recurring jobs
SELECT id, cronExpression, nextRun 
FROM public.jobrunr_recurring_jobs;

-- Check migrations
SELECT * FROM public.jobrunr_migrations;

JobRunr Database Tables Explanation

1. public.jobrunr_backgroundjobservers This table tracks the background job servers in the system.

Columns:

  • idUnique identifier for each background job server instance
  • nameName of the background job server
  • worker_pool_sizeNumber of worker threads in the job server
  • system_total_memoryTotal system memory available
  • free_memoryAvailable free memory
  • max_memoryMaximum memory allocation
  • is_leaderBoolean indicating if this server is the leader
  • last_hearthbeat Timestamp of the last heartbeat signal
  • started_at Timestamp when the server was started
  • server_statusCurrent status of the server (e.g., RUNNING, STOPPED)

2. public.jobrunr_jobs Stores all individual jobs processed by JobRunr.

Columns:

  • idUnique job identifier
  • versionVersion number for optimistic locking
  • job_signatureUnique signature of the job method and parameters
  • stateCurrent state of the job (ENQUEUED, PROCESSING, SUCCEEDED, FAILED)
  • created_at Timestamp when the job was created
  • updated_at Timestamp of last update
  • scheduled_atScheduled execution time
  • enqueued_atTime when the job was enqueued
  • processing_atTime when the job started processing
  • failed_atTime of job failure
  • succeeded_atTime of successful job completion
  • job_jsonJSON representation of the job details
  • job_state_jsonJSON with detailed job state information

3. public.jobrunr_metadata Stores miscellaneous metadata for JobRunr.

Columns:

  • keyUnique identifier for the metadata entry
  • valueValue associated with the key
  • created_at: Timestamp when metadata was created
  • updated_at: Timestamp of last update

4. public.jobrunr_migrations Tracks database schema migrations for JobRunr.

Columns:

  • scriptName of the migration script
  • executed_at Timestamp when the migration was executed
  • successBoolean indicating if migration was successful
  • script_contentFull content of the migration script

5. public.jobrunr_recurring_jobs Stores information about recurring jobs.

Columns:

  • idUnique identifier for the recurring job
  • versionVersion for optimistic locking
  • job_signatureSignature identifying the recurring job method
  • cronCron expression defining job recurrence
  • next_run Timestamp of the next scheduled job execution
  • created_at Timestamp when the recurring job was created
  • updated_at Timestamp of last update
  • job_jsonJSON representation of the recurring job details
  • stateCurrent state of the recurring job

Test JobRunr via Postman

POST to /trigger-job to enqueue a one-time job

request and response
c.e.jobrunr.service.ExampleJobService    : Performing background task with data: One-time Task
c.e.jobrunr.service.ExampleJobService    : Background task completed for: One-time Task
JobRunr Dashboard

POST to /schedule-recurring-job to schedule a recurring job

request and response
c.e.jobrunr.service.ExampleJobService    : Performing background task with data: Recurring Task
c.e.jobrunr.service.ExampleJobService    : Background task completed for: Recurring Task
Recurring Jobs

5. JobRunr Dashboard

One of JobRunr’s best features is its web-based dashboard that provides insights into job execution.

Enabling the Dashboard

Add this configuration application.properties:

jobrunr.dashboard.enabled=true
jobrunr.dashboard.port=8000

Start the application and visit:

http://localhost:8000/

The JobRunr dashboard shows active, failed, and scheduled jobs.

Dashboard
Background Job Servers
server info

6. Scheduling Recurring Jobs

Developers can also schedule recurring jobs using JobRunr.

Example: Run a Job Every Hour

@Service
@Slf4j
public class RecurringJobService {

    private final JobScheduler jobScheduler;

    public RecurringJobService(JobScheduler jobScheduler) {
        this.jobScheduler = jobScheduler;
    }

    @PostConstruct
    public void scheduleRecurringJob() {
        jobScheduler.scheduleRecurrently(Cron.hourly(), () -> log.info("Recurring job running..."));
    }
}

7. Handling Job Failures & Retries

JobRunr automatically retries failed jobs.

Example: Handling Exceptions in Jobs

@Service
public class FaultTolerantJobService {

    private final JobScheduler jobScheduler;

    public FaultTolerantJobService(JobScheduler jobScheduler) {
        this.jobScheduler = jobScheduler;
    }

    public void scheduleFailingJob() {
        jobScheduler.enqueue(() -> {
            throw new RuntimeException("Simulating a job failure");
        });
    }
}

JobRunr will retry the failed job up to 10 times before marking it as permanently failed.

JobRunr Pro

JobRunr Pro offers advanced features that enhance job processing, especially in production environments.

Why JobRunr Pro?

  • JobRunr Pro’s advanced features can significantly improve reliability and performance for production environments with high numbers of jobs.
  • The ability to define custom retry policies and batch jobs provides greater flexibility and control over job processing.
  • The enhanced dashboard and dedicated support make monitoring and managing JobRunr in production more manageable.

Comparing JobRunr with Other Job Schedulers

Comparing

JobRunr stands out for its simplicity and seamless Spring Boot integration, making it an excellent choice for background job execution.

9. When to Use JobRunr Without vs. With a Database

Without vs. With a Database

10. Conclusion

JobRunr is a powerful, developer-friendly background job processing library for Spring Boot applications.

  • Use in-memory mode for quick jobs and testing.
  • Use database mode for production-grade applications with persistence.
  • Take advantage of JobRunr’s dashboard for job monitoring.

Integrating JobRunr into your Spring Boot projects allows developers to manage background jobs with minimal effort.

Leave a Comment

Your email address will not be published. Required fields are marked *