Add Server-Timing headers to all API endpoints for per-request latency breakdown
Instrument every API endpoint with Server-Timing headers so sub-operation durations are visible in browser DevTools Network tab. Also adds Grafana dashboard panels for per-endpoint latency comparison (p50/p95 timeseries and p95 ranking bar gauge).
This commit is contained in:
parent
35f1987ac1
commit
2357722e80
4 changed files with 271 additions and 5 deletions
|
|
@ -1,7 +1,8 @@
|
|||
import logging
|
||||
import time
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, Response
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from api.auth import User, get_current_user
|
||||
|
|
@ -55,11 +56,17 @@ async def set_decision(
|
|||
user: Annotated[User, Depends(get_current_user)],
|
||||
listing_id: int,
|
||||
body: SetDecisionRequest,
|
||||
response: Response,
|
||||
) -> DecisionResponse:
|
||||
"""Set or update a like/dislike decision for a listing."""
|
||||
timings: list[str] = []
|
||||
t0_total = time.monotonic()
|
||||
t0 = time.monotonic()
|
||||
user_id = _get_user_id(user)
|
||||
timings.append(f"user_lookup;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
repo = DecisionRepository(engine)
|
||||
try:
|
||||
t0 = time.monotonic()
|
||||
result = decision_service.set_decision(
|
||||
repo,
|
||||
user_id=user_id,
|
||||
|
|
@ -67,19 +74,31 @@ async def set_decision(
|
|||
listing_type=body.listing_type,
|
||||
decision=body.decision,
|
||||
)
|
||||
timings.append(f"upsert;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
timings.append(f"total;dur={(time.monotonic() - t0_total) * 1000:.1f}")
|
||||
response.headers["Server-Timing"] = ", ".join(timings)
|
||||
return _to_response(result)
|
||||
|
||||
|
||||
@decision_router.get("", response_model=list[DecisionResponse])
|
||||
async def get_decisions(
|
||||
user: Annotated[User, Depends(get_current_user)],
|
||||
response: Response,
|
||||
) -> list[DecisionResponse]:
|
||||
"""Get all decisions for the current user."""
|
||||
timings: list[str] = []
|
||||
t0_total = time.monotonic()
|
||||
t0 = time.monotonic()
|
||||
user_id = _get_user_id(user)
|
||||
timings.append(f"user_lookup;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
repo = DecisionRepository(engine)
|
||||
t0 = time.monotonic()
|
||||
decisions = decision_service.get_user_decisions(repo, user_id)
|
||||
timings.append(f"fetch;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
timings.append(f"total;dur={(time.monotonic() - t0_total) * 1000:.1f}")
|
||||
response.headers["Server-Timing"] = ", ".join(timings)
|
||||
return [_to_response(d) for d in decisions]
|
||||
|
||||
|
||||
|
|
@ -87,17 +106,26 @@ async def get_decisions(
|
|||
async def delete_decision(
|
||||
user: Annotated[User, Depends(get_current_user)],
|
||||
listing_id: int,
|
||||
response: Response,
|
||||
listing_type: str = Query(..., description="RENT or BUY"),
|
||||
) -> dict[str, bool]:
|
||||
"""Remove a decision (un-like/un-dislike)."""
|
||||
timings: list[str] = []
|
||||
t0_total = time.monotonic()
|
||||
t0 = time.monotonic()
|
||||
user_id = _get_user_id(user)
|
||||
timings.append(f"user_lookup;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
repo = DecisionRepository(engine)
|
||||
try:
|
||||
t0 = time.monotonic()
|
||||
deleted = decision_service.remove_decision(
|
||||
repo, user_id, listing_id, listing_type
|
||||
)
|
||||
timings.append(f"delete;dur={(time.monotonic() - t0) * 1000:.1f}")
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
if not deleted:
|
||||
raise HTTPException(status_code=404, detail="Decision not found")
|
||||
timings.append(f"total;dur={(time.monotonic() - t0_total) * 1000:.1f}")
|
||||
response.headers["Server-Timing"] = ", ".join(timings)
|
||||
return {"success": True}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue