Skip to content

Commit ba3c5bd

Browse files
authored
Adding missing task run hierarchy to TaskRun table (#1332)
* Add task run hierarchical relationships to the database * Add depth and related runs to the retrieve run API response * Remove prisma optimize * restructure the migrations to create the index concurrently * Delete these tsbuildinfo files * Fix type error by adding depth to the run list presenter * Cleanup the task hierarchy, share more code * Remove some fields from the list run response
1 parent 6976311 commit ba3c5bd

File tree

21 files changed

+518
-132
lines changed

21 files changed

+518
-132
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,10 @@ export function RunInspector({
354354
: "–"}
355355
</Property.Value>
356356
</Property.Item>
357+
<Property.Item>
358+
<Property.Label>Run ID</Property.Label>
359+
<Property.Value>{run.id}</Property.Value>
360+
</Property.Item>
357361
</Property.Table>
358362
</div>
359363
) : tab === "context" ? (

apps/webapp/app/db.server.ts

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { PrismaClient, Prisma } from "@trigger.dev/database";
1+
import { Prisma, PrismaClient } from "@trigger.dev/database";
22
import invariant from "tiny-invariant";
33
import { z } from "zod";
4-
import { logger } from "./services/logger.server";
54
import { env } from "./env.server";
6-
import { singleton } from "./utils/singleton";
5+
import { logger } from "./services/logger.server";
76
import { isValidDatabaseUrl } from "./utils/db";
7+
import { singleton } from "./utils/singleton";
88

99
export type PrismaTransactionClient = Omit<
1010
PrismaClient,
@@ -94,6 +94,7 @@ function getClient() {
9494
url: databaseUrl.href,
9595
},
9696
},
97+
// @ts-expect-error
9798
log: [
9899
{
99100
emit: "stdout",
@@ -107,25 +108,16 @@ function getClient() {
107108
emit: "stdout",
108109
level: "warn",
109110
},
110-
// {
111-
// emit: "stdout",
112-
// level: "query",
113-
// },
114-
// {
115-
// emit: "event",
116-
// level: "query",
117-
// },
118-
],
111+
].concat(
112+
process.env.VERBOSE_PRISMA_LOGS === "1"
113+
? [
114+
{ emit: "event", level: "query" },
115+
{ emit: "stdout", level: "query" },
116+
]
117+
: []
118+
),
119119
});
120120

121-
// client.$on("query", (e) => {
122-
// console.log(`Query tooks ${e.duration}ms`, {
123-
// query: e.query,
124-
// params: e.params,
125-
// duration: e.duration,
126-
// });
127-
// });
128-
129121
// connect eagerly
130122
client.$connect();
131123

@@ -153,6 +145,7 @@ function getReplicaClient() {
153145
url: replicaUrl.href,
154146
},
155147
},
148+
// @ts-expect-error
156149
log: [
157150
{
158151
emit: "stdout",
@@ -166,7 +159,14 @@ function getReplicaClient() {
166159
emit: "stdout",
167160
level: "warn",
168161
},
169-
],
162+
].concat(
163+
process.env.VERBOSE_PRISMA_LOGS === "1"
164+
? [
165+
{ emit: "event", level: "query" },
166+
{ emit: "stdout", level: "query" },
167+
]
168+
: []
169+
),
170170
});
171171

172172
// connect eagerly

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

Lines changed: 110 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
RunStatus,
55
SerializedError,
66
TaskRunError,
7+
TriggerFunction,
78
conditionallyImportPacket,
89
createJsonErrorObject,
910
logger,
@@ -14,6 +15,47 @@ import assertNever from "assert-never";
1415
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
1516
import { generatePresignedUrl } from "~/v3/r2.server";
1617
import { BasePresenter } from "./basePresenter.server";
18+
import { prisma } from "~/db.server";
19+
20+
// Build 'select' object
21+
const commonRunSelect = {
22+
id: true,
23+
friendlyId: true,
24+
status: true,
25+
taskIdentifier: true,
26+
createdAt: true,
27+
startedAt: true,
28+
updatedAt: true,
29+
completedAt: true,
30+
expiredAt: true,
31+
delayUntil: true,
32+
ttl: true,
33+
tags: true,
34+
costInCents: true,
35+
baseCostInCents: true,
36+
usageDurationMs: true,
37+
idempotencyKey: true,
38+
isTest: true,
39+
depth: true,
40+
lockedToVersion: {
41+
select: {
42+
version: true,
43+
},
44+
},
45+
resumeParentOnCompletion: true,
46+
batch: {
47+
select: {
48+
id: true,
49+
friendlyId: true,
50+
},
51+
},
52+
} satisfies Prisma.TaskRunSelect;
53+
54+
type CommonRelatedRun = Prisma.Result<
55+
typeof prisma.taskRun,
56+
{ select: typeof commonRunSelect },
57+
"findFirstOrThrow"
58+
>;
1759

