3.7.15. Locking Operations in Workflows

In this chapter, you’ll learn how to lock operations in your workflows to avoid race conditions and ensure data consistency.

What is a Lock?#

A lock is a mechanism that restricts multiple accesses to a resource or a piece of code, preventing multiple processes from modifying it simultaneously.

Locks are particularly useful in workflows where concurrent executions may lead to commerce risks. For example, Medusa uses locks in its cart workflows to prevent issues like overselling products or double charging customers.

The Locking Module handles the locking mechanism using the underlying provider. You can use the Locking Module's service in your workflows to create locks around critical sections of your code.


How to Use Locks in Workflows#

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.

You can use these steps in your workflows to ensure that only one instance of a workflow can modify a resource at a time.

For example:

src/workflows/charge-customer.ts
1import { createWorkflow } from "@medusajs/framework/workflows-sdk"2import { acquireLockStep, releaseLockStep } from "@medusajs/medusa/core-flows"3import { chargeCustomerStep } from "./steps/charge-customer-step"4
5type WorkflowInput = {6  customer_id: string;7  order_id: string;8}9
10export const chargeCustomerWorkflow = createWorkflow(11  "charge-customer",12  (input: WorkflowInput) => {13    acquireLockStep({14      key: input.order_id,15      // Attempt to acquire the lock for two seconds before timing out16      timeout: 2,17      // Lock is only held for a maximum of ten seconds18      ttl: 10,19    })20
21    chargeCustomerStep(input)22
23    releaseLockStep({24      key: input.order_id,25    })26  }27)

In this example, the workflow attempts to acquire a lock on an order using its ID as the lock key.

Once the lock is acquired, it executes the chargeCustomerStep. After the step is complete, it releases the lock using the releaseLockStep.

If the lock cannot be acquired within the specified timeout period, the acquireLockStep will throw an error, and the workflow will fail.

Tip: If an error occurs in the workflow after a lock is acquired with acquireLockStep, the step's compensation function will release the lock automatically.

Locking Steps API#

Refer to the acquireLockStep and releaseLockStep for more information on the inputs of these steps.


How to Use Locks in Workflow Steps#

You can alternatively acquire and release locks within steps by resolving the Locking Module's service and using its acquire and release methods.

Warning: Do not acquire locks within a workflow and a step in the same workflow execution, as this can lead to deadlocks.

For example:

src/workflows/steps/charge-customer.ts
1import { createStep } from "@medusajs/framework/workflows-sdk"2
3type StepInput = {4  order_id: string5  customer_id: string6}7
8export const chargeCustomerStep = createStep(9  "charge-customer",10  async (input: StepInput, { container }) => {11    const lockingModuleService = container.resolve("locking")12
13    await lockingModuleService.acquire(input.order_id, {14      // Lock will auto-expire after 10 seconds15      expire: 10,16    })17
18    // TODO: charge customer19
20    await lockingModuleService.release(input.order_id)21  }22)

In this example, the chargeCustomerStep step acquires a lock on the order using the order_id as the lock key. After performing the operation, it releases the lock.

Tip: The step will wait until the lock is acquired based on the configured Locking Module Provider's options. For example, the Redis Locking Module Provider will wait five seconds before timing out.

Locking Module Service API#

Refer to the Locking Module reference for more information on the Locking Module's service methods.

Was this chapter helpful?
Ask Anything
FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break