| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- from __future__ import annotations
- from datetime import datetime, timedelta
- from typing import Any
- from supply_infra.config import InfraSettings, get_infra_settings
- from supply_infra.db.repositories.pipeline_lock_repo import PipelineLockRepository
- from supply_infra.db.repositories.pipeline_run_repo import PipelineRunRepository
- from supply_infra.db.session import get_session
- from supply_infra.pipeline.dates import china_now
- SCHEDULER_HEARTBEAT_KEY = "pipeline:scheduler"
- _ACTIVE_STATUSES = {
- "queued",
- "running",
- "cancelling",
- "blocked_previous_run",
- }
- def touch_scheduler_heartbeat(
- *,
- owner: str,
- settings: InfraSettings | None = None,
- ) -> None:
- active = settings or get_infra_settings()
- with get_session() as session:
- PipelineLockRepository(session).touch(
- lock_key=SCHEDULER_HEARTBEAT_KEY,
- owner=owner,
- lease_seconds=max(
- active.pipeline_lease_seconds,
- active.pipeline_heartbeat_seconds * 3,
- ),
- now=china_now(),
- )
- def run_alert_level(
- *,
- status: str,
- started_at: datetime | None,
- created_at: datetime,
- deadline_at: datetime | None,
- now: datetime,
- settings: InfraSettings,
- ) -> str | None:
- if status not in _ACTIVE_STATUSES:
- return None
- if deadline_at is not None:
- remaining = deadline_at - now
- if remaining <= timedelta(minutes=settings.pipeline_final_warn_before_next_minutes):
- return "final_warning"
- if remaining <= timedelta(minutes=settings.pipeline_critical_before_next_minutes):
- return "critical"
- baseline = started_at or created_at
- if now - baseline >= timedelta(hours=settings.pipeline_warn_after_hours):
- return "warning"
- return None
- def pipeline_health_snapshot(
- settings: InfraSettings | None = None,
- ) -> dict[str, Any]:
- active = settings or get_infra_settings()
- now = china_now()
- with get_session() as session:
- scheduler_lock = PipelineLockRepository(session).get(SCHEDULER_HEARTBEAT_KEY)
- latest = PipelineRunRepository(session).list_recent(limit=1)
- latest_run = latest[0] if latest else None
- scheduler_heartbeat_at = (
- scheduler_lock.heartbeat_at if scheduler_lock is not None else None
- )
- scheduler_lease_until = (
- scheduler_lock.lease_until if scheduler_lock is not None else None
- )
- latest_snapshot = (
- {
- "run_id": latest_run.run_id,
- "status": latest_run.status,
- "started_at": latest_run.started_at,
- "created_at": latest_run.created_at,
- "deadline_at": latest_run.deadline_at,
- }
- if latest_run is not None
- else None
- )
- scheduler_heartbeat = (
- scheduler_heartbeat_at.isoformat()
- if scheduler_heartbeat_at is not None
- else None
- )
- scheduler_running = bool(
- active.scheduler_enabled
- and scheduler_lease_until is not None
- and scheduler_lease_until > now
- )
- alert_level = None
- if latest_snapshot is not None:
- alert_level = run_alert_level(
- status=latest_snapshot["status"],
- started_at=latest_snapshot["started_at"],
- created_at=latest_snapshot["created_at"],
- deadline_at=latest_snapshot["deadline_at"],
- now=now,
- settings=active,
- )
- return {
- "scheduler_enabled": active.scheduler_enabled,
- "scheduler_running": scheduler_running,
- "scheduler_heartbeat_at": scheduler_heartbeat,
- "latest_run_id": latest_snapshot["run_id"] if latest_snapshot else None,
- "latest_run_status": latest_snapshot["status"] if latest_snapshot else None,
- "latest_run_alert_level": alert_level,
- "checked_at": now.isoformat(),
- }
|