import { parseNaturalLanguageDuration } from "@trigger.dev/core/v3/isomorphic"; import { logger } from "~/services/logger.server"; import { workerQueue } from "~/services/worker.server"; import { commonWorker } from "../commonWorker.server"; import { BaseService } from "./baseService.server"; import { enqueueRun } from "./enqueueRun.server"; import { ExpireEnqueuedRunService } from "./expireEnqueuedRun.server"; import { controlPlaneResolver } from "~/v3/runOpsMigration/controlPlaneResolver.server"; import { isV3Disabled } from "../engineDeprecation.server"; export class EnqueueDelayedRunService extends BaseService { public static async enqueue(runId: string, runAt?: Date) { await commonWorker.enqueue({ job: "v3.enqueueDelayedRun", payload: { runId }, availableAt: runAt, id: `v3.enqueueDelayed:${runId}`, }); } public static async reschedule(runId: string, runAt?: Date) { // We have to do this for now because it's possible that the workerQueue // was used when the run was first delayed, and EnqueueDelayedRunService.reschedule // is called from RescheduleTaskRunService, which allows the runAt to be changed // so if we don't dequeue the old job, we might end up with multiple jobs await workerQueue.dequeue(`v3.enqueueDelayedRun.${runId}`); await commonWorker.enqueue({ job: "v3.enqueueDelayedRun", payload: { runId }, availableAt: runAt, id: `v3.enqueueDelayed:${runId}`, }); } public async call(runId: string) { const run = await this.runStore.findRun( { id: runId, }, { include: { dependency: { include: { dependentBatchRun: { include: { dependentTaskAttempt: { include: { taskRun: true, }, }, }, }, dependentAttempt: { include: { taskRun: true, }, }, }, }, }, }, this._prisma ); if (!run) { logger.debug("Could not find delayed run to enqueue", { runId, }); return; } // v3 (engine V1) shutdown: don't enqueue delayed V1 runs into MarQS. v4 is unaffected. if (isV3Disabled() && run.engine === "V1") { logger.debug("[EnqueueDelayedRunService] Skipping enqueue for shut-down v3 run", { runId }); return; } const env = await controlPlaneResolver.resolveAuthenticatedEnv(run.runtimeEnvironmentId); if (!env) { logger.debug("EnqueueDelayedRunService: environment not found", { runId }); return; } if (run.status !== "DELAYED") { logger.debug("Delayed run cannot be enqueued because it's not in DELAYED status", { run, }); return; } await this._prisma.taskRun.update({ where: { id: run.id, }, data: { status: "PENDING", queuedAt: new Date(), }, }); if (run.ttl) { const expireAt = parseNaturalLanguageDuration(run.ttl); if (expireAt) { await ExpireEnqueuedRunService.enqueue(run.id, expireAt); } } await enqueueRun({ env, run: run, dependentRun: run.dependency?.dependentAttempt?.taskRun ?? run.dependency?.dependentBatchRun?.dependentTaskAttempt?.taskRun, }); } }