mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-15 19:57:07 +08:00
fix: 修复任务生命周期与调度就绪,优化执行和构建开销 (#3069)
* fix: harden task lifecycle and scheduler readiness * fix: confine log writes to the configured log directory * fix: verify complete build artifacts and untracked inputs * fix: reconcile scheduler state and make stop win startup races * fix: isolate cron generations and serialize scheduler recovery
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
const assert = require('node:assert/strict');
|
||||
const test = require('node:test');
|
||||
const fs = require('node:fs/promises');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const { spawn } = require('node:child_process');
|
||||
const load = require('../helpers/load-security-module.cjs');
|
||||
const { LogStreamManager } = require('../../back/shared/logStreamManager');
|
||||
|
||||
test(
|
||||
'manual execution captures a short child before slow status storage and flushes before release',
|
||||
{ timeout: 5000 },
|
||||
async (t) => {
|
||||
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'ql-manual-'));
|
||||
t.after(() => fs.rm(root, { recursive: true, force: true }));
|
||||
const logs = new LogStreamManager(root);
|
||||
const updates = [];
|
||||
let active = 0;
|
||||
let failSpawn = false;
|
||||
const CronService = load(path.resolve('back/services/cron.ts'), {
|
||||
'../config': { logPath: root },
|
||||
'../data/cron': {
|
||||
CrontabStatus: { queued: 3, running: 0, idle: 1 },
|
||||
CrontabModel: {
|
||||
update: async (data, options) => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 40));
|
||||
updates.push({ data, options });
|
||||
return [1];
|
||||
},
|
||||
},
|
||||
},
|
||||
'../data/runningInstance': {
|
||||
RunningInstanceModel: {},
|
||||
InstanceStatus: {},
|
||||
},
|
||||
'../config/util': { getUniqPath: async () => 'task' },
|
||||
'../config/const': { TASK_PREFIX: 'task ', QL_PREFIX: 'ql ' },
|
||||
'../schedule/client': {},
|
||||
'../shared/pLimit': {
|
||||
manualRunWithCronLimit: async (fn) => {
|
||||
active++;
|
||||
try {
|
||||
return await fn();
|
||||
} finally {
|
||||
active--;
|
||||
}
|
||||
},
|
||||
},
|
||||
'../shared/utils': {},
|
||||
'../shared/i18n': { t: (s) => s },
|
||||
'../shared/logReader': {},
|
||||
'../shared/logStreamManager': { logStreamManager: logs },
|
||||
'cross-spawn': {
|
||||
spawn: () =>
|
||||
failSpawn
|
||||
? spawn('true', { shell: '/nonexistent-ql-manual-shell' })
|
||||
: spawn(process.execPath, [
|
||||
'-e',
|
||||
'process.stdout.write("末尾\\n")',
|
||||
]),
|
||||
},
|
||||
}).default;
|
||||
const service = new CronService({ info() {}, error() {} });
|
||||
service.getDb = async () => ({
|
||||
id: 1,
|
||||
status: 3,
|
||||
command: 'ignored',
|
||||
log_path: '',
|
||||
});
|
||||
service.makeCommand = () => 'ignored';
|
||||
await service.runSingle(1);
|
||||
const [file] = await fs.readdir(path.join(root, 'task'));
|
||||
assert.equal(
|
||||
await fs.readFile(path.join(root, 'task', file), 'utf8'),
|
||||
'末尾\n',
|
||||
);
|
||||
assert.equal(logs.getOpenStreamCount(), 0);
|
||||
assert.equal(active, 0);
|
||||
assert.equal(updates.at(-1).data.status, 1);
|
||||
failSpawn = true;
|
||||
await service.runSingle(1);
|
||||
assert.equal(active, 0);
|
||||
assert.equal(updates.at(-1).data.status, 1);
|
||||
},
|
||||
);
|
||||
Reference in New Issue
Block a user