perf(telemetry): only recompute the dashboard when data changed - #458
Conversation
The dashboard re-ran every query on a fixed 5s cadence whether or not anything had happened. A stats response is ~14 aggregate queries over the whole selected window — measured at ~800ms of database CPU for a 30-day range against 200k rows — so an idle tab was burning that every 5 seconds, as was every MCP `telemetry-stats` call. Live refresh is now two-tiered. Each tick polls `/api/telemetry/cursor`, two indexed `max()` lookups, and only refetches the real payloads when the token moved (plus a 60s heartbeat, since the range is a sliding window). Costing almost nothing per tick is what buys the headroom to poll more often, so the cadence drops from 5s to 3s and new runs surface faster. Server-side, stats are memoized per filter behind the same cursor, with a 5s floor, a 60s ceiling and single-flight so simultaneous misses — an SSR render, a second tab, an agent — collapse into one aggregation. The `telemetry-stats` MCP tool shares it. Also on the request path: - `shouldUseMockData()` ran `select id from runs limit 1` on every single request; it is now memoized, permanently once real runs exist. - The page awaited its three `useFetch` calls in sequence, so each request only started once the previous had returned — three serialised database round trips in the server-rendered response. They now run as one wave. - The live feed asked for a range-wide `count(*)` it never displays. - The cursor probe is excluded from request logging. Measured and rejected along the way: collapsing the 14 aggregates into one scan (a materialized CTE ran 1.5x slower, grouping sets broke even — the cost is in the aggregation, not the repeated scans), and composite or covering indexes (within noise on every path, and the covering variant was both slower on filtered ranges and 5x the disk).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
2 Skipped Deployments
|
|
Thank you for following the naming conventions! 🙏 |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (12)
📝 WalkthroughWalkthroughTelemetry adds cursor-based live refresh, parallel dashboard fetches, cached statistics, mock-mode caching, and optional total-count queries for the runs feed. ChangesTelemetry refresh and caching
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Dashboard
participant CursorAPI
participant RunsAPI
participant StatsAPI
participant TelemetryQueries
Dashboard->>CursorAPI: Poll telemetry cursor
CursorAPI->>TelemetryQueries: getRunsCursor()
TelemetryQueries-->>CursorAPI: RunsCursor
CursorAPI-->>Dashboard: latestId
alt Cursor advanced
Dashboard->>RunsAPI: Refresh runs with optional total
Dashboard->>RunsAPI: Refresh feed withTotal=false
end
alt Cursor advanced or stats heartbeat expired
Dashboard->>StatsAPI: Refresh cached stats
end
Possibly related PRs
Suggested labels: ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The dashboard re-ran every query on a fixed 5s cadence whether or not anything had happened. A stats response is ~14 aggregate queries over the whole selected window — measured at ~800ms of database CPU for a 30-day range against 200k rows — so an idle tab was burning that every 5 seconds, as was every MCP
telemetry-statscall.Live refresh is now two-tiered. Each tick polls
/api/telemetry/cursor, two indexedmax()lookups, and only refetches the real payloads when the token moved (plus a 60s heartbeat, since the range is a sliding window). Costing almost nothing per tick is what buys the headroom to poll more often, so the cadence drops from 5s to 3s and new runs surface faster.Server-side, stats are memoized per filter behind the same cursor, with a 5s floor, a 60s ceiling and single-flight so simultaneous misses — an SSR render, a second tab, an agent — collapse into one aggregation. The
telemetry-statsMCP tool shares it.Also on the request path:
shouldUseMockData()ranselect id from runs limit 1on every single request; it is now memoized, permanently once real runs exist.useFetchcalls in sequence, so each request only started once the previous had returned — three serialised database round trips in the server-rendered response. They now run as one wave.count(*)it never displays.Measured and rejected along the way: collapsing the 14 aggregates into one scan (a materialized CTE ran 1.5x slower, grouping sets broke even — the cost is in the aggregation, not the repeated scans), and composite or covering indexes (within noise on every path, and the covering variant was both slower on filtered ranges and 5x the disk).
Summary by CodeRabbit
New Features
Performance
Bug Fixes
Tests