UNPKG

@systemfsoftware/trigger.dev

Version:

A Command-Line Interface for Trigger.dev (v3) projects

224 lines (223 loc) 7.28 kB
// src/workers/prod/worker-facade.ts import { ProdChildToWorkerMessages, ProdWorkerToChildMessages, clock, taskCatalog } from "@systemfsoftware/trigger.dev_core/v3"; import { ConsoleInterceptor, DevUsageManager, DurableClock, OtelTaskLogger, ProdUsageManager, TaskExecutor, getEnvVar, logLevels, usage } from "@systemfsoftware/trigger.dev_core/v3/workers"; import { ZodIpcConnection } from "@systemfsoftware/trigger.dev_core/v3/zodIpc"; import { ZodSchemaParsedError } from "@systemfsoftware/trigger.dev_core/v3/zodMessageHandler"; import "source-map-support/register.js"; import { TaskRunErrorCodes, TriggerTracer, logger, runtime } from "@systemfsoftware/trigger.dev_core/v3"; import { ProdRuntimeManager } from "@systemfsoftware/trigger.dev_core/v3/prod"; __WORKER_SETUP__; __IMPORTED_PROJECT_CONFIG__; var heartbeatIntervalMs = getEnvVar("USAGE_HEARTBEAT_INTERVAL_MS"); var usageEventUrl = getEnvVar("USAGE_EVENT_URL"); var triggerJWT = getEnvVar("TRIGGER_JWT"); var prodUsageManager = new ProdUsageManager(new DevUsageManager(), { heartbeatIntervalMs: heartbeatIntervalMs ? parseInt(heartbeatIntervalMs, 10) : void 0, url: usageEventUrl, jwt: triggerJWT }); usage.setGlobalUsageManager(prodUsageManager); var durableClock = new DurableClock(); clock.setGlobalClock(durableClock); var tracer = new TriggerTracer({ tracer: otelTracer, logger: otelLogger }); var consoleInterceptor = new ConsoleInterceptor(otelLogger, true); var triggerLogLevel = getEnvVar("TRIGGER_LOG_LEVEL"); var configLogLevel = triggerLogLevel ? triggerLogLevel : importedConfig ? importedConfig.logLevel : __PROJECT_CONFIG__.logLevel; var otelTaskLogger = new OtelTaskLogger({ logger: otelLogger, tracer, level: logLevels.includes(configLogLevel) ? configLogLevel : "info" }); logger.setGlobalTaskLogger(otelTaskLogger); var TaskFileImports = {}; var TaskFiles = {}; __TASKS__; (() => { for (const [importName, taskFile] of Object.entries(TaskFiles)) { const fileImports = TaskFileImports[importName]; for (const [exportName, task] of Object.entries(fileImports ?? {})) { if (typeof task === "object" && task !== null && "id" in task && typeof task.id === "string") { if (taskCatalog.taskExists(task.id)) { taskCatalog.registerTaskFileMetadata(task.id, { exportName, filePath: taskFile.filePath }); } } } } })(); var _execution; var _isRunning = false; var zodIpc = new ZodIpcConnection({ listenSchema: ProdWorkerToChildMessages, emitSchema: ProdChildToWorkerMessages, process, handlers: { EXECUTE_TASK_RUN: async ({ execution, traceContext, metadata }, sender) => { if (_isRunning) { console.error("Worker is already running a task"); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.TASK_ALREADY_RUNNING } } }); return; } process.title = `trigger-prod-worker: ${execution.task.id} ${execution.run.id}`; const task = taskCatalog.getTask(execution.task.id); if (!task) { console.error(`Could not find task ${execution.task.id}`); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.COULD_NOT_FIND_EXECUTOR } } }); return; } const executor = new TaskExecutor(task, { tracer, tracingSDK, consoleInterceptor, projectConfig: __PROJECT_CONFIG__, importedConfig, handleErrorFn: handleError }); try { _execution = execution; _isRunning = true; const measurement = usage.start(); const { result } = await executor.execute(execution, metadata, traceContext, measurement); const usageSample = usage.stop(measurement); return await sender.send("TASK_RUN_COMPLETED", { execution, result: { ...result, usage: { durationMs: usageSample.cpuTime } } }); } finally { _execution = void 0; _isRunning = false; } }, TASK_RUN_COMPLETED_NOTIFICATION: async ({ completion }) => { prodRuntimeManager.resumeTask(completion); }, WAIT_COMPLETED_NOTIFICATION: async () => { prodRuntimeManager.resumeAfterDuration(); }, CLEANUP: async ({ flush, kill }, sender) => { if (kill) { await flushAll(); await sender.send("READY_TO_DISPOSE", void 0); } else { if (flush) { await flushAll(); } } } } }); async function flushAll(timeoutInMs = 1e4) { const now = performance.now(); console.log(`Flushing at ${now}`); await Promise.all([flushUsage(), flushTracingSDK()]); const duration = performance.now() - now; console.log(`Flushed in ${duration}ms`); } async function flushUsage() { const now = performance.now(); console.log(`Flushing usage at ${now}`); await prodUsageManager.flush(); const duration = performance.now() - now; console.log(`Flushed usage in ${duration}ms`); } async function flushTracingSDK() { const now = performance.now(); console.log(`Flushing tracingSDK at ${now}`); await tracingSDK.flush(); const duration = performance.now() - now; console.log(`Flushed tracingSDK in ${duration}ms`); } process.on("SIGTERM", async () => { }); var prodRuntimeManager = new ProdRuntimeManager(zodIpc, { waitThresholdInMs: parseInt(process.env.TRIGGER_RUNTIME_WAIT_THRESHOLD_IN_MS ?? "30000", 10) }); runtime.setGlobalRuntimeManager(prodRuntimeManager); var taskMetadata = taskCatalog.getAllTaskMetadata(); if (typeof importedConfig?.machine === "string") { taskMetadata = taskMetadata.map((task) => { if (typeof task.machine?.preset !== "string") { return { ...task, machine: { preset: importedConfig.machine } }; } return task; }); } zodIpc.send("TASKS_READY", { tasks: taskMetadata }).catch((err) => { if (err instanceof ZodSchemaParsedError) { zodIpc.send("TASKS_FAILED_TO_PARSE", { zodIssues: err.error.issues, tasks: taskMetadata }); } else { console.error("Failed to send TASKS_READY message", err); } }); process.title = "trigger-prod-worker"; async function asyncHeartbeat(initialDelayInSeconds = 30, intervalInSeconds = 20) { async function _doHeartbeat() { while (true) { if (_isRunning && _execution) { try { await zodIpc.send("TASK_HEARTBEAT", { id: _execution.attempt.id }); } catch (err) { console.error("Failed to send HEARTBEAT message", err); } } await new Promise((resolve) => setTimeout(resolve, 1e3 * intervalInSeconds)); } } await new Promise((resolve) => setTimeout(resolve, 1e3 * initialDelayInSeconds)); return _doHeartbeat(); } asyncHeartbeat(5).catch((err) => { console.error("Failed to start asyncHeartbeat", err); });