Skip to content

Commit 287d09a

Browse files
committed
Added new DEQUEUED status
Cleaned up the API run statuses, including now detecting new clients and not breaking older clients by adding an API version header to all requests
1 parent e75319c commit 287d09a

40 files changed

Lines changed: 564 additions & 433 deletions

apps/webapp/app/api/versions.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import {
2+
API_VERSION_HEADER_NAME,
3+
API_VERSION as CORE_API_VERSION,
4+
} from "@trigger.dev/core/v3/serverOnly";
5+
import { z } from "zod";
6+
7+
export const CURRENT_API_VERSION = CORE_API_VERSION;
8+
9+
export const NON_SPECIFIC_API_VERSION = "none";
10+
11+
export type API_VERSIONS = typeof CURRENT_API_VERSION | typeof NON_SPECIFIC_API_VERSION;
12+
13+
export function getApiVersion(request: Request): API_VERSIONS {
14+
const apiVersion = request.headers.get(API_VERSION_HEADER_NAME);
15+
16+
if (apiVersion === CURRENT_API_VERSION) {
17+
return apiVersion;
18+
}
19+
20+
return NON_SPECIFIC_API_VERSION;
21+
}
22+
23+
// This has been copied from the core package to allow us to use these types in the webapp
24+
export const RunStatusUnspecifiedApiVersion = z.enum([
25+
/// Task is waiting for a version update because it cannot execute without additional information (task, queue, etc.). Replaces WAITING_FOR_DEPLOY
26+
"PENDING_VERSION",
27+
/// Task hasn't been deployed yet but is waiting to be executed
28+
"WAITING_FOR_DEPLOY",
29+
/// Task is waiting to be executed by a worker
30+
"QUEUED",
31+
/// Task is currently being executed by a worker
32+
"EXECUTING",
33+
/// Task has failed and is waiting to be retried
34+
"REATTEMPTING",
35+
/// Task has been paused by the system, and will be resumed by the system
36+
"FROZEN",
37+
/// Task has been completed successfully
38+
"COMPLETED",
39+
/// Task has been canceled by the user
40+
"CANCELED",
41+
/// Task has been completed with errors
42+
"FAILED",
43+
/// Task has crashed and won't be retried, most likely the worker ran out of resources, e.g. memory or storage
44+
"CRASHED",
45+
/// Task was interrupted during execution, mostly this happens in development environments
46+
"INTERRUPTED",
47+
/// Task has failed to complete, due to an error in the system
48+
"SYSTEM_FAILURE",
49+
/// Task has been scheduled to run at a specific time
50+
"DELAYED",
51+
/// Task has expired and won't be executed
52+
"EXPIRED",
53+
/// Task has reached it's maxDuration and has been stopped
54+
"TIMED_OUT",
55+
]);
56+
57+
export type RunStatusUnspecifiedApiVersion = z.infer<typeof RunStatusUnspecifiedApiVersion>;

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

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ import {
1010
import { Form, useFetcher } from "@remix-run/react";
1111
import { IconToggleLeft } from "@tabler/icons-react";
1212
import type { BulkActionType, TaskRunStatus, TaskTriggerSource } from "@trigger.dev/database";
13-
import { ListChecks, ListFilterIcon } from "lucide-react";
13+
import { ListFilterIcon } from "lucide-react";
1414
import { matchSorter } from "match-sorter";
1515
import { type ReactNode, useCallback, useEffect, useMemo, useState } from "react";
1616
import { z } from "zod";
17+
import { ListCheckedIcon } from "~/assets/icons/ListCheckedIcon";
1718
import { StatusIcon } from "~/assets/icons/StatusIcon";
1819
import { TaskIcon } from "~/assets/icons/TaskIcon";
1920
import { AppliedFilter } from "~/components/primitives/AppliedFilter";
@@ -55,8 +56,6 @@ import {
5556
TaskRunStatusCombo,
5657
} from "./TaskRunStatus";
5758
import { TaskTriggerSourceIcon } from "./TaskTriggerSource";
58-
import { ListCheckedIcon } from "~/assets/icons/ListCheckedIcon";
59-
import { cn } from "~/utils/cn";
6059

6160
export const RunStatus = z.enum(allTaskRunStatuses);
6261

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

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ export const allTaskRunStatuses = [
2424
"WAITING_FOR_DEPLOY",
2525
"PENDING_VERSION",
2626
"PENDING",
27+
"DEQUEUED",
2728
"EXECUTING",
2829
"RETRYING_AFTER_FAILURE",
2930
"WAITING_TO_RESUME",
3031
"COMPLETED_SUCCESSFULLY",
31-
"CANCELED",
3232
"COMPLETED_WITH_ERRORS",
33+
"CANCELED",
3334
"TIMED_OUT",
3435
"CRASHED",
3536
"PAUSED",
@@ -42,16 +43,15 @@ export const filterableTaskRunStatuses = [
4243
"PENDING_VERSION",
4344
"DELAYED",
4445
"PENDING",
45-
"WAITING_TO_RESUME",
46+
"DEQUEUED",
4647
"EXECUTING",
47-
"RETRYING_AFTER_FAILURE",
48+
"WAITING_TO_RESUME",
4849
"COMPLETED_SUCCESSFULLY",
49-
"CANCELED",
5050
"COMPLETED_WITH_ERRORS",
5151
"TIMED_OUT",
5252
"CRASHED",
53-
"INTERRUPTED",
5453
"SYSTEM_FAILURE",
54+
"CANCELED",
5555
"EXPIRED",
5656
] as const satisfies Readonly<Array<TaskRunStatus>>;
5757

@@ -60,6 +60,7 @@ const taskRunStatusDescriptions: Record<TaskRunStatus, string> = {
6060
PENDING: "Task is waiting to be executed.",
6161
PENDING_VERSION: "Run cannot execute until a version includes the task and queue.",
6262
WAITING_FOR_DEPLOY: "Run cannot execute until a version includes the task and queue.",
63+
DEQUEUED: "Task has been dequeued from the queue but is not yet executing.",
6364
EXECUTING: "Task is currently being executed.",
6465
RETRYING_AFTER_FAILURE: "Task is being reattempted after a failure.",
6566
WAITING_TO_RESUME: `You have used a "wait" function. When the wait is complete, the task will resume execution.`,
@@ -82,6 +83,7 @@ export const QUEUED_STATUSES = [
8283
] satisfies TaskRunStatus[];
8384

8485
export const RUNNING_STATUSES = [
86+
"DEQUEUED",
8587
"EXECUTING",
8688
"RETRYING_AFTER_FAILURE",
8789
"WAITING_TO_RESUME",
@@ -164,6 +166,8 @@ export function TaskRunStatusIcon({
164166
case "PENDING_VERSION":
165167
case "WAITING_FOR_DEPLOY":
166168
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
169+
case "DEQUEUED":
170+
return <RectangleStackIcon className={cn(runStatusClassNameColor(status), className)} />;
167171
case "EXECUTING":
168172
return <Spinner className={cn(runStatusClassNameColor(status), className)} />;
169173
case "WAITING_TO_RESUME":
@@ -205,6 +209,7 @@ export function runStatusClassNameColor(status: TaskRunStatus): string {
205209
return "text-amber-500";
206210
case "EXECUTING":
207211
case "RETRYING_AFTER_FAILURE":
212+
case "DEQUEUED":
208213
return "text-pending";
209214
case "WAITING_TO_RESUME":
210215
return "text-charcoal-500";
@@ -240,6 +245,8 @@ export function runStatusTitle(status: TaskRunStatus): string {
240245
case "PENDING_VERSION":
241246
case "WAITING_FOR_DEPLOY":
242247
return "Pending version";
248+
case "DEQUEUED":
249+
return "Dequeued";
243250
case "EXECUTING":
244251
return "Executing";
245252
case "WAITING_TO_RESUME":

apps/webapp/app/database-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const TaskRunStatus = {
3030
PENDING: "PENDING",
3131
PENDING_VERSION: "PENDING_VERSION",
3232
WAITING_FOR_DEPLOY: "WAITING_FOR_DEPLOY",
33+
DEQUEUED: "DEQUEUED",
3334
EXECUTING: "EXECUTING",
3435
WAITING_TO_RESUME: "WAITING_TO_RESUME",
3536
RETRYING_AFTER_FAILURE: "RETRYING_AFTER_FAILURE",

apps/webapp/app/models/taskRun.server.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ export function batchTaskRunItemStatusForRunStatus(
129129
case TaskRunStatus.WAITING_FOR_DEPLOY:
130130
case TaskRunStatus.WAITING_TO_RESUME:
131131
case TaskRunStatus.RETRYING_AFTER_FAILURE:
132+
case TaskRunStatus.DEQUEUED:
132133
case TaskRunStatus.EXECUTING:
133134
case TaskRunStatus.PAUSED:
134135
case TaskRunStatus.DELAYED:

apps/webapp/app/presenters/v3/ApiRetrieveRunPresenter.server.ts

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import {
22
AttemptStatus,
3-
RetrieveRunResponse,
43
RunStatus,
54
SerializedError,
65
TaskRunError,
@@ -12,10 +11,12 @@ import {
1211
} from "@trigger.dev/core/v3";
1312
import { Prisma, TaskRunAttemptStatus, TaskRunStatus } from "@trigger.dev/database";
1413
import assertNever from "assert-never";
14+
import { API_VERSIONS, CURRENT_API_VERSION, RunStatusUnspecifiedApiVersion } from "~/api/versions";
15+
import { $replica, prisma } from "~/db.server";
1516
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
1617
import { generatePresignedUrl } from "~/v3/r2.server";
17-
import { BasePresenter } from "./basePresenter.server";
18-
import { $replica, prisma } from "~/db.server";
18+
import { tracer } from "~/v3/tracer.server";
19+
import { startSpanWithEnv } from "~/v3/tracing.server";
1920

2021
// Build 'select' object
2122
const commonRunSelect = {
@@ -63,7 +64,9 @@ type CommonRelatedRun = Prisma.Result<
6364

6465
type FoundRun = NonNullable<Awaited<ReturnType<typeof ApiRetrieveRunPresenter.findRun>>>;
6566

66-
export class ApiRetrieveRunPresenter extends BasePresenter {
67+
export class ApiRetrieveRunPresenter {
68+
constructor(private readonly apiVersion: API_VERSIONS) {}
69+
6770
public static async findRun(friendlyId: string, env: AuthenticatedEnvironment) {
6871
return $replica.taskRun.findFirst({
6972
where: {
@@ -98,11 +101,8 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
98101
});
99102
}
100103

101-
public async call(
102-
taskRun: FoundRun,
103-
env: AuthenticatedEnvironment
104-
): Promise<RetrieveRunResponse | undefined> {
105-
return this.traceWithEnv("call", env, async (span) => {
104+
public async call(taskRun: FoundRun, env: AuthenticatedEnvironment) {
105+
return startSpanWithEnv(tracer, "ApiRetrieveRunPresenter.call", env, async () => {
106106
let $payload: any;
107107
let $payloadPresignedUrl: string | undefined;
108108
let $output: any;
@@ -167,7 +167,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
167167
}
168168

169169
return {
170-
...(await createCommonRunStructure(taskRun)),
170+
...(await createCommonRunStructure(taskRun, this.apiVersion)),
171171
payload: $payload,
172172
payloadPresignedUrl: $payloadPresignedUrl,
173173
output: $output,
@@ -180,13 +180,13 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
180180
attempts: [],
181181
relatedRuns: {
182182
root: taskRun.rootTaskRun
183-
? await createCommonRunStructure(taskRun.rootTaskRun)
183+
? await createCommonRunStructure(taskRun.rootTaskRun, this.apiVersion)
184184
: undefined,
185185
parent: taskRun.parentTaskRun
186-
? await createCommonRunStructure(taskRun.parentTaskRun)
186+
? await createCommonRunStructure(taskRun.parentTaskRun, this.apiVersion)
187187
: undefined,
188188
children: await Promise.all(
189-
taskRun.childRuns.map(async (r) => await createCommonRunStructure(r))
189+
taskRun.childRuns.map(async (r) => await createCommonRunStructure(r, this.apiVersion))
190190
),
191191
},
192192
};
@@ -205,7 +205,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
205205
}
206206
}
207207

208-
static isStatusFinished(status: RunStatus) {
208+
static isStatusFinished(status: RunStatus | RunStatusUnspecifiedApiVersion) {
209209
return (
210210
status === "COMPLETED" ||
211211
status === "FAILED" ||
@@ -216,7 +216,21 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
216216
);
217217
}
218218

219-
static apiStatusFromRunStatus(status: TaskRunStatus): RunStatus {
219+
static apiStatusFromRunStatus(
220+
status: TaskRunStatus,
221+
apiVersion: API_VERSIONS
222+
): RunStatus | RunStatusUnspecifiedApiVersion {
223+
switch (apiVersion) {
224+
case CURRENT_API_VERSION: {
225+
return this.apiStatusFromRunStatusV2(status);
226+
}
227+
default: {
228+
return this.apiStatusFromRunStatusV1(status);
229+
}
230+
}
231+
}
232+
233+
static apiStatusFromRunStatusV1(status: TaskRunStatus): RunStatusUnspecifiedApiVersion {
220234
switch (status) {
221235
case "DELAYED": {
222236
return "DELAYED";
@@ -237,6 +251,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
237251
case "RETRYING_AFTER_FAILURE": {
238252
return "REATTEMPTING";
239253
}
254+
case "DEQUEUED":
240255
case "EXECUTING": {
241256
return "EXECUTING";
242257
}
@@ -270,19 +285,77 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
270285
}
271286
}
272287

273-
static apiBooleanHelpersFromTaskRunStatus(status: TaskRunStatus) {
288+
static apiStatusFromRunStatusV2(status: TaskRunStatus): RunStatus {
289+
switch (status) {
290+
case "DELAYED": {
291+
return "DELAYED";
292+
}
293+
case "PENDING_VERSION": {
294+
return "PENDING_VERSION";
295+
}
296+
case "WAITING_FOR_DEPLOY": {
297+
return "PENDING_VERSION";
298+
}
299+
case "PENDING": {
300+
return "QUEUED";
301+
}
302+
case "PAUSED":
303+
case "WAITING_TO_RESUME": {
304+
return "WAITING";
305+
}
306+
case "DEQUEUED": {
307+
return "DEQUEUED";
308+
}
309+
case "RETRYING_AFTER_FAILURE":
310+
case "EXECUTING": {
311+
return "EXECUTING";
312+
}
313+
case "CANCELED": {
314+
return "CANCELED";
315+
}
316+
case "COMPLETED_SUCCESSFULLY": {
317+
return "COMPLETED";
318+
}
319+
case "SYSTEM_FAILURE": {
320+
return "SYSTEM_FAILURE";
321+
}
322+
case "CRASHED": {
323+
return "CRASHED";
324+
}
325+
case "INTERRUPTED":
326+
case "COMPLETED_WITH_ERRORS": {
327+
return "FAILED";
328+
}
329+
case "EXPIRED": {
330+
return "EXPIRED";
331+
}
332+
case "TIMED_OUT": {
333+
return "TIMED_OUT";
334+
}
335+
default: {
336+
assertNever(status);
337+
}
338+
}
339+
}
340+
341+
static apiBooleanHelpersFromTaskRunStatus(status: TaskRunStatus, apiVersion: API_VERSIONS) {
274342
return ApiRetrieveRunPresenter.apiBooleanHelpersFromRunStatus(
275-
ApiRetrieveRunPresenter.apiStatusFromRunStatus(status)
343+
ApiRetrieveRunPresenter.apiStatusFromRunStatus(status, apiVersion)
276344
);
277345
}
278346

279-
static apiBooleanHelpersFromRunStatus(status: RunStatus) {
347+
static apiBooleanHelpersFromRunStatus(status: RunStatus | RunStatusUnspecifiedApiVersion) {
280348
const isQueued =
281349
status === "QUEUED" ||
282350
status === "WAITING_FOR_DEPLOY" ||
283351
status === "DELAYED" ||
284352
status === "PENDING_VERSION";
285-
const isExecuting = status === "EXECUTING" || status === "REATTEMPTING" || status === "FROZEN";
353+
const isExecuting =
354+
status === "EXECUTING" ||
355+
status === "REATTEMPTING" ||
356+
status === "FROZEN" ||
357+
status === "DEQUEUED" ||
358+
status === "WAITING";
286359
const isCompleted =
287360
status === "COMPLETED" ||
288361
status === "CANCELED" ||
@@ -358,7 +431,7 @@ async function resolveSchedule(run: CommonRelatedRun) {
358431
};
359432
}
360433

361-
async function createCommonRunStructure(run: CommonRelatedRun) {
434+
async function createCommonRunStructure(run: CommonRelatedRun, apiVersion: API_VERSIONS) {
362435
const metadata = await parsePacket({
363436
data: run.metadata ?? undefined,
364437
dataType: run.metadataType,
@@ -369,7 +442,7 @@ async function createCommonRunStructure(run: CommonRelatedRun) {
369442
taskIdentifier: run.taskIdentifier,
370443
idempotencyKey: run.idempotencyKey ?? undefined,
371444
version: run.lockedToVersion?.version,
372-
status: ApiRetrieveRunPresenter.apiStatusFromRunStatus(run.status),
445+
status: ApiRetrieveRunPresenter.apiStatusFromRunStatus(run.status, apiVersion),
373446
createdAt: run.createdAt,
374447
startedAt: run.startedAt ?? undefined,
375448
updatedAt: run.updatedAt,
@@ -385,7 +458,7 @@ async function createCommonRunStructure(run: CommonRelatedRun) {
385458
tags: run.tags
386459
.map((t: { name: string }) => t.name)
387460
.sort((a: string, b: string) => a.localeCompare(b)),
388-
...ApiRetrieveRunPresenter.apiBooleanHelpersFromTaskRunStatus(run.status),
461+
...ApiRetrieveRunPresenter.apiBooleanHelpersFromTaskRunStatus(run.status, apiVersion),
389462
triggerFunction: resolveTriggerFunction(run),
390463
batchId: run.batch?.friendlyId,
391464
metadata,

0 commit comments

Comments
 (0)