Skip to content

Commit f8bce65

Browse files
committed
Add onStartAttempt hook and deprecate onSuccess
1 parent 98dd8c7 commit f8bce65

File tree

11 files changed

+357
-13
lines changed

11 files changed

+357
-13
lines changed

apps/webapp/app/components/runs/v3/RunIcon.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ export function RunIcon({ name, className, spanName }: TaskIconProps) {
9797
return <RunFunctionIcon className={cn(className, "text-text-dimmed")} />;
9898
case "task-hook-init":
9999
case "task-hook-onStart":
100+
case "task-hook-onStartAttempt":
100101
case "task-hook-onSuccess":
101102
case "task-hook-onWait":
102103
case "task-hook-onResume":

docs/tasks/overview.mdx

Lines changed: 61 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -247,19 +247,16 @@ export const myTask = task({
247247
});
248248
```
249249

250-
### `onStart` function
250+
### `onStartAttempt` function
251251

252-
When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects.
252+
<Info>The `onStartAttempt` function was introduced in v4.1.0</Info>
253253

254-
<Warning>
255-
This function will only be called once per run (not per attempt). If you want to run code before
256-
each attempt, use a middleware function.
257-
</Warning>
254+
Before a task run attempt starts, the `onStartAttempt` function is called. It's useful for sending notifications, logging, and other side effects.
258255

259256
```ts /trigger/on-start.ts
260-
export const taskWithOnStart = task({
261-
id: "task-with-on-start",
262-
onStart: async ({ payload, ctx }) => {
257+
export const taskWithOnStartAttempt = task({
258+
id: "task-with-on-start-attempt",
259+
onStartAttempt: async ({ payload, ctx }) => {
263260
//...
264261
},
265262
run: async (payload: any, { ctx }) => {
@@ -268,17 +265,33 @@ export const taskWithOnStart = task({
268265
});
269266
```
270267

271-
You can also define a global `onStart` function using `tasks.onStart()`.
268+
You can also define a global `onStartAttempt` function using `tasks.onStartAttempt()`.
272269

273270
```ts init.ts
274271
import { tasks } from "@trigger.dev/sdk";
275272

276-
tasks.onStart(({ ctx, payload, task }) => {
277-
console.log(`Run ${ctx.run.id} started on task ${task}`, ctx.run);
273+
tasks.onStartAttempt(({ ctx, payload, task }) => {
274+
console.log(
275+
`Run ${ctx.run.id} started on task ${task} attempt ${ctx.run.attempt.number}`,
276+
ctx.run
277+
);
278278
});
279279
```
280280

281-
<Info>Errors thrown in the `onStart` function will cause the attempt to fail.</Info>
281+
<Info>Errors thrown in the `onStartAttempt` function will cause the attempt to fail.</Info>
282+
283+
If you want to execute code before just the first attempt, you can use the `onStartAttempt` function and check `ctx.run.attempt.number === 1`:
284+
285+
```ts /trigger/on-start-attempt.ts
286+
export const taskWithOnStartAttempt = task({
287+
id: "task-with-on-start-attempt",
288+
onStartAttempt: async ({ payload, ctx }) => {
289+
if (ctx.run.attempt.number === 1) {
290+
console.log("Run started on attempt 1", ctx.run);
291+
}
292+
},
293+
});
294+
```
282295

283296
### `onWait` and `onResume` functions
284297

@@ -522,6 +535,41 @@ export const cancelExampleTask = task({
522535
point the process will be killed.
523536
</Note>
524537
538+
### `onStart` function (deprecated)
539+
540+
<Info>The `onStart` function was deprecated in v4.1.0. Use `onStartAttempt` instead.</Info>
541+
542+
When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects.
543+
544+
<Warning>
545+
This function will only be called once per run (not per attempt). If you want to run code before
546+
each attempt, use a middleware function or the `onStartAttempt` function.
547+
</Warning>
548+
549+
```ts /trigger/on-start.ts
550+
export const taskWithOnStart = task({
551+
id: "task-with-on-start",
552+
onStart: async ({ payload, ctx }) => {
553+
//...
554+
},
555+
run: async (payload: any, { ctx }) => {
556+
//...
557+
},
558+
});
559+
```
560+
561+
You can also define a global `onStart` function using `tasks.onStart()`.
562+
563+
```ts init.ts
564+
import { tasks } from "@trigger.dev/sdk";
565+
566+
tasks.onStart(({ ctx, payload, task }) => {
567+
console.log(`Run ${ctx.run.id} started on task ${task}`, ctx.run);
568+
});
569+
```
570+
571+
<Info>Errors thrown in the `onStart` function will cause the attempt to fail.</Info>
572+
525573
### `init` function (deprecated)
526574
527575
<Warning>

packages/core/src/v3/lifecycleHooks/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
RegisterHookFunctionParams,
1919
TaskWait,
2020
type LifecycleHooksManager,
21+
AnyOnStartAttemptHookFunction,
2122
} from "./types.js";
2223

2324
const NOOP_LIFECYCLE_HOOKS_MANAGER = new NoopLifecycleHooksManager();
@@ -81,6 +82,27 @@ export class LifecycleHooksAPI {
8182
return this.#getManager().getGlobalStartHooks();
8283
}
8384

85+
public registerTaskStartAttemptHook(
86+
taskId: string,
87+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
88+
): void {
89+
this.#getManager().registerTaskStartAttemptHook(taskId, hook);
90+
}
91+
92+
public registerGlobalStartAttemptHook(
93+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
94+
): void {
95+
this.#getManager().registerGlobalStartAttemptHook(hook);
96+
}
97+
98+
public getTaskStartAttemptHook(taskId: string): AnyOnStartAttemptHookFunction | undefined {
99+
return this.#getManager().getTaskStartAttemptHook(taskId);
100+
}
101+
102+
public getGlobalStartAttemptHooks(): RegisteredHookFunction<AnyOnStartAttemptHookFunction>[] {
103+
return this.#getManager().getGlobalStartAttemptHooks();
104+
}
105+
84106
public registerGlobalFailureHook(
85107
hook: RegisterHookFunctionParams<AnyOnFailureHookFunction>
86108
): void {

packages/core/src/v3/lifecycleHooks/manager.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
AnyOnCleanupHookFunction,
1515
TaskWait,
1616
AnyOnCancelHookFunction,
17+
AnyOnStartAttemptHookFunction,
1718
} from "./types.js";
1819

1920
export class StandardLifecycleHooksManager implements LifecycleHooksManager {
@@ -23,6 +24,15 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
2324
private globalStartHooks: Map<string, RegisteredHookFunction<AnyOnStartHookFunction>> = new Map();
2425
private taskStartHooks: Map<string, RegisteredHookFunction<AnyOnStartHookFunction>> = new Map();
2526

27+
private globalStartAttemptHooks: Map<
28+
string,
29+
RegisteredHookFunction<AnyOnStartAttemptHookFunction>
30+
> = new Map();
31+
private taskStartAttemptHooks: Map<
32+
string,
33+
RegisteredHookFunction<AnyOnStartAttemptHookFunction>
34+
> = new Map();
35+
2636
private globalFailureHooks: Map<string, RegisteredHookFunction<AnyOnFailureHookFunction>> =
2737
new Map();
2838
private taskFailureHooks: Map<string, RegisteredHookFunction<AnyOnFailureHookFunction>> =
@@ -129,6 +139,37 @@ export class StandardLifecycleHooksManager implements LifecycleHooksManager {
129139
return Array.from(this.globalStartHooks.values());
130140
}
131141

142+
registerGlobalStartAttemptHook(
143+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
144+
): void {
145+
const id = generateHookId(hook);
146+
this.globalStartAttemptHooks.set(id, {
147+
id,
148+
name: hook.id,
149+
fn: hook.fn,
150+
});
151+
}
152+
153+
registerTaskStartAttemptHook(
154+
taskId: string,
155+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
156+
): void {
157+
const id = generateHookId(hook);
158+
this.taskStartAttemptHooks.set(taskId, {
159+
id,
160+
name: hook.id,
161+
fn: hook.fn,
162+
});
163+
}
164+
165+
getTaskStartAttemptHook(taskId: string): AnyOnStartAttemptHookFunction | undefined {
166+
return this.taskStartAttemptHooks.get(taskId)?.fn;
167+
}
168+
169+
getGlobalStartAttemptHooks(): RegisteredHookFunction<AnyOnStartAttemptHookFunction>[] {
170+
return Array.from(this.globalStartAttemptHooks.values());
171+
}
172+
132173
registerGlobalInitHook(hook: RegisterHookFunctionParams<AnyOnInitHookFunction>): void {
133174
// if there is no id, lets generate one based on the contents of the function
134175
const id = generateHookId(hook);
@@ -527,6 +568,22 @@ export class NoopLifecycleHooksManager implements LifecycleHooksManager {
527568
return [];
528569
}
529570

571+
registerGlobalStartAttemptHook(): void {
572+
// Noop
573+
}
574+
575+
registerTaskStartAttemptHook(): void {
576+
// Noop
577+
}
578+
579+
getTaskStartAttemptHook(): undefined {
580+
return undefined;
581+
}
582+
583+
getGlobalStartAttemptHooks(): RegisteredHookFunction<AnyOnStartAttemptHookFunction>[] {
584+
return [];
585+
}
586+
530587
registerGlobalFailureHook(hook: RegisterHookFunctionParams<AnyOnFailureHookFunction>): void {
531588
// Noop
532589
}

packages/core/src/v3/lifecycleHooks/types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ export type OnStartHookFunction<TPayload, TInitOutput extends TaskInitOutput = T
3333

3434
export type AnyOnStartHookFunction = OnStartHookFunction<unknown, TaskInitOutput>;
3535

36+
export type TaskStartAttemptHookParams<TPayload = unknown> = {
37+
ctx: TaskRunContext;
38+
payload: TPayload;
39+
task: string;
40+
signal: AbortSignal;
41+
};
42+
43+
export type OnStartAttemptHookFunction<TPayload> = (
44+
params: TaskStartAttemptHookParams<TPayload>
45+
) => undefined | void | Promise<undefined | void>;
46+
47+
export type AnyOnStartAttemptHookFunction = OnStartAttemptHookFunction<unknown>;
48+
3649
export type TaskWait =
3750
| {
3851
type: "duration";
@@ -268,6 +281,17 @@ export interface LifecycleHooksManager {
268281
): void;
269282
getTaskStartHook(taskId: string): AnyOnStartHookFunction | undefined;
270283
getGlobalStartHooks(): RegisteredHookFunction<AnyOnStartHookFunction>[];
284+
285+
registerGlobalStartAttemptHook(
286+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
287+
): void;
288+
registerTaskStartAttemptHook(
289+
taskId: string,
290+
hook: RegisterHookFunctionParams<AnyOnStartAttemptHookFunction>
291+
): void;
292+
getTaskStartAttemptHook(taskId: string): AnyOnStartAttemptHookFunction | undefined;
293+
getGlobalStartAttemptHooks(): RegisteredHookFunction<AnyOnStartAttemptHookFunction>[];
294+
271295
registerGlobalFailureHook(hook: RegisterHookFunctionParams<AnyOnFailureHookFunction>): void;
272296
registerTaskFailureHook(
273297
taskId: string,

packages/core/src/v3/types/tasks.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
OnSuccessHookFunction,
1414
OnWaitHookFunction,
1515
OnCancelHookFunction,
16+
OnStartAttemptHookFunction,
1617
} from "../lifecycleHooks/types.js";
1718
import { RunTags } from "../schemas/api.js";
1819
import {
@@ -114,6 +115,13 @@ export type StartFnParams = Prettify<{
114115
signal: AbortSignal;
115116
}>;
116117

118+
export type StartAttemptFnParams = Prettify<{
119+
ctx: Context;
120+
init?: InitOutput;
121+
/** Abort signal that is aborted when a task run exceeds it's maxDuration or if the task run is cancelled. Can be used to automatically cancel downstream requests */
122+
signal: AbortSignal;
123+
}>;
124+
117125
export type CancelFnParams = Prettify<{
118126
ctx: Context;
119127
/** Abort signal that is aborted when a task run exceeds it's maxDuration or if the task run is cancelled. Can be used to automatically cancel downstream requests */
@@ -328,9 +336,18 @@ type CommonTaskOptions<
328336

329337
/**
330338
* onStart is called the first time a task is executed in a run (not before every retry)
339+
*
340+
* @deprecated Use onStartAttempt instead
331341
*/
332342
onStart?: OnStartHookFunction<TPayload, TInitOutput>;
333343

344+
/**
345+
* onStartAttempt is called before each attempt of a task is executed.
346+
*
347+
* You can detect the first attempt by checking `ctx.attempt.number === 1`.
348+
*/
349+
onStartAttempt?: OnStartAttemptHookFunction<TPayload>;
350+
334351
/**
335352
* onSuccess is called after the run function has successfully completed.
336353
*/
@@ -912,6 +929,7 @@ export type TaskMetadataWithFunctions = TaskMetadata & {
912929
onSuccess?: (payload: any, output: any, params: SuccessFnParams<any>) => Promise<void>;
913930
onFailure?: (payload: any, error: unknown, params: FailureFnParams<any>) => Promise<void>;
914931
onStart?: (payload: any, params: StartFnParams) => Promise<void>;
932+
onStartAttempt?: (payload: any, params: StartAttemptFnParams) => Promise<void>;
915933
parsePayload?: AnySchemaParseFn;
916934
};
917935
schema?: TaskSchema;

0 commit comments

Comments
 (0)