#[workflow]
Expand description
§Workflows
Entry-point macro to define a Restate Workflow.
Workflows are a sequence of steps that gets executed durably.
A workflow can be seen as a special type of Virtual Object with the following characteristics:
- Each workflow definition has a
run
handler that implements the workflow logic.- The
run
handler executes exactly one time for each workflow instance (object / key). - The
run
handler executes a set of durable steps/activities. These can either be:- Inline activities: for example a run block or sleep
- Calls to other handlers implementing the activities
- The
- You can submit a workflow in the same way as any handler invocation (via SDK clients or Restate services, over HTTP or Kafka).
- A workflow definition can implement other handlers that can be called multiple times, and can interact with the workflow:
- Query the workflow (get information out of it) by getting K/V state or awaiting promises that are resolved by the workflow.
- Signal the workflow (send information to it) by resolving promises that the workflow waits on.
- Workflows have access to the
WorkflowContext
andSharedWorkflowContext
, giving them some extra functionality, for example Durable Promises to signal workflows. - The K/V state of the workflow is isolated to the workflow execution, and can only be mutated by the
run
handler.
Note: Workflow retention time:
The retention time of a workflow execution is 24 hours after the finishing of the run
handler.
After this timeout any K/V state is cleared, the workflow’s shared handlers cannot be called anymore, and the Durable Promises are discarded.
The retention time can be configured via the Admin API per Workflow definition by setting workflow_completion_retention
.
§Implementing workflows
Have a look at the code example to get a better understanding of how workflows are implemented:
use restate_sdk::prelude::*;
#[restate_sdk::workflow]
pub trait SignupWorkflow {
async fn run(req: String) -> Result<bool, HandlerError>;
#[shared]
async fn click(click_secret: String) -> Result<(), HandlerError>;
#[shared]
async fn get_status() -> Result<String, HandlerError>;
}
pub struct SignupWorkflowImpl;
impl SignupWorkflow for SignupWorkflowImpl {
async fn run(&self, mut ctx: WorkflowContext<'_>, email: String) -> Result<bool, HandlerError> {
let secret = ctx.rand_uuid().to_string();
ctx.run(|| send_email_with_link(email.clone(), secret.clone())).await?;
ctx.set("status", "Email sent".to_string());
let click_secret = ctx.promise::<String>("email.clicked").await?;
ctx.set("status", "Email clicked".to_string());
Ok(click_secret == secret)
}
async fn click(&self, ctx: SharedWorkflowContext<'_>, click_secret: String) -> Result<(), HandlerError> {
ctx.resolve_promise::<String>("email.clicked", click_secret);
Ok(())
}
async fn get_status(&self, ctx: SharedWorkflowContext<'_>) -> Result<String, HandlerError> {
Ok(ctx.get("status").await?.unwrap_or("unknown".to_string()))
}
}
#[tokio::main]
async fn main() {
HttpServer::new(Endpoint::builder().bind(SignupWorkflowImpl.serve()).build())
.listen_and_serve("0.0.0.0:9080".parse().unwrap())
.await;
}
§The run handler
Every workflow needs a run
handler.
This handler has access to the same SDK features as Service and Virtual Object handlers.
In the example above, we use ctx.run
to log the sending of the email in Restate and avoid re-execution on replay.
Or call other handlers to execute activities.
§Shared handlers
To define a shared handler, simply annotate the handler with the #[shared]
annotation:
§Querying workflows
Similar to Virtual Objects, you can retrieve the K/V state of workflows via the other handlers defined in the workflow definition,
In the example we expose the status of the workflow to external clients.
Every workflow execution can be seen as a new object, so the state is isolated to a single workflow execution.
The state can only be mutated by the run
handler of the workflow. The other handlers can only read the state.
§Signaling workflows
You can use Durable Promises to interact with your running workflows: to let the workflow block until an event occurs, or to send a signal / information into or out of a running workflow. These promises are durable and distributed, meaning they survive crashes and can be resolved or rejected by any handler in the workflow.
Do the following:
- Create a promise that is durable and distributed in the
run
handler, and wait for its completion. In the example, we wait on the promiseemail.clicked
. - Resolve or reject the promise in another handler in the workflow. This can be done at most one time.
In the example, the
click
handler gets called when the user clicks a link in an email and resolves theemail.clicked
promise.
You can also use this pattern in reverse and let the run
handler resolve promises that other handlers are waiting on.
For example, the run
handler could resolve a promise when it finishes a step of the workflow, so that other handlers can request whether this step has been completed.
§Serving and registering workflows
You serve workflows in the same way as Services and Virtual Objects. Have a look at the Serving docs. Make sure you register the endpoint or Lambda handler in Restate before invoking it.
Tip: Workflows-as-code with Restate: Check out some examples of workflows-as-code with Restate on the use case page.
§Submitting workflows from a Restate service
Submit/query/signal:
Call the workflow handlers in the same way as for Services and Virtual Objects.
You can only call the run
handler (submit) once per workflow ID (here "someone"
).
Check out the Service Communication docs for more information.
§Submitting workflows over HTTP
Submit/query/signal:
Call any handler of the workflow in the same way as for Services and Virtual Objects.
This returns the result of the handler once it has finished.
Add /send
to the path for one-way calls.
You can only call the run
handler once per workflow ID (here "someone"
).
curl localhost:8080/SignupWorkflow/someone/run \
-H 'content-type: application/json' \
-d '"someone@restate.dev"'
Attach/peek: This lets you retrieve the result of a workflow or check if it’s finished.
curl localhost:8080/restate/workflow/SignupWorkflow/someone/attach
curl localhost:8080/restate/workflow/SignupWorkflow/someone/output
§Inspecting workflows
Have a look at the introspection docs on how to inspect workflows. You can use this to for example:
- Inspect the progress of a workflow by looking at the invocation journal
- Inspect the K/V state of a workflow
For more details, check the service
macro documentation.