feat(workflows-sdk,orchestration): async step as background task (#6886)

This commit is contained in:
Carlos R. L. Rodrigues
2024-04-03 11:17:00 +02:00
committed by GitHub
parent 3dcf5224a1
commit a164c0d512
17 changed files with 290 additions and 34 deletions
@@ -639,7 +639,7 @@ export class TransactionOrchestrator extends EventEmitter {
error: Error | any,
{ endRetry }: { endRetry?: boolean } = {}
) => {
const ret = TransactionOrchestrator.setStepFailure(
await TransactionOrchestrator.setStepFailure(
transaction,
step,
error,
@@ -652,15 +652,20 @@ export class TransactionOrchestrator extends EventEmitter {
step.definition.retryInterval ?? 0
)
}
return ret
}
if (!isAsync) {
hasSyncSteps = true
execution.push(
transaction
.handler(step.definition.action + "", type, payload, transaction)
.handler(
step.definition.action + "",
type,
payload,
transaction,
step,
this
)
.then(async (response: any) => {
if (this.hasExpired({ transaction, step }, Date.now())) {
await this.checkStepTimeout(transaction, step)
@@ -703,8 +708,34 @@ export class TransactionOrchestrator extends EventEmitter {
step.definition.action + "",
type,
payload,
transaction
transaction,
step,
this
)
.then(async (response: any) => {
if (!step.definition.backgroundExecution) {
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
)
await transaction.scheduleRetry(
step,
step.definition.retryInterval ?? 0
)
})
.catch(async (error) => {
if (
PermanentStepFailureError.isPermanentStepFailureError(error)
@@ -3,6 +3,7 @@ import {
DistributedTransaction,
TransactionPayload,
} from "./distributed-transaction"
import { TransactionOrchestrator } from "./transaction-orchestrator"
import {
TransactionHandlerType,
TransactionState,
@@ -14,7 +15,9 @@ export type TransactionStepHandler = (
actionId: string,
handlerType: TransactionHandlerType,
payload: TransactionPayload,
transaction?: DistributedTransaction
transaction: DistributedTransaction,
step: TransactionStep,
orchestrator: TransactionOrchestrator
) => Promise<unknown>
/**
@@ -59,11 +59,17 @@ export type TransactionStepsDefinition = {
/**
* If true, the step is executed asynchronously. This means that the workflow will not wait for the response of this step.
* Async steps require to have their responses set using "setStepSuccess" or "setStepFailure".
* Async steps require to have their responses set using "setStepSuccess" or "setStepFailure", unless it is combined with "backgroundExecution: true".
* If combined with a timeout, and any response is not set within that interval, the step will be marked as "TransactionStepStatus.TIMEOUT" and the workflow will be reverted immediately.
*/
async?: boolean
/**
* It applies to "async" steps only, allowing them to run in the background and automatically complete without external intervention.
* It is ideal for time-consuming tasks that will be complete after the execution, contrasting with standard "async" operations that require a response to be set in a later stage.
*/
backgroundExecution?: boolean
/**
* If true, the compensation function for this step is executed asynchronously. Which means, the response has to be set using "setStepSuccess" or "setStepFailure".
*/
@@ -6,6 +6,7 @@ import {
TransactionMetadata,
TransactionModelOptions,
TransactionOrchestrator,
TransactionStep,
TransactionStepHandler,
TransactionStepsDefinition,
} from "../transaction"
@@ -39,12 +40,14 @@ export type WorkflowStepHandlerArguments = {
compensate: { [actions: string]: unknown }
metadata: TransactionMetadata
transaction: DistributedTransaction
step: TransactionStep
orchestrator: TransactionOrchestrator
context?: Context
}
export type WorkflowStepHandler = (
args: WorkflowStepHandlerArguments
) => unknown
) => Promise<unknown>
export class WorkflowManager {
protected static workflows: Map<string, WorkflowDefinition> = new Map()
@@ -173,8 +176,10 @@ export class WorkflowManager {
return async (
actionId: string,
handlerType: TransactionHandlerType,
payload?: any,
transaction?: DistributedTransaction
payload: any,
transaction: DistributedTransaction,
step: TransactionStep,
orchestrator: TransactionOrchestrator
) => {
const command = handlers.get(actionId)
@@ -196,6 +201,8 @@ export class WorkflowManager {
compensate,
metadata,
transaction: transaction as DistributedTransaction,
step,
orchestrator,
context,
})
}