fix: use default retry settings when no catchError handlers are defined (#1852)

This commit is contained in:
Eric Allam
2025-03-31 13:57:02 +01:00
committed by GitHub
parent 0e77747d45
commit c963dcda57
4 changed files with 103 additions and 9 deletions
@@ -282,6 +282,12 @@ export class RunEngineTriggerTaskService extends WithRunEngine {
runtimeEnvironmentId: environment.id,
version: body.options?.lockToVersion,
},
select: {
id: true,
version: true,
sdkVersion: true,
cliVersion: true,
},
})
: undefined;
+10 -7
View File
@@ -1027,6 +1027,14 @@ export class TaskExecutor {
}
}
const defaultRetryResult =
typeof defaultDelay === "undefined"
? { status: "noop" as const }
: {
status: "retry" as const,
retry: { timestamp: Date.now() + defaultDelay, delay: defaultDelay },
};
// Check if retries are enabled in dev environment
if (
execution.environment.type === "DEVELOPMENT" &&
@@ -1040,7 +1048,7 @@ export class TaskExecutor {
const globalCatchErrorHooks = lifecycleHooks.getGlobalCatchErrorHooks();
if (globalCatchErrorHooks.length === 0 && !taskCatchErrorHook) {
return { status: "noop" };
return defaultRetryResult;
}
return this._tracer.startActiveSpan(
@@ -1085,12 +1093,7 @@ export class TaskExecutor {
}
// If no hooks handled the error, use default retry behavior
return typeof defaultDelay === "undefined"
? { status: "noop" as const }
: {
status: "retry" as const,
retry: { timestamp: Date.now() + defaultDelay, delay: defaultDelay },
};
return defaultRetryResult;
},
{
attributes: {
+50 -2
View File
@@ -1,6 +1,7 @@
import { describe, expect, test } from "vitest";
import { ConsoleInterceptor } from "../src/v3/consoleInterceptor.js";
import {
RetryOptions,
RunFnParams,
ServerBackgroundWorker,
TaskMetadataWithFunctions,
@@ -784,6 +785,48 @@ describe("TaskExecutor", () => {
expect((result as any).result.retry.delay).toBeLessThan(30100);
});
test("should use the default retry settings if no catch error hook is provided", async () => {
const expectedError = new Error("Task failed intentionally");
const task = {
id: "test-task",
fns: {
run: async (payload: any, params: RunFnParams<any>) => {
throw expectedError;
},
},
};
const result = await executeTask(task, { test: "data" }, undefined, {
maxAttempts: 3,
minTimeoutInMs: 1000,
maxTimeoutInMs: 5000,
factor: 2,
});
// Verify the final result contains the specific retry timing
expect(result).toEqual({
result: {
ok: false,
id: "test-run-id",
error: {
type: "BUILT_IN_ERROR",
message: "Task failed intentionally",
name: "Error",
stackTrace: expect.any(String),
},
retry: {
timestamp: expect.any(Number),
delay: expect.any(Number),
},
skippedRetrying: false,
},
});
expect((result as any).result.retry.delay).toBeGreaterThan(1000);
expect((result as any).result.retry.delay).toBeLessThan(3000);
});
test("should execute middleware hooks in correct order around other hooks", async () => {
const executionOrder: string[] = [];
@@ -1623,7 +1666,12 @@ describe("TaskExecutor", () => {
});
});
function executeTask(task: TaskMetadataWithFunctions, payload: any, signal?: AbortSignal) {
function executeTask(
task: TaskMetadataWithFunctions,
payload: any,
signal?: AbortSignal,
retrySettings?: RetryOptions
) {
const tracingSDK = new TracingSDK({
url: "http://localhost:4318",
});
@@ -1643,7 +1691,7 @@ function executeTask(task: TaskMetadataWithFunctions, payload: any, signal?: Abo
consoleInterceptor,
retries: {
enabledInDev: false,
default: {
default: retrySettings ?? {
maxAttempts: 1,
},
},
@@ -0,0 +1,37 @@
import { logger, task } from "@trigger.dev/sdk";
type RetryPayload = {
failCount: number;
};
export const retryTask = task({
id: "retry-task",
// Configure 5 retries with exponential backoff
retry: {
maxAttempts: 5,
factor: 1.8,
minTimeoutInMs: 20,
maxTimeoutInMs: 100,
randomize: false,
},
run: async (payload: RetryPayload, { ctx }) => {
const currentAttempt = ctx.attempt.number;
logger.info("Running retry task", {
currentAttempt,
desiredFailCount: payload.failCount,
});
// If we haven't reached the desired number of failures yet, throw an error
if (currentAttempt <= payload.failCount) {
const error = new Error(`Intentionally failing attempt ${currentAttempt}`);
logger.warn("Task failing", { error, currentAttempt });
throw error;
}
// If we've made it past the desired fail count, return success
logger.info("Task succeeded", { currentAttempt });
return {
attemptsTaken: currentAttempt,
};
},
});