162 lines
4.8 KiB
TypeScript
162 lines
4.8 KiB
TypeScript
import {
|
|
type TaskTriggerSource,
|
|
type PrismaClient,
|
|
type PrismaClientOrTransaction,
|
|
} from "@trigger.dev/database";
|
|
import { $replica, prisma } from "~/db.server";
|
|
import { getAllTaskIdentifiers } from "~/models/task.server";
|
|
import { logger } from "./logger.server";
|
|
import {
|
|
getTaskIdentifiersFromCache,
|
|
populateTaskIdentifierCache,
|
|
type TaskIdentifierEntry,
|
|
} from "./taskIdentifierCache.server";
|
|
|
|
function toTriggerSource(source: string | undefined): TaskTriggerSource {
|
|
const normalized = source?.toUpperCase();
|
|
if (normalized === "AGENT") return "AGENT";
|
|
if (normalized === "SCHEDULED" || normalized === "SCHEDULE") return "SCHEDULED";
|
|
return "STANDARD";
|
|
}
|
|
|
|
export async function syncTaskIdentifiers(
|
|
environmentId: string,
|
|
projectId: string,
|
|
workerId: string,
|
|
tasks: { id: string; triggerSource?: string }[],
|
|
db: PrismaClient = prisma
|
|
): Promise<void> {
|
|
const slugs = tasks.map((t) => t.id);
|
|
const now = new Date();
|
|
|
|
// Group slugs by resolved triggerSource for bulk updates
|
|
const slugsBySource = new Map<TaskTriggerSource, string[]>();
|
|
for (const task of tasks) {
|
|
const source = toTriggerSource(task.triggerSource);
|
|
const existing = slugsBySource.get(source);
|
|
if (existing) {
|
|
existing.push(task.id);
|
|
} else {
|
|
slugsBySource.set(source, [task.id]);
|
|
}
|
|
}
|
|
|
|
// Batch: insert new rows, update existing rows per source group, archive removed tasks
|
|
await db.$transaction([
|
|
// Insert any new task identifiers (skips rows that already exist)
|
|
db.taskIdentifier.createMany({
|
|
data: tasks.map((task) => ({
|
|
runtimeEnvironmentId: environmentId,
|
|
projectId,
|
|
slug: task.id,
|
|
currentTriggerSource: toTriggerSource(task.triggerSource),
|
|
currentWorkerId: workerId,
|
|
})),
|
|
skipDuplicates: true,
|
|
}),
|
|
// Update existing rows — one updateMany per distinct triggerSource value
|
|
...Array.from(slugsBySource.entries()).map(([source, taskSlugs]) =>
|
|
db.taskIdentifier.updateMany({
|
|
where: {
|
|
runtimeEnvironmentId: environmentId,
|
|
slug: { in: taskSlugs },
|
|
},
|
|
data: {
|
|
currentTriggerSource: source,
|
|
currentWorkerId: workerId,
|
|
lastSeenAt: now,
|
|
isInLatestDeployment: true,
|
|
},
|
|
})
|
|
),
|
|
// Archive tasks no longer in this deploy
|
|
db.taskIdentifier.updateMany({
|
|
where: {
|
|
runtimeEnvironmentId: environmentId,
|
|
slug: { notIn: slugs },
|
|
isInLatestDeployment: true,
|
|
},
|
|
data: { isInLatestDeployment: false },
|
|
}),
|
|
]);
|
|
|
|
const allIdentifiers = await db.taskIdentifier.findMany({
|
|
where: { runtimeEnvironmentId: environmentId },
|
|
select: {
|
|
slug: true,
|
|
currentTriggerSource: true,
|
|
isInLatestDeployment: true,
|
|
},
|
|
});
|
|
|
|
populateTaskIdentifierCache(
|
|
environmentId,
|
|
allIdentifiers.map((t) => ({
|
|
slug: t.slug,
|
|
triggerSource: t.currentTriggerSource,
|
|
isInLatestDeployment: t.isInLatestDeployment,
|
|
}))
|
|
).catch((error) => {
|
|
logger.error("Failed to populate task identifier cache after sync", { environmentId, error });
|
|
});
|
|
}
|
|
|
|
function sortEntries(entries: TaskIdentifierEntry[]): TaskIdentifierEntry[] {
|
|
return entries.sort((a, b) => {
|
|
if (a.isInLatestDeployment !== b.isInLatestDeployment) return a.isInLatestDeployment ? -1 : 1;
|
|
return a.slug.localeCompare(b.slug);
|
|
});
|
|
}
|
|
|
|
export async function getTaskIdentifiers(
|
|
environmentId: string,
|
|
db: PrismaClientOrTransaction = $replica
|
|
): Promise<TaskIdentifierEntry[]> {
|
|
const cached = await getTaskIdentifiersFromCache(environmentId);
|
|
if (cached) return sortEntries(cached);
|
|
|
|
const dbRows = await db.taskIdentifier.findMany({
|
|
where: { runtimeEnvironmentId: environmentId },
|
|
select: {
|
|
slug: true,
|
|
currentTriggerSource: true,
|
|
isInLatestDeployment: true,
|
|
},
|
|
});
|
|
|
|
if (dbRows.length > 0) {
|
|
const entries: TaskIdentifierEntry[] = dbRows.map((t) => ({
|
|
slug: t.slug,
|
|
triggerSource: t.currentTriggerSource,
|
|
isInLatestDeployment: t.isInLatestDeployment,
|
|
}));
|
|
|
|
populateTaskIdentifierCache(environmentId, entries).catch((error) => {
|
|
logger.error("Failed to populate task identifier cache after DB read", {
|
|
environmentId,
|
|
error,
|
|
});
|
|
});
|
|
|
|
return sortEntries(entries);
|
|
}
|
|
|
|
const legacyRows = await getAllTaskIdentifiers(db, environmentId);
|
|
const entries: TaskIdentifierEntry[] = legacyRows.map((t) => ({
|
|
slug: t.slug,
|
|
triggerSource: t.triggerSource,
|
|
isInLatestDeployment: true,
|
|
}));
|
|
|
|
if (entries.length > 0) {
|
|
populateTaskIdentifierCache(environmentId, entries).catch((error) => {
|
|
logger.error("Failed to populate task identifier cache after legacy fallback", {
|
|
environmentId,
|
|
error,
|
|
});
|
|
});
|
|
}
|
|
|
|
return sortEntries(entries);
|
|
}
|