Author: @aat
FTL's ability to compose functions lends itself well to defining both distributed workflows and FSM's.
This document deals with an initial implementation of FSM's.
What is the difference between FSM's and workflows? To quote this page:
In a workflow engine, transitioning to the next step occurs when a previous action is completed, whilst a state machine needs an external event that will cause branching to the next activity. In other words, state machine is event driven and workflow engine is not.
Given an FSM is a set of transitions in the form (from, event, to)
we can model each state as a function, and the event as the input to the to
function. As an example, given these verbs:
The transition would be (from, To, to)
. That is, when an event of type To
is received when in state from
, a transition would occur to state to
.
FSMs will be defined in code (see below), and represented in the schema (also below).
Each "execution" of a state machine definition must be given a unique key that is used to reference all future events. The first event sent with a new unique key will create a new execution. That is, each send of an event must be in the form (key, event)
.
go-runtime/ftl
for constructing an FSM.VerbService.FSMSend()
for sending an event to an FSM, including retry behaviour.backend/controller/fsm
for controlling the FSM and performing retries.With traditional schema evolution it would be very difficult to reason about changes to an FSM over time. Once versioning lands however, it will be much more straightforward: an FSM will continue execution using the versions of modules/verbs/types it referenced at deploy time. As with normal FTL versioning, extra constraints will need to be applied to persistent storage - databases, queues, etc.
Given the example FSM from here visualised as follows:
The FTL representation of this in Go might look something like:
The FTL schema extracted from this will look something like:
The execution of state machines will be modelled in a single table tracking each "execution". Each execution is associated with a unique user-provided key, and created on-demand. Each execution also references a specific deployment in order to support versioning.
We'll also introduce a retries table. This will initially be added purley so that states can retry, but at some point in the future we'll extend this to be more flexible and generalised.