Skip to content

Commit f31b29c

Browse files
committed
Add the ability to handle errors at the task and project level
1 parent 819b663 commit f31b29c

File tree

20 files changed

+381
-159
lines changed

20 files changed

+381
-159
lines changed

apps/webapp/app/components/primitives/TreeView/TreeView.tsx

Lines changed: 4 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,9 @@
11
import { VirtualItem, Virtualizer, useVirtualizer } from "@tanstack/react-virtual";
2-
import {
3-
Fragment,
4-
MutableRefObject,
5-
RefObject,
6-
useCallback,
7-
useEffect,
8-
useImperativeHandle,
9-
useReducer,
10-
useRef,
11-
useState,
12-
} from "react";
2+
import { MutableRefObject, RefObject, useCallback, useEffect, useReducer, useRef } from "react";
133
import { UnmountClosed } from "react-collapse";
144
import { cn } from "~/utils/cn";
15-
import { Changes, NodeState, NodesState, reducer } from "./reducer";
16-
import {
17-
applyFilterToState,
18-
concreteStateFromInput,
19-
firstVisibleNode,
20-
lastVisibleNode,
21-
selectedIdFromState,
22-
} from "./utils";
5+
import { NodeState, NodesState, reducer } from "./reducer";
6+
import { applyFilterToState, concreteStateFromInput, selectedIdFromState } from "./utils";
237

248
export type TreeViewProps<TData> = {
259
tree: FlatTree<TData>;
@@ -232,6 +216,7 @@ export function useTree<TData>({
232216
index,
233217
});
234218
},
219+
overscan: 20,
235220
});
236221

237222
const scrollToNodeFn = useCallback(

apps/webapp/app/consts.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ export const RUN_CHUNK_EXECUTION_BUFFER = 350;
1010
export const MAX_RUN_CHUNK_EXECUTION_LIMIT = 120000; // 2 minutes
1111
export const VERCEL_RESPONSE_TIMEOUT_STATUS_CODES = [408, 504];
1212
export const MAX_BATCH_TRIGGER_ITEMS = 100;
13+
export const MAX_TASK_RUN_ATTEMPTS = 250;

apps/webapp/app/v3/services/completeAttempt.server.ts

Lines changed: 21 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { Attributes } from "@opentelemetry/api";
22
import {
3-
RetryOptions,
43
TaskRunContext,
54
TaskRunExecution,
65
TaskRunExecutionResult,
76
TaskRunFailedExecutionResult,
87
TaskRunSuccessfulExecutionResult,
9-
defaultRetryOptions,
108
flattenAttributes,
119
} from "@trigger.dev/core/v3";
1210
import { PrismaClientOrTransaction } from "~/db.server";
@@ -16,8 +14,9 @@ import { safeJsonParse } from "~/utils/json";
1614
import { eventRepository } from "../eventRepository.server";
1715
import { marqs } from "../marqs.server";
1816
import { BaseService } from "./baseService.server";
19-
import { ResumeTaskRunDependenciesService } from "./resumeTaskRunDependencies.server";
2017
import { CancelAttemptService } from "./cancelAttempt.server";
18+
import { ResumeTaskRunDependenciesService } from "./resumeTaskRunDependencies.server";
19+
import { MAX_TASK_RUN_ATTEMPTS } from "~/consts";
2120

2221
type FoundAttempt = Awaited<ReturnType<typeof findAttempt>>;
2322

@@ -125,48 +124,31 @@ export class CompleteAttemptService extends BaseService {
125124
},
126125
});
127126

