Skip to main content

Configuration

Ratchet is designed to run in Jakarta EE without static global configuration. CDI owns the runtime objects, Ratchet consumes one immutable RatchetOptions bean that your application produces, and store-specific resources remain normal CDI resources.

Required producer

Your application must produce exactly one @ApplicationScoped RatchetOptions bean. If no producer is found, CDI fails deployment with UnsatisfiedResolutionException and the scheduler never starts — this is the intended kill-switch for deployments that pull ratchet onto the classpath without wanting it active. There is no automatic fallback chain.

How Ratchet Bootstraps

Two CDI beans drive startup:

BeanRole
RatchetProducerProduces the internal scheduler components that combine options with injectable dependencies
RatchetLifecycleStarts pollers, recurring scheduling, recovery timers, retention tasks, and cluster wakeup listeners

On shutdown, components stop in reverse order and static caches are cleared to release classloader references.

beans.xml

Ratchet's CDI beans use annotated discovery. Jakarta CDI 4.0 works without a beans.xml in most applications. If you define one, keep discovery mode as annotated or all:

<!-- src/main/webapp/WEB-INF/beans.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
bean-discovery-mode="annotated">
</beans>

If you use @CircuitBreakerProtected, enable its interceptor:

<beans xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
https://jakarta.ee/xml/ns/jakartaee/beans_4_0.xsd"
bean-discovery-mode="annotated">
<interceptors>
<class>run.ratchet.ri.cdi.CircuitBreakerInterceptor</class>
</interceptors>
</beans>

RatchetOptions

You have two idiomatic ways to produce RatchetOptions. Pick one per application.

Option A: Environment-driven producer

For container deployments, the smallest viable producer reads RATCHET_* environment variables (and MicroProfile Config, if present) via RatchetOptionsFactory.fromEnvironment():

import run.ratchet.api.RatchetOptions;
import run.ratchet.api.RatchetOptionsFactory;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

@ApplicationScoped
public class SchedulerConfiguration {

@Produces
@ApplicationScoped
RatchetOptions ratchetOptions() {
return RatchetOptionsFactory.fromEnvironment();
}
}

With zero arguments, fromEnvironment() reads exclusively from MicroProfile Config and environment variables, applying compiled-in defaults for keys absent from those sources. See Source Chain below to overlay custom RatchetConfigSource implementations.

Option B: Programmatic producer

For applications that want compile-time-checked configuration without env-var round-tripping, use the builder directly:

import run.ratchet.api.RatchetOptions;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;

@ApplicationScoped
public class SchedulerConfiguration {

@Produces
@ApplicationScoped
RatchetOptions ratchetOptions() {
return RatchetOptions.builder()
.polling(p -> p.batchSize(100).minDelayMs(500).burstDelayMs(100))
.execution(e -> e
.maxConcurrency("SINGLE", 30)
.maxConcurrency("BATCH_CHILD", 50)
.useVirtualThreads(false))
.node(n -> n
.heartbeatIntervalSeconds(5)
.orphanGraceSeconds(30))
.maintenance(m -> m
.jobRetentionDays(30)
.logRetentionDays(14))
.payload(p -> p.maxPayloadKb(200).maxResultBytes(65536))
.build();
}
}

Only produce one unqualified RatchetOptions bean per application. Multiple unqualified beans are treated as a deployment error because Ratchet cannot know which set of options should own the runtime.

Keep the producer method @ApplicationScoped so configuration sources are read once at bootstrap rather than on every injection.

Store Resources

Store resources stay container-native. Ratchet does not expect static connection configuration.

For MongoDB:

@Produces
@ApplicationScoped
MongoDatabase ratchetMongoDatabase(MongoClient client) {
return client.getDatabase("ratchet");
}

For SQL stores using a named persistence unit:

@ApplicationScoped
public class OrdersRatchetEntityManagerProvider implements RatchetEntityManagerProvider {

@PersistenceContext(unitName = "orders-pu")
EntityManager entityManager;

@Override
public EntityManager getEntityManager() {
return entityManager;
}
}

Option Reference

Polling

Builder methodDefaultDescription
batchSize(int)50Number of jobs claimed per poll cycle
minDelayMs(long)2000Minimum polling interval
maxDelayMs(long)10000Maximum polling interval when idle
burstDelayMs(long)500Polling interval under high job volume
idleThreshold(int)3Empty polls before entering idle mode
deepIdleDelayMs(long)30000Polling interval in deep idle mode
claimHeadroomFactor(int)0Extra claim headroom over current worker capacity

Execution

Builder methodDefaultDescription
useVirtualThreads(boolean)falseUse virtual threads where supported
queueSize(int)100Reserved for custom executor implementations
maxConcurrency("SINGLE", int)20One-off job concurrency
maxConcurrency("RECURRING", int)5Recurring child concurrency
maxConcurrency("BATCH_CHILD", int)30Batch child concurrency
maxConcurrency("BATCH_PARENT", int)2Batch parent coordination concurrency
maxConcurrency("CHAIN_STEP", int)10Chain step concurrency
maxConcurrency("WORKFLOW_BRANCH", int)10Workflow branch concurrency
maxConcurrency("WORKFLOW_JOIN", int)10Workflow join concurrency
virtualThreadLimit(String, int)unsetBackpressure limit for virtual-thread execution
rateLimitPerMinute(String, int)unsetPer-execution-type rate limit

Node and Store

