Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/early-points-jam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---

Gracefully shutdown task run processes using SIGTERM followed by SIGKILL after a 1s timeout. This also prevents cancelled or completed runs from leaving orphaned Ttask run processes behind
19 changes: 16 additions & 3 deletions packages/cli-v3/src/executions/taskRunProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export type TaskRunProcessOptions = {
machineResources: MachinePresetResources;
isWarmStart?: boolean;
cwd?: string;
gracefulTerminationTimeoutInMs?: number;
};

export type TaskRunProcessExecuteParams = {
Expand Down Expand Up @@ -114,7 +115,7 @@ export class TaskRunProcess {
console.error("Error cancelling task run process", { err });
}

await this.kill();
await this.#gracefullyTerminate(this.options.gracefulTerminationTimeoutInMs);
}

async cleanup(kill = true) {
Expand All @@ -131,7 +132,7 @@ export class TaskRunProcess {
}

if (kill) {
await this.kill("SIGKILL");
await this.#gracefullyTerminate(this.options.gracefulTerminationTimeoutInMs);
}
}

Expand Down Expand Up @@ -395,6 +396,18 @@ export class TaskRunProcess {
this._stderr.push(errorLine);
}

async #gracefullyTerminate(timeoutInMs: number = 1_000) {
logger.debug("gracefully terminating task run process", { pid: this.pid, timeoutInMs });

await this.kill("SIGTERM", timeoutInMs);

if (this._child?.connected) {
logger.debug("child process is still connected, sending SIGKILL", { pid: this.pid });

await this.kill("SIGKILL");
}
}

/** This will never throw. */
async kill(signal?: number | NodeJS.Signals, timeoutInMs?: number) {
logger.debug(`killing task run process`, {
Expand All @@ -420,7 +433,7 @@ export class TaskRunProcess {
const [error] = await tryCatch(killTimeout);

if (error) {
logger.debug("kill: failed to wait for child process to exit", { error });
logger.debug("kill: failed to wait for child process to exit", { killTimeout });
}
}

Expand Down
8 changes: 4 additions & 4 deletions packages/cli-v3/src/indexing/indexWorkerManifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export async function indexWorkerManifest({
}

resolved = true;
child.kill();
child.kill("SIGKILL");
reject(new Error("Worker timed out"));
}, 20_000);

Expand All @@ -79,21 +79,21 @@ export async function indexWorkerManifest({
} else {
resolve(message.payload.manifest);
}
child.kill();
child.kill("SIGKILL");
break;
}
case "TASKS_FAILED_TO_PARSE": {
clearTimeout(timeout);
resolved = true;
reject(new TaskMetadataParseError(message.payload.zodIssues, message.payload.tasks));
child.kill();
child.kill("SIGKILL");
break;
}
case "UNCAUGHT_EXCEPTION": {
clearTimeout(timeout);
resolved = true;
reject(new UncaughtExceptionError(message.payload.error, message.payload.origin));
child.kill();
child.kill("SIGKILL");
break;
}
}
Expand Down