Skip to content

Basic Concepts ​

Understanding the core building blocks helps before reading the API. Ratchet has a small set of concepts that compose to cover everything from one-off background tasks to multi-step workflows.

Jobs ​

A job is a unit of work that Ratchet persists and executes. Concretely, it is a serialized lambda: a method reference stored in the database and invoked later by a worker thread.

java
// This lambda IS the job
scheduler.enqueueNow(() -> sendWelcomeEmail(userId));

Every job has:

PropertyDescription
IDAuto-assigned UUIDv7 identifier
StatusCurrent lifecycle state (PENDING, RUNNING, SUCCEEDED, FAILED, CANCELED, PAUSED, WAITING)
PriorityExecution ordering: LOWEST, LOW, NORMAL, HIGH, CRITICAL
PayloadThe serialized lambda or method reference
ParametersKey-value pairs available at execution time via JobContext
TagsLabels for grouping and bulk operations

Jobs progress through a lifecycle from creation to completion (or failure).

Job Types ​

Ratchet supports several execution patterns:

  • Single: execute once, right now or after a delay
  • Recurring: execute on a cron schedule, creating child instances each time
  • Batch parent: coordinate parallel execution of child jobs
  • Batch child: individual items within a batch
  • Chained: execute after another job completes (success, failure, or conditional)

See Job Types for details on each pattern.

Schedules ​

A schedule determines when a job runs:

java
// Immediate
scheduler.enqueueNow(() -> doWork());

// Delayed
scheduler.schedule(Duration.ofMinutes(30), () -> doWork());

// Cron (recurring)
scheduler.scheduleRecurring("0 0 2 * * ?", ZoneId.of("UTC"), () -> doWork());

Recurring jobs use Quartz-compatible cron expressions. The schedule is evaluated by the poller, which checks for due jobs on a configurable interval.

Builders ​

Ratchet uses the fluent builder pattern to configure jobs before submission. Every enqueue, schedule, and scheduleRecurring call returns a builder:

java
scheduler.enqueue(() -> processInvoice(invoiceId))
    .withPriority(JobPriority.HIGH)    // Execution priority
    .withMaxRetries(3)                 // Retry on failure
    .withBackoff(BackoffPolicy.EXPONENTIAL, Duration.ofSeconds(5))
    .withTimeout(Duration.ofMinutes(10))
    .withTags("billing", "invoices")
    .withParam("invoiceId", String.valueOf(invoiceId))
    .withBusinessKey("invoice-" + invoiceId)
    .submit();                         // Persists and returns a JobHandle

The builder is not submitted until you call .submit(). Use .immediate() beforehand to flag the job for an immediate cross-node wakeup hint. See the JobBuilder API for all options.

JobHandle ​

A JobHandle is returned when you submit a job. It gives you the job's ID for tracking:

java
JobHandle handle = scheduler.enqueueNow(() -> doWork());
UUID jobId = handle.id();

JobContext ​

The JobContext is the runtime context available inside a running job. It provides access to the job's ID, parameters, and a structured logger:

java
scheduler.enqueue(() -> {
    JobContext ctx = JobContext.current();
    String orderId = ctx.param("orderId");
    ctx.logger().info("Processing order " + orderId);
    processOrder(orderId);
}).withParam("orderId", "12345")
  .submit();

For annotation-based jobs, you can accept the context as a parameter:

java
@Recurring(cron = "0 0 * * * ?")
public void hourlySync(JobContext ctx) {
    ctx.logger().info("Starting sync for job " + ctx.jobId());
}

JobResult ​

A JobResult represents the outcome of a job execution. Jobs that return void produce a success result automatically. For richer outcomes, your job can return a JobResult explicitly:

java
// Implicit success (void return)
scheduler.enqueueNow(() -> sendEmail(to, body));

// Explicit result for workflow branching
JobResult.success(invoiceTotal);
JobResult.failure("Payment declined", exception);

Results drive workflow branching: downstream jobs can inspect the parent result and conditionally execute.

Stores ​

A store is the persistence backend that holds jobs, execution history, locks, and node registrations. Ratchet separates the store into discrete SPI interfaces:

Store SPIResponsibility
JobCrudStoreCRUD operations on jobs
JobClaimStoreClaiming due jobs for execution (SKIP LOCKED on SQL stores, atomic updates on MongoDB)
JobAuditStoreExecution history and per-job log persistence (optional capability)
ArchiveStoreMoving completed jobs to archive
NodeStoreCluster node heartbeats
LockStoreDistributed advisory locks

You choose a store implementation as a Maven dependency:

xml
<!-- Pick one -->
<artifactId>ratchet-store-postgresql</artifactId>
<artifactId>ratchet-store-mysql</artifactId>
<artifactId>ratchet-store-oracle</artifactId>
<artifactId>ratchet-store-sqlserver</artifactId>
<artifactId>ratchet-store-mongodb</artifactId>

See Persistence for how stores work internally.

CDI Integration ​

Ratchet is CDI-native. The core entry point, JobSchedulerService, is a CDI bean that you inject:

java
@Inject
JobSchedulerService scheduler;

No factory classes, no static initializers, no XML configuration. CDI discovery finds the Ratchet beans automatically when the modules are on the classpath.

Annotations ​

The @Recurring annotation lets you declare cron-scheduled jobs on CDI bean methods:

java
@ApplicationScoped
public class MaintenanceJobs {

    @Recurring(cron = "0 0 3 * * ?", id = "nightly-cleanup")
    public void nightlyCleanup() {
        // Runs every night at 3 AM
    }
}

SPI Overrides ​

Many Ratchet behaviors are pluggable via CDI alternatives. To replace the default retry policy, for example, produce a CDI bean that implements RetryPolicy:

java
@ApplicationScoped
public class MyRetryPolicy implements RetryPolicy {
    @Override
    public boolean shouldRetry(int attempt, Throwable cause) {
        return true;
    }

    @Override
    public Duration getDelay(int attempt) {
        return Duration.ZERO;
    }
}

CDI selects your implementation over the default. See SPI Interfaces for the full list of extension points.

Events ​

Ratchet publishes CDI events for every lifecycle transition. You can observe them with standard CDI:

java
public void onJobCompleted(@Observes JobCompletedEvent event) {
    log.info("Job " + event.getJobId() + " completed in " + event.getExecutionTimeMs() + "ms");
}

Or register a programmatic listener:

java
scheduler.addEventListener(event -> {
    if (event instanceof JobFailedEvent failed) {
        alertOps(failed.getJobId(), failed.getErrorMessage());
    }
});

See the Event System for all event types.

What's next ​

Now that you know the vocabulary, the best way to learn is to build something:

  • Your First Job -- Build a complete job with retries, callbacks, and monitoring
  • Configuration -- Tune polling intervals, thread pools, and timeouts
  • Job Lifecycle -- Understand every state transition in detail