"""The scheduler thread: one per process, one tick every 30 seconds (AD-12).""" from __future__ import annotations import logging import threading from collections.abc import Callable from django.db import close_old_connections from . import services logger = logging.getLogger(__name__) TICK_SECONDS = 30 class Runner: def __init__(self, tick: Callable[[], None] = services.tick, interval: float = TICK_SECONDS): self._tick = tick self._interval = interval self._stop = threading.Event() self._thread: threading.Thread | None = None def start(self) -> None: """Start the thread; calling it again while it runs does nothing.""" if self._thread is not None and self._thread.is_alive(): return self._stop.clear() self._thread = threading.Thread(target=self._loop, name="scheduler", daemon=True) self._thread.start() def stop(self, timeout: float = 5) -> None: self._stop.set() if self._thread is not None: self._thread.join(timeout) def _loop(self) -> None: logger.info("Scheduler started") while not self._stop.is_set(): try: self._tick() except Exception: logger.exception("Scheduler tick failed") finally: close_old_connections() self._stop.wait(self._interval) logger.info("Scheduler stopped") runner = Runner()