133 lines
3.6 KiB
TypeScript
133 lines
3.6 KiB
TypeScript
import type { PrismaClientOrTransaction } from "~/db.server";
|
|
import { logger } from "~/services/logger.server";
|
|
import { commonWorker } from "../commonWorker.server";
|
|
import { BaseService } from "./baseService.server";
|
|
import { FinalizeTaskRunService } from "./finalizeTaskRun.server";
|
|
import { tryCatch } from "@trigger.dev/core/utils";
|
|
import { getEventRepositoryForStore } from "../eventRepository/index.server";
|
|
import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server";
|
|
import { isV3Disabled } from "../engineDeprecation.server";
|
|
|
|
export class ExpireEnqueuedRunService extends BaseService {
|
|
public static async ack(runId: string, tx?: PrismaClientOrTransaction) {
|
|
// We don't "dequeue" from the workerQueue here because it would be redundant and if this service
|
|
// is called for a run that has already started, nothing happens
|
|
await commonWorker.ack(`v3.expireRun:${runId}`);
|
|
}
|
|
|
|
public static async enqueue(runId: string, runAt?: Date) {
|
|
return await commonWorker.enqueue({
|
|
job: "v3.expireRun",
|
|
payload: { runId },
|
|
availableAt: runAt,
|
|
id: `v3.expireRun:${runId}`,
|
|
});
|
|
}
|
|
|
|
public async call(runId: string) {
|
|
const run = await this.runStore.findRun(
|
|
{
|
|
id: runId,
|
|
},
|
|
{
|
|
select: {
|
|
id: true,
|
|
status: true,
|
|
engine: true,
|
|
lockedAt: true,
|
|
ttl: true,
|
|
taskEventStore: true,
|
|
runtimeEnvironmentId: true,
|
|
friendlyId: true,
|
|
traceId: true,
|
|
spanId: true,
|
|
parentSpanId: true,
|
|
createdAt: true,
|
|
completedAt: true,
|
|
taskIdentifier: true,
|
|
projectId: true,
|
|
organizationId: true,
|
|
isTest: true,
|
|
},
|
|
},
|
|
this._prisma
|
|
);
|
|
|
|
if (!run) {
|
|
logger.debug("Could not find enqueued run to expire", {
|
|
runId,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
// v3 (engine V1) shutdown: skip expiring abandoned V1 runs. v4 is unaffected.
|
|
if (isV3Disabled() && run.engine === "V1") {
|
|
logger.debug("[ExpireEnqueuedRunService] Skipping expiry for shut-down v3 run", { runId });
|
|
return;
|
|
}
|
|
|
|
const env = await controlPlaneResolver.resolveEnv(run.runtimeEnvironmentId);
|
|
|
|
if (!env) {
|
|
logger.debug("ExpireEnqueuedRunService: environment not found", { runId });
|
|
return;
|
|
}
|
|
|
|
if (run.status !== "PENDING") {
|
|
logger.debug("Run cannot be expired because it's not in PENDING status", {
|
|
run,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
if (run.lockedAt) {
|
|
logger.debug("Run cannot be expired because it's locked", {
|
|
run,
|
|
});
|
|
|
|
return;
|
|
}
|
|
|
|
logger.debug("Expiring enqueued run", {
|
|
run,
|
|
});
|
|
|
|
const finalizeService = new FinalizeTaskRunService();
|
|
await finalizeService.call({
|
|
id: run.id,
|
|
status: "EXPIRED",
|
|
expiredAt: new Date(),
|
|
completedAt: new Date(),
|
|
attemptStatus: "FAILED",
|
|
error: {
|
|
type: "STRING_ERROR",
|
|
raw: `Run expired because the TTL (${run.ttl}) was reached`,
|
|
},
|
|
});
|
|
|
|
const eventRepository = await getEventRepositoryForStore(
|
|
run.taskEventStore,
|
|
env.organizationId
|
|
);
|
|
|
|
if (run.ttl) {
|
|
const [completeExpiredRunEventError] = await tryCatch(
|
|
eventRepository.completeExpiredRunEvent({
|
|
run,
|
|
endTime: new Date(),
|
|
ttl: run.ttl,
|
|
})
|
|
);
|
|
|
|
if (completeExpiredRunEventError) {
|
|
logger.error("[ExpireEnqueuedRunService] Failed to complete expired run event", {
|
|
error: completeExpiredRunEventError,
|
|
runId: run.id,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|