Builder methodDefaultDescription
nodeId(String)generatedStable logical node id
heartbeatIntervalSeconds(long)10Heartbeat interval
orphanGraceSeconds(long)60Grace window before orphan recovery
orphanScanIntervalMinutes(long)5Orphan recovery scan cadence
dynamicHeartbeatEnabled(boolean)trueAdjust heartbeat cadence by load
store.isolationCheckMode(FAIL)FAILSQL isolation validation behavior
store.priorityBoostIntervalMinutes(15)15Age-based priority boost interval; 0 disables boosting

Retention and Payloads

Builder methodDefaultDescription
dlqPurgeEnabled(boolean)trueEnable DLQ purging
dlqPurgeCron(String)0 0 2 * * ?DLQ purge schedule
dlqPurgeDays(long)90Dead-letter retention
jobArchiveEnabled(boolean)trueEnable job archiving
jobArchiveCron(String)0 0 1 * * ?Archive schedule
jobRetentionDays(long)90Completed-job retention
jobArchiveBatchSize(int)1000Jobs per archive pass
logPurgeEnabled(boolean)trueEnable job log purging
logPurgeCron(String)0 30 2 * * ?Log purge schedule
logRetentionDays(long)30Job log retention
payload.maxPayloadKb(int)100Serialized job payload cap
payload.maxResultBytes(long)65536Persisted result cap; 0 disables truncation

Recurring, Timeouts, and Circuit Breakers

Builder methodDefaultDescription
recurring.batchLimit(int)20Due recurring jobs spawned per cycle
recurring.pollMs(long)1000Recurring scheduler poll interval
recurring.maxPollMs(long)60000Maximum recurring scheduler backoff
recurring.startupGraceSeconds(long)60Grace period before orphaned recurring masters fire
recurring.convergenceWindowSeconds(long)0Startup cleanup convergence window
timeout.softTimeoutPercent(int)80Percent of SLA where warning fires
timeout.defaultSlaSeconds(long)1800Default job SLA timeout
circuitBreaker.enabled(boolean)trueMaster switch for built-in circuit breakers
circuitBreaker.profile(profile, builder)profile defaultsPer-profile thresholds

Source Chain

When you call RatchetOptionsFactory.fromEnvironment(...) from inside your producer, the factory reads from this chain in order of precedence (highest first):

  1. Caller-supplied RatchetConfigSource instances (passed as varargs)
  2. MicroProfile Config, when present
  3. Environment variables (canonical RATCHET_* names)
  4. Built-in defaults

This is not a runtime fallback — Ratchet only reads it when your producer explicitly asks it to. If you use Option B (programmatic) your producer skips the chain entirely.

To overlay a platform-specific config source on top of env + MP Config, pass it as a vararg:

@ApplicationScoped
public class SchedulerConfiguration {

@Inject PlatformRatchetConfigSource platformSource;

@Produces
@ApplicationScoped
RatchetOptions ratchetOptions() {
return RatchetOptionsFactory.fromEnvironment(platformSource);
}
}

@ApplicationScoped
public class PlatformRatchetConfigSource implements RatchetConfigSource {

@Override
public Optional<String> get(String propertyName, String environmentVariable) {
return platformConfig.lookup(propertyName)
.or(() -> platformConfig.lookup(environmentVariable));
}
}

The env lookup recognizes canonical ratchet.* property names and RATCHET_* environment variable names.

SPI Overrides

Override SPIs with CDI alternatives:

import static jakarta.interceptor.Interceptor.Priority.APPLICATION;

@Alternative
@Priority(APPLICATION)
@ApplicationScoped
public class AppClassPolicy implements ClassPolicy {

@Override
public boolean isAllowed(String className) {
return className.startsWith("com.example.");
}
}
SPIDefaultWhat to override
RetryPolicyUses job retry optionsCustom retry/no-retry decisions
ResilienceStrategyBuilt-in circuit breakerExternal resilience library
ClassPolicyEmpty allowlist; deployment fails fastApplication package allowlist
ErrorSanitizerCommon PII and credential redactionDomain-specific redaction
ExecutionTuningProviderRatchetOptions-backed settingsCustom concurrency logic
PollingStrategyProviderAdaptive polling strategyCustom poll cadence
ResultPersistenceStrategyJSON result metadata with size capCustom return-value persistence
CircuitBreakerConfigProviderRatchetOptions-backed profile settingsCustom built-in breaker thresholds
ClusterCoordinatorNo-opCross-node wakeups
StartupCoordinatorStore-backed startup leaseCustom startup coordination
MetricsCollectorNo-opMicrometer or another metrics backend
ExecutorProviderJakarta Concurrency managed executorsCustom executor ownership
RatchetEntityManagerProviderStore default providerSpecific SQL persistence unit
NodeIdentityProviderHostname-based with heartbeatCloud-specific node identity

ClassPolicy

The default PackagePrefixClassPolicy has an empty allowlist. Ratchet refuses to start until you provide a real allowlist, because jobs execute application code by design.

For demos and tests only, you can opt out through options:

RatchetOptions.builder()
.security(s -> s.allowEmptyClassPolicy(true))
.build();

In that mode the default policy still rejects every target class. Install a real ClassPolicy before running jobs.

What's Next

  • Batch processing -- Build parallel batch jobs with progress tracking and streaming pipelines
  • Recurring jobs -- Use @Recurring annotations and programmatic cron scheduling
  • Circuit breaker -- Protect external service calls with @CircuitBreakerProtected
  • Custom stores -- Use the TCK to validate your own persistence backend