fix(workflow-engine-*): Prevent passing shared context reference (#11873)

* fix(workflow-engine-*): Prevent passing shared context reference

* fix(workflow-engine-*): Prevent passing shared context reference

* prevent tests from hanging

* fix event handling

* add integration tests

* use interval for scheduled in tests

* skip tests for now

* Create silent-glasses-enjoy.md

* fix cancel

* changeset

* push multiple aliases

* test multiple field alias

* increase wait time to index on test

---------

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
Co-authored-by: Carlos R. L. Rodrigues <rodrigolr@gmail.com>
This commit is contained in:
Adrien de Peretti
2025-04-09 10:39:29 +02:00
committed by GitHub
co-authored by Carlos R. L. Rodrigues Carlos R. L. Rodrigues
parent 2a18a75353
commit 13e159d8ad
18 changed files with 353 additions and 58 deletions
@@ -383,6 +383,11 @@ export class RemoteJoiner {
`Cannot add alias "${alias}" for "${extend.serviceName}". It is already defined for Entity "${extend.entity}".`
)
}
service_.fieldAlias[alias].push({
...objAlias,
entity: extend.entity,
})
} else {
service_.fieldAlias[alias] = {
...objAlias,
@@ -741,6 +741,7 @@ export class TransactionOrchestrator extends EventEmitter {
this.emit(DistributedTransactionEvent.FINISH, { transaction })
}
const asyncStepsToStart: any[] = []
for (const step of nextSteps.next) {
const curState = step.getStates()
const type = step.isCompensating()
@@ -923,8 +924,8 @@ export class TransactionOrchestrator extends EventEmitter {
return await transaction.handler(...handlerArgs)
}
execution.push(
transaction.saveCheckpoint().then(() => {
asyncStepsToStart.push({
handler: async () => {
let promise: Promise<unknown>
if (TransactionOrchestrator.traceStep) {
@@ -936,7 +937,7 @@ export class TransactionOrchestrator extends EventEmitter {
promise = stepHandler()
}
promise
return promise
.then(async (response: any) => {
const output = response?.__type ? response.output : response
@@ -990,8 +991,8 @@ export class TransactionOrchestrator extends EventEmitter {
response,
})
})
})
)
},
})
}
}
@@ -1005,6 +1006,10 @@ export class TransactionOrchestrator extends EventEmitter {
}
}
if (asyncStepsToStart.length > 0) {
execution.push(...asyncStepsToStart.map((step) => step.handler()))
}
await promiseAll(execution)
if (nextSteps.next.length === 0) {
@@ -391,6 +391,7 @@ export class LocalWorkflow {
async cancel(
transactionOrTransactionId: string | DistributedTransactionType,
_?: unknown, // not used but a common argument on other methods called dynamically
context?: Context,
subscribe?: DistributedTransactionEvents
) {
@@ -73,4 +73,9 @@ export type Context<TManager = unknown> = {
* A string indicating the idempotencyKey of the parent workflow execution.
*/
parentStepIdempotencyKey?: string
/**
* preventReleaseEvents
*/
preventReleaseEvents?: boolean
}
@@ -91,9 +91,10 @@ function createContextualWorkflowRunner<
flow.container = executionContainer
}
const { eventGroupId, parentStepIdempotencyKey } = context
const { eventGroupId, parentStepIdempotencyKey, preventReleaseEvents } =
context
if (!parentStepIdempotencyKey) {
if (!preventReleaseEvents) {
attachOnFinishReleaseEvents(events, flow, { logOnError })
}
@@ -196,6 +196,7 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
transactionId:
step.__step__ + "-" + (stepContext.transactionId ?? ulid()),
parentStepIdempotencyKey: stepContext.idempotencyKey,
preventReleaseEvents: true,
},
})
@@ -207,15 +208,12 @@ export function createWorkflow<TData, TResult, THooks extends any[]>(
)
},
async (transaction, stepContext) => {
if (!transaction) {
return
}
const { container, ...sharedContext } = stepContext
const transactionId = step.__step__ + "-" + stepContext.transactionId
await workflow(container).cancel({
transaction: (transaction as WorkflowResult<any>).transaction,
transactionId: isString(transaction) ? transaction : undefined,
transaction: (transaction as WorkflowResult<any>)?.transaction,
transactionId,
container,
context: {
...sharedContext,