@bangdao-ai/acw-tools 1.4.6 → 1.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +61 -13
- package/manifest.json +1 -1
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -77,9 +77,10 @@ let isMainInstance = false; // 是否是主实例(负责定时任务)
|
|
|
77
77
|
/**
|
|
78
78
|
* 尝试获取锁文件(使用排他写入避免竞争条件)
|
|
79
79
|
* 只有主实例才能获取锁,负责运行定时任务
|
|
80
|
+
* @param {boolean} force - 是否强制获取锁(新启动的进程会强制抢占)
|
|
80
81
|
* @returns {boolean} 是否成功获取锁
|
|
81
82
|
*/
|
|
82
|
-
function tryAcquireLock() {
|
|
83
|
+
function tryAcquireLock(force = false) {
|
|
83
84
|
try {
|
|
84
85
|
// 检查锁文件是否存在
|
|
85
86
|
if (fs.existsSync(LOCK_FILE)) {
|
|
@@ -104,7 +105,24 @@ function tryAcquireLock() {
|
|
|
104
105
|
}
|
|
105
106
|
|
|
106
107
|
if (lockData) {
|
|
107
|
-
//
|
|
108
|
+
// 如果是强制获取(新启动的进程),直接抢占
|
|
109
|
+
if (force) {
|
|
110
|
+
logger.info('强制抢占锁(新启动的进程优先)', {
|
|
111
|
+
myPid: process.pid,
|
|
112
|
+
oldPid: lockData.pid
|
|
113
|
+
});
|
|
114
|
+
// 直接覆盖锁文件
|
|
115
|
+
const newLockData = {
|
|
116
|
+
pid: process.pid,
|
|
117
|
+
timestamp: Date.now(),
|
|
118
|
+
hostname: os.hostname()
|
|
119
|
+
};
|
|
120
|
+
fs.writeFileSync(LOCK_FILE, JSON.stringify(newLockData), 'utf8');
|
|
121
|
+
logger.info('成功抢占锁,成为主实例', { pid: process.pid });
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 非强制模式:检查锁是否过期(超过 2 分钟认为进程已死)
|
|
108
126
|
const lockAge = Date.now() - lockData.timestamp;
|
|
109
127
|
const LOCK_TIMEOUT = 2 * 60 * 1000; // 2 分钟
|
|
110
128
|
|
|
@@ -197,10 +215,12 @@ function releaseLock() {
|
|
|
197
215
|
* 定期更新锁文件时间戳(心跳)
|
|
198
216
|
* 只有主实例需要运行,用于维护锁的有效性
|
|
199
217
|
*/
|
|
218
|
+
let heartbeatInterval = null;
|
|
219
|
+
|
|
200
220
|
function startLockHeartbeat() {
|
|
201
221
|
logger.info('启动心跳检测', { pid: process.pid, interval: '30秒' });
|
|
202
222
|
|
|
203
|
-
setInterval(() => {
|
|
223
|
+
heartbeatInterval = setInterval(() => {
|
|
204
224
|
try {
|
|
205
225
|
// 主实例:更新心跳时间戳
|
|
206
226
|
if (fs.existsSync(LOCK_FILE)) {
|
|
@@ -212,18 +232,45 @@ function startLockHeartbeat() {
|
|
|
212
232
|
fs.writeFileSync(LOCK_FILE, JSON.stringify(lockData), 'utf8');
|
|
213
233
|
logger.debug('心跳更新成功', { pid: process.pid });
|
|
214
234
|
} else {
|
|
215
|
-
//
|
|
216
|
-
logger.
|
|
217
|
-
|
|
235
|
+
// 锁被其他进程抢占,降级为从实例
|
|
236
|
+
logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
237
|
+
logger.info('锁被其他进程抢占,降级为从实例', {
|
|
238
|
+
myPid: process.pid,
|
|
239
|
+
newMainPid: lockData.pid
|
|
240
|
+
});
|
|
241
|
+
logger.info("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
242
|
+
|
|
243
|
+
// 停止心跳
|
|
244
|
+
if (heartbeatInterval) {
|
|
245
|
+
clearInterval(heartbeatInterval);
|
|
246
|
+
heartbeatInterval = null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// 降级为从实例
|
|
250
|
+
isMainInstance = false;
|
|
251
|
+
|
|
252
|
+
// 启动从实例监控,以便将来可以重新升级
|
|
253
|
+
startSlaveInstanceMonitor();
|
|
218
254
|
}
|
|
219
255
|
} else {
|
|
220
|
-
//
|
|
221
|
-
if (tryAcquireLock()) {
|
|
256
|
+
// 锁文件不存在,尝试重新获取(非强制模式)
|
|
257
|
+
if (tryAcquireLock(false)) {
|
|
222
258
|
logger.info('锁文件丢失,重新获取成功', { pid: process.pid });
|
|
223
259
|
} else {
|
|
224
|
-
//
|
|
225
|
-
logger.
|
|
226
|
-
|
|
260
|
+
// 无法重新获取锁,降级为从实例
|
|
261
|
+
logger.info('锁文件丢失且无法重新获取,降级为从实例', { pid: process.pid });
|
|
262
|
+
|
|
263
|
+
// 停止心跳
|
|
264
|
+
if (heartbeatInterval) {
|
|
265
|
+
clearInterval(heartbeatInterval);
|
|
266
|
+
heartbeatInterval = null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// 降级为从实例
|
|
270
|
+
isMainInstance = false;
|
|
271
|
+
|
|
272
|
+
// 启动从实例监控
|
|
273
|
+
startSlaveInstanceMonitor();
|
|
227
274
|
}
|
|
228
275
|
}
|
|
229
276
|
} catch (error) {
|
|
@@ -2413,8 +2460,9 @@ Tips:当返回多个匹配时,请使用返回列表中的完整规则名称
|
|
|
2413
2460
|
|
|
2414
2461
|
// --- 启动 stdio 传输 ---
|
|
2415
2462
|
async function main() {
|
|
2416
|
-
//
|
|
2417
|
-
|
|
2463
|
+
// 新启动的进程强制获取锁,成为主实例
|
|
2464
|
+
// 这样用户手动启动的MCP会自动抢占,旧实例会在心跳检测时降级
|
|
2465
|
+
isMainInstance = tryAcquireLock(true);
|
|
2418
2466
|
|
|
2419
2467
|
const transport = new StdioServerTransport();
|
|
2420
2468
|
await server.connect(transport);
|
package/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ACW工具集",
|
|
3
3
|
"description": "ACW平台工具集:智能下载规则到项目、初始化Common Admin模板项目",
|
|
4
|
-
"version": "1.4.
|
|
4
|
+
"version": "1.4.7",
|
|
5
5
|
"author": "邦道科技 - 产品技术中心",
|
|
6
6
|
"homepage": "https://www.npmjs.com/package/@bangdao-ai/acw-tools",
|
|
7
7
|
"repository": "https://www.npmjs.com/package/@bangdao-ai/acw-tools?activeTab=readme",
|
package/package.json
CHANGED