1860
export class ApiRetrieveRunPresenter extends BasePresenter {
1961
public async call(
@@ -22,7 +64,7 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
2264
showSecretDetails: boolean
2365
): Promise<RetrieveRunResponse | undefined> {
2466
return this.traceWithEnv("call", env, async (span) => {
25-
const taskRun = await this._prisma.taskRun.findUnique({
67+
const taskRun = await this._replica.taskRun.findFirst({
2668
where: {
2769
friendlyId,
2870
runtimeEnvironmentId: env.id,
@@ -36,6 +78,23 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
3678
lockedToVersion: true,
3779
schedule: true,
3880
tags: true,
81+
batch: {
82+
select: {
83+
id: true,
84+
friendlyId: true,
85+
},
86+
},
87+
parentTaskRun: {
88+
select: commonRunSelect,
89+
},
90+
rootTaskRun: {
91+
select: commonRunSelect,
92+
},
93+
childRuns: {
94+
select: {
95+
...commonRunSelect,
96+
},
97+
},
3998
},
4099
});
41100

@@ -101,29 +160,11 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
101160
const apiStatus = ApiRetrieveRunPresenter.apiStatusFromRunStatus(taskRun.status);
102161

103162
return {
104-
id: taskRun.friendlyId,
105-
status: apiStatus,
106-
taskIdentifier: taskRun.taskIdentifier,
107-
idempotencyKey: taskRun.idempotencyKey ?? undefined,
108-
version: taskRun.lockedToVersion ? taskRun.lockedToVersion.version : undefined,
109-
createdAt: taskRun.createdAt ?? undefined,
110-
updatedAt: taskRun.updatedAt ?? undefined,
111-
startedAt: taskRun.startedAt ?? taskRun.lockedAt ?? undefined,
112-
finishedAt: ApiRetrieveRunPresenter.isStatusFinished(apiStatus)
113-
? taskRun.updatedAt
114-
: undefined,
115-
delayedUntil: taskRun.delayUntil ?? undefined,
163+
...createCommonRunStructure(taskRun),
116164
payload: $payload,
117165
payloadPresignedUrl: $payloadPresignedUrl,
118166
output: $output,
119167
outputPresignedUrl: $outputPresignedUrl,
120-
isTest: taskRun.isTest,
121-
ttl: taskRun.ttl ?? undefined,
122-
expiredAt: taskRun.expiredAt ?? undefined,
123-
tags: taskRun.tags.map((t) => t.name).sort((a, b) => a.localeCompare(b)),
124-
costInCents: taskRun.costInCents,
125-
baseCostInCents: taskRun.baseCostInCents,
126-
durationMs: taskRun.usageDurationMs,
127168
schedule: taskRun.schedule
128169
? {
129170
id: taskRun.schedule.friendlyId,
@@ -138,7 +179,6 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
138179
},
139180
}
140181
: undefined,
141-
...ApiRetrieveRunPresenter.apiBooleanHelpersFromRunStatus(apiStatus),
142182
attempts: !showSecretDetails
143183
? []
144184
: taskRun.attempts.map((a) => ({
@@ -150,6 +190,13 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
150190
completedAt: a.completedAt ?? undefined,
151191
error: ApiRetrieveRunPresenter.apiErrorFromError(a.error),
152192
})),
193+
relatedRuns: {
194+
root: taskRun.rootTaskRun ? createCommonRunStructure(taskRun.rootTaskRun) : undefined,
195+
parent: taskRun.parentTaskRun
196+
? createCommonRunStructure(taskRun.parentTaskRun)
197+
: undefined,
198+
children: taskRun.childRuns.map((r) => createCommonRunStructure(r)),
199+
},
153200
};
154201
});
155202
}
@@ -225,6 +272,12 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
225272
}
226273
}
227274

