155 lines
3.8 KiB
JavaScript
155 lines
3.8 KiB
JavaScript
|
|
// exe-engine/src/controller/scheduler-v2.js
|
|||
|
|
// EXE-Engine · 调度器 v2
|
|||
|
|
// 优先队列 + 依赖图的增强调度
|
|||
|
|
// PRJ-EXE-001 · Phase 1 · ZY-EXE-P1-005
|
|||
|
|
// 版权:国作登字-2026-A-00037559
|
|||
|
|
|
|||
|
|
'use strict';
|
|||
|
|
|
|||
|
|
const { randomUUID } = require('crypto');
|
|||
|
|
|
|||
|
|
// 优先级数值映射:数值越小优先级越高(high=0 最先出队)
|
|||
|
|
const PRIORITY_MAP = { high: 0, normal: 1, low: 2 };
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 调度器 v2
|
|||
|
|
*
|
|||
|
|
* 本体论锚定:调度器 = 笔的时间管理器。
|
|||
|
|
* 哪些字先写、哪些字后写、哪些字要等前面的字写完才能写,
|
|||
|
|
* 调度器用优先队列和依赖图来决定。
|
|||
|
|
*/
|
|||
|
|
class SchedulerV2 {
|
|||
|
|
/**
|
|||
|
|
* @param {object} deps
|
|||
|
|
* @param {AgentController} deps.agentController Agent 调度器
|
|||
|
|
* @param {number} [deps.maxConcurrency=3] 最大并发数
|
|||
|
|
*/
|
|||
|
|
constructor(deps = {}) {
|
|||
|
|
this._agentController = deps.agentController || null;
|
|||
|
|
this._maxConcurrency = deps.maxConcurrency || 3;
|
|||
|
|
|
|||
|
|
// 待处理队列
|
|||
|
|
this._queue = [];
|
|||
|
|
// 已完成任务 { taskId: result }
|
|||
|
|
this._completed = new Map();
|
|||
|
|
// 失败任务 { taskId: error }
|
|||
|
|
this._failed = new Map();
|
|||
|
|
// 正在执行的任务
|
|||
|
|
this._running = new Set();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 入队任务
|
|||
|
|
* @param {object} task
|
|||
|
|
* @param {string} [task.taskId] 任务 ID(自动生成)
|
|||
|
|
* @param {string} task.agentId Agent ID
|
|||
|
|
* @param {string} [task.priority='normal'] 优先级
|
|||
|
|
* @param {string[]} [task.dependsOn=[]] 依赖任务 ID 列表
|
|||
|
|
* @param {object} task.payload 任务载荷
|
|||
|
|
* @returns {string} taskId
|
|||
|
|
*/
|
|||
|
|
enqueue(task) {
|
|||
|
|
const taskId = task.taskId || `task-${randomUUID().slice(0, 8)}`;
|
|||
|
|
|
|||
|
|
const entry = {
|
|||
|
|
taskId,
|
|||
|
|
agentId: task.agentId,
|
|||
|
|
priority: task.priority || 'normal',
|
|||
|
|
dependsOn: task.dependsOn || [],
|
|||
|
|
payload: task.payload || {},
|
|||
|
|
status: 'pending',
|
|||
|
|
enqueuedAt: new Date().toISOString()
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
this._queue.push(entry);
|
|||
|
|
|
|||
|
|
// 按优先级排序(同优先级保持 FIFO)
|
|||
|
|
this._queue.sort((a, b) => {
|
|||
|
|
const pa = PRIORITY_MAP[a.priority] ?? 1;
|
|||
|
|
const pb = PRIORITY_MAP[b.priority] ?? 1;
|
|||
|
|
return pa - pb;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return taskId;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 出队:返回优先级最高且依赖已满足的任务
|
|||
|
|
* @returns {object|null}
|
|||
|
|
*/
|
|||
|
|
dequeue() {
|
|||
|
|
for (let i = 0; i < this._queue.length; i++) {
|
|||
|
|
const task = this._queue[i];
|
|||
|
|
if (this._areDependenciesMet(task)) {
|
|||
|
|
this._queue.splice(i, 1);
|
|||
|
|
task.status = 'running';
|
|||
|
|
this._running.add(task.taskId);
|
|||
|
|
return task;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 标记任务完成
|
|||
|
|
* @param {string} taskId
|
|||
|
|
* @param {*} result 执行结果
|
|||
|
|
*/
|
|||
|
|
complete(taskId, result) {
|
|||
|
|
this._completed.set(taskId, {
|
|||
|
|
result,
|
|||
|
|
completedAt: new Date().toISOString()
|
|||
|
|
});
|
|||
|
|
this._running.delete(taskId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 标记任务失败
|
|||
|
|
* @param {string} taskId
|
|||
|
|
* @param {string} error 错误信息
|
|||
|
|
*/
|
|||
|
|
fail(taskId, error) {
|
|||
|
|
this._failed.set(taskId, {
|
|||
|
|
error,
|
|||
|
|
failedAt: new Date().toISOString()
|
|||
|
|
});
|
|||
|
|
this._running.delete(taskId);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取当前队列状态
|
|||
|
|
* @returns {object[]}
|
|||
|
|
*/
|
|||
|
|
getQueue() {
|
|||
|
|
return [...this._queue];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取调度器指标
|
|||
|
|
* @returns {object}
|
|||
|
|
*/
|
|||
|
|
getStatus() {
|
|||
|
|
return {
|
|||
|
|
pending: this._queue.length,
|
|||
|
|
running: this._running.size,
|
|||
|
|
completed: this._completed.size,
|
|||
|
|
failed: this._failed.size,
|
|||
|
|
maxConcurrency: this._maxConcurrency
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ── 内部方法 ──
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 检查任务依赖是否全部满足
|
|||
|
|
* @param {object} task
|
|||
|
|
* @returns {boolean}
|
|||
|
|
*/
|
|||
|
|
_areDependenciesMet(task) {
|
|||
|
|
if (!task.dependsOn || task.dependsOn.length === 0) return true;
|
|||
|
|
return task.dependsOn.every(depId => this._completed.has(depId));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
module.exports = SchedulerV2;
|