@kb-labs/workflow-engine 2.107.0 → 2.110.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +8 -10
- package/dist/index.js +18 -29
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { WorkflowSpec, RunTrigger, IdempotencyKey, ConcurrencyGroup, WorkflowRun, JobRun, StepRun, RetryPolicy, ArtifactMergeConfig } from '@kb-labs/workflow-contracts';
|
|
2
2
|
import { ILogger, ICache, IEventBus, IAnalytics, ISnapshotManager, Unsubscribe, IJobScheduler, JobDefinition, JobHandle, CronExpression, JobFilter } from '@kb-labs/core-platform';
|
|
3
3
|
import { JobPriority, WorkflowEventName } from '@kb-labs/workflow-constants';
|
|
4
|
+
import { ClassifiedFailure, IExecutionBackend } from '@kb-labs/core-contracts';
|
|
4
5
|
import { ArtifactClient } from '@kb-labs/workflow-artifacts';
|
|
5
6
|
import { IEntityRegistry } from '@kb-labs/core-registry';
|
|
6
7
|
import { PlatformServices, JobHandlerDecl, WorkflowHandlerDecl, CronDecl, PluginContextDescriptor } from '@kb-labs/plugin-contracts';
|
|
7
|
-
import { IExecutionBackend } from '@kb-labs/core-contracts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* @deprecated Use ILogger from @kb-labs/core-platform instead.
|
|
@@ -229,7 +229,13 @@ declare class WorkflowEngine {
|
|
|
229
229
|
* Mark job as failed and optionally schedule retry.
|
|
230
230
|
* Implements exponential/linear backoff retry logic.
|
|
231
231
|
*/
|
|
232
|
-
markJobFailed(runId: string, jobId: string, error: Error,
|
|
232
|
+
markJobFailed(runId: string, jobId: string, error: Error, failure?: ClassifiedFailure, policyOverride?: false | {
|
|
233
|
+
max: number;
|
|
234
|
+
backoff?: 'exp' | 'lin';
|
|
235
|
+
initialIntervalMs?: number;
|
|
236
|
+
maxIntervalMs?: number;
|
|
237
|
+
on?: string[];
|
|
238
|
+
}): Promise<void>;
|
|
233
239
|
/**
|
|
234
240
|
* Recursively cancel jobs blocked on a failed/cancelled upstream job, so the
|
|
235
241
|
* DAG does not leave dependents stuck in 'queued'. Cancellation cascades:
|
|
@@ -300,14 +306,6 @@ declare class WorkflowEngine {
|
|
|
300
306
|
* Re-queues jobs that were interrupted during previous shutdown.
|
|
301
307
|
*/
|
|
302
308
|
resumeInterruptedJobs(): Promise<void>;
|
|
303
|
-
/**
|
|
304
|
-
* Determine if job should be retried based on retry policy.
|
|
305
|
-
*/
|
|
306
|
-
private shouldRetryJob;
|
|
307
|
-
/**
|
|
308
|
-
* Calculate backoff delay using exponential or linear strategy.
|
|
309
|
-
*/
|
|
310
|
-
private calculateBackoff;
|
|
311
309
|
/**
|
|
312
310
|
* Move permanently failed job to Dead Letter Queue.
|
|
313
311
|
*/
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parse, stringify } from 'yaml';
|
|
|
4
4
|
import { WorkflowSpecSchema, evaluateExpression } from '@kb-labs/workflow-contracts';
|
|
5
5
|
import { randomUUID } from 'crypto';
|
|
6
6
|
import { WORKFLOW_REDIS_CHANNEL, EVENT_NAMES, IDEMPOTENCY_TTL_ENV, CONCURRENCY_TTL_ENV } from '@kb-labs/workflow-constants';
|
|
7
|
+
import { classifyFailure, decideRetry } from '@kb-labs/core-retry';
|
|
7
8
|
import { createFileSystemArtifactClient } from '@kb-labs/workflow-artifacts';
|
|
8
9
|
import { existsSync } from 'fs';
|
|
9
10
|
import { nanoid } from 'nanoid';
|
|
@@ -794,7 +795,7 @@ var WorkflowEngine = class {
|
|
|
794
795
|
* Mark job as failed and optionally schedule retry.
|
|
795
796
|
* Implements exponential/linear backoff retry logic.
|
|
796
797
|
*/
|
|
797
|
-
async markJobFailed(runId, jobId, error,
|
|
798
|
+
async markJobFailed(runId, jobId, error, failure, policyOverride) {
|
|
798
799
|
const run = await this.stateStore.getRun(runId);
|
|
799
800
|
if (!run) {
|
|
800
801
|
this.logger.warn("Cannot mark job as failed: run not found", { runId, jobId });
|
|
@@ -820,13 +821,26 @@ var WorkflowEngine = class {
|
|
|
820
821
|
jobId,
|
|
821
822
|
attempt: (job.attempt || 0) + 1
|
|
822
823
|
});
|
|
824
|
+
const classified = failure ?? classifyFailure(error, { source: "execution", phase: "response" });
|
|
825
|
+
const retryConfig = policyOverride !== void 0 && typeof policyOverride !== "boolean" ? {
|
|
826
|
+
maxAttempts: policyOverride.max + 1,
|
|
827
|
+
retryOn: policyOverride.on ?? ["command", "network", "timeout", "rate_limit", "server", "infrastructure"],
|
|
828
|
+
initialDelayMs: policyOverride.initialIntervalMs ?? 1e3,
|
|
829
|
+
backoff: policyOverride.backoff === "lin" ? "linear" : "exponential",
|
|
830
|
+
maxDelayMs: policyOverride.maxIntervalMs ?? 3e4,
|
|
831
|
+
respectRetryAfter: true,
|
|
832
|
+
requireIdempotencyForUnsafeFailures: true
|
|
833
|
+
} : void 0;
|
|
834
|
+
const retryDecision = policyOverride === false ? { retry: false, delayMs: 0 } : retryConfig ? decideRetry({ failure: classified, attempt: job.attempt || 0, policy: retryConfig }) : decideRetry({ failure: classified, attempt: job.attempt || 0 });
|
|
823
835
|
this.analytics?.track("workflow.job.failed", {
|
|
824
836
|
runId,
|
|
825
837
|
jobId,
|
|
826
838
|
jobName: job.jobName,
|
|
827
839
|
attempt: (job.attempt || 0) + 1,
|
|
828
840
|
errorMessage: error.message,
|
|
829
|
-
willRetry:
|
|
841
|
+
willRetry: retryDecision.retry,
|
|
842
|
+
failureKind: classified.kind,
|
|
843
|
+
failureCode: classified.code
|
|
830
844
|
}).catch(() => {
|
|
831
845
|
});
|
|
832
846
|
await this.events.publish({
|
|
@@ -835,8 +849,8 @@ var WorkflowEngine = class {
|
|
|
835
849
|
jobId,
|
|
836
850
|
payload: { jobName: job.jobName, error: error.message, attempt: (job.attempt || 0) + 1 }
|
|
837
851
|
});
|
|
838
|
-
if (
|
|
839
|
-
const backoffMs =
|
|
852
|
+
if (retryDecision.retry) {
|
|
853
|
+
const backoffMs = retryDecision.delayMs;
|
|
840
854
|
this.logger.info("Scheduling job retry", {
|
|
841
855
|
runId,
|
|
842
856
|
jobId,
|
|
@@ -1295,31 +1309,6 @@ var WorkflowEngine = class {
|
|
|
1295
1309
|
this.logger.info("Resumed interrupted jobs", { count: resumedCount });
|
|
1296
1310
|
}
|
|
1297
1311
|
}
|
|
1298
|
-
/**
|
|
1299
|
-
* Determine if job should be retried based on retry policy.
|
|
1300
|
-
*/
|
|
1301
|
-
shouldRetryJob(job) {
|
|
1302
|
-
const retryPolicy = job.retries || { max: 3};
|
|
1303
|
-
const attempt = job.attempt || 0;
|
|
1304
|
-
return attempt < retryPolicy.max;
|
|
1305
|
-
}
|
|
1306
|
-
/**
|
|
1307
|
-
* Calculate backoff delay using exponential or linear strategy.
|
|
1308
|
-
*/
|
|
1309
|
-
calculateBackoff(attempt, policy) {
|
|
1310
|
-
const config = {
|
|
1311
|
-
backoff: policy?.backoff || "exp",
|
|
1312
|
-
initialIntervalMs: policy?.initialIntervalMs || 1e3,
|
|
1313
|
-
maxIntervalMs: policy?.maxIntervalMs || 6e4
|
|
1314
|
-
};
|
|
1315
|
-
let backoffMs;
|
|
1316
|
-
if (config.backoff === "exp") {
|
|
1317
|
-
backoffMs = config.initialIntervalMs * Math.pow(2, attempt);
|
|
1318
|
-
} else {
|
|
1319
|
-
backoffMs = config.initialIntervalMs * (attempt + 1);
|
|
1320
|
-
}
|
|
1321
|
-
return Math.min(backoffMs, config.maxIntervalMs);
|
|
1322
|
-
}
|
|
1323
1312
|
/**
|
|
1324
1313
|
* Move permanently failed job to Dead Letter Queue.
|
|
1325
1314
|
*/
|