Files
Random-image-api/backend/app/core/random_request_stats.py
T
2026-02-22 18:58:55 +08:00

84 lines
2.6 KiB
Python

from __future__ import annotations
import asyncio
import time
from collections import deque
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class RandomRequestStatsSnapshot:
total_requests: int
total_ok: int
total_error: int
in_flight: int
window_seconds: int
last_window_requests: int
last_window_ok: int
last_window_error: int
last_window_success_rate: float
class RandomRequestStats:
def __init__(self, *, window_seconds: int = 60) -> None:
self._window_s = max(1, int(window_seconds))
self._lock = asyncio.Lock()
self._total_requests = 0
self._total_ok = 0
self._total_error = 0
self._in_flight = 0
# (monotonic_seconds, ok)
self._events: deque[tuple[float, bool]] = deque()
def _purge(self, *, now_m: float) -> None:
cutoff = float(now_m) - float(self._window_s)
while self._events:
t, _ok = self._events[0]
if float(t) >= cutoff:
break
self._events.popleft()
async def on_begin(self) -> float:
now_m = float(time.monotonic())
async with self._lock:
self._total_requests += 1
self._in_flight += 1
return now_m
async def on_end(self, *, status_code: int) -> None:
now_m = float(time.monotonic())
ok = 200 <= int(status_code) < 400
async with self._lock:
self._in_flight = max(0, int(self._in_flight) - 1)
if ok:
self._total_ok += 1
else:
self._total_error += 1
self._events.append((now_m, bool(ok)))
self._purge(now_m=now_m)
async def snapshot(self) -> RandomRequestStatsSnapshot:
now_m = float(time.monotonic())
async with self._lock:
self._purge(now_m=now_m)
last_total = int(len(self._events))
last_ok = int(sum(1 for _t, ok in self._events if ok))
last_error = int(last_total - last_ok)
rate = float(last_ok) / float(last_total) if last_total > 0 else 0.0
return RandomRequestStatsSnapshot(
total_requests=int(self._total_requests),
total_ok=int(self._total_ok),
total_error=int(self._total_error),
in_flight=int(self._in_flight),
window_seconds=int(self._window_s),
last_window_requests=int(last_total),
last_window_ok=int(last_ok),
last_window_error=int(last_error),
last_window_success_rate=float(rate),
)