← Files FlowerARCHIVED FILE

skills/flower-action-runtime-guide/references/20-execution-modes.md

4.21 KB · Oct 2, 2026 · 00:29 UTC

↓ Download file

# Execution Modes

Choose the executor contract from the real lifetime and ownership of the work.
Do not wrap a blocking action in `CompletableFuture` merely to make the API look
asynchronous.

| Contract | Use when | Lifecycle result |
| --- | --- | --- |
| `SynchronousActionExecutor` | Work is short, bounded, and may safely finish in the caller's execution budget | Terminal result returned inline |
| `AsyncActionExecutor` | Work is in-process and asynchronous, but the current process still owns completion | Completion stage eventually produces the terminal result |
| `DeferredActionExecutor` | An external worker, queue, remote service, long-running job, or later callback owns completion | Dispatch returns an operation descriptor; the run becomes `WAITING_EXTERNAL` |

## Synchronous Execution

Use synchronous execution only for bounded work. A synchronous run that is
already `RUNNING` cannot truthfully be marked cancelled while its side effect
continues. If meaningful cancellation or long duration is required, use an
async or deferred design with a cooperative cancellation hook.

In 0.3, `ActionExecutor` is only the common definition/dispatch contract.
`SynchronousActionExecutor`, `AsyncActionExecutor`, and
`DeferredActionExecutor` expose their own mode-specific operations. Do not add
an `execute(...)` method that merely throws to an async or deferred executor.

## In-Process Async Execution

Use a host-managed, bounded executor. Share it according to the application's
resource policy and expose saturation through metrics. Never create one thread
pool per action, use an unbounded queue without an explicit capacity decision,
or block a Flower tick with `Future.get()` or `join()`.

An async stage does not make the work durable. If the process can restart while
work is running, either provide recovery that can reconstruct the attempt or
use deferred execution backed by an external durable system.

## Durable Deferred Execution

`DeferredActionExecutor` should return a dispatch descriptor containing the
external operation identity and useful timing or metadata. Persist the
transition to `WAITING_EXTERNAL` before treating dispatch as accepted.

The runtime persists `RUNNING + attemptToken`, invokes dispatch, and then
persists `WAITING_EXTERNAL + operationId`. A process can stop after the
external system accepts work but before the second transition commits. The
external operation can then exist while the Run remains `RUNNING` without its
operation id. Deferred execution therefore requires all of:

- deterministic operation id generation from stable Run/attempt data;
- idempotent external dispatch under that id;
- authenticated, tenant-scoped callbacks;
- reconciliation for old `RUNNING` and `WAITING_EXTERNAL` Runs;
- timeout and orphan-operation policy;
- a transactional outbox when database-to-queue delivery must be atomic.

Complete the run through `CompletableActionRuntime.complete(...)`, supplying
the run id, current attempt token, and terminal result. Reject stale or forged
attempt tokens. A duplicate completion for the already-recorded terminal
attempt should return a consistent idempotent result rather than execute a
second side effect.

The callback adapter must authenticate the sender, enforce tenant ownership,
validate the run id and attempt token, and map only a terminal external result.
It must not bypass the runtime by writing `ActionRun` directly.

## Cancellation

Cancellation is a state transition plus, when supported, a cooperative request
to the active executor or external operation. Make the external cancellation
hook idempotent for the tuple of run id, attempt token, and operation id.

Concurrent completion and cancellation are normal. Versioned CAS decides the
single terminal winner. The losing request must observe and report the stored
terminal truth; it must not overwrite it or issue a second domain mutation.

`CANCELLED` means the runtime no longer accepts a normal completion for the
Run. It does not prove that remote work physically stopped. Preserve
`ACTION_CANCELLED_EXTERNAL_CANCEL_FAILED`, `MANUAL_REVIEW`, and cancellation
warning output in APIs, audit views, and user interfaces. Do not collapse an
unconfirmed external cancellation into a plain “cancelled” label.

SHA-256: 9bed9d3c52345468a7daf45e82647292e6b8d4601974c54f8abb2f55c4b59067