fix: verify task termination and preserve startup configuration

This commit is contained in:
whyour
2026-09-22 22:23:18 +08:00
parent 6acb665369
commit 2d6c2206b3
9 changed files with 386 additions and 27 deletions
+10 -11
View File
@@ -491,17 +491,6 @@ export function psTree(pid: number): Promise<number[]> {
export async function killTask(pid: number, waitForExit = false) {
const descendants = await psTree(pid);
if (!waitForExit) {
if (descendants.length) {
try {
[pid, ...descendants]
.reverse()
.forEach((target) => process.kill(target, 15));
} catch {}
} else process.kill(pid, 2);
return;
}
const pids = [...descendants.reverse(), pid];
const signal = (target: number, sig: NodeJS.Signals) => {
try {
process.kill(target, sig);
@@ -509,6 +498,16 @@ export async function killTask(pid: number, waitForExit = false) {
if (error.code !== 'ESRCH') throw error;
}
};
if (!waitForExit) {
if (descendants.length) {
// A child may exit after psTree; keep signalling the remaining tree.
for (const target of [pid, ...descendants].reverse()) {
signal(target, 'SIGTERM');
}
} else signal(pid, 'SIGINT');
return;
}
const pids = [...descendants.reverse(), pid];
for (const target of pids) signal(target, 'SIGTERM');
const alive = async (target: number) => {
try {
+3 -3
View File
@@ -58,9 +58,9 @@ export default class ScriptService {
taskLimit.removeQueuedCron(relativePath.replace(/ /g, '-'));
pid = (await getPid(`${TASK_COMMAND} ${relativePath} now`)) as number;
}
try {
await killTask(pid);
} catch (error) {}
if (pid) {
await killTask(pid, true);
}
return { code: 200 };
}
+12 -10
View File
@@ -322,20 +322,22 @@ export default class SubscriptionService {
public async stop(ids: number[]) {
const docs = await SubscriptionModel.findAll({ where: { id: ids } });
let failure: unknown;
for (const doc of docs) {
if (doc.pid) {
try {
await killTask(doc.pid);
} catch (error) {
this.logger.error(error);
try {
if (doc.pid) {
await killTask(doc.pid, true);
}
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: null } as any,
{ where: { id: doc.id, pid: doc.pid ?? null } },
);
} catch (error) {
this.logger.error(error);
failure ??= error;
}
}
await SubscriptionModel.update(
{ status: SubscriptionStatus.idle, pid: undefined },
{ where: { id: ids } },
);
if (failure) throw failure;
}
private async runSingle(subscriptionId: number) {
+2 -1
View File
@@ -31,7 +31,7 @@ export function runCron(cmd: string, cron: ICron): Promise<number | void> {
Logger.info(
`[schedule][停止已运行任务] 任务ID: ${cron.id}, PID: ${existingCron.pid}`,
);
await killTask(existingCron.pid);
await killTask(existingCron.pid, true);
// Mark old running instances as stopped
const stoppedAt = dayjs().unix();
await RunningInstanceModel.update(
@@ -53,6 +53,7 @@ export function runCron(cmd: string, cron: ICron): Promise<number | void> {
Logger.error(
`[schedule][检查已运行任务失败] 任务ID: ${cron.id}, 错误: ${error}`,
);
throw error;
}
Logger.info(