fix: clear telegram polling cleanup timers

This commit is contained in:
Ayaan Zaidi
2026-03-12 09:30:21 +05:30
committed by Ayaan Zaidi
parent 70abee69e9
commit fbc1bd6f8e
3 changed files with 34 additions and 8 deletions

View File

@@ -398,6 +398,20 @@ describe("monitorTelegramProvider (grammY)", () => {
expect(createdBotStops[0]).toHaveBeenCalledTimes(1);
});
it("clears bounded cleanup timers after a clean stop", async () => {
vi.useFakeTimers();
try {
const abort = new AbortController();
mockRunOnceAndAbort(abort);
await monitorTelegramProvider({ token: "tok", abortSignal: abort.signal });
expect(vi.getTimerCount()).toBe(0);
} finally {
vi.useRealTimers();
}
});
it("surfaces non-recoverable errors", async () => {
runSpy.mockImplementationOnce(() =>
makeRunnerStub({

View File

@@ -17,6 +17,23 @@ const POLL_STALL_THRESHOLD_MS = 90_000;
const POLL_WATCHDOG_INTERVAL_MS = 30_000;
const POLL_STOP_GRACE_MS = 15_000;
const waitForGracefulStop = async (stop: () => Promise<void>) => {
let timer: ReturnType<typeof setTimeout> | undefined;
try {
await Promise.race([
stop(),
new Promise<void>((resolve) => {
timer = setTimeout(resolve, POLL_STOP_GRACE_MS);
timer.unref?.();
}),
]);
} finally {
if (timer) {
clearTimeout(timer);
}
}
};
type TelegramBot = ReturnType<typeof createTelegramBot>;
type TelegramPollingSessionOpts = {
@@ -271,14 +288,8 @@ export class TelegramPollingSession {
clearTimeout(forceCycleTimer);
}
this.opts.abortSignal?.removeEventListener("abort", stopOnAbort);
await Promise.race([
stopRunner(),
new Promise<void>((resolve) => setTimeout(resolve, POLL_STOP_GRACE_MS)),
]);
await Promise.race([
stopBot(),
new Promise<void>((resolve) => setTimeout(resolve, POLL_STOP_GRACE_MS)),
]);
await waitForGracefulStop(stopRunner);
await waitForGracefulStop(stopBot);
this.#activeRunner = undefined;
if (this.#activeFetchAbort === fetchAbortController) {
this.#activeFetchAbort = undefined;