fix(cron): the ticker supervisor respawns only a ticker that crashed, not one that returned
SupervisedTickerThread (#111010) treated any ended thread as dead. An external provider's start() (Chronos) arms remote one-shots and returns by design, so every housekeeping tick logged "Cron ticker thread died without a stop request; restarting" at ERROR and re-ran start() - a fresh recover_interrupted + NAS list/arm reconcile once a minute on every hosted instance. Track whether the target escaped with an exception and respawn only then; the built-in ticker returns normally only on stop_event.
This commit is contained in:
@@ -23,11 +23,22 @@ class SupervisedTickerThread:
|
|||||||
stop_event: threading.Event, name: str = "cron-scheduler") -> None:
|
stop_event: threading.Event, name: str = "cron-scheduler") -> None:
|
||||||
self._target, self._args, self._kwargs = target, args, dict(kwargs or {})
|
self._target, self._args, self._kwargs = target, args, dict(kwargs or {})
|
||||||
self._stop_event, self._name = stop_event, name
|
self._stop_event, self._name = stop_event, name
|
||||||
|
# An external provider's start() (Chronos) arms remote one-shots and RETURNS by design;
|
||||||
|
# only a target that escaped with an exception is a dead ticker worth respawning.
|
||||||
|
self._crashed = False
|
||||||
self._thread = self._spawn()
|
self._thread = self._spawn()
|
||||||
self.restarts = 0
|
self.restarts = 0
|
||||||
|
|
||||||
|
def _run(self) -> None:
|
||||||
|
try:
|
||||||
|
self._target(*self._args, **self._kwargs)
|
||||||
|
except BaseException:
|
||||||
|
self._crashed = True
|
||||||
|
raise
|
||||||
|
|
||||||
def _spawn(self) -> threading.Thread:
|
def _spawn(self) -> threading.Thread:
|
||||||
return threading.Thread(target=self._target, args=self._args, kwargs=self._kwargs, daemon=True, name=self._name)
|
self._crashed = False
|
||||||
|
return threading.Thread(target=self._run, daemon=True, name=self._name)
|
||||||
|
|
||||||
def start(self) -> None:
|
def start(self) -> None:
|
||||||
self._thread.start()
|
self._thread.start()
|
||||||
@@ -39,8 +50,8 @@ class SupervisedTickerThread:
|
|||||||
self._thread.join(timeout)
|
self._thread.join(timeout)
|
||||||
|
|
||||||
def restart_if_dead(self) -> bool:
|
def restart_if_dead(self) -> bool:
|
||||||
"""Respawn the ticker when it ended without ``stop_event``; True when a restart happened."""
|
"""Respawn the ticker when it crashed without ``stop_event``; True when a restart happened."""
|
||||||
if self._stop_event.is_set() or self._thread.is_alive():
|
if self._stop_event.is_set() or self._thread.is_alive() or not self._crashed:
|
||||||
return False
|
return False
|
||||||
self.restarts += 1
|
self.restarts += 1
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|||||||
Reference in New Issue
Block a user