Async Services and Flows
Choose the right async pattern in Rierino based on trigger type, recovery model, and operational needs.
Rierino supports several async patterns. Each solves a different problem.
Pick the pattern by answering two questions:
What starts the work?
Who is responsible for recovery?
Most async patterns are at-least-once in practice. Design handlers and sagas to be idempotent.
1. Fire & Forget sagas
Fire & Forget runs a saga asynchronously and returns early.
You can enable it in three places:
In the saga definition with Fire & Forget
On an API request with the
X-AsyncheaderOn nested saga calls with
fireForget=true
How it behaves
At saga level, Fire & Forget always returns immediately.
If fire & forget state is configured, Rierino stores status and result data.
The response includes a reference value. Use it to query progress later.
At request level, X-Async supports two modes:
gatewayreturns from the gateway immediatelybackendforwards an async saga call to the backend
gateway returns a request ID even if the backend is unavailable.
backend returns a backend process ID after forwarding the request.
Best fit
Use this for short-running background work.
Use it when the caller can track status and retry safely.
Single-step or low-fanout sagas are the safest fit.
Tradeoffs
Fire & Forget does not resume automatically from a partial step.
Recovery is usually caller-driven.
If a failure happens mid-flow, retry logic must handle duplicates safely.
Design notes
Keep the flow small and deterministic.
Persist a business key with the returned reference.
Make downstream writes idempotent.
Related docs:
2. CDC-driven flows
CDC flows start from data changes, not API calls.
Typical pattern:
Store records to process in a table or state
Let CDC publish change records
Consume those records in a runner
Run business logic from each change
How it behaves
CDC managers track resume tokens or offsets.
On restart, they resume from the last stored checkpoint when supported.
Spring runners can store CDC resume state with offset.state.
CDC stream settings also support offset.partitioned, offset.initial, pollMs, and asPulse.
This gives automatic recovery across restarts.
Best fit
Use CDC when persistence is the source of truth.
Use it for outbox-style integration and incremental sync.
Use it when new or changed records should trigger processing.
Tradeoffs
CDC is usually at-least-once.
A record can be processed more than once after retry or restart.
Business logic must tolerate duplicates and partial repeats.
Operational notes
CDC managers should usually run on a single replica.
They do not partition consumed changes by themselves.
If you need scale-out processing, publish CDC outputs to Kafka first.
Then run business logic on partitioned consumers.
Related docs:
3. Message queues and event streams
This pattern sends work to a stream and processes it asynchronously.
Kafka is the common example. Samza runners are a common execution model.
How it behaves
The producer publishes an event and returns.
One or more runners consume the event later.
Checkpointing depends on the broker and runner type.
Delivery is commonly at-least-once.
Ordering also depends on the runner:
Sync Samza Event Runner preserves message order
Async Samza Event Runner increases parallelism and does not preserve order
Best fit
Use streams for high-throughput async services.
Use them when producers and consumers should evolve independently.
Use them when buffering traffic spikes matters.
Tradeoffs
You accept broker-specific delivery and retry behavior.
Consumers must be idempotent.
If order matters, choose the runner type carefully.
Design notes
Use stable message keys when partition order matters.
Keep messages self-contained when possible.
Route failures to DLQ or compensating flows when needed.
Related docs:
4. Scheduled and self-waiting sagas
Scheduled sagas start from time, not from an external request.
They are Rierino’s built-in timer-based async pattern.
How it behaves
Scheduling is configured on the saga definition.
You can use:
Period and Period Unit
Initial Delay
Max Repeat Count
Cron Expression
Cron Time Zone
The runner timer triggers the saga directly.
This works well for polling external systems.
It also works for internal housekeeping jobs.
Rierino also supports self-waiting saga patterns.
Saga handler settings include offset.state for storing self-waiting offsets.
Best fit
Use scheduled sagas for time-based or self-timing jobs.
Use them for polling REST systems without push support.
Use them for short and simple recurring tasks.
Tradeoffs
Scheduling can duplicate work on replicated runners.
Timers do not use streams for coordination.
You must scope execution to the correct runners.
Operational notes
Always set Allowed For Runners on scheduled sagas.
Otherwise, every timer-enabled runner can trigger the same saga.
To avoid duplicate runs:
Run a single replica
Or trigger only from partition
0in a StatefulSetUse Python batch runners for long-running or complex scheduled jobs.
Related docs:
5. Human-assigned flows
Human-assigned flows pause automation and wait for a person.
They use the task orchestration actions.
How it behaves
StartTask stores the current process as a task record.
You can assign the task to:
Specific roles
A specific user
A timeout path
You can also define:
taskNametaskSpectaskActiontimeouttimeoutOn
Later, ProceedTask resumes the process.
If the timeout expires, timeout handling can continue the flow differently.
Saga conditions can branch on TIMEOUT.
Best fit
Use this for approvals and manual exceptions.
Use it when an operator must review data before continuing.
Use it when automation should pause safely for human input.
Tradeoffs
This pattern adds operational latency by design.
Task state must stay consistent and queryable.
Proceed and timeout flows should run near the same task state.
Design notes
Store enough context for the assignee.
Keep the resumed payload small and explicit.
Design clear timeout and escalation paths.
Related docs:
How to choose the right pattern
Choose Fire & Forget when an API needs fast acknowledgement.
Choose CDC when data persistence should trigger the work.
Choose Event streams when you need buffering and loose coupling.
Choose Scheduled sagas when time or loop should drive execution.
Choose Human-assigned tasks when a person must continue the process.
Last updated
