Add navigation & usage metrics for end-user experience visibility
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).
This commit is contained in:
parent
1ae00b7cbf
commit
35f1987ac1
11 changed files with 1236 additions and 26 deletions
|
|
@ -1,6 +1,8 @@
|
|||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
import time
|
||||
from typing import Any, Generator
|
||||
from api.metrics import record_db_query
|
||||
from data_access import Listing
|
||||
from models.listing import (
|
||||
BuyListing,
|
||||
|
|
@ -55,8 +57,10 @@ class ListingRepository:
|
|||
if limit:
|
||||
query = query.limit(limit)
|
||||
|
||||
t0 = time.monotonic()
|
||||
with Session(self.engine) as session:
|
||||
rows = list(session.exec(query).all())
|
||||
record_db_query("get_listings", model.__tablename__, time.monotonic() - t0, len(rows))
|
||||
logging.debug(f"Found {len(rows)} listings")
|
||||
return rows
|
||||
|
||||
|
|
@ -110,8 +114,11 @@ class ListingRepository:
|
|||
query = sa_select(func.count(model.id))
|
||||
query = self._apply_query_filters(query, model, query_parameters)
|
||||
|
||||
t0 = time.monotonic()
|
||||
with Session(self.engine) as session:
|
||||
return session.execute(query).scalar() or 0
|
||||
result = session.execute(query).scalar() or 0
|
||||
record_db_query("count_listings", model.__tablename__, time.monotonic() - t0, result)
|
||||
return result
|
||||
|
||||
def stream_listings_optimized(
|
||||
self,
|
||||
|
|
@ -157,8 +164,10 @@ class ListingRepository:
|
|||
batch_limit = min(page_size, limit - total_yielded)
|
||||
query = query.order_by(model.id).limit(batch_limit)
|
||||
|
||||
t0 = time.monotonic()
|
||||
with Session(self.engine) as session:
|
||||
results = session.execute(query).fetchall()
|
||||
record_db_query("stream_listings_page", model.__tablename__, time.monotonic() - t0, len(results))
|
||||
|
||||
if not results:
|
||||
break
|
||||
|
|
@ -364,6 +373,7 @@ class ListingRepository:
|
|||
|
||||
Checks both RentListing and BuyListing tables and returns the latest.
|
||||
"""
|
||||
t0 = time.monotonic()
|
||||
with Session(self.engine) as session:
|
||||
rent_max = session.execute(
|
||||
sa_select(func.max(RentListing.last_seen))
|
||||
|
|
@ -371,6 +381,7 @@ class ListingRepository:
|
|||
buy_max = session.execute(
|
||||
sa_select(func.max(BuyListing.last_seen))
|
||||
).scalar()
|
||||
record_db_query("get_last_updated", "rent", time.monotonic() - t0)
|
||||
|
||||
candidates = [t for t in (rent_max, buy_max) if t is not None]
|
||||
return max(candidates) if candidates else None
|
||||
|
|
@ -385,9 +396,12 @@ class ListingRepository:
|
|||
filtering against API results.
|
||||
"""
|
||||
model = RentListing if listing_type == ListingType.RENT else BuyListing
|
||||
t0 = time.monotonic()
|
||||
with Session(self.engine) as session:
|
||||
result = session.execute(sa_select(model.id))
|
||||
return {row[0] for row in result.fetchall()}
|
||||
ids = {row[0] for row in result.fetchall()}
|
||||
record_db_query("get_listing_ids", model.__tablename__, time.monotonic() - t0, len(ids))
|
||||
return ids
|
||||
|
||||
async def mark_seen(self, listing_id: int, listing_type: ListingType = ListingType.RENT) -> None:
|
||||
query_params = QueryParameters(listing_type=listing_type)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue