Skip to content

Commit ed0c3e4

Browse files
authored
fix: stop creating TaskRunTag records and join table entries during triggering (#3369)
The TaskRun.runTags string array already stores tag names, making the TaskRunTag M2M relation redundant write overhead. Remove createTags calls, connect: tags, and join table writes from both V1 and V2 trigger paths. Simplify the add-tags API to just push to runTags directly.
1 parent c09983e commit ed0c3e4

File tree

12 files changed

+37
-188
lines changed

12 files changed

+37
-188
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: improvement
4+
---
5+
6+
Stop creating TaskRunTag records and _TaskRunToTaskRunTag join table entries during task triggering. The denormalized runTags string array on TaskRun already stores tag names, making the M2M relation redundant write overhead.
Lines changed: 0 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -1,108 +1 @@
1-
import { Prisma } from "@trigger.dev/database";
2-
import { prisma } from "~/db.server";
3-
import { generateFriendlyId } from "~/v3/friendlyIdentifiers";
4-
import { PrismaClientOrTransaction } from "@trigger.dev/database";
5-
61
export const MAX_TAGS_PER_RUN = 10;
7-
const MAX_RETRIES = 3;
8-
9-
export async function createTag(
10-
{ tag, projectId }: { tag: string; projectId: string },
11-
prismaClient: PrismaClientOrTransaction = prisma
12-
) {
13-
if (tag.trim().length === 0) return;
14-
15-
let attempts = 0;
16-
const friendlyId = generateFriendlyId("runtag");
17-
18-
while (attempts < MAX_RETRIES) {
19-
try {
20-
return await prisma.taskRunTag.upsert({
21-
where: {
22-
projectId_name: {
23-
projectId,
24-
name: tag,
25-
},
26-
},
27-
create: {
28-
friendlyId,
29-
name: tag,
30-
projectId,
31-
},
32-
update: {},
33-
});
34-
} catch (error) {
35-
if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === "P2002") {
36-
// Handle unique constraint violation (conflict)
37-
attempts++;
38-
if (attempts >= MAX_RETRIES) {
39-
throw new Error(`Failed to create tag after ${MAX_RETRIES} attempts due to conflicts.`);
40-
}
41-
} else {
42-
throw error; // Re-throw other errors
43-
}
44-
}
45-
}
46-
}
47-
48-
export type TagRecord = {
49-
id: string;
50-
name: string;
51-
};
52-
53-
export async function createTags(
54-
{
55-
tags,
56-
projectId,
57-
}: {
58-
tags: string | string[] | undefined;
59-
projectId: string;
60-
},
61-
prismaClient: PrismaClientOrTransaction = prisma
62-
): Promise<TagRecord[]> {
63-
if (!tags) {
64-
return [];
65-
}
66-
67-
const tagsArray = typeof tags === "string" ? [tags] : tags;
68-
69-
if (tagsArray.length === 0) {
70-
return [];
71-
}
72-
73-
const tagRecords: TagRecord[] = [];
74-
for (const tag of tagsArray) {
75-
const tagRecord = await createTag(
76-
{
77-
tag,
78-
projectId,
79-
},
80-
prismaClient
81-
);
82-
if (tagRecord) {
83-
tagRecords.push({ id: tagRecord.id, name: tagRecord.name });
84-
}
85-
}
86-
87-
return tagRecords;
88-
}
89-
90-
export async function getTagsForRunId({
91-
friendlyId,
92-
environmentId,
93-
}: {
94-
friendlyId: string;
95-
environmentId: string;
96-
}) {
97-
const run = await prisma.taskRun.findFirst({
98-
where: {
99-
friendlyId,
100-
runtimeEnvironmentId: environmentId,
101-
},
102-
select: {
103-
tags: true,
104-
},
105-
});
106-
107-
return run?.tags ?? undefined;
108-
}

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ const commonRunSelect = {
3434
metadata: true,
3535
metadataType: true,
3636
ttl: true,
37-
tags: true,
3837
costInCents: true,
3938
baseCostInCents: true,
4039
usageDurationMs: true,
@@ -459,9 +458,7 @@ async function createCommonRunStructure(run: CommonRelatedRun, apiVersion: API_V
459458
durationMs: run.usageDurationMs,
460459
isTest: run.isTest,
461460
depth: run.depth,
462-
tags: run.tags
463-
.map((t: { name: string }) => t.name)
464-
.sort((a: string, b: string) => a.localeCompare(b)),
461+
tags: [...(run.runTags ?? [])].sort((a: string, b: string) => a.localeCompare(b)),
465462
...ApiRetrieveRunPresenter.apiBooleanHelpersFromTaskRunStatus(run.status, apiVersion),
466463
triggerFunction: resolveTriggerFunction(run),
467464
batchId: run.batch?.friendlyId,

apps/webapp/app/routes/api.v1.runs.$runId.tags.ts

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { type ActionFunctionArgs, json } from "@remix-run/server-runtime";
22
import { AddTagsRequestBody } from "@trigger.dev/core/v3";
33
import { z } from "zod";
44
import { prisma } from "~/db.server";
5-
import { createTag, getTagsForRunId, MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server";
5+
import { MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server";
66
import { authenticateApiRequest } from "~/services/apiAuth.server";
77

88
const ParamsSchema = z.object({
@@ -37,17 +37,23 @@ export async function action({ request, params }: ActionFunctionArgs) {
3737
return json({ error: "Invalid request body", issues: body.error.issues }, { status: 400 });
3838
}
3939

40-
const existingTags =
41-
(await getTagsForRunId({
40+
const run = await prisma.taskRun.findFirst({
41+
where: {
4242
friendlyId: parsedParams.data.runId,
43-
environmentId: authenticationResult.environment.id,
44-
})) ?? [];
43+
runtimeEnvironmentId: authenticationResult.environment.id,
44+
},
45+
select: {
46+
runTags: true,
47+
},
48+
});
49+
50+
const existingTags = run?.runTags ?? [];
4551

4652
//remove duplicate tags from the new tags
4753
const bodyTags = typeof body.data.tags === "string" ? [body.data.tags] : body.data.tags;
4854
const newTags = bodyTags.filter((tag) => {
4955
if (tag.trim().length === 0) return false;
50-
return !existingTags.map((t) => t.name).includes(tag);
56+
return !existingTags.includes(tag);
5157
});
5258

5359
if (existingTags.length + newTags.length > MAX_TAGS_PER_RUN) {
@@ -65,29 +71,12 @@ export async function action({ request, params }: ActionFunctionArgs) {
6571
return json({ message: "No new tags to add" }, { status: 200 });
6672
}
6773

68-
//create tags
69-
let tagIds: string[] = existingTags.map((t) => t.id);
70-
if (newTags.length > 0) {
71-
for (const tag of newTags) {
72-
const tagRecord = await createTag({
73-
tag,
74-
projectId: authenticationResult.environment.projectId,
75-
});
76-
if (tagRecord) {
77-
tagIds.push(tagRecord.id);
78-
}
79-
}
80-
}
81-
8274
await prisma.taskRun.update({
8375
where: {
8476
friendlyId: parsedParams.data.runId,
8577
runtimeEnvironmentId: authenticationResult.environment.id,
8678
},
8779
data: {
88-
tags: {
89-
connect: tagIds.map((id) => ({ id })),
90-
},
9180
runTags: {
9281
push: newTags,
9382
},

apps/webapp/app/routes/resources.runs.$runParam.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
2323
taskIdentifier: true,
2424
friendlyId: true,
2525
isTest: true,
26-
tags: {
27-
select: {
28-
name: true,
29-
},
30-
},
26+
runTags: true,
3127
machinePreset: true,
3228
lockedToVersion: {
3329
select: {
@@ -178,7 +174,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
178174
run: {
179175
id: run.friendlyId,
180176
createdAt: run.createdAt,
181-
tags: run.tags.map((tag) => tag.name),
177+
tags: run.runTags ?? [],
182178
isTest: run.isTest,
183179
idempotencyKey: run.idempotencyKey ?? undefined,
184180
startedAt: run.startedAt ?? run.createdAt,
@@ -244,7 +240,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => {
244240
isCustomQueue: !run.queue.startsWith("task/"),
245241
concurrencyKey: run.concurrencyKey,
246242
},
247-
tags: run.tags.map((tag) => tag.name),
243+
tags: run.runTags ?? [],
248244
baseCostInCents: run.baseCostInCents,
249245
costInCents: run.costInCents,
250246
totalCostInCents: run.costInCents + run.baseCostInCents,

apps/webapp/app/runEngine/services/triggerTask.server.ts

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import {
2020
stringifyDuration,
2121
} from "@trigger.dev/core/v3/isomorphic";
2222
import type { PrismaClientOrTransaction } from "@trigger.dev/database";
23-
import { createTags } from "~/models/taskRunTag.server";
2423
import type { AuthenticatedEnvironment } from "~/services/apiAuth.server";
2524
import { logger } from "~/services/logger.server";
2625
import { parseDelay } from "~/utils/delays";
@@ -288,14 +287,13 @@ export class RunEngineTriggerTaskService {
288287
)
289288
: undefined;
290289

291-
//upsert tags
292-
const tags = await createTags(
293-
{
294-
tags: body.options?.tags,
295-
projectId: environment.projectId,
296-
},
297-
this.prisma
298-
);
290+
const tags = (
291+
body.options?.tags
292+
? typeof body.options.tags === "string"
293+
? [body.options.tags]
294+
: body.options.tags
295+
: []
296+
).filter((tag) => tag.trim().length > 0);
299297

300298
const depth = parentRun ? parentRun.depth + 1 : 0;
301299

apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -750,7 +750,6 @@ export class SharedQueueConsumer {
750750
take: 1,
751751
orderBy: { number: "desc" },
752752
},
753-
tags: true,
754753
checkpoints: {
755754
take: 1,
756755
orderBy: {
@@ -1648,7 +1647,7 @@ export const AttemptForExecutionGetPayload = {
16481647
costInCents: true,
16491648
baseCostInCents: true,
16501649
maxDurationInSeconds: true,
1651-
tags: true,
1650+
runTags: true,
16521651
taskEventStore: true,
16531652
},
16541653
},
@@ -1725,7 +1724,7 @@ class SharedQueueTasks {
17251724
context: taskRun.context,
17261725
createdAt: taskRun.createdAt,
17271726
startedAt: taskRun.startedAt ?? taskRun.createdAt,
1728-
tags: taskRun.tags.map((tag) => tag.name),
1727+
tags: taskRun.runTags ?? [],
17291728
isTest: taskRun.isTest,
17301729
idempotencyKey: taskRun.idempotencyKey ?? undefined,
17311730
durationMs: taskRun.usageDurationMs,

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ export class CreateTaskRunAttemptService extends BaseService {
5252
runtimeEnvironmentId: environment.id,
5353
},
5454
include: {
55-
tags: true,
5655
attempts: {
5756
take: 1,
5857
orderBy: {
@@ -209,7 +208,7 @@ export class CreateTaskRunAttemptService extends BaseService {
209208
payloadType: taskRun.payloadType,
210209
context: taskRun.context,
211210
createdAt: taskRun.createdAt,
212-
tags: taskRun.tags.map((tag) => tag.name),
211+
tags: taskRun.runTags ?? [],
213212
isTest: taskRun.isTest,
214213
idempotencyKey: taskRun.idempotencyKey ?? undefined,
215214
startedAt: taskRun.startedAt ?? taskRun.createdAt,

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

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import { Prisma } from "@trigger.dev/database";
1414
import { z } from "zod";
1515
import { env } from "~/env.server";
16-
import { createTag, MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server";
16+
import { MAX_TAGS_PER_RUN } from "~/models/taskRunTag.server";
1717
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
1818
import { autoIncrementCounter } from "~/services/autoIncrementCounter.server";
1919
import { logger } from "~/services/logger.server";
@@ -345,21 +345,8 @@ export class TriggerTaskServiceV1 extends BaseService {
345345

346346
span.setAttribute("queueName", queueName);
347347

348-
//upsert tags
349-
let tagIds: string[] = [];
350348
const bodyTags =
351349
typeof body.options?.tags === "string" ? [body.options.tags] : body.options?.tags;
352-
if (bodyTags && bodyTags.length > 0) {
353-
for (const tag of bodyTags) {
354-
const tagRecord = await createTag({
355-
tag,
356-
projectId: environment.projectId,
357-
});
358-
if (tagRecord) {
359-
tagIds.push(tagRecord.id);
360-
}
361-
}
362-
}
363350

364351
const depth = dependentAttempt
365352
? dependentAttempt.taskRun.depth + 1
@@ -409,12 +396,6 @@ export class TriggerTaskServiceV1 extends BaseService {
409396
maxAttempts: body.options?.maxAttempts,
410397
taskEventStore: store,
411398
ttl,
412-
tags:
413-
tagIds.length === 0
414-
? undefined
415-
: {
416-
connect: tagIds.map((id) => ({ id })),
417-
},
418399
parentTaskRunId:
419400
dependentAttempt?.taskRun.id ??
420401
parentAttempt?.taskRun.id ??

internal-packages/run-engine/src/engine/index.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -637,13 +637,7 @@ export class RunEngine {
637637
priorityMs,
638638
queueTimestamp: queueTimestamp ?? delayUntil ?? new Date(),
639639
ttl: resolvedTtl,
640-
tags:
641-
tags.length === 0
642-
? undefined
643-
: {
644-
connect: tags,
645-
},
646-
runTags: tags.length === 0 ? undefined : tags.map((tag) => tag.name),
640+
runTags: tags.length === 0 ? undefined : tags,
647641
oneTimeUseToken,
648642
parentTaskRunId,
649643
rootTaskRunId,

0 commit comments

Comments
 (0)