feat(worker): IO Worker 分道(WK-1)

io worker 进程内按任务声明的 lane 分文件 / 网络 / 维护三条道,各自并发(缺省 2/4/1)、
各自排队;维护道在文件道或网络道有排队/运行时让路(连续 10 分钟放行一个),调度器内联的
清理/回填/保留期裁剪也走维护道。并发在 设置 → 自动化 → 高级 热生效,env 覆盖;旧
WORKER_CONCURRENCY 映射到网络道。运行状态页按道显示运行中/并发/排队。归属表写进
docs/runbook/dev.md,ci 与注册表逐项核对。

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 00:24:21 +08:00
1 parent 68f53390f6
commit da33cad81d
38 files changed
+1240 -105

No files matched your search

@@ -12,6 +12,7 @@ import {
} from "../../src/modules/schedules/registry.ts";
import { Scheduler } from "../../src/modules/schedules/scheduler.ts";
import type { ServerContext } from "../../src/modules/types.ts";
import { LaneController, setLaneController } from "../../src/worker/lanes.ts";
import { Browser, bearer, createHarness, type Harness, pgAvailable } from "../auth-settings/harness.ts";
import { DEV_KEY, json } from "../helpers.ts";
@@ -94,6 +95,58 @@ describe.skipIf(!hasPg)("定时任务注册表(真实 PG)", () => {
await h?.close();
});
it("WK-1:lane=maintenance 的内联任务在文件道 / 网络道有活时让路(保持到期),空闲后执行;立即执行不让路", async () => {
let busy = true;
const logger = ctx.logger;
const lanes = new LaneController({
engine: { queueCounts: async (g) => g.map(() => ({ queued: busy ? 1 : 0, running: 0 })) },
logger,
lanes: { file: ["f"], network: ["n"], maintenance: [] },
readSettings: async () => ({ file: 2, network: 4, maintenance: 1 }),
busyCacheMs: 0,
maxYieldMs: 3_600_000,
});
await lanes.refresh();
setLaneController(ctx, lanes);
try {
registerScheduledJob(ctx, counting("test.lane_maint", { lane: "maintenance" }));
registerScheduledJob(ctx, counting("test.lane_light"));
const started = await sched.tick();
await sched.idle();
expect(started).toContain("test.lane_light");
expect(started).not.toContain("test.lane_maint");
expect(calls.get("test.lane_maint") ?? 0).toBe(0);
expect(lanes.yielding()).toBe(true);
// 立即执行:不让路
await h.db
.updateTable("scheduled_job")
.set({ run_requested_at: new Date() })
.where("id", "=", "test.lane_maint")
.execute();
expect(await sched.tick()).toContain("test.lane_maint");
await sched.idle();
expect(calls.get("test.lane_maint")).toBe(1);
expect(lanes.active("maintenance")).toBe(0);
// 到期未跑的:空闲后执行
await h.db
.updateTable("scheduled_job")
.set({ next_run_at: new Date(Date.now() - 1000) })
.where("id", "=", "test.lane_maint")
.execute();
expect(await sched.tick()).not.toContain("test.lane_maint");
busy = false;
expect(await sched.tick()).toContain("test.lane_maint");
await sched.idle();
expect(calls.get("test.lane_maint")).toBe(2);
} finally {
setLaneController(ctx, null);
reg.unregister("test.lane_maint");
reg.unregister("test.lane_light");
}
});
it("登记默认值;用户改过的开关与调度不被再次登记覆盖;reset 恢复默认", async () => {
registerScheduledJob(ctx, counting("test.keep"));
await sched.tick();