feat(engine): 实现同步引擎与 engine_status 快照

apps/engine 独立进程(syncRound + 防重叠调度);上游 log/token;QPU 采样;
server 读 engine_status;时刻出参 toUnixSec;中文日志;zod-openapi 扩展顺序修复。
This commit is contained in:
2026-10-07 12:54:13 +08:00
parent 3e12db1706
commit d47cb5543c
28 changed files with 676 additions and 102 deletions
+75
View File
@@ -0,0 +1,75 @@
/**
* 调度循环(方案 A):一轮结束 → setTimeout 下一枪;inFlight 防重叠。
* 启停不得抹掉已有 last_round。
*/
import { appLogger } from "@new-api-key-usage/core";
import { syncRound, type SyncRoundDeps } from "./sync-round.ts";
const log = appLogger("engine");
export interface LoopOptions extends SyncRoundDeps {
intervalMs: number;
}
export function createSyncLoop(opts: LoopOptions) {
let inFlight = false;
let stopped = false;
let timer: ReturnType<typeof setTimeout> | null = null;
let last_round: unknown = null;
async function persist(running: boolean) {
await opts.repos.engine.upsertSnapshot({
running,
sync_interval_ms: opts.intervalMs,
last_round,
});
}
async function runOnce(): Promise<void> {
if (stopped) return;
if (inFlight) {
log.warn("上一轮尚未结束,跳过本枪");
schedule();
return;
}
inFlight = true;
try {
last_round = await syncRound(opts);
await persist(!stopped);
} catch (e) {
log.error(`同步轮次异常:${e}`);
await persist(!stopped).catch(() => {});
} finally {
inFlight = false;
schedule();
}
}
function schedule() {
if (stopped) return;
timer = setTimeout(() => {
void runOnce();
}, opts.intervalMs);
}
return {
start() {
log.info(`同步引擎启动:周期=${opts.intervalMs}ms`);
void (async () => {
const snap = await opts.repos.engine.getSnapshot();
last_round = snap?.last_round ?? null;
await persist(true).catch(() => {});
void runOnce();
})();
},
async stop() {
stopped = true;
if (timer) clearTimeout(timer);
// 等在跑的一轮结束,避免 stop 与 upsert 交错
for (let i = 0; i < 50 && inFlight; i++) {
await new Promise((r) => setTimeout(r, 100));
}
await persist(false).catch(() => {});
},
};
}