docs: document locks for nested workflows (#14130)

* docs: document locks for nested workflows

* smal text change

* fix build error
This commit is contained in:
Shahed Nasser authored and GitHub committed 2025-11-26 15:36:58 +02:00
1 parent 76660fb6cc
commit c1ede88a7e
53 files changed
+712 -208

No files matched your search

@@ -20,8 +20,8 @@ The [Locking Module](!resources!/infrastructure-modules/locking) handles the loc
Medusa provides two steps that you can use to create locks in your workflows:
- `acquireLockStep`: Attempt to acquire a lock. If the lock is already held by another process, this step will wait until the lock is released. It will fail if a timeout occurs before acquiring the lock.
- `releaseLockStep`: Release a previously acquired lock.
- [acquireLockStep](!resources!/references/medusa-workflows/steps/acquireLockStep): Attempt to acquire a lock. If the lock is already held by another process, this step will wait until the lock is released. It will fail if a timeout occurs before acquiring the lock.
- [releaseLockStep](!resources!/references/medusa-workflows/steps/releaseLockStep): Release a previously acquired lock.
You can use these steps in your workflows to ensure that only one instance of a workflow can modify a resource at a time.
@@ -128,4 +128,61 @@ The step will wait until the lock is acquired based on the configured Locking Mo
### Locking Module Service API
Refer to the [Locking Module reference](!resources!/references/locking-service) for more information on the Locking Module's service methods.
Refer to the [Locking Module reference](!resources!/references/locking-service) for more information on the Locking Module's service methods.
---
## Locks in Nested Workflows
[Nested workflows](../execute-another-workflow/page.mdx) can't acquire locks. So, if you're using a workflow that acquires locks within another workflow, the parent workflow must handle the locking mechanism.
For example, consider a custom workflow that executes Medusa's [completeCartWorkflow](!resources!/references/medusa-workflows/completeCartWorkflow). The `completeCartWorkflow` acquires locks on the cart to prevent race conditions.
Therefore, the custom workflow must acquire and release the locks around the `completeCartWorkflow` execution. For example:
export const nestedWorkflowLockHighlights = [
["16", "acquireLockStep", "Acquire a lock on the cart before executing the nested workflow."],
["23", "completeCartWorkflow", "Execute the nested workflow that requires the lock."],
["30", "releaseLockStep", "Release the lock after the nested workflow is complete."]
]
```ts title="src/workflows/custom-complete-cart.ts" highlights={nestedWorkflowLockHighlights}
import { createWorkflow } from "@medusajs/framework/workflows-sdk"
import {
acquireLockStep,
releaseLockStep,
completeCartWorkflow
} from "@medusajs/medusa/core-flows"
type WorkflowInput = {
cart_id: string;
}
export const customCompleteCartWorkflow = createWorkflow(
"custom-complete-cart",
(input: WorkflowInput) => {
// acquire the same lock as the nested workflow
acquireLockStep({
key: input.cart_id,
timeout: 30,
ttl: 60 * 2,
})
// Execute the nested workflow
completeCartWorkflow.runAsStep({
input: {
id: input.cart_id,
}
})
// release the lock after the nested workflow is complete
releaseLockStep({
key: input.cart_id,
})
}
)
```
In the example above, the `customCompleteCartWorkflow` acquires a lock on the cart before executing the `completeCartWorkflow` and releases it afterward.
Make sure you're acquiring the same lock key as the nested workflow.
+1 -1
View File
@@ -132,7 +132,7 @@ export const generatedEditDates = {
"app/learn/fundamentals/scheduled-jobs/interval/page.mdx": "2025-09-02T08:36:12.714Z",
"app/learn/debugging-and-testing/feature-flags/create/page.mdx": "2025-09-02T08:36:12.714Z",
"app/learn/debugging-and-testing/feature-flags/page.mdx": "2025-09-02T08:36:12.714Z",
"app/learn/fundamentals/workflows/locks/page.mdx": "2025-09-15T09:37:00.808Z",
"app/learn/fundamentals/workflows/locks/page.mdx": "2025-11-26T11:40:04.279Z",
"app/learn/codemods/page.mdx": "2025-09-29T15:40:03.620Z",
"app/learn/codemods/replace-imports/page.mdx": "2025-10-09T11:37:44.754Z",
"app/learn/fundamentals/admin/translations/page.mdx": "2025-10-30T11:55:32.221Z",
+2 -2
View File
@@ -840,9 +840,9 @@ export const generatedSidebars = [
"isPathHref": true,
"type": "link",
"path": "/learn/fundamentals/workflows/execute-another-workflow",
"title": "Execute Nested Workflows",
"title": "Nested Workflows",
"children": [],
"chapterTitle": "3.7.13. Execute Nested Workflows",
"chapterTitle": "3.7.13. Nested Workflows",
"number": "3.7.13."
},
{
+85 -28
View File
@@ -22299,8 +22299,8 @@ The [Locking Module](https://docs.medusajs.com/resources/infrastructure-modules/
Medusa provides two steps that you can use to create locks in your workflows:
- `acquireLockStep`: Attempt to acquire a lock. If the lock is already held by another process, this step will wait until the lock is released. It will fail if a timeout occurs before acquiring the lock.
- `releaseLockStep`: Release a previously acquired lock.
- [acquireLockStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/acquireLockStep/index.html.md): Attempt to acquire a lock. If the lock is already held by another process, this step will wait until the lock is released. It will fail if a timeout occurs before acquiring the lock.
- [releaseLockStep](https://docs.medusajs.com/resources/references/medusa-workflows/steps/releaseLockStep/index.html.md): Release a previously acquired lock.
You can use these steps in your workflows to ensure that only one instance of a workflow can modify a resource at a time.
@@ -22391,6 +22391,57 @@ The step will wait until the lock is acquired based on the configured Locking Mo
Refer to the [Locking Module reference](https://docs.medusajs.com/resources/references/locking-service/index.html.md) for more information on the Locking Module's service methods.
***
## Locks in Nested Workflows
[Nested workflows](https://docs.medusajs.com/learn/fundamentals/workflows/execute-another-workflow/index.html.md) can't acquire locks. So, if you're using a workflow that acquires locks within another workflow, the parent workflow must handle the locking mechanism.
For example, consider a custom workflow that executes Medusa's [completeCartWorkflow](https://docs.medusajs.com/resources/references/medusa-workflows/completeCartWorkflow/index.html.md). The `completeCartWorkflow` acquires locks on the cart to prevent race conditions.
Therefore, the custom workflow must acquire and release the locks around the `completeCartWorkflow` execution. For example:
```ts title="src/workflows/custom-complete-cart.ts" highlights={nestedWorkflowLockHighlights}
import { createWorkflow } from "@medusajs/framework/workflows-sdk"
import {
acquireLockStep,
releaseLockStep,
completeCartWorkflow
} from "@medusajs/medusa/core-flows"
type WorkflowInput = {
cart_id: string;
}
export const customCompleteCartWorkflow = createWorkflow(
"custom-complete-cart",
(input: WorkflowInput) => {
// acquire the same lock as the nested workflow
acquireLockStep({
key: input.cart_id,
timeout: 30,
ttl: 60 * 2,
})
// Execute the nested workflow
completeCartWorkflow.runAsStep({
input: {
id: input.cart_id,
}
})
// release the lock after the nested workflow is complete
releaseLockStep({
key: input.cart_id,
})
}
)
```
In the example above, the `customCompleteCartWorkflow` acquires a lock on the cart before executing the `completeCartWorkflow` and releases it afterward.
Make sure you're acquiring the same lock key as the nested workflow.
# Long-Running Workflows
@@ -45263,44 +45314,50 @@ For example, Medusa uses the Locking Module in inventory management to ensure th
You can use the Locking Module as part of the [workflows](https://docs.medusajs.com/docs/learn/fundamentals/workflows/index.html.md) you build for your custom features. A workflow is a special function composed of a series of steps that guarantees data consistency and reliable roll-back mechanism.
In a step of your workflow, you can resolve the Locking Module's service and use its methods to execute an asynchronous job, acquire a lock, or release locks.
In a workflow, you can either use:
- Medusa's [acquireLockStep](https://docs.medusajs.com/references/medusa-workflows/steps/acquireLockStep/index.html.md) and [releaseLockStep](https://docs.medusajs.com/references/medusa-workflows/steps/releaseLockStep/index.html.md) to create locks around critical steps in your workflow.
- The Locking Module's service directly in your steps to have more control over the locking mechanism
For example:
```ts
import { Modules } from "@medusajs/framework/utils"
import {
createStep,
createWorkflow,
} from "@medusajs/framework/workflows-sdk"
### Workflow
const step1 = createStep(
"step-1",
async ({}, { container }) => {
const lockingModuleService = container.resolve(
Modules.LOCKING
)
const productModuleService = container.resolve(
Modules.PRODUCT
)
```ts title="src/workflows/charge-customer.ts" highlights={workflowLockHighlights}
import { createWorkflow } from "@medusajs/framework/workflows-sdk"
import { acquireLockStep, releaseLockStep } from "@medusajs/medusa/core-flows"
import { chargeCustomerStep } from "./steps/charge-customer-step"
await lockingModuleService.execute("prod_123", async () => {
await productModuleService.deleteProduct("prod_123")
type WorkflowInput = {
customer_id: string;
order_id: string;
}
export const chargeCustomerWorkflow = createWorkflow(
"charge-customer",
(input: WorkflowInput) => {
acquireLockStep({
key: input.order_id,
// Attempt to acquire the lock for two seconds before timing out
timeout: 2,
// Lock is only held for a maximum of ten seconds
ttl: 10,
})
}
)
export const workflow = createWorkflow(
"workflow-1",
() => {
step1()
chargeCustomerStep(input)
releaseLockStep({
key: input.order_id,
})
}
)
```
In the example above, you create a workflow that has a step. In the step, you resolve the services of the Locking and Product modules from the [Medusa container](https://docs.medusajs.com/docs/learn/fundamentals/medusa-container/index.html.md).
In the example above, you create a workflow that acquires a lock on an order using the `acquireLockStep` before charging a customer. The lock is then released using the `releaseLockStep` after the operation is complete.
Then, you use the `execute` method of the Locking Module to acquire a lock for the product with the ID `prod_123` and execute an asynchronous function, which deletes the product.
This ensures that only one instance of the workflow can modify the order at a time, preventing issues like double charging the customer.
### Step
***
+1 -1
View File
@@ -441,7 +441,7 @@ export const sidebars = [
{
type: "link",
path: "/learn/fundamentals/workflows/execute-another-workflow",
title: "Execute Nested Workflows",
title: "Nested Workflows",
},
{
type: "link",