Guarantees
What AutoFlow guarantees, and what it does not
Where another page seems to promise more, this one is right.
Guaranteed
- A workflow survives crashes. It survives a restart, a failure of the instance running it and a network outage, and may run for weeks; work a dead instance left in flight is queued again by the next instance that claims its shards.
- A round is all or nothing. Everything a round of the script produces commits in one transaction at the moment the script waits, or not at all; nothing is ever half-applied.
- History is append-only. Events are never rewritten and their sequence ids are gapless, so what happened is what history says happened.
- One workflow per idempotency key. Within a namespace, a key maps to at most one workflow for as long as that workflow’s data exists; resubmitting it returns the existing workflow instead of starting a second.
- At most one active run per workflow. Only one round of a given workflow executes at a time, on one instance, so rounds never race each other.
- It scales across nodes. The keyspace is split into shards, each owned by at most one AutoFlow instance at a time; capacity grows by attaching another workflow database, which running instances pick up without a restart.
- Waiting is free. A workflow that sleeps or waits on a channel holds no resources; its state is already durable, so suspending it costs nothing.
- Action calls survive outages. When the call into a module fails at the infrastructure level, autocore retries it, by default 20 attempts over about an hour and three quarters, before the workflow sees an error; an error the module returns as its answer is final.
- Every attempt carries the same idempotency key. Each action invocation carries a key derived from the workflow and the call site, presented unchanged on every retry, so the destination can collapse repeats.
- The script is sandboxed. A workflow has no filesystem, no network, no environment, no randomness and no wall clock; its only ways out are actions,
print(), and atime.now()derived from history. - Secrets are opaque. A
sensitive_stringorsensitive_bytesvalue can be handed to an action but never read, printed, hashed or probed withinby the script. - Tokens gate access. Reading, canceling or sending to a workflow requires that workflow’s token, so holding a workflow key or an idempotency key is not holding the workflow.
Not guaranteed
- Exactly-once side effects. An action can run more than once if a crash lands between its work and the recording of its result; the guarantee is at-least-once execution with a stable idempotency key, which the module or the system behind it has to honor.
- Unbounded lifetime. A workflow has a schedule-to-complete timeout of 30 days by default and 60 days at most; when it elapses, the workflow ends as timed out.
- Retention beyond 90 days. Workflow data is retired after the retention window, 90 days by default; a workflow idle for longer than that, sleeping or waiting, is retired with it and silently gone, so 90 days is the longest supported wait.
- Delivery of every channel value. A workflow receives at most 10,000 channel values; sends beyond that are refused, a value accepted while the count is close to the cap can still be dropped, and a value with a forged channel token is accepted and dropped without the sender noticing.
- Immediate cancellation. Cancellation is an asynchronous, best-effort signal: it takes effect at the workflow’s next wait, and a workflow that never waits again never observes it.
- Recovery from every crash, forever. Re-queueing a task after an infrastructure abort has a finite budget, five attempts by default; once spent, the workflow ends as system-failed.
- Upgrading a running workflow. A workflow runs the exact script bytes it was started with, stored in its history; changing the script in your repository does not change a running workflow, and there is no versioning API to migrate one.
Sources: doc/autocore.md in the Relay repository and the AutoFlow user manual.
Last updated on