guanghulab/backend/api-server/services/execution-watchdog.js
2026-05-10 13:12:44 +08:00

101 lines
2.5 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 执行看门狗 · 超时保护 + 死锁防护
*
* 定期检查执行锁是否超时。
* 如果一个操作执行超过 MAX_EXECUTION_TIME标记为超时并释放锁。
* 这是防止死锁的兜底机制。
*
* 版权:国作登字-2026-A-00037559
*/
'use strict';
var executionLock = require('../middleware/execution-lock');
// 最大执行时间15 分钟
var MAX_EXECUTION_TIME = 15 * 60 * 1000;
// 警告时间10 分钟
var WARN_TIME = 10 * 60 * 1000;
// 检查间隔30 秒
var CHECK_INTERVAL = 30 * 1000;
var watchdogTimer = null;
/**
* 启动看门狗
* 定期扫描活跃锁表,检测超时操作
*/
function startWatchdog() {
if (watchdogTimer) return;
watchdogTimer = setInterval(function() {
var activeLocks = executionLock.getActiveLocks();
var now = Date.now();
for (var entry of activeLocks) {
var devId = entry[0];
var lock = entry[1];
if (lock.state !== 'running') continue;
var elapsed = now - lock.startTime;
// 超时:强制释放
if (elapsed > MAX_EXECUTION_TIME) {
console.error(
'[WATCHDOG] 执行超时: ' + lock.executionId +
' (' + lock.operation + ') - ' + Math.round(elapsed / 1000) + 's'
);
lock.state = 'timeout';
lock.error = {
message: '执行超时(超过 ' + Math.round(MAX_EXECUTION_TIME / 60000) + ' 分钟)'
};
executionLock.logStep(devId, {
message: '⏰ 执行超时,看门狗已强制释放锁',
success: false
});
// 延迟释放(给前端时间获取超时状态)
(function(id) {
setTimeout(function() { executionLock.releaseLock(id); }, 30000);
})(devId);
}
// 警告
else if (elapsed > WARN_TIME && !lock.warned) {
console.warn(
'[WATCHDOG] 执行时间较长: ' + lock.executionId +
' (' + lock.operation + ') - ' + Math.round(elapsed / 1000) + 's'
);
lock.warned = true;
}
}
}, CHECK_INTERVAL);
// 不阻止 Node.js 进程退出
if (watchdogTimer.unref) {
watchdogTimer.unref();
}
console.log('[WATCHDOG] 执行看门狗已启动 · 检查间隔 ' + (CHECK_INTERVAL / 1000) + 's · 超时阈值 ' + (MAX_EXECUTION_TIME / 60000) + 'min');
}
/**
* 停止看门狗
*/
function stopWatchdog() {
if (watchdogTimer) {
clearInterval(watchdogTimer);
watchdogTimer = null;
}
}
module.exports = {
startWatchdog: startWatchdog,
stopWatchdog: stopWatchdog,
MAX_EXECUTION_TIME: MAX_EXECUTION_TIME
};