fix: serialize local-agent heartbeats
This commit is contained in:
16
local-agent/serialized-runner.mjs
Normal file
16
local-agent/serialized-runner.mjs
Normal 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;
|
||||
};
|
||||
}
|
||||
@@ -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" });
|
||||
|
||||
39
tests/local-agent-serialized-runner.test.mjs
Normal file
39
tests/local-agent-serialized-runner.test.mjs
Normal 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);
|
||||
});
|
||||
Reference in New Issue
Block a user