echoapi-cron-scheduler-batch 1.0.2 → 1.0.4
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 -47
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -42,7 +42,6 @@ class CronScheduler {
|
|
|
42
42
|
is_cancel INTEGER DEFAULT 0,
|
|
43
43
|
api_token TEXT,
|
|
44
44
|
project_id TEXT,
|
|
45
|
-
user_uid TEXT, -- 新增:存储用户UID
|
|
46
45
|
cases TEXT, -- 存储 runners 数组 JSON
|
|
47
46
|
create_dtime INTEGER
|
|
48
47
|
)
|
|
@@ -168,7 +167,6 @@ class CronScheduler {
|
|
|
168
167
|
job_id: record.job_info.job_id,
|
|
169
168
|
report_name: record.job_info.name,
|
|
170
169
|
project_id: record.job_info.project_id,
|
|
171
|
-
user_uid: record.job_info.user_uid, // 放入最终报文
|
|
172
170
|
execution_id: executionId,
|
|
173
171
|
start_at: this.formatTimeToISO(record.job_info.start_time),
|
|
174
172
|
end_at: this.formatTimeToISO(endTime),
|
|
@@ -295,22 +293,21 @@ class CronScheduler {
|
|
|
295
293
|
* 创建或更新任务
|
|
296
294
|
*/
|
|
297
295
|
createJob(payload) {
|
|
298
|
-
const { job_id, name, frequency, api_token, project_id,
|
|
296
|
+
const { job_id, name, frequency, api_token, project_id, runners } = payload;
|
|
299
297
|
const casesJson = JSON.stringify(Array.isArray(runners) ? runners : []);
|
|
300
298
|
const freqJson = JSON.stringify(frequency);
|
|
301
299
|
|
|
302
300
|
const stmt = this.db.prepare(`
|
|
303
|
-
INSERT INTO jobs (job_id, name, frequency, api_token, project_id, cases,
|
|
301
|
+
INSERT INTO jobs (job_id, name, frequency, api_token, project_id, cases, create_dtime)
|
|
304
302
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
305
303
|
ON CONFLICT(job_id) DO UPDATE SET
|
|
306
304
|
name=excluded.name,
|
|
307
305
|
frequency=excluded.frequency,
|
|
308
306
|
cases=excluded.cases,
|
|
309
307
|
api_token=excluded.api_token,
|
|
310
|
-
user_uid=excluded.user_uid, -- 更新时也同步 UID
|
|
311
308
|
is_cancel=0
|
|
312
309
|
`);
|
|
313
|
-
stmt.run(job_id, name, freqJson, api_token, project_id,
|
|
310
|
+
stmt.run(job_id, name, freqJson, api_token, project_id, casesJson, Date.now());
|
|
314
311
|
|
|
315
312
|
// 使用增量更新,不再 loadJobs()
|
|
316
313
|
this.upsertTimer(job_id, name, frequency, { is_cancel: 0 });
|
|
@@ -395,7 +392,6 @@ class CronScheduler {
|
|
|
395
392
|
job_id: job.job_id,
|
|
396
393
|
name: job.name,
|
|
397
394
|
project_id: job.project_id,
|
|
398
|
-
user_uid: job.user_uid, // 将 UID 存入内存
|
|
399
395
|
start_time: Date.now()
|
|
400
396
|
// 如果数据库有创建人字段,也可以存入:creator: job.creator
|
|
401
397
|
}
|
|
@@ -404,8 +400,10 @@ class CronScheduler {
|
|
|
404
400
|
runners.forEach((runner, index) => {
|
|
405
401
|
// 负载均衡:选取当前存活的 worker
|
|
406
402
|
const workers = Object.values(cluster.workers).filter(w => w.isConnected());
|
|
403
|
+
console.log(`[Dispatch] Active workers count: ${workers.length}`);
|
|
407
404
|
const worker = _.sample(workers);
|
|
408
405
|
if (worker) {
|
|
406
|
+
console.log(`[Dispatch] Sending Unit ${index} to Worker ${worker.process.pid}`);
|
|
409
407
|
worker.send({
|
|
410
408
|
action: 'EXECUTE_UNIT',
|
|
411
409
|
payload: {
|
|
@@ -416,6 +414,8 @@ class CronScheduler {
|
|
|
416
414
|
unit_index: index
|
|
417
415
|
}
|
|
418
416
|
});
|
|
417
|
+
} else {
|
|
418
|
+
console.error(`[Dispatch Error] No active workers available to handle job ${job.job_id}`);
|
|
419
419
|
}
|
|
420
420
|
});
|
|
421
421
|
} catch (e) {
|
|
@@ -428,50 +428,64 @@ class CronScheduler {
|
|
|
428
428
|
// ==========================================
|
|
429
429
|
|
|
430
430
|
_startWorker() {
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
431
|
+
try {
|
|
432
|
+
console.log(`[Worker ${process.pid}] Starting initialization...`);
|
|
433
|
+
const { run: runner } = require('runner-runtime');
|
|
434
|
+
const net = require('net');
|
|
435
|
+
|
|
436
|
+
process.on('message', async (msg) => {
|
|
437
|
+
if (msg.action === 'EXECUTE_UNIT') {
|
|
438
|
+
const { executionId, api_token, test_events, option } = msg.payload;
|
|
439
|
+
const socketPath = path.join(os.tmpdir(), `echoapi_${uuidv4()}.sock`);
|
|
440
|
+
|
|
441
|
+
const server = net.createServer((socket) => {
|
|
442
|
+
socket.on('data', async (stream) => {
|
|
443
|
+
try {
|
|
444
|
+
const info = JSON.parse(stream.toString());
|
|
445
|
+
const { action, data } = info;
|
|
446
|
+
await this._handleUnitScript(socket, action, data);
|
|
447
|
+
} catch (e) {
|
|
448
|
+
socket.write(JSON.stringify({ status: 'error', message: e.message }) + "\n\n");
|
|
449
|
+
}
|
|
450
|
+
});
|
|
448
451
|
});
|
|
449
|
-
});
|
|
450
452
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
453
|
+
// --- 这里是关键!注册错误监听 ---
|
|
454
|
+
server.on('error', (err) => {
|
|
455
|
+
// 在服务器上看到这个日志,就能定位是权限问题还是路径问题
|
|
456
|
+
console.error(`[Worker Socket Server Error] PID: ${process.pid}`);
|
|
457
|
+
console.error(`[Error Details] Code: ${err.code} | Message: ${err.message}`);
|
|
458
|
+
console.error(`[Attempted Path] ${socketPath}`);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
server.listen(socketPath, () => {
|
|
462
|
+
const finalOptions = _.cloneDeep(option || {});
|
|
463
|
+
const base64Pipe = Buffer.from(socketPath).toString('base64');
|
|
464
|
+
_.set(finalOptions, 'env.ELECTRON_PIPE', base64Pipe);
|
|
465
|
+
|
|
466
|
+
runner(test_events, finalOptions, (res) => {
|
|
467
|
+
if (res?.action === 'complete') {
|
|
468
|
+
process.send({
|
|
469
|
+
action: 'UNIT_COMPLETED',
|
|
470
|
+
payload: { executionId, data: res.data, api_token }
|
|
471
|
+
});
|
|
472
|
+
server.close();
|
|
473
|
+
if (fs.existsSync(socketPath)) {
|
|
474
|
+
try { fs.unlinkSync(socketPath); } catch (e) { }
|
|
475
|
+
}
|
|
465
476
|
}
|
|
466
|
-
}
|
|
477
|
+
});
|
|
467
478
|
});
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
})
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
process.on('uncaughtException', (err) => {
|
|
482
|
+
console.error('[Worker Fatal Error] 捕获到沙箱崩溃:', err.message);
|
|
483
|
+
// 即使崩溃也不要让子进程立即退出,或者让 Master 自动重启它
|
|
484
|
+
});
|
|
485
|
+
} catch (error) {
|
|
486
|
+
console.error(`[Worker ${process.pid}] Critical Boot Error:`, err.stack);
|
|
487
|
+
process.exit(1);
|
|
488
|
+
}
|
|
475
489
|
}
|
|
476
490
|
|
|
477
491
|
async _handleUnitScript(socket, action, data) {
|