Skip to main content

RetryPolicy

Configurable retry policies for each step.


Policy Types

exponential()

Exponential backoff with jitter. Recommended for most cases.

.retry(exponential(3, Duration.ofSeconds(1)))
// Attempts: 1s, 2s, 4s (with jitter)
ParameterTypeDescription
maxAttemptsintMaximum number of attempts
initialDelayDurationInitial delay between attempts

With custom limits

.retry(exponential()
.maxAttempts(5)
.initialDelay(Duration.ofSeconds(1))
.maxDelay(Duration.ofMinutes(5))
.multiplier(2.0)
.jitterFactor(0.1))

fixed()

Fixed delay between attempts.

.retry(fixed(3, Duration.ofSeconds(5)))
// Attempts: 5s, 5s, 5s
ParameterTypeDescription
maxAttemptsintMaximum number of attempts
delayDurationFixed delay between attempts

infinite()

Infinite retry. Use only for operations that must eventually succeed.

.retry(infinite())
// Attempts: 1min, 2min, 4min, 8min... (up to maxDelay)

With limits

.retry(infinite()
.initialDelay(Duration.ofMinutes(1))
.maxDelay(Duration.ofHours(1)))
Warning

Use infinite() only for idempotent operations such as notifications or cache updates.


none()

No retry. Failure on the first attempt results in compensation.

.retry(none())
// Fails immediately

Comparison

PolicyAttemptsDelayUse
exponential()LimitedGrowingExternal APIs, databases
fixed()LimitedConstantSimple operations
infinite()UnlimitedGrowingNotifications, webhooks
none()1-Operations that cannot fail

Global Configuration

Define a default policy in application.yml:

sagaweaw:
retry:
default-policy: exponential
max-attempts: 3
initial-delay: 1s
max-delay: 30s
multiplier: 2.0
jitter-factor: 0.1

Steps without an explicit .retry() will use the global policy.


Full Example

@Override
public void define(SagaBuilder<PaymentContext> builder) {
builder
// Internal API: fast retry
.step("validate")
.invoke(this::validate)
.retry(exponential(3, Duration.ofMillis(100)))

// External API: slower retry
.step("charge-stripe")
.invoke(this::chargeStripe)
.compensate(this::refundStripe)
.retry(exponential()
.maxAttempts(5)
.initialDelay(Duration.ofSeconds(2))
.maxDelay(Duration.ofSeconds(30)))

// Notification: must always succeed
.step("send-receipt")
.invoke(this::sendReceipt)
.retry(infinite()
.initialDelay(Duration.ofMinutes(1))
.maxDelay(Duration.ofHours(1)))

.build();
}


Kotlin

The sagaweaw-kotlin module provides kotlin.time.Duration support so you don't need Duration.ofSeconds():

// Java
.retryPolicy(RetryPolicy.exponential(3, Duration.ofSeconds(5)))

// Kotlin
retry(exponentialRetry(3, 5.seconds))
retry(fixedRetry(3, 2.seconds))
retry(infiniteRetry(1.minutes))

See the Kotlin guide → for setup.