fix(core-flows): lock draft order workflows (#13668)

I added a lock in all workflows of the draft order. I don't think there are drawbacks and it will make sure we don't run into concurrency issues in with draft orders. I don't see why we would not add this in the order workflows, let me know and I can add it too.

I also didn't see any workflow that is long enough to justify adding a timeout of more than 2 seconds, but let me know if you think otherwise, we can discuss adjustments :)

CLOSES-1228
This commit is contained in:
William Bouchard
2025-10-03 14:05:22 -04:00
committed by GitHub
parent 2d43a498cf
commit 8acfb88c2b
21 changed files with 216 additions and 26 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/core-flows": patch
---
fix(core-flows): lock draft order workflows
@@ -25,6 +25,7 @@ import {
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const addDraftOrderItemsWorkflowId = "add-draft-order-items"
@@ -56,6 +57,12 @@ export const addDraftOrderItemsWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.OrderEditAddNewItemWorkflowInput>
) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & { promotions: { code: string }[] } =
useRemoteQueryStep({
entry_point: "orders",
@@ -142,6 +149,10 @@ export const addDraftOrderItemsWorkflow = createWorkflow(
input: orderChangeActionInput,
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -23,6 +23,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validatePromoCodesToAddStep } from "../steps/validate-promo-codes-to-add"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const addDraftOrderPromotionWorkflowId = "add-draft-order-promotion"
@@ -63,6 +64,12 @@ export interface AddDraftOrderPromotionWorkflowInput {
export const addDraftOrderPromotionWorkflow = createWorkflow(
addDraftOrderPromotionWorkflowId,
function (input: WorkflowData<AddDraftOrderPromotionWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
@@ -131,6 +138,10 @@ export const addDraftOrderPromotionWorkflow = createWorkflow(
input: orderChangeActionInput,
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -31,6 +31,7 @@ import { prepareShippingMethod } from "../../order/utils/prepare-shipping-method
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
const validateShippingOptionStep = createStep(
"validate-shipping-option",
@@ -99,6 +100,12 @@ export interface AddDraftOrderShippingMethodsWorkflowInput {
export const addDraftOrderShippingMethodsWorkflow = createWorkflow(
addDraftOrderShippingMethodsWorkflowId,
function (input: WorkflowData<AddDraftOrderShippingMethodsWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & {
promotions: {
code: string
@@ -232,6 +239,10 @@ export const addDraftOrderShippingMethodsWorkflow = createWorkflow(
input: [orderChangeActionInput],
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(order.id))
}
)
@@ -8,6 +8,7 @@ import type { OrderDTO, OrderWorkflow } from "@medusajs/framework/types"
import { useRemoteQueryStep } from "../../common"
import { createOrderChangeStep, previewOrderChangeStep } from "../../order"
import { validateDraftOrderStep } from "../steps"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const beginDraftOrderEditWorkflowId = "begin-draft-order-edit"
@@ -35,6 +36,12 @@ export const beginDraftOrderEditWorkflowId = "begin-draft-order-edit"
export const beginDraftOrderEditWorkflow = createWorkflow(
beginDraftOrderEditWorkflowId,
function (input: WorkflowData<OrderWorkflow.BeginorderEditWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
@@ -57,6 +64,10 @@ export const beginDraftOrderEditWorkflow = createWorkflow(
createOrderChangeStep(orderChangeInput)
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -17,6 +17,7 @@ import { restoreDraftOrderShippingMethodsStep } from "../steps/restore-draft-ord
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const cancelDraftOrderEditWorkflowId = "cancel-draft-order-edit"
@@ -52,6 +53,12 @@ export interface CancelDraftOrderEditWorkflowInput {
export const cancelDraftOrderEditWorkflow = createWorkflow(
cancelDraftOrderEditWorkflowId,
function (input: WorkflowData<CancelDraftOrderEditWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & {
promotions: {
code: string
@@ -166,5 +173,9 @@ export const cancelDraftOrderEditWorkflow = createWorkflow(
shippingMethods: shippingToRestore as any,
})
})
releaseLockStep({
key: input.order_id,
})
}
)
@@ -1,31 +1,17 @@
import {
ChangeActionType,
MathBN,
OrderChangeStatus,
} from "@medusajs/framework/utils"
import {
createWorkflow,
transform,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import {
BigNumberInput,
OrderChangeDTO,
OrderDTO,
} from "@medusajs/framework/types"
import { ChangeActionType, MathBN, OrderChangeStatus, } from "@medusajs/framework/utils"
import { createWorkflow, transform, WorkflowResponse, } from "@medusajs/framework/workflows-sdk"
import { BigNumberInput, OrderChangeDTO, OrderDTO, } from "@medusajs/framework/types"
import { reserveInventoryStep } from "../../cart"
import {
prepareConfirmInventoryInput,
requiredOrderFieldsForInventoryConfirmation,
} from "../../cart/utils/prepare-confirm-inventory-input"
import { useRemoteQueryStep } from "../../common"
import {
createOrUpdateOrderPaymentCollectionWorkflow,
previewOrderChangeStep,
} from "../../order"
import { createOrUpdateOrderPaymentCollectionWorkflow, previewOrderChangeStep, } from "../../order"
import { confirmOrderChanges } from "../../order/steps/confirm-order-changes"
import { deleteReservationsByLineItemsStep } from "../../reservation"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const confirmDraftOrderEditWorkflowId = "confirm-draft-order-edit"
@@ -63,6 +49,12 @@ export interface ConfirmDraftOrderEditWorkflowInput {
export const confirmDraftOrderEditWorkflow = createWorkflow(
confirmDraftOrderEditWorkflowId,
function (input: ConfirmDraftOrderEditWorkflowInput) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: [
@@ -221,6 +213,10 @@ export const confirmDraftOrderEditWorkflow = createWorkflow(
},
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(orderPreview)
}
)
@@ -6,6 +6,7 @@ import {
import {
createStep,
createWorkflow,
parallelize,
StepResponse,
WorkflowData,
WorkflowResponse,
@@ -13,6 +14,7 @@ import {
import type { IOrderModuleService, OrderDTO } from "@medusajs/framework/types"
import { emitEventStep, useRemoteQueryStep } from "../../common"
import { validateDraftOrderStep } from "../steps/validate-draft-order"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const convertDraftOrderWorkflowId = "convert-draft-order"
@@ -99,6 +101,12 @@ export const convertDraftOrderWorkflow = createWorkflow(
function (
input: WorkflowData<ConvertDraftOrderWorkflowInput>
): WorkflowResponse<OrderDTO> {
acquireLockStep({
key: input.id,
timeout: 2,
ttl: 10,
})
const order = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
@@ -113,10 +121,15 @@ export const convertDraftOrderWorkflow = createWorkflow(
const updatedOrder = convertDraftOrderStep({ id: input.id })
emitEventStep({
eventName: OrderWorkflowEvents.PLACED,
data: { id: updatedOrder.id },
})
parallelize(
releaseLockStep({
key: input.id,
}),
emitEventStep({
eventName: OrderWorkflowEvents.PLACED,
data: { id: updatedOrder.id },
})
)
return new WorkflowResponse(updatedOrder)
}
@@ -1,9 +1,9 @@
import {
WorkflowData,
WorkflowResponse,
createStep,
createWorkflow,
transform,
WorkflowData,
WorkflowResponse,
} from "@medusajs/framework/workflows-sdk"
import type { OrderDTO } from "@medusajs/framework/types"
import { Modules } from "@medusajs/framework/utils"
@@ -16,6 +16,7 @@ import { createDraftOrderShippingMethodAdjustmentsStep } from "../steps/create-d
import { removeDraftOrderLineItemAdjustmentsStep } from "../steps/remove-draft-order-line-item-adjustments"
import { removeDraftOrderShippingMethodAdjustmentsStep } from "../steps/remove-draft-order-shipping-method-adjustments"
import { updateDraftOrderPromotionsStep } from "../steps/update-draft-order-promotions"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const refreshDraftOrderAdjustmentsWorkflowId =
"refresh-draft-order-adjustments"
@@ -78,6 +79,12 @@ export interface RefreshDraftOrderAdjustmentsWorkflowInput {
export const refreshDraftOrderAdjustmentsWorkflow = createWorkflow(
refreshDraftOrderAdjustmentsWorkflowId,
function (input: WorkflowData<RefreshDraftOrderAdjustmentsWorkflowInput>) {
acquireLockStep({
key: input.order.id,
timeout: 2,
ttl: 10,
})
const promotionCodesToApply = getPromotionCodesToApply({
cart: input.order,
promo_codes: input.promo_codes,
@@ -118,6 +125,10 @@ export const refreshDraftOrderAdjustmentsWorkflow = createWorkflow(
})
)
releaseLockStep({
key: input.order.id,
})
return new WorkflowResponse(void 0)
}
)
@@ -21,6 +21,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validateDraftOrderRemoveActionItemStep } from "../steps/validate-draft-order-remove-action-item"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const removeDraftOrderActionItemWorkflowId =
"remove-draft-order-action-item"
@@ -50,6 +51,12 @@ export const removeDraftOrderActionItemWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.DeleteOrderEditItemActionWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & {
promotions: {
code: string
@@ -111,6 +118,10 @@ export const removeDraftOrderActionItemWorkflow = createWorkflow(
})
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -25,6 +25,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validateDraftOrderShippingMethodActionStep } from "../steps/validate-draft-order-shipping-method-action"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const removeDraftOrderActionShippingMethodWorkflowId =
"remove-draft-order-action-shipping-method"
@@ -54,6 +55,12 @@ export const removeDraftOrderActionShippingMethodWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.DeleteOrderEditShippingMethodWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
@@ -119,6 +126,10 @@ export const removeDraftOrderActionShippingMethodWorkflow = createWorkflow(
})
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -23,6 +23,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validatePromoCodesToRemoveStep } from "../steps/validate-promo-codes-to-remove"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const removeDraftOrderPromotionsWorkflowId =
"remove-draft-order-promotions"
@@ -64,6 +65,12 @@ export interface RemoveDraftOrderPromotionsWorkflowInput {
export const removeDraftOrderPromotionsWorkflow = createWorkflow(
removeDraftOrderPromotionsWorkflowId,
function (input: WorkflowData<RemoveDraftOrderPromotionsWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
@@ -132,6 +139,10 @@ export const removeDraftOrderPromotionsWorkflow = createWorkflow(
input: orderChangeActionInput,
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -19,6 +19,7 @@ import {
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const removeDraftOrderShippingMethodWorkflowId =
"remove-draft-order-shipping-method"
@@ -60,6 +61,12 @@ export interface RemoveDraftOrderShippingMethodWorkflowInput {
export const removeDraftOrderShippingMethodWorkflow = createWorkflow(
removeDraftOrderShippingMethodWorkflowId,
function (input: WorkflowData<RemoveDraftOrderShippingMethodWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & {
promotions: {
code: string
@@ -136,6 +143,10 @@ export const removeDraftOrderShippingMethodWorkflow = createWorkflow(
input: orderChangeActionInput,
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(order.id))
}
)
@@ -12,6 +12,7 @@ import {
updateOrderChangesStep,
} from "../../order"
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const requestDraftOrderEditId = "request-draft-order-edit"
@@ -71,6 +72,12 @@ export type RequestDraftOrderEditWorkflowInput = {
export const requestDraftOrderEditWorkflow = createWorkflow(
requestDraftOrderEditId,
function (input: RequestDraftOrderEditWorkflowInput) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "version", "status", "is_draft_order", "canceled_at"],
@@ -105,6 +112,10 @@ export const requestDraftOrderEditWorkflow = createWorkflow(
},
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(order.id))
}
)
@@ -22,6 +22,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validateDraftOrderUpdateActionItemStep } from "../steps/validate-draft-order-update-action-item"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const updateDraftOrderActionItemId = "update-draft-order-action-item"
@@ -53,6 +54,12 @@ export const updateDraftOrderActionItemWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.UpdateOrderEditAddNewItemWorkflowInput>
) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO & {
promotions: {
code: string
@@ -136,6 +143,10 @@ export const updateDraftOrderActionItemWorkflow = createWorkflow(
})
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -26,6 +26,7 @@ import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-chan
import { validateDraftOrderShippingMethodActionStep } from "../steps/validate-draft-order-shipping-method-action"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const updateDraftOrderActionShippingMethodWorkflowId =
"update-draft-order-action-shipping-method"
@@ -58,6 +59,12 @@ export const updateDraftOrderActionShippingMethodWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.UpdateOrderEditShippingMethodWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
@@ -161,6 +168,10 @@ export const updateDraftOrderActionShippingMethodWorkflow = createWorkflow(
})
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -27,6 +27,7 @@ import { getDraftOrderPromotionContextStep } from "../steps/get-draft-order-prom
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const updateDraftOrderItemWorkflowId = "update-draft-order-item"
@@ -55,6 +56,12 @@ export const updateDraftOrderItemWorkflow = createWorkflow(
function (
input: WorkflowData<OrderWorkflow.OrderEditUpdateItemQuantityWorkflowInput>
): WorkflowResponse<OrderPreviewDTO> {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: draftOrderFieldsForRefreshSteps,
@@ -135,6 +142,10 @@ export const updateDraftOrderItemWorkflow = createWorkflow(
})
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -25,6 +25,7 @@ import { updateDraftOrderShippingMethodStep } from "../steps/update-draft-order-
import { validateDraftOrderChangeStep } from "../steps/validate-draft-order-change"
import { draftOrderFieldsForRefreshSteps } from "../utils/fields"
import { refreshDraftOrderAdjustmentsWorkflow } from "./refresh-draft-order-adjustments"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const updateDraftOrderShippingMethodWorkflowId =
"update-draft-order-shipping-method"
@@ -83,6 +84,12 @@ export interface UpdateDraftOrderShippingMethodWorkflowInput {
export const updateDraftOrderShippingMethodWorkflow = createWorkflow(
updateDraftOrderShippingMethodWorkflowId,
function (input: WorkflowData<UpdateDraftOrderShippingMethodWorkflowInput>) {
acquireLockStep({
key: input.order_id,
timeout: 2,
ttl: 10,
})
const order: OrderDTO = useRemoteQueryStep({
entry_point: "orders",
fields: ["id", "status", "is_draft_order"],
@@ -171,6 +178,10 @@ export const updateDraftOrderShippingMethodWorkflow = createWorkflow(
input: [orderChangeActionInput as any],
})
releaseLockStep({
key: input.order_id,
})
return new WorkflowResponse(previewOrderChangeStep(input.order_id))
}
)
@@ -17,6 +17,7 @@ import {
import { emitEventStep, useRemoteQueryStep } from "../../common"
import { previewOrderChangeStep, registerOrderChangesStep } from "../../order"
import { validateDraftOrderStep } from "../steps/validate-draft-order"
import { acquireLockStep, releaseLockStep } from "../../locking"
export const updateDraftOrderWorkflowId = "update-draft-order"
@@ -149,6 +150,12 @@ export const updateDraftOrderStep = createStep(
export const updateDraftOrderWorkflow = createWorkflow(
updateDraftOrderWorkflowId,
function (input: WorkflowData<UpdateDraftOrderWorkflowInput>) {
acquireLockStep({
key: input.id,
timeout: 2,
ttl: 10,
})
const order = useRemoteQueryStep({
entry_point: "orders",
fields: [
@@ -312,6 +319,10 @@ export const updateDraftOrderWorkflow = createWorkflow(
const preview = previewOrderChangeStep(input.id)
releaseLockStep({
key: input.id,
})
return new WorkflowResponse(preview)
}
)
@@ -11,7 +11,7 @@ export interface AcquireLockStepInput {
*/
key: string | string[]
/**
* The maximum time to wait for acquiring the lock. If the lock cannot be acquired within this time, an error is thrown.
* The maximum time (in seconds) to wait for acquiring the lock. If the lock cannot be acquired within this time, an error is thrown.
*
* @defaultValue 0
*/