Files
ServiceMap-Public/backend/app/scheduler.py
T

68 lines
2.4 KiB
Python

from __future__ import annotations
import asyncio
import logging
from time import monotonic
from .probes import probe_many
from .store import Store
logger = logging.getLogger(__name__)
async def scheduled_probe_loop(
store: Store,
*,
interval_seconds: int,
timeout_seconds: float,
retries: int,
concurrency: int,
retention_days: int,
) -> None:
next_due: dict[str, float] = {}
while True:
sleep_seconds = interval_seconds
try:
# The Explore demo is deliberately synthetic. Pausing scheduled
# probes while it is active keeps its seeded incident and history
# deterministic and avoids any dependency on external services.
if not getattr(store, "demo_active", lambda: False)():
services = store.list_services()
now = monotonic()
due = [
service
for service in services
if service.enabled
and service.monitoring_enabled
and now >= next_due.get(service.id, 0)
]
if due:
results = await probe_many(
due,
timeout_seconds=None,
retries=None,
concurrency=concurrency,
trigger="scheduled",
default_timeout_seconds=timeout_seconds,
default_retries=retries,
)
for result in results:
store.save_probe(result)
for service in due:
next_due[service.id] = now + (service.interval_seconds or interval_seconds)
next_intervals = [
service.interval_seconds or interval_seconds
for service in services
if service.enabled and service.monitoring_enabled
]
sleep_seconds = min([interval_seconds, *next_intervals]) if next_intervals else interval_seconds
else:
sleep_seconds = interval_seconds
store.prune_probes(retention_days)
except asyncio.CancelledError:
raise
except Exception: # pragma: no cover - defensive daemon boundary
logger.exception("Scheduled probe cycle failed")
await asyncio.sleep(max(1, sleep_seconds))