2026-05-21 10:05:40 +02:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
|
|
2026-05-22 17:05:06 +02:00
|
|
|
from .base import StoreConnectionMixin
|
2026-05-21 10:05:40 +02:00
|
|
|
|
2026-05-22 17:05:06 +02:00
|
|
|
|
2026-06-17 23:17:31 +02:00
|
|
|
def _update_metric_increments(
|
|
|
|
|
latency_ms: float,
|
|
|
|
|
*,
|
|
|
|
|
is_success: bool,
|
|
|
|
|
is_dns_failure: bool,
|
|
|
|
|
is_reconcile_failure: bool,
|
|
|
|
|
is_bad_ip: bool,
|
|
|
|
|
) -> dict[str, float]:
|
|
|
|
|
increments: dict[str, float] = {
|
|
|
|
|
"update_requests_total": 1,
|
|
|
|
|
"update_latency_ms_total": latency_ms,
|
|
|
|
|
"update_latency_samples_total": 1,
|
|
|
|
|
}
|
|
|
|
|
optional_increments = {
|
|
|
|
|
"update_success_total": is_success,
|
|
|
|
|
"dns_failures_total": is_dns_failure,
|
|
|
|
|
"dns_reconcile_failures_total": is_reconcile_failure,
|
|
|
|
|
"bad_ip_total": is_bad_ip,
|
|
|
|
|
}
|
|
|
|
|
increments.update({name: 1 for name, enabled in optional_increments.items() if enabled})
|
|
|
|
|
return increments
|
|
|
|
|
|
|
|
|
|
|
2026-05-22 17:05:06 +02:00
|
|
|
class MetricsMixin(StoreConnectionMixin):
|
2026-05-21 10:05:40 +02:00
|
|
|
def increment_metric(self, name: str, amount: float = 1.0) -> None:
|
|
|
|
|
now = datetime.now(UTC).isoformat()
|
|
|
|
|
with self.connect() as conn:
|
|
|
|
|
conn.execute(
|
|
|
|
|
"""
|
|
|
|
|
INSERT INTO service_metrics(name, value, updated_at)
|
|
|
|
|
VALUES (?, ?, ?)
|
|
|
|
|
ON CONFLICT(name) DO UPDATE SET
|
|
|
|
|
value=value + excluded.value,
|
|
|
|
|
updated_at=excluded.updated_at
|
|
|
|
|
""",
|
|
|
|
|
(name, amount, now),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def set_metric(self, name: str, value: float) -> None:
|
|
|
|
|
now = datetime.now(UTC).isoformat()
|
|
|
|
|
with self.connect() as conn:
|
|
|
|
|
conn.execute(
|
|
|
|
|
"""
|
|
|
|
|
INSERT INTO service_metrics(name, value, updated_at)
|
|
|
|
|
VALUES (?, ?, ?)
|
|
|
|
|
ON CONFLICT(name) DO UPDATE SET
|
|
|
|
|
value=excluded.value,
|
|
|
|
|
updated_at=excluded.updated_at
|
|
|
|
|
""",
|
|
|
|
|
(name, value, now),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def get_service_metrics(self) -> dict[str, float]:
|
|
|
|
|
with self.connect() as conn:
|
|
|
|
|
rows = conn.execute("SELECT name, value FROM service_metrics").fetchall()
|
|
|
|
|
return {str(row["name"]): float(row["value"]) for row in rows}
|
|
|
|
|
|
|
|
|
|
def record_update_metrics(
|
|
|
|
|
self,
|
|
|
|
|
latency_ms: float,
|
|
|
|
|
*,
|
|
|
|
|
is_success: bool = False,
|
|
|
|
|
is_dns_failure: bool = False,
|
|
|
|
|
is_reconcile_failure: bool = False,
|
|
|
|
|
is_bad_ip: bool = False,
|
|
|
|
|
) -> None:
|
2026-06-17 23:17:31 +02:00
|
|
|
increments = _update_metric_increments(
|
|
|
|
|
latency_ms,
|
|
|
|
|
is_success=is_success,
|
|
|
|
|
is_dns_failure=is_dns_failure,
|
|
|
|
|
is_reconcile_failure=is_reconcile_failure,
|
|
|
|
|
is_bad_ip=is_bad_ip,
|
|
|
|
|
)
|
2026-05-21 10:05:40 +02:00
|
|
|
|
|
|
|
|
now = datetime.now(UTC).isoformat()
|
|
|
|
|
with self.connect() as conn:
|
|
|
|
|
conn.executemany(
|
|
|
|
|
"""
|
|
|
|
|
INSERT INTO service_metrics(name, value, updated_at)
|
|
|
|
|
VALUES (?, ?, ?)
|
|
|
|
|
ON CONFLICT(name) DO UPDATE SET
|
|
|
|
|
value=value + excluded.value,
|
|
|
|
|
updated_at=excluded.updated_at
|
|
|
|
|
""",
|
|
|
|
|
[(name, value, now) for name, value in increments.items()],
|
|
|
|
|
)
|
|
|
|
|
conn.execute(
|
|
|
|
|
"""
|
|
|
|
|
INSERT INTO service_metrics(name, value, updated_at)
|
|
|
|
|
VALUES ('last_update_latency_ms', ?, ?)
|
|
|
|
|
ON CONFLICT(name) DO UPDATE SET
|
|
|
|
|
value=excluded.value,
|
|
|
|
|
updated_at=excluded.updated_at
|
|
|
|
|
""",
|
|
|
|
|
(latency_ms, now),
|
|
|
|
|
)
|