Skip to main content

Event System Reference

Observing and reacting to job lifecycle events. Ratchet publishes events at every significant state transition, enabling monitoring, alerting, and custom integrations.

Listening to Events

CDI Observers

Use CDI @Observes for type-safe event handling:

@ApplicationScoped
public class JobMonitor {

public void onJobStarted(@Observes JobStartedEvent event) {
log.info("Job {} started on node {}", event.getJobId(), event.getNodeId());
}

public void onJobFailed(@Observes JobFailedEvent event) {
log.error("Job {} failed (attempt {}): {}",
event.getJobId(), event.getRetryAttempt(), event.getErrorMessage());
}

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

Programmatic Listeners

Register listeners via JobSchedulerService.addEventListener():

scheduler.addEventListener(event -> {
if (event instanceof JobFailedEvent failed) {
metrics.counter("jobs.failed").increment();
} else if (event instanceof JobCompletedEvent completed) {
metrics.timer("jobs.duration")
.record(completed.getExecutionTimeMs(), TimeUnit.MILLISECONDS);
}
});

Event Base Class

All job lifecycle events extend AbstractJobSchedulerEvent:

public abstract class AbstractJobSchedulerEvent implements Serializable {
public UUID getJobId()
public String getBusinessKey()
public JobType getJobType()
public JobPriority getPriority()
public String getNodeId()
public Instant getTimestamp()
}
MethodReturn TypeDescription
getJobId()UUIDUUIDv7 database ID of the job that triggered this event
getBusinessKey()StringHuman-readable business key (may be null)
getJobType()JobTypeJob category: SINGLE, BATCH, CHAIN, WORKFLOW, RECURRING, SYSTEM
getPriority()JobPriorityPriority level of the job
getNodeId()StringIdentifier of the cluster node that processed this job
getTimestamp()InstantWhen this event was created

Job Lifecycle Events

JobStartedEvent

Fired when a job begins execution.

public class JobStartedEvent extends AbstractJobSchedulerEvent

No additional fields beyond the base class.

public void onStarted(@Observes JobStartedEvent event) {
log.info("[{}] Job {} ({}) started on node {}",
event.getTimestamp(), event.getJobId(),
event.getJobType(), event.getNodeId());
}

JobCompletedEvent

Fired when a job completes successfully.

public class JobCompletedEvent extends AbstractJobSchedulerEvent {
public Long getExecutionTimeMs()
}
MethodReturn TypeDescription
getExecutionTimeMs()LongExecution duration in milliseconds (may be null)
public void onCompleted(@Observes JobCompletedEvent event) {
metrics.timer("job.duration").record(
event.getExecutionTimeMs(), TimeUnit.MILLISECONDS);
}

JobFailedEvent

Fired when a job fails after its final attempt (all retries exhausted, or marked @DoNotRetry).

public class JobFailedEvent extends AbstractJobSchedulerEvent {
public String getErrorMessage()
public Integer getRetryAttempt()
}
MethodReturn TypeDescription
getErrorMessage()StringError message from the failure
getRetryAttempt()IntegerFinal retry attempt number
public void onFailed(@Observes JobFailedEvent event) {
log.error("Job {} failed on attempt {}: {}",
event.getJobId(), event.getRetryAttempt(), event.getErrorMessage());
}

JobRetryingEvent

Fired when a job is being retried after a failure.

public class JobRetryingEvent extends AbstractJobSchedulerEvent {
public String getErrorMessage()
public Integer getRetryAttempt()
public Instant getScheduledTime()
}
MethodReturn TypeDescription
getErrorMessage()StringError from the failure that triggered the retry
getRetryAttempt()IntegerCurrent retry attempt number
getScheduledTime()InstantWhen the retry is scheduled (after backoff)
public void onRetrying(@Observes JobRetryingEvent event) {
log.warn("Job {} retrying (attempt {}), next run at {}",
event.getJobId(), event.getRetryAttempt(), event.getScheduledTime());
}

JobCancellingEvent

Fired when a job cancellation is initiated (before the state transition completes).

public class JobCancellingEvent extends AbstractJobSchedulerEvent
public String getPreviousStatus()
public Long getExecutionTimeMs()
MethodReturn TypeDescription
getPreviousStatus()StringStatus before cancellation
getExecutionTimeMs()LongExecution duration in milliseconds when known

JobCancelledEvent

Fired when a job cancellation is confirmed.

public class JobCancelledEvent extends AbstractJobSchedulerEvent
public String getPreviousStatus()
public Long getExecutionTimeMs()
MethodReturn TypeDescription
getPreviousStatus()StringStatus before cancellation
getExecutionTimeMs()LongExecution duration in milliseconds when known
public void onCancelled(@Observes JobCancelledEvent event) {
log.info("Job {} canceled", event.getJobId());
}

JobPausedEvent

Fired when a job is paused.

public class JobPausedEvent extends AbstractJobSchedulerEvent

No additional fields.

JobResumedEvent

Fired when a paused job is resumed.

public class JobResumedEvent extends AbstractJobSchedulerEvent

No additional fields.

JobDlqEvent

Fired when a job is moved to the Dead Letter Queue after exhausting all retries.

public class JobDlqEvent extends AbstractJobSchedulerEvent {
public String getErrorMessage()
public Integer getRetryAttempt()
}
MethodReturn TypeDescription
getErrorMessage()StringError from the final failure
getRetryAttempt()IntegerTotal retry attempts before DLQ
public void onDlq(@Observes JobDlqEvent event) {
alertService.sendDlqAlert(event.getJobId(), event.getErrorMessage());
metrics.counter("jobs.dlq").increment();
}

Batch Events

BatchCompletingEvent

Fired when a batch is finishing (the last child job is completing).

public class BatchCompletingEvent extends AbstractJobSchedulerEvent
public Integer getTotalItems()
public Integer getCompletedItems()
public Integer getFailedItems()
MethodReturn TypeDescription
getTotalItems()IntegerTotal child jobs in the batch
getCompletedItems()IntegerSuccessfully completed child jobs so far
getFailedItems()IntegerFailed child jobs so far

BatchCompletedEvent

Fired when a batch is fully complete (all child jobs have finished).

public class BatchCompletedEvent extends AbstractJobSchedulerEvent {
public Integer getTotalItems()
public Integer getCompletedItems()
public Integer getFailedItems()
}
MethodReturn TypeDescription
getTotalItems()IntegerTotal child jobs in the batch
getCompletedItems()IntegerSuccessfully completed child jobs
getFailedItems()IntegerFailed child jobs
public void onBatchCompleted(@Observes BatchCompletedEvent event) {
double successRate = event.getTotalItems() > 0
? (double) event.getCompletedItems() / event.getTotalItems()
: 1.0;

log.info("Batch {} complete: {}/{} succeeded, {} failed",
event.getJobId(), event.getCompletedItems(),
event.getTotalItems(), event.getFailedItems());

if (event.getFailedItems() > 0) {
alertService.batchPartialFailure(event.getJobId(), event.getFailedItems());
}
}

Chain Event Types

ChainStartedEvent

Fired when a workflow chain begins execution.

public class ChainStartedEvent extends AbstractJobSchedulerEvent {
public UUID getParentJobId()
}
MethodReturn TypeDescription
getParentJobId()UUIDUUIDv7 ID of the parent job that owns this chain

ChainCompletedEvent

Fired when a workflow chain succeeds.

public class ChainCompletedEvent extends AbstractJobSchedulerEvent {
public UUID getParentJobId()
}

ChainFailedEvent

Fired when a workflow chain fails.

public class ChainFailedEvent extends AbstractJobSchedulerEvent {
public UUID getParentJobId()
public String getErrorMessage()
}
public void onChainFailed(@Observes ChainFailedEvent event) {
log.error("Chain for parent job {} failed: {}",
event.getParentJobId(), event.getErrorMessage());
}

Workflow and Observability Events

WorkflowBranchTriggeredEvent

Fired when a workflow condition matches and a branch is triggered.

public class WorkflowBranchTriggeredEvent extends AbstractJobSchedulerEvent
public String getBranchCondition()
public String getNextJobId()
MethodReturn TypeDescription
getBranchCondition()StringDescription of the branch condition that matched
getNextJobId()StringString form of the child job ID scheduled for the branch

PerformanceMetricsEvent

System-level performance metrics snapshot. Unlike other events, this does not extend AbstractJobSchedulerEvent because it represents aggregate system metrics, not a per-job event.

public record PerformanceMetricsEvent(
Map<String, Object> performanceData
) implements Serializable

The performanceData map typically contains:

KeyTypeDescription
queueDepthNumberPending jobs in the queue
readyJobsNumberJobs ready for immediate execution
oldestJobAgeNumberAge of oldest pending job (seconds)
processingRateNumberJobs processed per minute
successRateNumberPercentage of successful completions
avgDurationNumberAverage execution duration (ms)
threadUtilizationNumberThread pool utilization percentage
activeThreadsNumberCurrently active worker threads
queuedTasksNumberTasks waiting in thread pool queue
cpuUsageNumberCPU utilization
memoryUsageNumberMemory usage (bytes)
memoryPercentNumberMemory as percentage of available
public void onMetrics(@Observes PerformanceMetricsEvent event) {
Map<String, Object> data = event.performanceData();
dashboard.update("queue_depth", data.get("queueDepth"));
dashboard.update("processing_rate", data.get("processingRate"));
dashboard.update("success_rate", data.get("successRate"));
}

Example: Comprehensive Monitoring

@ApplicationScoped
public class SchedulerMonitoring {

@Inject Logger log;
@Inject MeterRegistry metrics;
@Inject AlertingService alerts;

public void onStarted(@Observes JobStartedEvent e) {
metrics.counter("jobs.started",
"type", e.getJobType().name(),
"priority", e.getPriority().name()).increment();
}

public void onCompleted(@Observes JobCompletedEvent e) {
metrics.counter("jobs.completed", "type", e.getJobType().name()).increment();
if (e.getExecutionTimeMs() != null) {
metrics.timer("jobs.duration", "type", e.getJobType().name())
.record(e.getExecutionTimeMs(), TimeUnit.MILLISECONDS);
}
}

public void onFailed(@Observes JobFailedEvent e) {
metrics.counter("jobs.failed", "type", e.getJobType().name()).increment();
}

public void onDlq(@Observes JobDlqEvent e) {
metrics.counter("jobs.dlq", "type", e.getJobType().name()).increment();
alerts.notify("Job " + e.getJobId() + " moved to DLQ: " + e.getErrorMessage());
}

public void onBatchCompleted(@Observes BatchCompletedEvent e) {
metrics.gauge("batch.success_rate",
(double) e.getCompletedItems() / Math.max(e.getTotalItems(), 1));
}

public void onMetrics(@Observes PerformanceMetricsEvent e) {
e.performanceData().forEach((key, value) -> {
if (value instanceof Number num) {
metrics.gauge("scheduler." + key, num.doubleValue());
}
});
}
}

See Also