diff --git a/back/shared/pLimit.ts b/back/shared/pLimit.ts index f84763ad..38865e69 100644 --- a/back/shared/pLimit.ts +++ b/back/shared/pLimit.ts @@ -131,13 +131,39 @@ class TaskLimit { let runs = this.queuedCrons.get(cron.id); const result = runs?.length ? [...runs, fn] : [fn]; const repeatTimes = this.repeatCronNotifyMap.get(cron.id) || 0; - if (result?.length > 5) { + + // Check instance mode from database to determine queue limit + let maxQueueSize = 10; // Default for multi-instance mode (increased from 5) + let isSingleInstanceMode = false; + try { + const { CrontabModel } = await import('../data/cron'); + const cronRecord = await CrontabModel.findOne({ + where: { id: Number(cron.id) }, + }); + + // Default to single instance mode (0) for backward compatibility + // allow_multiple_instances is 1 for multi-instance, 0 or null/undefined for single instance + isSingleInstanceMode = cronRecord?.allow_multiple_instances !== 1; + + if (isSingleInstanceMode) { + // For single instance mode, allow up to 2 queued tasks + // This allows the new task to be queued while the old one is being killed + maxQueueSize = 2; + } + } catch (error) { + Logger.error( + `[schedule][检查实例模式失败] 任务ID: ${cron.id}, 错误: ${error}`, + ); + } + + if (result?.length > maxQueueSize) { if (repeatTimes < 3) { this.repeatCronNotifyMap.set(cron.id, repeatTimes + 1); + const modeStr = isSingleInstanceMode ? '单实例' : '多实例'; this.client.systemNotify( { title: '任务重复运行', - content: `任务:${cron.name},命令:${cron.command},定时:${cron.schedule},处于运行中的超过 5 个,请检查定时设置`, + content: `任务:${cron.name}(${modeStr}模式),命令:${cron.command},定时:${cron.schedule},处于运行中的超过 ${maxQueueSize} 个,请检查定时设置`, }, (err, res) => { if (err) { diff --git a/back/shared/runCron.ts b/back/shared/runCron.ts index 9ccfdb05..4b67fd5d 100644 --- a/back/shared/runCron.ts +++ b/back/shared/runCron.ts @@ -15,11 +15,12 @@ export function runCron(cmd: string, cron: ICron): Promise { }); // Default to single instance mode (0) for backward compatibility - const allowSingleInstances = - existingCron?.allow_multiple_instances === 0; + // allow_multiple_instances is 1 for multi-instance, 0 or null/undefined for single instance + const isSingleInstanceMode = + existingCron?.allow_multiple_instances !== 1; if ( - allowSingleInstances && + isSingleInstanceMode && existingCron && existingCron.pid && (existingCron.status === CrontabStatus.running || @@ -49,6 +50,12 @@ export function runCron(cmd: string, cron: ICron): Promise { ); const cp = spawn(cmd, { shell: '/bin/bash' }); + // Update status to running after spawning the process + await CrontabModel.update( + { status: CrontabStatus.running, pid: cp.pid }, + { where: { id: Number(cron.id) } }, + ); + cp.stderr.on('data', (data) => { Logger.info( '[schedule][执行任务失败] 命令: %s, 错误信息: %j', @@ -66,6 +73,11 @@ export function runCron(cmd: string, cron: ICron): Promise { cp.on('exit', async (code) => { taskLimit.removeQueuedCron(cron.id); + // Update status to idle after task completes + await CrontabModel.update( + { status: CrontabStatus.idle, pid: undefined }, + { where: { id: Number(cron.id) } }, + ); Logger.info( '[schedule][执行任务结束] 参数: %s, 退出码: %j', JSON.stringify({