Workflow Context API
At the moment, this API is available in Java only.
A Workflow’s primary function is to orchestrate interactions with other entities.
Workflow Class and Method
A Workflow is implemented as a class in a Grainite application. This user-defined Workflow class must implement an instance method that accepts a WorkflowContext, an input Value argument, and returns a Value result. This method is the entry point to the Workflow - it implements Workflow logic.
When a client starts a workflow, it provides the instance method as an argument, and an instance of the Workflow class is created. Workflow 'state' consists of the execution state of the Workflow method. This execution state is automatically durable, across system restarts. The Workflow logic is allowed to keep anything it needs in local and instance variables (serializability is not a requirement). The Workflow method is given a Workflow Context. This context provides access to Grainite Workflow Orchestration functions for executing Activities, Awaiting, and Sleeping.
Start an Activity
All external interactions must be executed as Activities. Types of Activities in Grainite are:
GrainOp - Query/Update/Invoke an action on a grain
TopicAppend - Append to a topic
Function - Interactions with external data sources
Workflow - Start/Query/Signal another Workflow
Invoke an action “getInfo” on a grain (GrainOp)
Append an event to a topic (TopicAppend)
Interact with an external service (Function)
Start another Workflow from an Activity
Invoke a signal method on a Workflow from an Activity
Configuring Retries
Grain and Function Activities can be configured for automatic retries upon failure. A retry policy can specify a retry delay and maximum retry duration. If the activity continues to fail after retries, the Activity (Promise) is marked as failed.
Awaiting activity completion
Workflow can use the await method to wait for previously-started activity (or activities) to complete. The await method can simultaneously wait for
Promises (previously-started activities to complete with success or failure)
Condition expression (to become true)
Maximum duration
Wait for multiple Promises to complete
Wait for any of the multiple Promises to complete
Check the status of a Promise
Waiting for a condition
Wait for the Maximum duration
Logging
Each Workflow instance maintains a log of its progress. Activity starts, awaits, and activity completions are automatically logged by the system. However, Workflow logic can also append to this log, to capture more detail.
Query Methods
Query methods allow clients to get information about a Workflow state and are implemented by the developer. Queries are instance methods, therefore have access to instance vars. Any Workflow state which can be queried should be kept in instance vars. Queries are strictly read-only methods and are not allowed to use Workflow Context methods.
Signal Methods
Signal methods allow clients to change the execution of a Workflow by modifying Workflow execution state. The Workflow developer decides what signals they want to implement (if any). Signals are instance methods, therefore have access to instance vars. Any Workflow state which needs to be updatable should be kept in instance vars. Signals can use all Workflow Context methods except awaits.
Things to avoid
Workflow execution state is made durable not by saving the pojo state, but by saving a log of events that drive the Workflow. Under the covers, the Workflow method is replayed multiple times against the event-log, to resurrect the Workflow execution state.
The replay approach requires deterministic execution of the workflow method. That is, all the function calls that return data to the Workflow logic, must always produce the same data, given the same inputs.
The above requirement (and others) place the following constraints on workflow code:
Do not call non-deterministic functions such as random number/UUID generation, or current time. Instead, use deterministic replacements on WorkflowContext:
currentTimeMillis()
,newRandom()
,randomUUID()
Do not use
Thread.sleep
as this blocks thread execution. Instead, useWorkflowContext.await()
Do not create threads, since this will result in non-deterministic code path execution.
Do not throw or catch
java.lang.Error
, since the workflow framework internally uses subclasses of Error to implement 'yield' semantics (code is free to throw/catch Exception)Do not use '
finally
' clause since this might intercept Errors.Do not use non-final static variables.
Last updated