| /** | |
| * Task #226 — competition driver 的 in-memory 进程状态单例。 | |
| * | |
| * 这层故意是 in-memory 而不是 DB:driver 是 single-instance background | |
| * worker(整 process 内只允许一个 run 同时进行 — 由 admin /start 端点的 | |
| * 单飞锁拦截),状态读取频率高(前端 5s 轮询),DB 走一遍没必要。 | |
| * | |
| * 当 api-server 重启 → 内存被清,状态会从 'idle' 重新开始;但已落地的 | |
| * task_ledger / network_version_metrics / submission_feedback_ledger 仍 | |
| * 在 DB,driver 下次手动触发时会按 plan_competition_<runId> 续跑。 | |
| * | |
| * 路由层用 getCompetitionState / setCompetitionState / patchCompetitionState | |
| * 三个口子拿和写,没有别的入口 — 严格封装。 | |
| */ | |
| export type CompetitionStatus = | |
| | "idle" | |
| | "running" | |
| | "completed" | |
| | "failed"; | |
| export interface CompetitionRunState { | |
| /** 当前 run 的 id,idle 时为 null。 */ | |
| runId: string | null; | |
| status: CompetitionStatus; | |
| startedAt: string | null; | |
| finishedAt: string | null; | |
| /** 已完成的 task 数(含从 ledger 续跑当 done 算上的)。 */ | |
| completed: number; | |
| /** 失败 task 数。 */ | |
| failed: number; | |
| /** 本 run 的总 task 数(loadManifest 后才知道,之前为 0)。 */ | |
| total: number; | |
| /** 已完成 task 的均值 EF1%(仅 done 行), driver 实时算的。 */ | |
| meanEf1Percent: number; | |
| /** 完成后 result.zip 的绝对路径, idle/running 时 null。 */ | |
| resultZipPath: string | null; | |
| /** 飞轮 in-vivo 触发后产生的事件计数(promote+rolledback 等)。 */ | |
| flywheelEvents: number; | |
| /** 错误信息(failed 状态下非空)。 */ | |
| lastError: string | null; | |
| /** 一行人类可读 note,UI 用来显示当前在做什么。 */ | |
| note: string; | |
| } | |
| const idle: CompetitionRunState = { | |
| runId: null, | |
| status: "idle", | |
| startedAt: null, | |
| finishedAt: null, | |
| completed: 0, | |
| failed: 0, | |
| total: 0, | |
| meanEf1Percent: 0, | |
| resultZipPath: null, | |
| flywheelEvents: 0, | |
| lastError: null, | |
| note: "", | |
| }; | |
| let current: CompetitionRunState = { ...idle }; | |
| export function getCompetitionState(): CompetitionRunState { | |
| return { ...current }; | |
| } | |
| export function setCompetitionState(next: Partial<CompetitionRunState> & { status: CompetitionStatus }): CompetitionRunState { | |
| current = { ...idle, ...next }; | |
| return { ...current }; | |
| } | |
| export function patchCompetitionState( | |
| patch: Partial<CompetitionRunState>, | |
| ): CompetitionRunState { | |
| current = { ...current, ...patch }; | |
| return { ...current }; | |
| } | |
| export function resetCompetitionStateForTest(): void { | |
| current = { ...idle }; | |
| } | |