143 lines
4.5 KiB
TypeScript
143 lines
4.5 KiB
TypeScript
import { ownerEngine } from "@trigger.dev/core/v3/isomorphic";
|
|
import { env } from "~/env.server";
|
|
import { logger } from "~/services/logger.server";
|
|
import { marqs } from "~/v3/marqs/index.server";
|
|
import { commonWorker } from "../commonWorker.server";
|
|
import { BaseService } from "./baseService.server";
|
|
|
|
export class ExecuteTasksWaitingForDeployService extends BaseService {
|
|
public async call(backgroundWorkerId: string) {
|
|
// Kill-switch for the legacy V1 WAITING_FOR_DEPLOY drain. Set to "1" to
|
|
// neuter any jobs already enqueued (V2 has its own PENDING_VERSION path).
|
|
if (env.LEGACY_RUN_ENGINE_WAITING_FOR_DEPLOY_DISABLED === "1") {
|
|
return;
|
|
}
|
|
|
|
const backgroundWorker = await this._prisma.backgroundWorker.findFirst({
|
|
where: {
|
|
id: backgroundWorkerId,
|
|
},
|
|
include: {
|
|
runtimeEnvironment: {
|
|
include: {
|
|
project: true,
|
|
organization: true,
|
|
},
|
|
},
|
|
tasks: {
|
|
select: {
|
|
slug: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!backgroundWorker) {
|
|
logger.error("Background worker not found", { id: backgroundWorkerId });
|
|
return;
|
|
}
|
|
|
|
const maxCount = env.LEGACY_RUN_ENGINE_WAITING_FOR_DEPLOY_BATCH_SIZE;
|
|
|
|
const runsWaitingForDeploy = await this.runStore.findRuns(
|
|
{
|
|
where: {
|
|
runtimeEnvironmentId: backgroundWorker.runtimeEnvironmentId,
|
|
projectId: backgroundWorker.projectId,
|
|
status: "WAITING_FOR_DEPLOY",
|
|
taskIdentifier: {
|
|
in: backgroundWorker.tasks.map((task) => task.slug),
|
|
},
|
|
},
|
|
orderBy: {
|
|
createdAt: "asc",
|
|
},
|
|
select: {
|
|
id: true,
|
|
status: true,
|
|
taskIdentifier: true,
|
|
concurrencyKey: true,
|
|
queue: true,
|
|
updatedAt: true,
|
|
createdAt: true,
|
|
},
|
|
take: maxCount + 1,
|
|
},
|
|
this._replica
|
|
);
|
|
|
|
if (!runsWaitingForDeploy.length) {
|
|
return;
|
|
}
|
|
|
|
// Defense-in-depth: the open-predicate findRuns fan-out can select runs from
|
|
// either DB, but the status flip below is a single control-plane updateMany. A
|
|
// run-ops id (NEW-resident) run can only reach WAITING_FOR_DEPLOY via a misconfiguration
|
|
// (it is a V1/cuid-only status — V2 uses PENDING_VERSION). Surface it loudly rather
|
|
// than silently strand the run, and only mutate the LEGACY-resident runs the
|
|
// control-plane client can actually reach.
|
|
const newResidentRuns = runsWaitingForDeploy.filter((run) => ownerEngine(run.id) === "NEW");
|
|
if (newResidentRuns.length) {
|
|
logger.error(
|
|
"WAITING_FOR_DEPLOY selected NEW-resident runs; skipping their control-plane status flip",
|
|
{ runIds: newResidentRuns.map((run) => run.id) }
|
|
);
|
|
}
|
|
const legacyRuns = runsWaitingForDeploy.filter((run) => !newResidentRuns.includes(run));
|
|
|
|
const pendingRuns = await this._prisma.taskRun.updateMany({
|
|
where: {
|
|
id: {
|
|
in: legacyRuns.map((run) => run.id),
|
|
},
|
|
},
|
|
data: {
|
|
status: "PENDING",
|
|
},
|
|
});
|
|
|
|
if (pendingRuns.count) {
|
|
logger.debug("Task runs waiting for deploy are now ready for execution", {
|
|
tasks: legacyRuns.map((run) => run.id),
|
|
total: pendingRuns.count,
|
|
});
|
|
}
|
|
|
|
// Only enqueue the runs whose status was actually flipped (the legacy set) — never
|
|
// marqs-enqueue a NEW-resident run we couldn't transition out of WAITING_FOR_DEPLOY.
|
|
for (const run of legacyRuns) {
|
|
await marqs?.enqueueMessage(
|
|
backgroundWorker.runtimeEnvironment,
|
|
run.queue,
|
|
run.id,
|
|
{
|
|
type: "EXECUTE",
|
|
taskIdentifier: run.taskIdentifier,
|
|
projectId: backgroundWorker.runtimeEnvironment.projectId,
|
|
environmentId: backgroundWorker.runtimeEnvironment.id,
|
|
environmentType: backgroundWorker.runtimeEnvironment.type,
|
|
},
|
|
run.concurrencyKey ?? undefined
|
|
);
|
|
}
|
|
|
|
if (runsWaitingForDeploy.length > maxCount) {
|
|
await ExecuteTasksWaitingForDeployService.enqueue(
|
|
backgroundWorkerId,
|
|
new Date(Date.now() + env.LEGACY_RUN_ENGINE_WAITING_FOR_DEPLOY_BATCH_STAGGER_MS)
|
|
);
|
|
}
|
|
}
|
|
|
|
static async enqueue(backgroundWorkerId: string, runAt?: Date) {
|
|
return await commonWorker.enqueue({
|
|
id: `v3.executeTasksWaitingForDeploy:${backgroundWorkerId}`,
|
|
job: "v3.executeTasksWaitingForDeploy",
|
|
payload: {
|
|
backgroundWorkerId,
|
|
},
|
|
availableAt: runAt,
|
|
});
|
|
}
|
|
}
|