-
Notifications
You must be signed in to change notification settings - Fork 43
docs: durable & async Fission RFC series (statestore, async invocation, workflows) #314
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
5ef549f
ad1c4d8
b36eb0c
b371302
2acbf33
471603c
48e65c4
74e7e63
8506b37
53c4223
67189a6
7f3dbb0
18e370b
a26a23b
e2e4cf9
ff91068
65b3f3e
97a1d70
6cf17ba
3cf5bda
1b13367
e0fa5fa
c4c7b9b
d254106
48fbfc8
ba93cf9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| --- | ||
| title: "Statestore" | ||
| weight: 16 | ||
| description: > | ||
| A durable state substrate — key/value, an append-only event log, and a visibility-timeout queue — behind one interface with pluggable drivers. | ||
| --- | ||
|
|
||
| **The statestore is the durable substrate the control plane writes to when a feature needs state that outlives a single request or a single pod.** | ||
|
|
||
| It exposes three capabilities behind one interface — a **key/value** store, an append-only **event log**, and a visibility-timeout **queue** — served by a pluggable driver. | ||
| Fission itself never deploys a database product: you either use the bundled embedded driver for development, or point the external driver at a database you already run. | ||
|
|
||
| The statestore is what makes Fission's newer durable features possible. | ||
| Starting with Fission {{< release-version >}}, three subsystems build on it: | ||
|
|
||
| - **[Durable Workflows]({{% ref "/docs/usage/workflows/_index.md" %}})** record every step of a run in the event log, so a run survives a controller restart and resumes exactly where it stopped. | ||
| - **[Asynchronous invocation]({{% ref "/docs/usage/function/async-invocation.md" %}})** enqueues each fire-and-forget call on the queue and delivers it in the background with retries and a dead-letter queue. | ||
| - **Eventing** uses the event log and queue as its zero-broker transport. | ||
|
|
||
| The statestore is off by default; a feature that needs it will tell you to enable it. | ||
|
|
||
| ```mermaid | ||
| flowchart TB | ||
| wf["Workflow Engine"]:::fission | ||
| async["Async Router / Worker"]:::fission | ||
| evt["Eventing"]:::fission | ||
| subgraph ss["Statestore"] | ||
| kv["Key/Value"]:::store | ||
| log["Event Log (append-only, CAS)"]:::store | ||
| queue["Queue (visibility timeout)"]:::store | ||
| end | ||
| driver["Driver"]:::fission | ||
| embedded["SQLite on a PVC<br/>(embedded)"]:::pod | ||
| external["Postgres via DSN Secret<br/>(external)"]:::pod | ||
|
|
||
| wf --> log | ||
| async --> queue | ||
| evt --> log | ||
| evt --> queue | ||
| kv --> driver | ||
| log --> driver | ||
| queue --> driver | ||
| driver -->|"embedded"| embedded | ||
| driver -->|"external"| external | ||
|
|
||
| classDef fission fill:#e8f0fe,stroke:#2d70de,color:#1f2a43 | ||
| classDef pod fill:#e6f7f1,stroke:#11a37f,color:#1f2a43,stroke-dasharray:5 3 | ||
| classDef store fill:#fff7e0,stroke:#dba514,color:#1f2a43,stroke-dasharray:5 3 | ||
| ``` | ||
|
|
||
| ## Embedded vs external | ||
|
|
||
| The driver is chosen with `statestore.mode`. | ||
| The two modes differ only in where the state lives; the interface the features use is identical. | ||
|
|
||
| | Mode | Driver | Where state lives | Use it for | | ||
| | --- | --- | --- | --- | | ||
| | `embedded` | SQLite | A bundled SQLite file on a `PersistentVolumeClaim` | Development, single-node, and evaluation. Simple to run; not highly available. | | ||
| | `external` | Postgres | A database **you** run and manage | Production, high availability, and anything that needs KEDA autoscaling. | | ||
|
|
||
| {{% notice warning %}} | ||
| KEDA autoscaling for asynchronous invocation requires `statestore.mode=external`. | ||
| The KEDA PostgreSQL scaler reads the backlog directly from the database and cannot reach the embedded SQLite file inside the pod. | ||
| {{% /notice %}} | ||
|
|
||
| ## Enable the statestore | ||
|
|
||
| The statestore is **off by default**. | ||
| Enable it and pick a mode with Helm values. | ||
|
|
||
| Embedded (SQLite on a PVC — development): | ||
|
|
||
| ```bash | ||
| helm upgrade --install fission fission-charts/fission-all \ | ||
| --namespace fission \ | ||
| --set statestore.enabled=true \ | ||
| --set statestore.mode=embedded \ | ||
| --set statestore.embedded.size=1Gi | ||
| ``` | ||
|
|
||
| External (Postgres — production): create a Secret holding the DSN, then point the chart at it: | ||
|
|
||
| ```bash | ||
| kubectl create secret generic statestore-postgres \ | ||
| --namespace fission \ | ||
| --from-literal=dsn='postgres://user:password@postgres.db.svc:5432/fission?sslmode=require' | ||
|
|
||
| helm upgrade --install fission fission-charts/fission-all \ | ||
| --namespace fission \ | ||
| --set statestore.enabled=true \ | ||
| --set statestore.mode=external | ||
| ``` | ||
|
|
||
| | Helm value | Default | Meaning | | ||
| | --- | --- | --- | | ||
| | `statestore.enabled` | `false` | Provision the statestore. Required by workflows, async invocation, and eventing. | | ||
| | `statestore.mode` | `embedded` | `embedded` (SQLite on a PVC) or `external` (a Postgres DSN Secret). | | ||
| | `statestore.embedded.size` | `1Gi` | Size of the PVC backing the embedded SQLite file. | | ||
| | `statestore.external` | — | Name of the DSN Secret for external mode (defaults to `statestore-postgres`, key `dsn`). | | ||
|
|
||
| Fission runs no database of its own in either mode: embedded is a file on a volume, and external is a database you already operate. | ||
|
|
||
| ## Related | ||
|
|
||
| - [Durable Workflows]({{% ref "/docs/usage/workflows/_index.md" %}}) — multi-step orchestration recorded in the event log. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LanguageTool] reported by reviewdog 🐶 |
||
| - [Asynchronous Invocation]({{% ref "/docs/usage/function/async-invocation.md" %}}) — fire-and-forget calls delivered from the queue. | ||
| - [Architecture overview]({{% ref "/docs/architecture/_index.md" %}}) — how the statestore sits alongside the other components. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| --- | ||
| title: "Workflows" | ||
| weight: 7 | ||
| description: > | ||
| A durable state machine over functions — a Workflow definition and its WorkflowRun executions, recorded step by step in the statestore so a run survives restarts and resumes where it stopped. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [LanguageTool] reported by reviewdog 🐶 |
||
| --- | ||
|
|
||
| **A workflow is a durable state machine that orchestrates several functions as one reliable unit of work.** | ||
|
|
||
| A single function is the right tool for one step. | ||
| Real processes are usually several steps with logic between them — validate an order, screen it for fraud, charge the card, fulfill or reject — where some steps run in parallel, some are conditional, and some can fail transiently and must be retried. | ||
| You *can* wire that together by having functions call each other, but then the orchestration lives in your code, nothing records how far a given execution got, and a crash midway leaves you guessing. | ||
| A workflow makes the orchestration a first-class, durable object instead. | ||
|
|
||
| ## Definition and execution | ||
|
|
||
| Workflows use two custom resources, mirroring the split between a program and a running process: | ||
|
|
||
| - A **`Workflow`** is the *definition* — a named state machine that says which functions run, in what order, with what branching, retries, and error handling. | ||
| - A **`WorkflowRun`** is one *execution* of that definition against a specific input. You create a run each time you want the workflow to happen; each run has its own state and history. | ||
|
|
||
| The definition is authored once and reused; every invocation is a new run. | ||
|
|
||
| ## What makes it durable | ||
|
|
||
| Every step a run takes — scheduled, succeeded, failed, retried, a timer fired, branches joined — is appended to an event log in the [statestore]({{% ref "/docs/architecture/statestore.md" %}}) using compare-and-swap, so the log is the single source of truth for where a run is. | ||
| The engine's own state is derived: it rebuilds a run's position by folding its event log, then decides the next step. | ||
|
|
||
| That design buys four things: | ||
|
|
||
| - **Restart survival.** If the controller restarts mid-run, it reads the log back and continues — nothing is re-run that already succeeded, and nothing is lost. | ||
| - **Resume exactly where it stopped.** A run picks up from its last recorded step, not from the beginning. | ||
| - **Retries with backoff.** A transient function failure (a 5xx) is retried automatically; a permanent one (a 4xx typed error) is not. | ||
| - **Typed-error routing.** A step can catch a named business error (`PaymentDeclined`) and route to a different state, separately from infrastructure retries. | ||
|
|
||
| ```mermaid | ||
| flowchart TB | ||
| trigger["CLI / Trigger"]:::user -->|"create WorkflowRun"| engine["Workflow Engine"]:::fission | ||
| engine -->|"invoke step (internal path)"| router["Router"]:::fission | ||
| router --> pod["Function Pod"]:::pod | ||
| engine -->|"append every step (CAS)"| log["Statestore Event Log"]:::store | ||
| engine -->|"durable delay"| timers["wf-timers Queue"]:::store | ||
| log -.->|"fold to resume"| engine | ||
|
|
||
| classDef user fill:#ffffff,stroke:#94a3b8,color:#1f2a43 | ||
| classDef fission fill:#e8f0fe,stroke:#2d70de,color:#1f2a43 | ||
| classDef pod fill:#e6f7f1,stroke:#11a37f,color:#1f2a43,stroke-dasharray:5 3 | ||
| classDef store fill:#fff7e0,stroke:#dba514,color:#1f2a43,stroke-dasharray:5 3 | ||
| ``` | ||
|
|
||
| ## The state types | ||
|
|
||
| A workflow is built from a small set of state types: | ||
|
|
||
| - **Task** — invoke a function. | ||
| - **Choice** — branch on the data, with no function call. | ||
| - **Parallel** — run several branches concurrently and join their results. | ||
| - **Map** — run one branch per element of an array, with a concurrency limit. | ||
| - **Wait** — pause the run durably for a set duration. | ||
| - **Succeed** / **Fail** — terminate the run. | ||
|
|
||
| See [Authoring workflows]({{% ref "/docs/usage/workflows/authoring.md" %}}) for the full field reference. | ||
|
|
||
| ## When to use a workflow | ||
|
|
||
| Reach for a workflow when an operation is **multiple steps that must complete reliably as a whole** — especially with parallelism, conditional routing, retries, durable waits, or a need to know afterward exactly what happened. | ||
|
|
||
| Prefer the simpler tools when they fit: | ||
|
|
||
| - A single function, possibly async, is enough for one unit of work — see [Asynchronous invocation]({{% ref "/docs/usage/function/async-invocation.md" %}}). | ||
| - Independent event-driven reactions are better modeled as separate [triggers]({{% ref "/docs/concepts/triggers.md" %}}). | ||
|
|
||
| ## Related | ||
|
|
||
| - [Workflows usage guide]({{% ref "/docs/usage/workflows/_index.md" %}}) — enable, author, run, and inspect workflows. | ||
| - [Statestore]({{% ref "/docs/architecture/statestore.md" %}}) — the durable event log a run is recorded in. | ||
| - [Functions]({{% ref "/docs/concepts/functions.md" %}}) — the steps a workflow orchestrates. | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[LanguageTool] reported by reviewdog 🐶
Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE)
Suggestions:
driverRule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US
Category: MISC