docs: document locks for nested workflows (#14130)
* docs: document locks for nested workflows * smal text change * fix build error
This commit is contained in:
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.
|
||||
@@ -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",
|
||||
|
||||
@@ -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."
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
***
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in new issue
Block a user