Skip to content

Commit d63d41f

Browse files
committed
fix: stop creating TaskRunTag records and join table entries during triggering
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. Prisma's upsert with empty update: {} generates DO UPDATE SET name=name which creates dead tuples and WAL writes even when nothing changes. Verified locally: DO UPDATE advances WAL and creates new tuple versions, while DO NOTHING writes zero WAL. This change eliminates these writes entirely rather than switching to createMany. refs TRI-8451
1 parent 5ea36e0 commit d63d41f

File tree

8 files changed

+28
-172
lines changed

8 files changed

+28
-172
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/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/runEngine/services/triggerTask.server.ts

Lines changed: 5 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";
@@ -287,14 +286,11 @@ export class RunEngineTriggerTaskService {
287286
)
288287
: undefined;
289288

290-
//upsert tags
291-
const tags = await createTags(
292-
{
293-
tags: body.options?.tags,
294-
projectId: environment.projectId,
295-
},
296-
this.prisma
297-
);
289+
const tags = body.options?.tags
290+
? typeof body.options.tags === "string"
291+
? [body.options.tags]
292+
: body.options.tags
293+
: [];
298294

299295
const depth = parentRun ? parentRun.depth + 1 : 0;
300296

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,

internal-packages/run-engine/src/engine/systems/debounceSystem.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export type DebounceOptions = {
3232
payloadType: string;
3333
metadata?: string;
3434
metadataType?: string;
35-
tags?: { id: string; name: string }[];
35+
tags?: string[];
3636
maxAttempts?: number;
3737
maxDurationInSeconds?: number;
3838
machine?: string;
@@ -876,10 +876,7 @@ return 0
876876

877877
// Handle tags update - replace existing tags
878878
if (updateData.tags !== undefined) {
879-
updatePayload.runTags = updateData.tags.map((t) => t.name);
880-
updatePayload.tags = {
881-
set: updateData.tags.map((t) => ({ id: t.id })),
882-
};
879+
updatePayload.runTags = updateData.tags;
883880
}
884881

885882
const updatedRun = await prisma.taskRun.update({

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ export type TriggerParams = {
194194
priorityMs?: number;
195195
queueTimestamp?: Date;
196196
ttl?: string;
197-
tags: { id: string; name: string }[];
197+
tags: string[];
198198
parentTaskRunId?: string;
199199
rootTaskRunId?: string;
200200
replayedFromTaskRunFriendlyId?: string;

0 commit comments

Comments
 (0)