275+
static apiBooleanHelpersFromTaskRunStatus(status: TaskRunStatus) {
276+
return ApiRetrieveRunPresenter.apiBooleanHelpersFromRunStatus(
277+
ApiRetrieveRunPresenter.apiStatusFromRunStatus(status)
278+
);
279+
}
280+
228281
static apiBooleanHelpersFromRunStatus(status: RunStatus) {
229282
const isQueued = status === "QUEUED" || status === "WAITING_FOR_DEPLOY" || status === "DELAYED";
230283
const isExecuting = status === "EXECUTING" || status === "REATTEMPTING" || status === "FROZEN";
@@ -275,3 +328,39 @@ export class ApiRetrieveRunPresenter extends BasePresenter {
275328
}
276329
}
277330
}
331+
332+
function createCommonRunStructure(run: CommonRelatedRun) {
333+
return {
334+
id: run.friendlyId,
335+
taskIdentifier: run.taskIdentifier,
336+
idempotencyKey: run.idempotencyKey ?? undefined,
337+
version: run.lockedToVersion?.version,
338+
status: ApiRetrieveRunPresenter.apiStatusFromRunStatus(run.status),
339+
createdAt: run.createdAt,
340+
startedAt: run.startedAt ?? undefined,
341+
updatedAt: run.updatedAt,
342+
finishedAt: run.completedAt ?? undefined,
343+
expiredAt: run.expiredAt ?? undefined,
344+
delayedUntil: run.delayUntil ?? undefined,
345+
ttl: run.ttl ?? undefined,
346+
costInCents: run.costInCents,
347+
baseCostInCents: run.baseCostInCents,
348+
durationMs: run.usageDurationMs,
349+
isTest: run.isTest,
350+
depth: run.depth,
351+
tags: run.tags
352+
.map((t: { name: string }) => t.name)
353+
.sort((a: string, b: string) => a.localeCompare(b)),
354+
...ApiRetrieveRunPresenter.apiBooleanHelpersFromTaskRunStatus(run.status),
355+
triggerFunction: resolveTriggerFunction(run),
356+
batchId: run.batch?.friendlyId,
357+
};
358+
}
359+
360+
function resolveTriggerFunction(run: CommonRelatedRun): TriggerFunction {
361+
if (run.batch) {
362+
return run.resumeParentOnCompletion ? "batchTriggerAndWait" : "batchTrigger";
363+
} else {
364+
return run.resumeParentOnCompletion ? "triggerAndWait" : "trigger";
365+
}
366+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ export class ApiRunListPresenter extends BasePresenter {
253253
costInCents: run.costInCents,
254254
baseCostInCents: run.baseCostInCents,
255255
durationMs: run.usageDurationMs,
256+
depth: run.depth,
256257
...ApiRetrieveRunPresenter.apiBooleanHelpersFromRunStatus(
257258
ApiRetrieveRunPresenter.apiStatusFromRunStatus(run.status)
258259
),

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ export class RunListPresenter extends BasePresenter {
167167
baseCostInCents: number;
168168
usageDurationMs: BigInt;
169169
tags: string[];
170+
depth: number;
170171
}[]
171172
>`
172173
SELECT
@@ -190,6 +191,7 @@ export class RunListPresenter extends BasePresenter {
190191
tr."baseCostInCents" AS "baseCostInCents",
191192
tr."costInCents" AS "costInCents",
192193
tr."usageDurationMs" AS "usageDurationMs",
194+
tr."depth" AS "depth",
193195
array_remove(array_agg(tag.name), NULL) AS "tags"
194196
FROM
195197
${sqlDatabaseSchema}."TaskRun" tr
@@ -333,6 +335,7 @@ WHERE
333335
baseCostInCents: run.baseCostInCents,
334336
usageDurationMs: Number(run.usageDurationMs),
335337
tags: run.tags.sort((a, b) => a.localeCompare(b)),
338+
depth: run.depth,
336339
};
337340
}),
338341
pagination: {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ export class SpanPresenter extends BasePresenter {
229229
};
230230

231231
return {
232+
id: run.id,
232233
friendlyId: run.friendlyId,
233234
status: run.status,
234235
createdAt: run.createdAt,

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.v3.$projectParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -593,6 +593,14 @@ function RunBody({
593593
: "–"}
594594
</Property.Value>
595595
</Property.Item>
596+
<Property.Item>
597+
<Property.Label>Run ID</Property.Label>
598+
<Property.Value>{run.friendlyId}</Property.Value>
599+
</Property.Item>
600+
<Property.Item>
601+
<Property.Label>Internal ID</Property.Label>
602+
<Property.Value>{run.id}</Property.Value>
603+
</Property.Item>
596604
</Property.Table>
597605
</div>
598606
) : tab === "context" ? (

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export class BatchTriggerTaskService extends BaseService {
113113
options: {
114114
...item.options,
115115
dependentBatch: dependentAttempt?.id ? batch.friendlyId : undefined, // Only set dependentBatch if dependentAttempt is set which means batchTriggerAndWait was called
116+
parentBatch: dependentAttempt?.id ? undefined : batch.friendlyId, // Only set parentBatch if dependentAttempt is NOT set which means batchTrigger was called
116117
},
117118
},
118119
{

0 commit comments

Comments
 (0)