mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 19:10:58 +00:00
fix(telegram): keep polling watchdog active for wedged runner
This commit is contained in:
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
|
|||||||
|
|
||||||
- Heartbeat: clamp oversized scheduler delays through the shared safe timer helper, preventing `every` values over Node's timeout cap from becoming a 1 ms crash loop. Fixes #71414. (#71478) Thanks @hclsys.
|
- Heartbeat: clamp oversized scheduler delays through the shared safe timer helper, preventing `every` values over Node's timeout cap from becoming a 1 ms crash loop. Fixes #71414. (#71478) Thanks @hclsys.
|
||||||
- Telegram: remove the startup persisted-offset `getUpdates` preflight so polling restarts do not self-conflict before the runner starts. Fixes #69304. (#69779) Thanks @chinar-amrutkar.
|
- Telegram: remove the startup persisted-offset `getUpdates` preflight so polling restarts do not self-conflict before the runner starts. Fixes #69304. (#69779) Thanks @chinar-amrutkar.
|
||||||
|
- Telegram: keep the polling stall watchdog active even when grammY reports the runner as not running while its task is still pending, so a rebuilt transport cannot leave `getUpdates` silent until a manual gateway restart. Fixes #69064. Thanks @LDLoeb.
|
||||||
- Browser/Playwright: ignore benign already-handled route races during guarded navigation so browser-page tasks no longer fail when Playwright tears down a route mid-flight. (#68708) Thanks @Steady-ai.
|
- Browser/Playwright: ignore benign already-handled route races during guarded navigation so browser-page tasks no longer fail when Playwright tears down a route mid-flight. (#68708) Thanks @Steady-ai.
|
||||||
- Browser/downloads: seed managed Chrome profiles with OpenClaw download prefs and capture unmanaged click-triggered downloads under the guarded downloads directory, while explicit download waiters still own their target file. (#64558) Thanks @Pearcekieser.
|
- Browser/downloads: seed managed Chrome profiles with OpenClaw download prefs and capture unmanaged click-triggered downloads under the guarded downloads directory, while explicit download waiters still own their target file. (#64558) Thanks @Pearcekieser.
|
||||||
- Browser/Chrome: stop passing redundant `--disable-setuid-sandbox` when `browser.noSandbox` is enabled; `--no-sandbox` remains the effective sandbox opt-out. (#67939) Thanks @sebykrueger.
|
- Browser/Chrome: stop passing redundant `--disable-setuid-sandbox` when `browser.noSandbox` is enabled; `--no-sandbox` remains the effective sandbox opt-out. (#67939) Thanks @sebykrueger.
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ describe("TelegramPollingLivenessTracker", () => {
|
|||||||
expect(
|
expect(
|
||||||
tracker.detectStall({
|
tracker.detectStall({
|
||||||
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
||||||
runnerIsRunning: true,
|
|
||||||
}),
|
}),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
|
|
||||||
@@ -45,7 +44,6 @@ describe("TelegramPollingLivenessTracker", () => {
|
|||||||
now = 120_001;
|
now = 120_001;
|
||||||
const stall = tracker.detectStall({
|
const stall = tracker.detectStall({
|
||||||
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
||||||
runnerIsRunning: true,
|
|
||||||
});
|
});
|
||||||
expect(stall?.message).toContain("Polling stall detected (no completed getUpdates");
|
expect(stall?.message).toContain("Polling stall detected (no completed getUpdates");
|
||||||
expect(stall?.message).toContain("inFlight=0 outcome=not-started");
|
expect(stall?.message).toContain("inFlight=0 outcome=not-started");
|
||||||
@@ -54,7 +52,6 @@ describe("TelegramPollingLivenessTracker", () => {
|
|||||||
expect(
|
expect(
|
||||||
tracker.detectStall({
|
tracker.detectStall({
|
||||||
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
||||||
runnerIsRunning: true,
|
|
||||||
}),
|
}),
|
||||||
).toBeNull();
|
).toBeNull();
|
||||||
});
|
});
|
||||||
@@ -69,7 +66,6 @@ describe("TelegramPollingLivenessTracker", () => {
|
|||||||
now = 120_001;
|
now = 120_001;
|
||||||
const stall = tracker.detectStall({
|
const stall = tracker.detectStall({
|
||||||
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
thresholdMs: POLL_STALL_THRESHOLD_MS,
|
||||||
runnerIsRunning: true,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(stall?.message).toContain("active getUpdates stuck");
|
expect(stall?.message).toContain("active getUpdates stuck");
|
||||||
|
|||||||
@@ -89,14 +89,7 @@ export class TelegramPollingLivenessTracker {
|
|||||||
this.#inFlightGetUpdates = Math.max(0, this.#inFlightGetUpdates - 1);
|
this.#inFlightGetUpdates = Math.max(0, this.#inFlightGetUpdates - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
detectStall(params: {
|
detectStall(params: { thresholdMs: number; now?: number }): TelegramPollingStall | null {
|
||||||
thresholdMs: number;
|
|
||||||
runnerIsRunning: boolean;
|
|
||||||
now?: number;
|
|
||||||
}): TelegramPollingStall | null {
|
|
||||||
if (!params.runnerIsRunning) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const now = params.now ?? this.#now();
|
const now = params.now ?? this.#now();
|
||||||
const activeElapsed =
|
const activeElapsed =
|
||||||
this.#inFlightGetUpdates > 0 && this.#lastGetUpdatesStartedAt != null
|
this.#inFlightGetUpdates > 0 && this.#lastGetUpdatesStartedAt != null
|
||||||
|
|||||||
@@ -387,6 +387,60 @@ describe("TelegramPollingSession", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forces a restart when the runner task is pending but reports not running", async () => {
|
||||||
|
const abort = new AbortController();
|
||||||
|
const firstRunnerStop = vi.fn(async () => undefined);
|
||||||
|
const secondRunnerStop = vi.fn(async () => undefined);
|
||||||
|
createTelegramBotMock.mockReturnValue(makeBot());
|
||||||
|
|
||||||
|
let firstTaskResolve: (() => void) | undefined;
|
||||||
|
const firstTask = new Promise<void>((resolve) => {
|
||||||
|
firstTaskResolve = resolve;
|
||||||
|
});
|
||||||
|
let cycle = 0;
|
||||||
|
runMock.mockImplementation(() => {
|
||||||
|
cycle += 1;
|
||||||
|
if (cycle === 1) {
|
||||||
|
return {
|
||||||
|
task: () => firstTask,
|
||||||
|
stop: async () => {
|
||||||
|
await firstRunnerStop();
|
||||||
|
firstTaskResolve?.();
|
||||||
|
},
|
||||||
|
isRunning: () => false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
task: async () => {
|
||||||
|
abort.abort();
|
||||||
|
},
|
||||||
|
stop: secondRunnerStop,
|
||||||
|
isRunning: () => false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const watchdogHarness = installPollingStallWatchdogHarness();
|
||||||
|
|
||||||
|
const log = vi.fn();
|
||||||
|
const session = createPollingSession({
|
||||||
|
abortSignal: abort.signal,
|
||||||
|
log,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const runPromise = session.runUntilAbort();
|
||||||
|
const watchdog = await watchdogHarness.waitForWatchdog();
|
||||||
|
watchdog?.();
|
||||||
|
await runPromise;
|
||||||
|
|
||||||
|
expect(runMock).toHaveBeenCalledTimes(2);
|
||||||
|
expect(firstRunnerStop).toHaveBeenCalledTimes(1);
|
||||||
|
expect(log).toHaveBeenCalledWith(expect.stringContaining("Polling stall detected"));
|
||||||
|
} finally {
|
||||||
|
watchdogHarness.restore();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("honors a custom polling stall threshold", async () => {
|
it("honors a custom polling stall threshold", async () => {
|
||||||
const abort = new AbortController();
|
const abort = new AbortController();
|
||||||
const botStop = vi.fn(async () => undefined);
|
const botStop = vi.fn(async () => undefined);
|
||||||
|
|||||||
@@ -295,7 +295,6 @@ export class TelegramPollingSession {
|
|||||||
|
|
||||||
const stall = liveness.detectStall({
|
const stall = liveness.detectStall({
|
||||||
thresholdMs: this.#stallThresholdMs,
|
thresholdMs: this.#stallThresholdMs,
|
||||||
runnerIsRunning: runner.isRunning(),
|
|
||||||
});
|
});
|
||||||
if (stall) {
|
if (stall) {
|
||||||
this.#transportState.markDirty();
|
this.#transportState.markDirty();
|
||||||
|
|||||||
Reference in New Issue
Block a user