Skip to main content
The @RestartPolicy annotation (since 1.0.0-rc.7) tunes the managed restart loop of a stream: how long FlowWarden waits between resubscription attempts after the stream died at runtime, and whether it ever gives up. Place it on a class alongside @ChangeStream. Without the annotation, every stream uses the defaults below — which are exactly the behaviour FlowWarden had before the annotation existed. Nothing changes for an application that does not use it.

What the restart loop does

A stream can die at runtime, after it started successfully: the cursor is closed by a network outage or a primary stepdown, the server returns a non-resumable error, or the watched collection is dropped or renamed (an invalidate event). In every case FlowWarden:
  1. evicts the stream’s state and reports it stopped — onStreamStopped(CRASHED) on the metrics provider;
  2. hands the stream to the restart loop, which re-enters the full startup path (resume cascade included) after a backoff delay;
  3. on success, reports onStreamRestarted(streamName, attempt, cause) and resets the attempt counter;
  4. on a transient failure (server still down, cascade probe failure), schedules the next attempt with a longer delay.
A HistoryLostException escaping an attempt — the resume cascade escalated to onHistoryLost = FAIL — is always terminal: the loop stops for that stream, a second CRASHED signal carries the exception, and under SINGLE_LEADER the lease is released so a standby can take over. A manual startStream (actuator or FlowWarden API) during the backoff wins over the loop: the installed stream is left alone. Restart attempts run on one dedicated thread per manager (fw-stream-restart / fw-stream-restart-reactive), so a slow resubscription never delays heartbeat probes or checkpoint flushes of healthy streams.

Attributes

Two deliberate differences from @RetryPolicy:
  • maxAttempts counts resubscription attempts — there is no “initial attempt” to include — and 0 means unlimited, which is the default. @RetryPolicy.maxAttempts must be >= 1.
  • jitter defaults to false, so a stream without the annotation keeps the deterministic cadence it always had. @RetryPolicy.jitter defaults to true.

Backoff Algorithm

Same shape as @RetryPolicy:
With the defaults the delays are: The counter resets on the first successful resubscription: a stream that dies again later starts over at attempt 1.

Giving up

When maxAttempts is set and exhausted, the stream takes the same terminal path as a history loss:
  • the stream is reported stopped — onStreamStopped(CRASHED) with the original death cause;
  • the loop stops for that stream — no further attempts, no further WARN lines;
  • under SINGLE_LEADER, the lease is released so another instance can take over (and surface the same failure if the cause is global);
  • the stream stays stopped until an operator restarts it — POST /actuator/flowwarden/{streamName}/restart or FlowWarden.startStream(..).
The give-up is immediate once the ceiling is crossed — it does not wait for another backoff.
Every death notification consumes one attempt, not only failed startStream calls. In particular, a reactive subscription that terminates synchronously right after a successful resubscription re-arms the loop and counts as a consumed attempt.

When to set it

Validation

Validated fail-fast at startup, on both the annotation path and the registration API:
  • maxAttempts >= 0
  • multiplier finite and >= 1 — a shrinking cadence would degenerate into a hot loop against a down server; NaN and infinities are rejected rather than silently collapsing to maxDelay
  • initialDelay and maxDelay parse (<number>ms|s|m) and are strictly positive — same reason
A violation fails application startup with a BeanCreationException naming the attribute.

Programmatic equivalent

RestartPolicySpec on StreamSpec.Builder.restartPolicy(..), same defaults:

See Also

@RetryPolicy

Retry of a failed handler invocation — per event, not per stream

@Checkpoint

Resume cascade and the onHistoryLost terminal path the loop shares

StreamMetricsProvider

onStreamStopped(CRASHED) and onStreamRestarted signals

StreamSpec

RestartPolicySpec for contributed streams