Skip to content

Commit 704f619

Browse files
snopokeclaude
andcommitted
Harden the heartbeat thread
Keep the beat loop alive across unexpected errors, and round the derived stale timeout up so a sub-second interval can't produce 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent 4f4496c commit 704f619

2 files changed

Lines changed: 12 additions & 2 deletions

File tree

taskbadger/_heartbeat.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,14 @@ def _ensure_thread(self) -> None:
8181

8282
def _run(self) -> None:
8383
while True:
84-
self._wake.wait(self._beat())
84+
try:
85+
timeout = self._beat()
86+
except Exception:
87+
# Never let the thread die: every registered task would then go
88+
# stale with nothing to restart the pings.
89+
log.exception("heartbeat beat failed")
90+
timeout = 1.0
91+
self._wake.wait(timeout)
8592
self._wake.clear()
8693

8794
def _beat(self) -> float | None:

taskbadger/_integrations.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import collections
1414
import logging
15+
import math
1516
import re
1617

1718
from . import sdk
@@ -131,7 +132,9 @@ def resolve_heartbeat_options(heartbeat_interval, stale_timeout, system):
131132
heartbeat_interval = None
132133

133134
if stale_timeout is None and heartbeat_interval:
134-
stale_timeout = int(heartbeat_interval * STALE_TIMEOUT_FACTOR)
135+
# `stale_timeout` is whole seconds, so round up: a sub-second interval
136+
# must not produce a timeout of 0.
137+
stale_timeout = max(1, math.ceil(heartbeat_interval * STALE_TIMEOUT_FACTOR))
135138

136139
return heartbeat_interval, stale_timeout
137140

0 commit comments

Comments
 (0)