fix: serialize local-agent heartbeats

This commit is contained in:
kris
2026-03-31 21:49:46 +08:00
parent 02fcc56332
commit be31503d22
3 changed files with 59 additions and 1 deletions

View File

@@ -0,0 +1,16 @@
export function createSerializedRunner(task) {
let activePromise = null;
return function runSerialized(...args) {
if (activePromise) {
return activePromise;
}
activePromise = Promise.resolve(task(...args))
.finally(() => {
activePromise = null;
});
return activePromise;
};
}

View File

@@ -7,6 +7,7 @@ import os from "node:os";
import { join, resolve } from "node:path";
import { discoverCodexProjectCandidatesInWorker } from "./codex-session-discovery.mjs";
import { buildCodexTaskExecution } from "./codex-task-runner.mjs";
import { createSerializedRunner } from "./serialized-runner.mjs";
async function loadConfig(configPath) {
const raw = await readFile(resolve(configPath), "utf8");
@@ -493,7 +494,7 @@ const runtime = {
lastProjectDiscoverySummary: null,
};
async function heartbeat() {
async function performHeartbeat() {
try {
const heartbeatProjects = await resolveHeartbeatProjects(config, runtime);
const result = await postHeartbeat(config, runtime, heartbeatProjects);
@@ -567,6 +568,8 @@ async function heartbeat() {
}
}
const heartbeat = createSerializedRunner(performHeartbeat);
const server = createServer(async (request, response) => {
if (request.url === "/health") {
response.writeHead(200, { "Content-Type": "application/json" });

View File

@@ -0,0 +1,39 @@
import test from "node:test";
import assert from "node:assert/strict";
import { createSerializedRunner } from "../local-agent/serialized-runner.mjs";
test("createSerializedRunner coalesces overlapping calls into the same promise", async () => {
let runs = 0;
let release;
const gate = new Promise((resolve) => {
release = resolve;
});
const runner = createSerializedRunner(async () => {
runs += 1;
await gate;
return { runs };
});
const first = runner();
const second = runner();
assert.equal(first, second);
assert.equal(runs, 1);
release();
const result = await first;
assert.deepEqual(result, { runs: 1 });
});
test("createSerializedRunner allows the next call after the current run finishes", async () => {
let runs = 0;
const runner = createSerializedRunner(async () => {
runs += 1;
return runs;
});
assert.equal(await runner(), 1);
assert.equal(await runner(), 2);
assert.equal(runs, 2);
});