fix(cancel-workflow-exec): move cancellation tracking for multi-task envs to redis (#2573)

* fix(cancel-workflow-exec): move cancellation tracking for multi-task envs to redis

* cleanup cancellation keys after execution
This commit is contained in:
Vikhyath Mondreti
2025-12-24 11:51:09 -08:00
committed by GitHub
parent cb8b9c547a
commit 77521a3a57
8 changed files with 234 additions and 23 deletions

View File

@@ -1,3 +1,4 @@
import { isExecutionCancelled, isRedisCancellationEnabled } from '@/lib/execution/cancellation'
import { createLogger } from '@/lib/logs/console/logger'
import { BlockType } from '@/executor/constants'
import type { DAG } from '@/executor/dag/builder'
@@ -23,6 +24,10 @@ export class ExecutionEngine {
private finalOutput: NormalizedBlockOutput = {}
private pausedBlocks: Map<string, PauseMetadata> = new Map()
private allowResumeTriggers: boolean
private cancelledFlag = false
private lastCancellationCheck = 0
private readonly useRedisCancellation: boolean
private readonly CANCELLATION_CHECK_INTERVAL_MS = 500
constructor(
private context: ExecutionContext,
@@ -31,6 +36,35 @@ export class ExecutionEngine {
private nodeOrchestrator: NodeExecutionOrchestrator
) {
this.allowResumeTriggers = this.context.metadata.resumeFromSnapshot === true
this.useRedisCancellation = isRedisCancellationEnabled() && !!this.context.executionId
}
private async checkCancellation(): Promise<boolean> {
if (this.cancelledFlag) {
return true
}
if (this.useRedisCancellation) {
const now = Date.now()
if (now - this.lastCancellationCheck < this.CANCELLATION_CHECK_INTERVAL_MS) {
return false
}
this.lastCancellationCheck = now
const cancelled = await isExecutionCancelled(this.context.executionId!)
if (cancelled) {
this.cancelledFlag = true
logger.info('Execution cancelled via Redis', { executionId: this.context.executionId })
}
return cancelled
}
if (this.context.abortSignal?.aborted) {
this.cancelledFlag = true
return true
}
return false
}
async run(triggerBlockId?: string): Promise<ExecutionResult> {
@@ -39,7 +73,7 @@ export class ExecutionEngine {
this.initializeQueue(triggerBlockId)
while (this.hasWork()) {
if (this.context.abortSignal?.aborted && this.executing.size === 0) {
if ((await this.checkCancellation()) && this.executing.size === 0) {
break
}
await this.processQueue()
@@ -54,7 +88,7 @@ export class ExecutionEngine {
this.context.metadata.endTime = new Date(endTime).toISOString()
this.context.metadata.duration = endTime - startTime
if (this.context.abortSignal?.aborted) {
if (this.cancelledFlag) {
return {
success: false,
output: this.finalOutput,
@@ -75,7 +109,7 @@ export class ExecutionEngine {
this.context.metadata.endTime = new Date(endTime).toISOString()
this.context.metadata.duration = endTime - startTime
if (this.context.abortSignal?.aborted) {
if (this.cancelledFlag) {
return {
success: false,
output: this.finalOutput,
@@ -234,7 +268,7 @@ export class ExecutionEngine {
private async processQueue(): Promise<void> {
while (this.readyQueue.length > 0) {
if (this.context.abortSignal?.aborted) {
if (await this.checkCancellation()) {
break
}
const nodeId = this.dequeue()