- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
- Get Started
- Product
- Resources
- Tools & SDKs
- Framework
- Reference
5.8.11. Long-Running Workflows
In this chapter, you’ll learn what a long-running workflow is and how to configure it.
What is a Long-Running Workflow?#
When you execute a workflow, you wait until the workflow finishes execution to receive the output.
A long-running workflow is a workflow that continues its execution in the background. You don’t receive its output immediately. Instead, you subscribe to the workflow execution to listen to status changes and receive its result once the execution is finished.
Why use Long-Running Workflows?#
Long-running workflows are useful if:
- A task takes too long. For example, you're importing data from a CSV file.
- The workflow's steps wait for an external action to finish before resuming execution. For example, before you import the data from the CSV file, you wait until the import is confirmed by the user.
Configure Long-Running Workflows#
A workflow is considered long-running if at least one step has its async
configuration set to true
and doesn't return a step response.
For example, consider the following workflow and steps:
10})11 12const step2 = createStep(13 {14 name: "step-2",15 async: true,16 },17 async () => {18 console.log("Waiting to be successful...")19 }20)21 22const step3 = createStep("step-3", async () => {23 return new StepResponse("Finished three steps")24})25 26const myWorkflow = createWorkflow(27 "hello-world", 28 function () {29 step1()30 step2()31 const message = step3()32 33 return new WorkflowResponse({34 message,35 })36})37 38export default myWorkflow
The second step has in its configuration object async
set to true
and it doesn't return a step response. This indicates that this step is an asynchronous step.
So, when you execute the hello-world
workflow, it continues its execution in the background once it reaches the second step.
Change Step Status#
Once the workflow's execution reaches an async step, it'll wait in the background for the step to succeed or fail before it moves to the next step.
To fail or succeed a step, use the Workflow Engine Module's main service that is registered in the Medusa Container under the Modules.WORKFLOW_ENGINE
(or workflowsModuleService
) key.
Retrieve Transaction ID#
Before changing the status of a workflow execution's async step, you must have the execution's transaction ID.
When you execute the workflow, the object returned has a transaction
property, which is an object that holds the details of the workflow execution's transaction. Use its transactionId
to later change async steps' statuses:
Change Step Status to Successful#
The Workflow Engine Module's main service has a setStepSuccess
method to set a step's status to successful. If you use it on a workflow execution's async step, the workflow continues execution to the next step.
For example, consider the following step:
8} from "@medusajs/framework/workflows-sdk"9 10type SetStepSuccessStepInput = {11 transactionId: string12};13 14export const setStepSuccessStep = createStep(15 "set-step-success-step",16 async function (17 { transactionId }: SetStepSuccessStepInput,18 { container }19 ) {20 const workflowEngineService = container.resolve(21 Modules.WORKFLOW_ENGINE22 )23 24 await workflowEngineService.setStepSuccess({25 idempotencyKey: {26 action: TransactionHandlerType.INVOKE,27 transactionId,28 stepId: "step-2",29 workflowId: "hello-world",30 },31 stepResponse: new StepResponse("Done!"),32 options: {33 container,34 },35 })36 }37)
In this step (which you use in a workflow other than the long-running workflow), you resolve the Workflow Engine Module's main service and set step-2
of the previous workflow as successful.
The setStepSuccess
method of the workflow engine's main service accepts as a parameter an object having the following properties:
idempotencyKey
objectThe details of the workflow execution.
idempotencyKey
objectstepResponse
StepResponseasync
step doesn't have a response, you set its response when changing its status.options
Record<string, any>OptionalOptions to pass to the step.
options
Record<string, any>OptionalChange Step Status to Failed#
The Workflow Engine Module's main service also has a setStepFailure
method that changes a step's status to failed. It accepts the same parameter as setStepSuccess
.
After changing the async step's status to failed, the workflow execution fails and the compensation functions of previous steps are executed.
For example:
8} from "@medusajs/framework/workflows-sdk"9 10type SetStepFailureStepInput = {11 transactionId: string12};13 14export const setStepFailureStep = createStep(15 "set-step-success-step",16 async function (17 { transactionId }: SetStepFailureStepInput,18 { container }19 ) {20 const workflowEngineService = container.resolve(21 Modules.WORKFLOW_ENGINE22 )23 24 await workflowEngineService.setStepFailure({25 idempotencyKey: {26 action: TransactionHandlerType.INVOKE,27 transactionId,28 stepId: "step-2",29 workflowId: "hello-world",30 },31 stepResponse: new StepResponse("Failed!"),32 options: {33 container,34 },35 })36 }37)
You use this step in another workflow that changes the status of an async step in a long-running workflow's execution to failed.
Access Long-Running Workflow Status and Result#
To access the status and result of a long-running workflow execution, use the subscribe
and unsubscribe
methods of the Workflow Engine Module's main service.
For example:
10 11 const workflowEngineService = req.scope.resolve<12 IWorkflowEngineService13 >(14 Modules.WORKFLOW_ENGINE15 )16 17 const subscriptionOptions = {18 workflowId: "hello-world",19 transactionId: transaction.transactionId,20 subscriberId: "hello-world-subscriber",21 }22 23 await workflowEngineService.subscribe({24 ...subscriptionOptions,25 subscriber: async (data) => {26 if (data.eventType === "onFinish") {27 console.log("Finished execution", data.result)28 // unsubscribe29 await workflowEngineService.unsubscribe({30 ...subscriptionOptions,31 subscriberOrId: subscriptionOptions.subscriberId,32 })33 } else if (data.eventType === "onStepFailure") {34 console.log("Workflow failed", data.step)35 }36 },37 })38 39 res.send(result)40}
In the above example, you execute the long-running workflow hello-world
and resolve the Workflow Engine Module's main service from the Medusa container.
subscribe Method#
The main service's subscribe
method allows you to listen to changes in the workflow execution’s status. It accepts an object having three properties:
workflowId
stringtransactionId
stringsubscriberId
stringsubscriber
(data: { eventType: string, result?: any }) => Promise<void>eventType
property, which you use to check the status of the workflow execution.If the value of eventType
in the subscriber
function's first parameter is onFinish
, the workflow finished executing. The first parameter then also has a result
property holding the workflow's output.
unsubscribe Method#
You can unsubscribe from the workflow using the workflow engine's unsubscribe
method, which requires the same object parameter as the subscribe
method.
However, instead of the subscriber
property, it requires a subscriberOrId
property whose value is the same subscriberId
passed to the subscribe
method.
Example: Restaurant-Delivery Recipe#
To find a full example of a long-running workflow, refer to the restaurant-delivery recipe.
In the recipe, you use a long-running workflow that moves an order from placed to completed. The workflow waits for the restaurant to accept the order, the driver to pick up the order, and other external actions.