UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev projects

252 lines • 10.2 kB
import { TaskRunProcess } from "../../executions/taskRunProcess.js"; export class TaskRunProcessProvider { workerManifest; env; logger; processKeepAliveEnabled; processKeepAliveMaxExecutionCount; // Process keep-alive state persistentProcess = null; executionCount = 0; constructor(opts) { this.workerManifest = opts.workerManifest; this.env = opts.env; this.logger = opts.logger; this.processKeepAliveEnabled = opts.processKeepAliveEnabled; this.processKeepAliveMaxExecutionCount = opts.processKeepAliveMaxExecutionCount; } get hasPersistentProcess() { return !!this.persistentProcess; } async handleImmediateRetry() { if (!this.processKeepAliveEnabled) { // For immediate retries, we need to ensure we have a clean process if (this.persistentProcess) { // If the process is not prepared for the next attempt, we need to get a fresh one if (!this.persistentProcess.isPreparedForNextAttempt) { this.sendDebugLog("existing task run process not prepared for retry, will get fresh process"); await this.persistentProcess.kill("SIGKILL"); this.persistentProcess = null; } } } } /** * Gets a TaskRunProcess, either by reusing an existing one or creating a new one */ async getProcess(opts) { this.sendDebugLog("Getting TaskRunProcess", { processKeepAliveEnabled: this.processKeepAliveEnabled, hasPersistentProcess: !!this.persistentProcess, executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, isWarmStart: opts.isWarmStart, }); // If process keep-alive is disabled, always create a new process if (!this.processKeepAliveEnabled) { this.sendDebugLog("Creating new TaskRunProcess (keep-alive disabled)"); return this.createTaskRunProcess(opts); } // If process keep-alive is enabled and we have a healthy persistent process, reuse it if (this.shouldReusePersistentProcess()) { this.sendDebugLog("Reusing persistent TaskRunProcess", { executionCount: this.executionCount, }); return this.persistentProcess; } // Create new process (keep-alive enabled but no reusable process available) this.sendDebugLog("Creating new TaskRunProcess", { hadPersistentProcess: !!this.persistentProcess, reason: this.processKeepAliveEnabled ? "execution limit reached or unhealthy" : "keep-alive disabled", }); const existingPersistentProcess = this.persistentProcess; // Clean up old persistent process if it exists if (existingPersistentProcess) { await this.cleanupProcess(existingPersistentProcess); } this.persistentProcess = this.createTaskRunProcess(opts); this.executionCount = 0; return this.persistentProcess; } /** * Returns a process after execution, handling keep-alive logic and cleanup */ async returnProcess(process) { this.sendDebugLog("Returning TaskRunProcess", { processKeepAliveEnabled: this.processKeepAliveEnabled, executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, }); if (!this.processKeepAliveEnabled) { // Keep-alive disabled - immediately cleanup the process this.sendDebugLog("Keep-alive disabled, cleaning up process immediately"); await process.cleanup(true); return; } // Keep-alive enabled - check if we should keep the process alive if (this.shouldKeepProcessAlive(process)) { this.sendDebugLog("Keeping TaskRunProcess alive for next run", { executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, }); // Call cleanup(false) to prepare for next run but keep process alive await process.cleanup(false); this.persistentProcess = process; this.executionCount++; } else { this.sendDebugLog("Not keeping TaskRunProcess alive, cleaning up", { executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, isHealthy: process.isHealthy, }); // Cleanup the process completely await process.cleanup(true); } } async suspendProcess(flush, process) { if (this.persistentProcess) { if (process) { if (this.persistentProcess.pid === process.pid) { this.sendDebugLog("Suspending matching persistent TaskRunProcess (process provided)", { pid: process.pid, flush, }); this.persistentProcess = null; this.executionCount = 0; await process.suspend({ flush }); } else { this.sendDebugLog("Suspending TaskRunProcess (does not match persistent process)", { pid: process.pid, flush, }); await process.suspend({ flush }); } } else { this.sendDebugLog("Suspending persistent TaskRunProcess (no process provided)", { pid: this.persistentProcess.pid, flush, }); this.persistentProcess = null; this.executionCount = 0; } } else { if (process) { this.sendDebugLog("Suspending non-persistent TaskRunProcess (process provided)", { pid: process.pid, flush, }); await process.suspend({ flush }); } else { this.sendDebugLog("Suspending non-persistent TaskRunProcess (no process provided)", { flush, }); } } } /** * Handles process abort/kill scenarios */ async handleProcessAbort(process) { this.sendDebugLog("Handling process abort"); // If this was our persistent process, clear it if (this.persistentProcess?.pid === process.pid) { this.persistentProcess = null; this.executionCount = 0; } // Kill the process await process.cleanup(true); } async killProcess(process) { this.sendDebugLog("Killing process"); // If this was our persistent process, clear it if (this.persistentProcess?.pid === process.pid) { this.persistentProcess = null; this.executionCount = 0; } // Kill the process await this.cleanupProcess(process); } /** * Forces cleanup of any persistent process */ async cleanup() { if (this.persistentProcess) { this.sendDebugLog("cleanup() called"); await this.cleanupProcess(this.persistentProcess); } } /** * Gets metrics about the provider state */ get metrics() { return { processKeepAlive: { enabled: this.processKeepAliveEnabled, executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, hasPersistentProcess: !!this.persistentProcess, }, }; } createTaskRunProcess({ taskRunEnv, isWarmStart }) { const processEnv = this.buildProcessEnvironment(taskRunEnv); const taskRunProcess = new TaskRunProcess({ workerManifest: this.workerManifest, env: processEnv, serverWorker: { id: "managed", contentHash: this.env.TRIGGER_CONTENT_HASH, version: this.env.TRIGGER_DEPLOYMENT_VERSION, engine: "V2", }, machineResources: { cpu: Number(this.env.TRIGGER_MACHINE_CPU), memory: Number(this.env.TRIGGER_MACHINE_MEMORY), }, isWarmStart, }).initialize(); return taskRunProcess; } buildProcessEnvironment(taskRunEnv) { return { ...taskRunEnv, ...this.env.gatherProcessEnv(), HEARTBEAT_INTERVAL_MS: String(this.env.TRIGGER_HEARTBEAT_INTERVAL_SECONDS * 1000), }; } shouldReusePersistentProcess() { this.sendDebugLog("Checking if persistent process should be reused", { executionCount: this.executionCount, maxExecutionCount: this.processKeepAliveMaxExecutionCount, pid: this.persistentProcess?.pid ?? "unknown", isBeingKilled: this.persistentProcess?.isBeingKilled ?? "unknown", }); return (!!this.persistentProcess && this.executionCount < this.processKeepAliveMaxExecutionCount && this.persistentProcess.isHealthy); } shouldKeepProcessAlive(process) { return this.executionCount < this.processKeepAliveMaxExecutionCount && process.isHealthy; } async cleanupProcess(taskRunProcess) { if (taskRunProcess && taskRunProcess.pid !== undefined) { this.sendDebugLog("Cleaning up TaskRunProcess", { pid: taskRunProcess.pid }); await taskRunProcess.kill("SIGKILL").catch(() => { }); } } sendDebugLog(message, properties) { this.logger.sendDebugLog({ runId: undefined, // Provider doesn't have access to current run ID message: `[taskRunProcessProvider] ${message}`, properties, }); } } //# sourceMappingURL=taskRunProcessProvider.js.map