128-
if (completion.retry !== undefined) {
129-
const retryConfig = taskRunAttempt.backgroundWorkerTask.retryConfig
130-
? {
131-
...defaultRetryOptions,
132-
...RetryOptions.parse(taskRunAttempt.backgroundWorkerTask.retryConfig),
133-
}
134-
: undefined;
135-
127+
if (completion.retry !== undefined && taskRunAttempt.number < MAX_TASK_RUN_ATTEMPTS) {
136128
const environment = env ?? (await this.#getEnvironment(execution.environment.id));
137129

138130
const retryAt = new Date(completion.retry.timestamp);
139131

140132
// Retry the task run
141-
await eventRepository.recordEvent(
142-
retryConfig?.maxAttempts
143-
? `Retry ${execution.attempt.number}/${retryConfig?.maxAttempts - 1} delay`
144-
: `Retry #${execution.attempt.number} delay`,
145-
{
146-
taskSlug: taskRunAttempt.taskRun.taskIdentifier,
147-
environment,
148-
attributes: {
149-
metadata: this.#generateMetadataAttributesForNextAttempt(execution),
150-
properties: {
151-
retryAt: retryAt.toISOString(),
152-
factor: retryConfig?.factor,
153-
maxAttempts: retryConfig?.maxAttempts,
154-
minTimeoutInMs: retryConfig?.minTimeoutInMs,
155-
maxTimeoutInMs: retryConfig?.maxTimeoutInMs,
156-
randomize: retryConfig?.randomize,
157-
},
158-
runId: taskRunAttempt.taskRunId,
159-
style: {
160-
icon: "schedule-attempt",
161-
},
162-
queueId: taskRunAttempt.queueId,
163-
queueName: taskRunAttempt.taskRun.queue,
133+
await eventRepository.recordEvent(`Retry #${execution.attempt.number} delay`, {
134+
taskSlug: taskRunAttempt.taskRun.taskIdentifier,
135+
environment,
136+
attributes: {
137+
metadata: this.#generateMetadataAttributesForNextAttempt(execution),
138+
properties: {
139+
retryAt: retryAt.toISOString(),
164140
},
165-
context: taskRunAttempt.taskRun.traceContext as Record<string, string | undefined>,
166-
spanIdSeed: `retry-${taskRunAttempt.number + 1}`,
167-
endTime: retryAt,
168-
}
169-
);
141+
runId: taskRunAttempt.taskRunId,
142+
style: {
143+
icon: "schedule-attempt",
144+
},
145+
queueId: taskRunAttempt.queueId,
146+
queueName: taskRunAttempt.taskRun.queue,
147+
},
148+
context: taskRunAttempt.taskRun.traceContext as Record<string, string | undefined>,
149+
spanIdSeed: `retry-${taskRunAttempt.number + 1}`,
150+
endTime: retryAt,
151+
});
170152

171153
logger.debug("Retrying", { taskRun: taskRunAttempt.taskRun.friendlyId });
172154

packages/cli-v3/src/commands/dev.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ async function startDev(
138138
if (config.status === "file") {
139139
logger.log(`${basename(config.path)} changed...`);
140140
logger.debug("New config", { config: config.config });
141-
rerender(await getDevReactElement(config.config, authorization));
141+
rerender(await getDevReactElement(config.config, authorization, config.path));
142142
} else {
143143
logger.debug("New config", { config: config.config });
144144
rerender(await getDevReactElement(config.config, authorization));
@@ -148,7 +148,8 @@ async function startDev(
148148

149149
async function getDevReactElement(
150150
configParam: ResolvedConfig,
151-
authorization: { apiUrl: string; accessToken: string }
151+
authorization: { apiUrl: string; accessToken: string },
152+
configPath?: string
152153
) {
153154
const accessToken = authorization.accessToken;
154155
const apiUrl = authorization.apiUrl;
@@ -175,11 +176,18 @@ async function startDev(
175176
projectName={devEnv.data.name}
176177
debuggerOn={options.debugger}
177178
debugOtel={options.debugOtel}
179+
configPath={configPath}
178180
/>
179181
);
180182
}
181183

182-
const devReactElement = render(await getDevReactElement(config.config, authorization));
184+
const devReactElement = render(
185+
await getDevReactElement(
186+
config.config,
187+
authorization,
188+
config.status === "file" ? config.path : undefined
189+
)
190+
);
183191

184192
rerender = devReactElement.rerender;
185193

@@ -205,6 +213,7 @@ type DevProps = {
205213
projectName: string;
206214
debuggerOn: boolean;
207215
debugOtel: boolean;
216+
configPath?: string;
208217
};
209218

210219
function useDev({
@@ -215,6 +224,7 @@ function useDev({
215224
projectName,
216225
debuggerOn,
217226
debugOtel,
227+
configPath,
218228
}: DevProps) {
219229
useEffect(() => {
220230
const websocketUrl = new URL(apiUrl);
@@ -334,10 +344,24 @@ function useDev({
334344
importResolve("./workers/dev/worker-setup.js", import.meta.url)
335345
).href.replace("file://", "");
336346

337-
const entryPointContents = workerFacade
347+
let entryPointContents = workerFacade
338348
.replace("__TASKS__", createTaskFileImports(taskFiles))
339349
.replace("__WORKER_SETUP__", `import { tracingSDK, sender } from "${workerSetupPath}";`);
340350

351+
if (configPath) {
352+
logger.debug("Importing project config from", { configPath });
353+
354+
entryPointContents = entryPointContents.replace(
355+
"__IMPORTED_PROJECT_CONFIG__",
356+
`import importedConfig from "${configPath}";`
357+
);
358+
} else {
359+
entryPointContents = entryPointContents.replace(
360+
"__IMPORTED_PROJECT_CONFIG__",
361+
`const importedConfig = undefined;`
362+
);
363+
}
364+
341365
let firstBuild = true;
342366

343367
logger.log(chalk.dim("⎔ Building background worker..."));

packages/cli-v3/src/types.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,24 @@
1-
import { TaskMetadataWithFilePath } from "@trigger.dev/core/v3";
1+
import {
2+
HandleErrorFnParams,
3+
HandleErrorResult,
4+
InitFnParams,
5+
InitOutput,
6+
MiddlewareFnParams,
7+
RunFnParams,
8+
TaskMetadataWithFilePath,
9+
} from "@trigger.dev/core/v3";
210

311
export type TaskMetadataWithFunctions = TaskMetadataWithFilePath & {
412
fns: {
5-
run: (payload: any, params: any) => Promise<any>;
6-
init?: (payload: any, params: any) => Promise<void>;
7-
cleanup?: (payload: any, params: any) => Promise<void>;
8-
middleware?: (payload: any, params: any) => Promise<void>;
13+
run: (payload: any, params: RunFnParams<any>) => Promise<any>;
14+
init?: (payload: any, params: InitFnParams) => Promise<InitOutput>;
15+
cleanup?: (payload: any, params: RunFnParams<any>) => Promise<void>;
16+
middleware?: (payload: any, params: MiddlewareFnParams) => Promise<void>;
17+
handleError?: (
18+
payload: any,
19+
error: unknown,
20+
params: HandleErrorFnParams<any>
21+
) => HandleErrorResult;
922
};
1023
};
1124

packages/cli-v3/src/workers/dev/backgroundWorker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,9 +366,9 @@ export class BackgroundWorker {
366366
const taskRunProcess = new TaskRunProcess(
367367
this.path,
368368
{
369-
...this.#readEnvVars(),
370369
...this.params.env,
371370
...(payload.environment ?? {}),
371+
...this.#readEnvVars(),
372372
},
373373
this.metadata,
374374
this.params

0 commit comments

Comments
 (0)