feat(desktop): add foreground publish admission scheduler
Publish is now treated as the runtime foreground channel and AI monitoring as background. A new in-memory publish-scheduler owns queue selection, per-platform active locks, and 3-6s post-completion platform cooldowns. The runtime controller tracks lease-in-flight per kind so a monitor lease no longer blocks a publish lease, derives total concurrency from hardware class, current process health, and an optional GEO_DESKTOP_MAX_TOTAL_CONCURRENCY override, and reserves a publish slot when budgeting monitor capacity. Monitor admission returns zero whenever publish has backlog or an in-flight lease, so a cooldown-blocked publish queue still prevents monitor from consuming the foreground reserve. Runtime diagnostics expose publishScheduler alongside monitorScheduler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
enqueuePublishTask,
|
||||
getPublishSchedulerSnapshot,
|
||||
initPublishScheduler,
|
||||
notePublishTaskActivated,
|
||||
notePublishTaskCompleted,
|
||||
selectNextPublishTask,
|
||||
} from "./publish-scheduler";
|
||||
|
||||
describe("publish scheduler", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
vi.setSystemTime(new Date("2026-04-24T00:00:00.000Z"));
|
||||
initPublishScheduler();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
initPublishScheduler();
|
||||
});
|
||||
|
||||
it("skips same-platform queued tasks while selecting an allowed platform", () => {
|
||||
enqueuePublishTask({ taskId: "toutiao-active", platform: "toutiao", routing: "rabbitmq-primary" });
|
||||
notePublishTaskActivated("toutiao-active", "toutiao");
|
||||
enqueuePublishTask({ taskId: "toutiao-next", platform: "toutiao", routing: "rabbitmq-primary" });
|
||||
enqueuePublishTask({ taskId: "zhihu-next", platform: "zhihu", routing: "rabbitmq-primary" });
|
||||
|
||||
const selected = selectNextPublishTask({
|
||||
maxConcurrency: 2,
|
||||
activeCount: 1,
|
||||
});
|
||||
|
||||
expect(selected?.taskId).toBe("zhihu-next");
|
||||
expect(getPublishSchedulerSnapshot().leasingCount).toBe(1);
|
||||
});
|
||||
|
||||
it("holds same-platform tasks during completion cooldown", () => {
|
||||
enqueuePublishTask({ taskId: "first", platform: "toutiao", routing: "rabbitmq-primary" });
|
||||
expect(selectNextPublishTask({ maxConcurrency: 2, activeCount: 0 })?.taskId).toBe("first");
|
||||
notePublishTaskActivated("first", "toutiao");
|
||||
notePublishTaskCompleted("first", "toutiao");
|
||||
|
||||
enqueuePublishTask({ taskId: "second", platform: "toutiao", routing: "rabbitmq-primary" });
|
||||
|
||||
expect(selectNextPublishTask({ maxConcurrency: 2, activeCount: 0 })).toBeNull();
|
||||
|
||||
vi.advanceTimersByTime(6_001);
|
||||
|
||||
expect(selectNextPublishTask({ maxConcurrency: 2, activeCount: 0 })?.taskId).toBe("second");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user