"""Foreground reconciliation runner suitable for a systemd service.""" import sys import time from collections.abc import Callable from datetime import UTC, datetime from typing import TextIO from production_analytics.calculations.config import finite_number from production_analytics.service.downtime import DowntimeReconciliationService class DowntimeReconciliationRunner: def __init__( self, service: DowntimeReconciliationService, *, machine_id: str, poll_interval_seconds: float = 300, clock: Callable[[], datetime] | None = None, sleep: Callable[[float], None] = time.sleep, stdout: TextIO | None = None, stderr: TextIO | None = None, ) -> None: self.service, self.machine_id = service, machine_id self.interval = finite_number(poll_interval_seconds, "poll interval", positive=True) self.clock, self.sleep = clock or (lambda: datetime.now(UTC)), sleep self.stdout, self.stderr = stdout or sys.stdout, stderr or sys.stderr def run(self) -> None: try: while True: now = self.clock() try: count = self.service.reconcile_once(now) print( f"{now.isoformat()} machine={self.machine_id!r} reconciled={count}", file=self.stdout, flush=True, ) except Exception as exc: print( f"{now.isoformat()} machine={self.machine_id!r} reconciliation failed: " f"{type(exc).__name__}", file=self.stderr, flush=True, ) self.sleep(self.interval) except KeyboardInterrupt: return