chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
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);
|
||||
}
|
||||
Reference in New Issue
Block a user