fix: workflow async concurrency (#13769)

* executeAsync

* || 1

* wip

* stepId

* stepId

* wip

* wip

* continue versioning management changes

* fix and improve concurrency

* update in memory engine

* remove duplicated test

* fix script

* Create weak-drinks-confess.md

* fixes

* fix

* fix

* continuation

* centralize merge checkepoint

* centralize merge checkpoint

* fix locking

* rm only

* Continue improvements and fixes

* fixes

* fixes

* hasAwaiting will be recomputed

* fix orchestrator engine

* bump version on async parallel steps only

* mark as delivered fix

* changeset

* check partitions

* avoid saving when having parent step

* cart test

---------

Co-authored-by: Carlos R. L. Rodrigues <rodrigolr@gmail.com>
Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2025-10-20 15:29:19 +02:00
committed by GitHub
parent d97a60d3c1
commit 516f5a3896
31 changed files with 2712 additions and 1406 deletions

View File

@@ -111,8 +111,12 @@ function createContextualWorkflowRunner<
flow.container = executionContainer
}
const { eventGroupId, parentStepIdempotencyKey, preventReleaseEvents } =
context
const {
eventGroupId,
parentStepIdempotencyKey,
preventReleaseEvents,
cancelingFromParentStep,
} = context
if (!preventReleaseEvents) {
attachOnFinishReleaseEvents(events, flow, { logOnError })
@@ -123,6 +127,7 @@ function createContextualWorkflowRunner<
parentStepIdempotencyKey,
sourcePath: options?.sourcePath,
preventReleaseEvents,
cancelingFromParentStep,
}
context.isCancelling = isCancel
@@ -609,13 +614,13 @@ function attachOnFinishReleaseEvents(
if (logOnError) {
const workflowName = transaction.getFlow().modelId
transaction
.getErrors()
.forEach((err) =>
logger.error(
`${workflowName}:${err?.action}:${err?.handlerType} - ${err?.error?.message}${EOL}${err?.error?.stack}`
)
transaction.getErrors().forEach((err) => {
const errMsg = err?.error?.message ? " - " + err?.error?.message : ""
logger.error(
`${workflowName}:${err?.action}:${err?.handlerType}${errMsg}${EOL}${err?.error?.stack}`
)
})
}
const eventBusService = (

View File

@@ -24,12 +24,25 @@ import {
CreateWorkflowComposerContext,
HookHandler,
ReturnWorkflow,
StepExecutionContext,
StepFunction,
WorkflowData,
} from "./type"
global[OrchestrationUtils.SymbolMedusaWorkflowComposerContext] = null
const buildTransactionId = (
step: { __step__: string },
stepContext: StepExecutionContext
) => {
return (
step.__step__ +
"-" +
(stepContext.transactionId ?? ulid()) +
(stepContext.attempt > 1 ? `-attempt-${stepContext.attempt}` : "")
)
}
/**
* This function creates a workflow with the provided name and a constructor function.
* The constructor function builds the workflow from steps created by the {@link createStep} function.
@@ -207,8 +220,7 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
const executionContext = {
...(sharedContext?.context ?? {}),
transactionId:
step.__step__ + "-" + (stepContext.transactionId ?? ulid()),
transactionId: buildTransactionId(step, stepContext),
parentStepIdempotencyKey: stepContext.idempotencyKey,
preventReleaseEvents: true,
runId: stepContext.runId,
@@ -218,7 +230,10 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
if (workflowEngine && isAsync) {
transaction = await workflowEngine.run(name, {
input: stepInput as any,
transactionId: executionContext.transactionId,
context: executionContext,
throwOnError: false,
logOnError: true,
})
} else {
transaction = await workflow.run({
@@ -235,9 +250,6 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
},
async (transaction, stepContext) => {
// The step itself has failed, there is nothing to revert
if (!transaction) {
return
}
const { container, ...sharedContext } = stepContext
const isAsync = stepContext[" stepDefinition"]?.async
@@ -248,27 +260,28 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
const executionContext = {
...(sharedContext?.context ?? {}),
transactionId:
step.__step__ + "-" + (stepContext.transactionId ?? ulid()),
transactionId: buildTransactionId(step, stepContext),
parentStepIdempotencyKey: stepContext.idempotencyKey,
preventReleaseEvents: true,
cancelingFromParentStep: true,
}
const transactionId = step.__step__ + "-" + stepContext.transactionId
if (workflowEngine && isAsync) {
await workflowEngine.cancel(name, {
transactionId: transactionId,
transactionId: executionContext.transactionId,
context: executionContext,
})
} else {
await workflow(container).cancel({
transaction: (transaction as WorkflowResult<any>)?.transaction,
transactionId,
transaction: ((transaction as WorkflowResult<any>) ?? {})
?.transaction,
transactionId: executionContext.transactionId,
container,
context: executionContext,
})
}
return
}
)(input) as ReturnType<StepFunction<TData, TResult>>