fix(): Event group id propagation and event managements (#12157)

This commit is contained in:
Adrien de Peretti
2025-04-14 15:57:52 -03:00
committed by GitHub
parent 3a481290ea
commit 2f6963a5fb
22 changed files with 777 additions and 592 deletions
@@ -62,13 +62,13 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
{
payload: {
prop: 123,
}
)
},
})
await strategy.resume(transaction)
@@ -144,10 +144,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
expect(actionOrder).toEqual(["one", "two", "three", "four", "five", "six"])
@@ -216,10 +216,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -296,10 +296,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
expect(actionOrder).toEqual(["one", "two", "three"])
@@ -376,11 +376,11 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
{ prop: 123 }
)
payload: { prop: 123 },
})
await strategy.resume(transaction)
@@ -471,10 +471,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
const resposes = transaction.getContext()
@@ -538,10 +538,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
strategy.resume(transaction)
@@ -611,10 +611,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -678,10 +678,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -736,10 +736,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -797,13 +797,13 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
{
payload: {
myPayloadProp: "test",
}
)
},
})
await strategy.resume(transaction)
@@ -818,11 +818,10 @@ describe("Transaction Orchestrator", () => {
"firstMethod",
TransactionHandlerType.INVOKE
)
await strategy.registerStepSuccess(
mocktransactionId,
undefined,
transaction
)
await strategy.registerStepSuccess({
responseIdempotencyKey: mocktransactionId,
transaction,
})
expect(transaction.getState()).toBe(TransactionState.DONE)
expect(transaction.getFlow().hasWaitingSteps).toBe(false)
@@ -883,10 +882,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
const mocktransactionId = TransactionOrchestrator.getKeyName(
"transaction-name",
@@ -909,7 +908,11 @@ describe("Transaction Orchestrator", () => {
expect(mocks.two).toHaveBeenCalledTimes(0)
const registerBeforeAllowed = await strategy
.registerStepSuccess(mockSecondStepId, handler)
.registerStepSuccess({
responseIdempotencyKey: mockSecondStepId,
handler,
transaction,
})
.catch((e) => e.message)
expect(registerBeforeAllowed).toEqual(
@@ -917,11 +920,11 @@ describe("Transaction Orchestrator", () => {
)
expect(transaction.getState()).toBe(TransactionState.INVOKING)
const resumedTransaction = await strategy.registerStepFailure(
mocktransactionId,
null,
handler
)
const resumedTransaction = await strategy.registerStepFailure({
responseIdempotencyKey: mocktransactionId,
handler,
transaction,
})
expect(resumedTransaction.getState()).toBe(TransactionState.COMPENSATING)
expect(mocks.compensateOne).toHaveBeenCalledTimes(1)
@@ -932,13 +935,12 @@ describe("Transaction Orchestrator", () => {
"firstMethod",
TransactionHandlerType.COMPENSATE
)
await strategy.registerStepSuccess(
mocktransactionIdCompensate,
undefined,
resumedTransaction
)
await strategy.registerStepSuccess({
responseIdempotencyKey: mocktransactionIdCompensate,
transaction: resumedTransaction,
})
expect(resumedTransaction.getState()).toBe(TransactionState.REVERTED)
expect(transaction.getState()).toBe(TransactionState.REVERTED)
})
it("Should hold the status REVERTED if the steps failed and the compensation succeed and has some no compensations step set", async () => {
@@ -1010,10 +1012,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1082,10 +1084,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1123,10 +1125,10 @@ describe("Transaction Orchestrator", () => {
},
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1219,10 +1221,10 @@ describe("Transaction Orchestrator", () => {
},
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1328,10 +1330,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1448,10 +1450,10 @@ describe("Transaction Orchestrator", () => {
definition: flow,
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -1561,10 +1563,10 @@ describe("Transaction Orchestrator", () => {
},
})
const transaction = await strategy.beginTransaction(
"transaction_id_123",
handler
)
const transaction = await strategy.beginTransaction({
transactionId: "transaction_id_123",
handler,
})
await strategy.resume(transaction)
@@ -92,4 +92,9 @@ export class SkipExecutionError extends Error {
error?.name === "SkipExecutionError"
)
}
constructor(message?: string) {
super(message)
this.name = "SkipExecutionError"
}
}
@@ -512,10 +512,13 @@ export class TransactionOrchestrator extends EventEmitter {
}
}
private static async skipStep(
transaction: DistributedTransactionType,
private static async skipStep({
transaction,
step,
}: {
transaction: DistributedTransactionType
step: TransactionStep
): Promise<{
}): Promise<{
stopExecution: boolean
}> {
const hasStepTimedOut =
@@ -721,72 +724,25 @@ export class TransactionOrchestrator extends EventEmitter {
const flow = transaction.getFlow()
const nextSteps = await this.checkAllSteps(transaction)
const execution: Promise<void | unknown>[] = []
const hasTimedOut = await this.checkTransactionTimeout(
transaction,
nextSteps.current
)
if (hasTimedOut) {
if (await this.checkTransactionTimeout(transaction, nextSteps.current)) {
continue
}
if (nextSteps.remaining === 0) {
if (transaction.hasTimeout()) {
void transaction.clearTransactionTimeout()
}
await transaction.saveCheckpoint()
this.emit(DistributedTransactionEvent.FINISH, { transaction })
await this.finalizeTransaction(transaction)
return
}
const execution: Promise<void | unknown>[] = []
for (const step of nextSteps.next) {
const curState = step.getStates()
const type = step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE
const { stopStepExecution } = this.prepareStepForExecution(step, flow)
step.lastAttempt = Date.now()
step.attempts++
if (curState.state === TransactionStepState.NOT_STARTED) {
if (!step.startedAt) {
step.startedAt = Date.now()
}
if (step.isCompensating()) {
step.changeState(TransactionStepState.COMPENSATING)
if (step.definition.noCompensation) {
step.changeState(TransactionStepState.REVERTED)
continue
}
} else if (flow.state === TransactionState.INVOKING) {
step.changeState(TransactionStepState.INVOKING)
}
// Should stop the execution if next step cant be handled
if (!stopStepExecution) {
continue
}
step.changeStatus(TransactionStepStatus.WAITING)
const payload = new TransactionPayload(
{
model_id: flow.modelId,
idempotency_key: TransactionOrchestrator.getKeyName(
flow.modelId,
flow.transactionId,
step.definition.action!,
type
),
action: step.definition.action + "",
action_type: type,
attempt: step.attempts,
timestamp: Date.now(),
},
transaction.payload,
transaction.getContext()
)
if (step.hasTimeout() && !step.timedOutAt && step.attempts === 1) {
await transaction.scheduleStepTimeout(step, step.definition.timeout!)
}
@@ -800,217 +756,368 @@ export class TransactionOrchestrator extends EventEmitter {
? step.definition.compensateAsync
: step.definition.async
const setStepFailure = async (
error: Error | any,
{
endRetry,
response,
}: {
endRetry?: boolean
response?: unknown
} = {}
) => {
if (isDefined(response) && step.saveResponse) {
transaction.addResponse(
step.definition.action!,
step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE,
response
)
// Save checkpoint before executing step
await transaction.saveCheckpoint().catch((error) => {
if (SkipExecutionError.isSkipExecutionError(error)) {
continueExecution = false
return
}
const ret = await TransactionOrchestrator.setStepFailure(
transaction,
step,
error,
endRetry ? 0 : step.definition.maxRetries
)
throw error
})
if (isAsync && !ret.stopExecution) {
await transaction.scheduleRetry(step, 0)
}
return ret
if (!continueExecution) {
break
}
const traceData = {
action: step.definition.action + "",
type,
step_id: step.id,
step_uuid: step.uuid + "",
attempts: step.attempts,
failures: step.failures,
async: !!(type === "invoke"
? step.definition.async
: step.definition.compensateAsync),
idempotency_key: payload.metadata.idempotency_key,
}
const handlerArgs = [
step.definition.action + "",
type,
payload,
transaction,
step,
this,
] as Parameters<TransactionStepHandler>
const promise = this.createStepExecutionPromise(transaction, step)
if (!isAsync) {
const stepHandler = async () => {
return await transaction.handler(...handlerArgs)
}
let promise: Promise<unknown>
if (TransactionOrchestrator.traceStep) {
promise = TransactionOrchestrator.traceStep(stepHandler, traceData)
} else {
promise = stepHandler()
}
execution.push(
promise
.then(async (response: any) => {
if (this.hasExpired({ transaction, step }, Date.now())) {
await this.checkStepTimeout(transaction, step)
await this.checkTransactionTimeout(
transaction,
nextSteps.next.includes(step) ? nextSteps.next : [step]
)
}
const output = response?.__type ? response.output : response
if (SkipStepResponse.isSkipStepResponse(output)) {
await TransactionOrchestrator.skipStep(transaction, step)
return
}
await TransactionOrchestrator.setStepSuccess(
transaction,
step,
response
)
})
.catch(async (error) => {
const response = error?.getStepResponse?.()
if (this.hasExpired({ transaction, step }, Date.now())) {
await this.checkStepTimeout(transaction, step)
await this.checkTransactionTimeout(
transaction,
nextSteps.next.includes(step) ? nextSteps.next : [step]
)
}
if (
PermanentStepFailureError.isPermanentStepFailureError(error)
) {
await setStepFailure(error, {
endRetry: true,
response,
})
return
}
await setStepFailure(error, {
response,
})
})
this.executeSyncStep(promise, transaction, step, nextSteps)
)
} else {
const stepHandler = async () => {
return await transaction.handler(...handlerArgs)
}
execution.push(
transaction.saveCheckpoint().then(() => {
let promise: Promise<unknown>
if (TransactionOrchestrator.traceStep) {
promise = TransactionOrchestrator.traceStep(
stepHandler,
traceData
)
} else {
promise = stepHandler()
}
promise
.then(async (response: any) => {
const output = response?.__type ? response.output : response
if (SkipStepResponse.isSkipStepResponse(output)) {
await TransactionOrchestrator.skipStep(transaction, step)
} else {
if (
!step.definition.backgroundExecution ||
step.definition.nested
) {
const eventName =
DistributedTransactionEvent.STEP_AWAITING
transaction.emit(eventName, { step, transaction })
return
}
if (this.hasExpired({ transaction, step }, Date.now())) {
await this.checkStepTimeout(transaction, step)
await this.checkTransactionTimeout(
transaction,
nextSteps.next.includes(step) ? nextSteps.next : [step]
)
}
await TransactionOrchestrator.setStepSuccess(
transaction,
step,
response
)
}
// check nested flow
await transaction.scheduleRetry(step, 0)
})
.catch(async (error) => {
const response = error?.getStepResponse?.()
if (
PermanentStepFailureError.isPermanentStepFailureError(error)
) {
await setStepFailure(error, {
endRetry: true,
response,
})
return
}
await setStepFailure(error, {
response,
})
})
})
this.executeAsyncStep(promise, transaction, step, nextSteps)
)
}
}
try {
await transaction.saveCheckpoint()
} catch (error) {
if (SkipExecutionError.isSkipExecutionError(error)) {
break
} else {
throw error
}
}
await promiseAll(execution)
if (nextSteps.next.length === 0) {
continueExecution = false
}
}
// Recompute the current flow flags
await this.checkAllSteps(transaction)
await transaction.saveCheckpoint().catch((error) => {
if (!SkipExecutionError.isSkipExecutionError(error)) {
throw error
}
})
}
/**
* Finalize the transaction when all steps are complete
*/
private async finalizeTransaction(
transaction: DistributedTransactionType
): Promise<void> {
if (transaction.hasTimeout()) {
void transaction.clearTransactionTimeout()
}
await transaction.saveCheckpoint().catch((error) => {
if (!SkipExecutionError.isSkipExecutionError(error)) {
throw error
}
})
this.emit(DistributedTransactionEvent.FINISH, { transaction })
}
/**
* Prepare a step for execution by setting state and incrementing attempts
*/
private prepareStepForExecution(
step: TransactionStep,
flow: TransactionFlow
): { stopStepExecution: boolean } {
const curState = step.getStates()
step.lastAttempt = Date.now()
step.attempts++
if (curState.state === TransactionStepState.NOT_STARTED) {
if (!step.startedAt) {
step.startedAt = Date.now()
}
if (step.isCompensating()) {
step.changeState(TransactionStepState.COMPENSATING)
if (step.definition.noCompensation) {
step.changeState(TransactionStepState.REVERTED)
return { stopStepExecution: false }
}
} else if (flow.state === TransactionState.INVOKING) {
step.changeState(TransactionStepState.INVOKING)
}
}
step.changeStatus(TransactionStepStatus.WAITING)
return { stopStepExecution: true }
}
/**
* Create the payload for a step execution
*/
private createStepPayload(
transaction: DistributedTransactionType,
step: TransactionStep,
flow: TransactionFlow
): TransactionPayload {
const type = step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE
return new TransactionPayload(
{
model_id: flow.modelId,
idempotency_key: TransactionOrchestrator.getKeyName(
flow.modelId,
flow.transactionId,
step.definition.action!,
type
),
action: step.definition.action + "",
action_type: type,
attempt: step.attempts,
timestamp: Date.now(),
},
transaction.payload,
transaction.getContext()
)
}
/**
* Prepare handler arguments for step execution
*/
private prepareHandlerArgs(
transaction: DistributedTransactionType,
step: TransactionStep,
flow: TransactionFlow,
payload: TransactionPayload
): Parameters<TransactionStepHandler> {
const type = step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE
return [
step.definition.action + "",
type,
payload,
transaction,
step,
this,
] as Parameters<TransactionStepHandler>
}
/**
* Create the step execution promise with optional tracing
*/
private createStepExecutionPromise(
transaction: DistributedTransactionType,
step: TransactionStep
): () => Promise<any> {
const type = step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE
const handlerArgs = this.prepareHandlerArgs(
transaction,
step,
transaction.getFlow(),
this.createStepPayload(transaction, step, transaction.getFlow())
)
const traceData = {
action: step.definition.action + "",
type,
step_id: step.id,
step_uuid: step.uuid + "",
attempts: step.attempts,
failures: step.failures,
async: !!(type === "invoke"
? step.definition.async
: step.definition.compensateAsync),
idempotency_key: handlerArgs[2].metadata.idempotency_key,
}
const stepHandler = async () => {
return await transaction.handler(...handlerArgs)
}
// Return the appropriate promise based on tracing configuration
if (TransactionOrchestrator.traceStep) {
return () => TransactionOrchestrator.traceStep!(stepHandler, traceData)
} else {
return stepHandler
}
}
/**
* Execute a synchronous step and handle its result
*/
private executeSyncStep(
promiseFn: () => Promise<any>,
transaction: DistributedTransactionType,
step: TransactionStep,
nextSteps: { next: TransactionStep[] }
): Promise<void | unknown> {
return promiseFn()
.then(async (response: any) => {
await this.handleStepExpiration(transaction, step, nextSteps)
const output = response?.__type ? response.output : response
if (SkipStepResponse.isSkipStepResponse(output)) {
await TransactionOrchestrator.skipStep({
transaction,
step,
})
return
}
await this.handleStepSuccess(transaction, step, response)
})
.catch(async (error) => {
if (SkipExecutionError.isSkipExecutionError(error)) {
return
}
const response = error?.getStepResponse?.()
await this.handleStepExpiration(transaction, step, nextSteps)
if (PermanentStepFailureError.isPermanentStepFailureError(error)) {
await this.handleStepFailure(transaction, step, error, true, response)
return
}
await this.handleStepFailure(transaction, step, error, false, response)
})
}
/**
* Execute an asynchronous step and handle its result
*/
private executeAsyncStep(
promiseFn: () => Promise<any>,
transaction: DistributedTransactionType,
step: TransactionStep,
nextSteps: { next: TransactionStep[] }
): Promise<void | unknown> {
return promiseFn()
.then(async (response: any) => {
const output = response?.__type ? response.output : response
if (SkipStepResponse.isSkipStepResponse(output)) {
await TransactionOrchestrator.skipStep({
transaction,
step,
})
} else {
if (!step.definition.backgroundExecution || step.definition.nested) {
const eventName = DistributedTransactionEvent.STEP_AWAITING
transaction.emit(eventName, { step, transaction })
return
}
await this.handleStepExpiration(transaction, step, nextSteps)
await this.handleStepSuccess(transaction, step, response)
}
})
.catch(async (error) => {
if (SkipExecutionError.isSkipExecutionError(error)) {
return
}
const response = error?.getStepResponse?.()
if (PermanentStepFailureError.isPermanentStepFailureError(error)) {
await this.handleStepFailure(transaction, step, error, true, response)
return
}
await this.handleStepFailure(transaction, step, error, false, response)
})
}
/**
* Check if step or transaction has expired and handle timeouts
*/
private async handleStepExpiration(
transaction: DistributedTransactionType,
step: TransactionStep,
nextSteps: { next: TransactionStep[] }
): Promise<void> {
if (this.hasExpired({ transaction, step }, Date.now())) {
await this.checkStepTimeout(transaction, step)
await this.checkTransactionTimeout(
transaction,
nextSteps.next.includes(step) ? nextSteps.next : [step]
)
}
}
/**
* Handle successful step completion
*/
private async handleStepSuccess(
transaction: DistributedTransactionType,
step: TransactionStep,
response: unknown
): Promise<void> {
const isAsync = step.isCompensating()
? step.definition.compensateAsync
: step.definition.async
if (isDefined(response) && step.saveResponse) {
transaction.addResponse(
step.definition.action!,
step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE,
response
)
}
const ret = await TransactionOrchestrator.setStepSuccess(
transaction,
step,
response
)
if (isAsync && !ret.stopExecution) {
await transaction.scheduleRetry(step, 0)
}
}
/**
* Handle step failure
*/
private async handleStepFailure(
transaction: DistributedTransactionType,
step: TransactionStep,
error: Error | any,
isPermanent: boolean,
response?: unknown
): Promise<void> {
const isAsync = step.isCompensating()
? step.definition.compensateAsync
: step.definition.async
if (isDefined(response) && step.saveResponse) {
transaction.addResponse(
step.definition.action!,
step.isCompensating()
? TransactionHandlerType.COMPENSATE
: TransactionHandlerType.INVOKE,
response
)
}
const ret = await TransactionOrchestrator.setStepFailure(
transaction,
step,
error,
isPermanent ? 0 : step.definition.maxRetries
)
if (isAsync && !ret.stopExecution) {
await transaction.scheduleRetry(step, 0)
}
}
/**
@@ -1288,12 +1395,19 @@ export class TransactionOrchestrator extends EventEmitter {
* @param payload - payload to be passed to all the transaction steps
* @param flowMetadata - flow metadata which can include event group id for example
*/
public async beginTransaction(
transactionId: string,
handler: TransactionStepHandler,
payload?: unknown,
public async beginTransaction({
transactionId,
handler,
payload,
flowMetadata,
onLoad,
}: {
transactionId: string
handler: TransactionStepHandler
payload?: unknown
flowMetadata?: TransactionFlow["metadata"]
): Promise<DistributedTransactionType> {
onLoad?: (transaction: DistributedTransactionType) => Promise<void> | void
}): Promise<DistributedTransactionType> {
const existingTransaction =
await TransactionOrchestrator.loadTransactionById(this.id, transactionId)
@@ -1320,6 +1434,10 @@ export class TransactionOrchestrator extends EventEmitter {
)
}
if (onLoad) {
await onLoad(transaction)
}
return transaction
}
@@ -1423,11 +1541,15 @@ export class TransactionOrchestrator extends EventEmitter {
* @param handler - The handler function to execute the step
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
*/
public async skipStep(
responseIdempotencyKey: string,
handler?: TransactionStepHandler,
public async skipStep({
responseIdempotencyKey,
handler,
transaction,
}: {
responseIdempotencyKey: string
handler?: TransactionStepHandler
transaction?: DistributedTransactionType
): Promise<DistributedTransactionType> {
}): Promise<DistributedTransactionType> {
const [curTransaction, step] =
await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(
responseIdempotencyKey,
@@ -1440,7 +1562,10 @@ export class TransactionOrchestrator extends EventEmitter {
transaction: curTransaction,
})
await TransactionOrchestrator.skipStep(curTransaction, step)
await TransactionOrchestrator.skipStep({
transaction: curTransaction,
step,
})
await this.executeNext(curTransaction)
} else {
@@ -1459,12 +1584,19 @@ export class TransactionOrchestrator extends EventEmitter {
* @param transaction - The current transaction. If not provided it will be loaded based on the responseIdempotencyKey
* @param response - The response of the step
*/
public async registerStepSuccess(
responseIdempotencyKey: string,
handler?: TransactionStepHandler,
transaction?: DistributedTransactionType,
public async registerStepSuccess({
responseIdempotencyKey,
handler,
transaction,
response,
onLoad,
}: {
responseIdempotencyKey: string
handler?: TransactionStepHandler
transaction?: DistributedTransactionType
response?: unknown
): Promise<DistributedTransactionType> {
onLoad?: (transaction: DistributedTransactionType) => Promise<void> | void
}): Promise<DistributedTransactionType> {
const [curTransaction, step] =
await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(
responseIdempotencyKey,
@@ -1472,6 +1604,10 @@ export class TransactionOrchestrator extends EventEmitter {
transaction
)
if (onLoad) {
await onLoad(curTransaction)
}
if (step.getStates().status === TransactionStepStatus.WAITING) {
this.emit(DistributedTransactionEvent.RESUME, {
transaction: curTransaction,
@@ -1502,12 +1638,19 @@ export class TransactionOrchestrator extends EventEmitter {
* @param transaction - The current transaction
* @param response - The response of the step
*/
public async registerStepFailure(
responseIdempotencyKey: string,
error?: Error | any,
handler?: TransactionStepHandler,
public async registerStepFailure({
responseIdempotencyKey,
error,
handler,
transaction,
onLoad,
}: {
responseIdempotencyKey: string
error?: Error | any
handler?: TransactionStepHandler
transaction?: DistributedTransactionType
): Promise<DistributedTransactionType> {
onLoad?: (transaction: DistributedTransactionType) => Promise<void> | void
}): Promise<DistributedTransactionType> {
const [curTransaction, step] =
await TransactionOrchestrator.getTransactionAndStepFromIdempotencyKey(
responseIdempotencyKey,
@@ -1515,6 +1658,10 @@ export class TransactionOrchestrator extends EventEmitter {
transaction
)
if (onLoad) {
await onLoad(curTransaction)
}
if (step.getStates().status === TransactionStepStatus.WAITING) {
this.emit(DistributedTransactionEvent.RESUME, {
transaction: curTransaction,
@@ -52,11 +52,11 @@ export class GlobalWorkflow extends WorkflowManager {
const orchestrator = workflow.orchestrator
const transaction = await orchestrator.beginTransaction(
uniqueTransactionId,
workflow.handler(this.container, this.context),
input
)
const transaction = await orchestrator.beginTransaction({
transactionId: uniqueTransactionId,
handler: workflow.handler(this.container, this.context),
payload: input,
})
if (this.subscribe.onStepBegin) {
transaction.once("stepBegin", this.subscribe.onStepBegin)
@@ -104,12 +104,11 @@ export class GlobalWorkflow extends WorkflowManager {
}
})
return await workflow.orchestrator.registerStepSuccess(
idempotencyKey,
workflow.handler(this.container, this.context),
undefined,
response
)
return await workflow.orchestrator.registerStepSuccess({
responseIdempotencyKey: idempotencyKey,
handler: workflow.handler(this.container, this.context),
response,
})
}
async registerStepFailure(
@@ -137,10 +136,10 @@ export class GlobalWorkflow extends WorkflowManager {
}
})
return await workflow.orchestrator.registerStepFailure(
idempotencyKey,
return await workflow.orchestrator.registerStepFailure({
responseIdempotencyKey: idempotencyKey,
error,
workflow.handler(this.container, this.context)
)
handler: workflow.handler(this.container, this.context),
})
}
}
@@ -113,7 +113,7 @@ export class LocalWorkflow {
return target[prop]
}
return async (...args) => {
return (...args) => {
const ctxIndex = MedusaContext.getIndex(target, prop as string)
const hasContext = args[ctxIndex!]?.__type === MedusaContextType
@@ -125,8 +125,12 @@ export class LocalWorkflow {
args[ctxIndex] = context
}
} else if (hasContext) {
args[ctxIndex!].eventGroupId ??= this_.medusaContext?.eventGroupId
}
return await target[prop].apply(target, [...args])
const method = target[prop]
return method.apply(target, [...args])
}
},
})
@@ -355,12 +359,18 @@ export class LocalWorkflow {
this.medusaContext = context
const { handler, orchestrator } = this.workflow
const transaction = await orchestrator.beginTransaction(
uniqueTransactionId,
handler(this.container_, context),
input,
flowMetadata
)
const transaction = await orchestrator.beginTransaction({
transactionId: uniqueTransactionId,
handler: handler(this.container_, context),
payload: input,
flowMetadata,
onLoad: (transaction) => {
if (this.medusaContext) {
this.medusaContext.eventGroupId =
transaction.getFlow().metadata?.eventGroupId
}
},
})
const { cleanUpEventListeners } = this.registerEventCallbacks({
orchestrator,
@@ -402,6 +412,11 @@ export class LocalWorkflow {
? await this.getRunningTransaction(transactionOrTransactionId, context)
: transactionOrTransactionId
if (this.medusaContext) {
this.medusaContext.eventGroupId =
transaction.getFlow().metadata?.eventGroupId
}
const { cleanUpEventListeners } = this.registerEventCallbacks({
orchestrator,
transaction,
@@ -432,12 +447,17 @@ export class LocalWorkflow {
subscribe,
})
const transaction = await orchestrator.registerStepSuccess(
idempotencyKey,
handler(this.container_, context),
undefined,
response
)
const transaction = await orchestrator.registerStepSuccess({
responseIdempotencyKey: idempotencyKey,
handler: handler(this.container_, context),
response,
onLoad: (transaction) => {
if (this.medusaContext) {
this.medusaContext.eventGroupId =
transaction.getFlow().metadata?.eventGroupId
}
},
})
try {
return transaction
@@ -461,11 +481,17 @@ export class LocalWorkflow {
subscribe,
})
const transaction = await orchestrator.registerStepFailure(
idempotencyKey,
const transaction = await orchestrator.registerStepFailure({
responseIdempotencyKey: idempotencyKey,
error,
handler(this.container_, context)
)
handler: handler(this.container_, context),
onLoad: (transaction) => {
if (this.medusaContext) {
this.medusaContext.eventGroupId =
transaction.getFlow().metadata?.eventGroupId
}
},
})
try {
return transaction