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
@@ -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
|
||||
|
||||
***
|
||||
|
||||
|
||||
Reference in new issue
Block a user