feat(orchestration): skip on permanent failure (#12027)
What:
- Added step config `skipOnPermanentFailure`. Skip all the next steps when the current step fails. If a string is used, the workflow will resume from the given step.
- Fix `continueOnPermanentFailure` to continue the execution of the flow when a step fails.
```ts
createWorkflow("some-workflow", () => {
errorStep().config({
skipOnPermanentFailure: true,
})
nextStep1() // skipped
nextStep2() // skipped
})
createWorkflow("some-workflow", () => {
errorStep().config({
skipOnPermanentFailure: "resume-from-here",
});
nextStep1(); // skipped
nextStep2(); // skipped
nextStep3().config({ name: "resume-from-here" }); // executed
nextStep4(); // executed
});
```
This commit is contained in:
committed by
GitHub
parent
1c5e82af51
commit
e180253d60
@@ -1246,7 +1246,7 @@ describe("Transaction Orchestrator", () => {
|
||||
expect(transaction.getState()).toBe(TransactionState.REVERTED)
|
||||
})
|
||||
|
||||
it("should continue the transaction and skip children steps when the Transaction Step Timeout is reached but the step is set to 'continueOnPermanentFailure'", async () => {
|
||||
it("should continue the transaction and skip children steps when the Transaction Step Timeout is reached but the step is set to 'skipOnPermanentFailure'", async () => {
|
||||
const mocks = {
|
||||
f1: jest.fn(() => {
|
||||
return "content f1"
|
||||
@@ -1313,7 +1313,7 @@ describe("Transaction Orchestrator", () => {
|
||||
{
|
||||
timeout: 0.1, // 100ms
|
||||
action: "action2",
|
||||
continueOnPermanentFailure: true,
|
||||
skipOnPermanentFailure: true,
|
||||
next: {
|
||||
action: "action4",
|
||||
},
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
isDefined,
|
||||
isErrorLike,
|
||||
isObject,
|
||||
isString,
|
||||
MedusaError,
|
||||
promiseAll,
|
||||
serializeError,
|
||||
@@ -437,7 +438,10 @@ export class TransactionOrchestrator extends EventEmitter {
|
||||
} else if (curState.state === TransactionStepState.REVERTED) {
|
||||
hasReverted = true
|
||||
} else if (curState.state === TransactionStepState.FAILED) {
|
||||
if (stepDef.definition.continueOnPermanentFailure) {
|
||||
if (
|
||||
stepDef.definition.continueOnPermanentFailure ||
|
||||
stepDef.definition.skipOnPermanentFailure
|
||||
) {
|
||||
hasIgnoredFailure = true
|
||||
} else {
|
||||
hasFailed = true
|
||||
@@ -696,12 +700,28 @@ export class TransactionOrchestrator extends EventEmitter {
|
||||
|
||||
if (!step.isCompensating()) {
|
||||
if (
|
||||
step.definition.continueOnPermanentFailure &&
|
||||
(step.definition.continueOnPermanentFailure ||
|
||||
step.definition.skipOnPermanentFailure) &&
|
||||
!TransactionTimeoutError.isTransactionTimeoutError(timeoutError!)
|
||||
) {
|
||||
for (const childStep of step.next) {
|
||||
const child = flow.steps[childStep]
|
||||
child.changeState(TransactionStepState.SKIPPED_FAILURE)
|
||||
if (step.definition.skipOnPermanentFailure) {
|
||||
const until = isString(step.definition.skipOnPermanentFailure)
|
||||
? step.definition.skipOnPermanentFailure
|
||||
: undefined
|
||||
|
||||
let stepsToSkip: string[] = [...step.next]
|
||||
while (stepsToSkip.length > 0) {
|
||||
const currentStep = flow.steps[stepsToSkip.shift()!]
|
||||
|
||||
if (until && currentStep.definition.action === until) {
|
||||
break
|
||||
}
|
||||
currentStep.changeState(TransactionStepState.SKIPPED_FAILURE)
|
||||
|
||||
if (currentStep.next?.length > 0) {
|
||||
stepsToSkip = stepsToSkip.concat(currentStep.next)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
flow.state = TransactionState.WAITING_TO_COMPENSATE
|
||||
@@ -1351,7 +1371,7 @@ export class TransactionOrchestrator extends EventEmitter {
|
||||
states[parent].next?.push(id)
|
||||
}
|
||||
|
||||
const definitionCopy = { ...obj }
|
||||
const definitionCopy = { ...obj } as TransactionStepsDefinition
|
||||
delete definitionCopy.next
|
||||
|
||||
if (definitionCopy.async) {
|
||||
|
||||
@@ -25,10 +25,17 @@ export type TransactionStepsDefinition = {
|
||||
|
||||
/**
|
||||
* Indicates whether the workflow should continue even if there is a permanent failure in this step.
|
||||
* In case it is set to true, the children steps of this step will not be executed and their status will be marked as TransactionStepState.SKIPPED_FAILURE.
|
||||
* In case it is set to true, the the current step will be marked as TransactionStepState.PERMANENT_FAILURE and the next steps will be executed.
|
||||
*/
|
||||
continueOnPermanentFailure?: boolean
|
||||
|
||||
/**
|
||||
* Indicates whether the workflow should skip all subsequent steps in case of a permanent failure in this step.
|
||||
* In case it is set to true, the next steps of the workflow will not be executed and their status will be marked as TransactionStepState.SKIPPED_FAILURE.
|
||||
* In case it is a string, the next steps until the step name provided will be skipped and the workflow will be resumed from the step provided.
|
||||
*/
|
||||
skipOnPermanentFailure?: boolean | string
|
||||
|
||||
/**
|
||||
* If true, no compensation action will be triggered for this step in case of a failure.
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user