fix(companion): dedupe heartbeat loop scheduled timers
This commit is contained in:
@@ -760,6 +760,18 @@
|
|||||||
],
|
],
|
||||||
"test_status": "pnpm test:run src/companion/platformClients.integration.test.ts src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts + pnpm typecheck passing"
|
"test_status": "pnpm test:run src/companion/platformClients.integration.test.ts src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/heartbeatLoop.test.ts + pnpm typecheck passing"
|
||||||
},
|
},
|
||||||
|
"companion-heartbeat-loop-timer-dedup": {
|
||||||
|
"status": "completed",
|
||||||
|
"date": "2026-02-17",
|
||||||
|
"updated": "2026-02-17",
|
||||||
|
"summary": "Prevented duplicate pending timers in `CompanionHeartbeatLoop` by clearing any existing scheduled timeout before re-scheduling, with regression coverage for `tickNow()` during active loops.",
|
||||||
|
"files_modified": [
|
||||||
|
"src/companion/heartbeatLoop.ts",
|
||||||
|
"src/companion/heartbeatLoop.test.ts",
|
||||||
|
"docs/plans/state.json"
|
||||||
|
],
|
||||||
|
"test_status": "pnpm test:run src/companion/heartbeatLoop.test.ts src/companion/platformClients.test.ts src/companion/runtimeClient.test.ts src/companion/platformClients.integration.test.ts + pnpm typecheck passing"
|
||||||
|
},
|
||||||
"browser-tools-activation-clarity": {
|
"browser-tools-activation-clarity": {
|
||||||
"status": "completed",
|
"status": "completed",
|
||||||
"date": "2026-02-17",
|
"date": "2026-02-17",
|
||||||
|
|||||||
@@ -217,4 +217,21 @@ describe('CompanionHeartbeatLoop', () => {
|
|||||||
lastFailure: null,
|
lastFailure: null,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('tickNow while running does not accumulate duplicate scheduled timers', async () => {
|
||||||
|
const publishHeartbeat = vi.fn(async () => buildStatusResult());
|
||||||
|
const loop = new CompanionHeartbeatLoop({ publishHeartbeat }, { intervalMs: 1000 });
|
||||||
|
|
||||||
|
loop.start(false);
|
||||||
|
expect(vi.getTimerCount()).toBe(1);
|
||||||
|
|
||||||
|
await loop.tickNow();
|
||||||
|
expect(vi.getTimerCount()).toBe(1);
|
||||||
|
expect(publishHeartbeat).toHaveBeenCalledTimes(1);
|
||||||
|
|
||||||
|
await vi.advanceTimersByTimeAsync(1000);
|
||||||
|
expect(publishHeartbeat).toHaveBeenCalledTimes(2);
|
||||||
|
|
||||||
|
loop.stop();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -138,6 +138,10 @@ export class CompanionHeartbeatLoop {
|
|||||||
if (!this.started) {
|
if (!this.started) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (this.timer) {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = null;
|
||||||
|
}
|
||||||
const delay = this.computeDelayMs();
|
const delay = this.computeDelayMs();
|
||||||
this.timer = setTimeout(() => {
|
this.timer = setTimeout(() => {
|
||||||
void this.tick();
|
void this.tick();
|
||||||
|
|||||||
Reference in New Issue
Block a user