Instrument DB query timing (11 operations across 3 repositories), streaming lifecycle (TTFB, duration, feature count), cache operation latency, listing detail step breakdown, and frontend page load / time-to-first-listing / stream download / detail load metrics. Adds 16 new OTel instruments, extends the perf ingestion endpoint with 4 new frontend metrics, and adds ~20 Grafana dashboard panels across 4 new rows (DB Query Performance, Streaming Performance, Listing Detail Breakdown, Cache Performance, Frontend Navigation).
56 lines
1.9 KiB
Python
56 lines
1.9 KiB
Python
"""Frontend performance metrics ingestion endpoint."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Response
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
import api.metrics as app_metrics
|
|
|
|
ALLOWED_METRICS = {
|
|
"worker_roundtrip", "worker_compute", "main_thread", "feature_count",
|
|
"page_load", "time_to_first_listing", "stream_download", "listing_detail_load",
|
|
}
|
|
MAX_BATCH_SIZE = 100
|
|
|
|
|
|
class PerfSample(BaseModel):
|
|
metric: str
|
|
operation: str = Field(max_length=50)
|
|
value: float = Field(ge=0, le=3600)
|
|
|
|
@field_validator("metric")
|
|
@classmethod
|
|
def validate_metric(cls, v: str) -> str:
|
|
if v not in ALLOWED_METRICS:
|
|
raise ValueError(f"Unknown metric: {v}")
|
|
return v
|
|
|
|
|
|
perf_router = APIRouter(tags=["perf"])
|
|
|
|
|
|
@perf_router.post("/api/perf")
|
|
async def record_perf(samples: list[PerfSample]) -> Response:
|
|
if len(samples) > MAX_BATCH_SIZE:
|
|
samples = samples[:MAX_BATCH_SIZE]
|
|
|
|
for s in samples:
|
|
attrs = {"operation": s.operation}
|
|
if s.metric == "worker_roundtrip":
|
|
app_metrics.frontend_worker_roundtrip.record(s.value, attrs)
|
|
elif s.metric == "worker_compute":
|
|
app_metrics.frontend_worker_compute.record(s.value, attrs)
|
|
elif s.metric == "main_thread":
|
|
app_metrics.frontend_main_thread.record(s.value, attrs)
|
|
elif s.metric == "feature_count":
|
|
app_metrics.frontend_feature_count.record(s.value)
|
|
elif s.metric == "page_load":
|
|
app_metrics.frontend_page_load.record(s.value, attrs)
|
|
elif s.metric == "time_to_first_listing":
|
|
app_metrics.frontend_time_to_first_listing.record(s.value, attrs)
|
|
elif s.metric == "stream_download":
|
|
app_metrics.frontend_stream_download.record(s.value)
|
|
elif s.metric == "listing_detail_load":
|
|
app_metrics.frontend_listing_detail_load.record(s.value)
|
|
|
|
return Response(status_code=204)
|