6.6 KiB
Entity History Phase 3 — Analytics Dashboard
Date: 2026-02-26
Summary
Implemented the final phase of the TimescaleDB entity history plan: continuous aggregates, analytics API endpoints, and dashboard visualization widgets. This completes the full history tracking pipeline from database triggers → hypertables → continuous aggregates → API → UI.
Changes
New Files
-
migrations/20260226200000_continuous_aggregates.sql— TimescaleDB continuous aggregates migration:execution_status_hourly— execution status transitions per hour by action_ref and statusexecution_throughput_hourly— execution creation volume per hour by action_refevent_volume_hourly— event creation volume per hour by trigger_refworker_status_hourly— worker status transitions per hour by worker nameenforcement_volume_hourly— enforcement creation volume per hour by rule_ref- Auto-refresh policies: 30-min interval for most, 1-hour for workers; 7-day lookback window
- Initial
CALL refresh_continuous_aggregate()for all five views
-
crates/common/src/repositories/analytics.rs— Analytics repository:- Row types:
ExecutionStatusBucket,ExecutionThroughputBucket,EventVolumeBucket,WorkerStatusBucket,EnforcementVolumeBucket,FailureRateSummary AnalyticsTimeRangewithdefault()(24h),last_hours(),last_days()constructors- Query methods for each aggregate (global and per-entity-ref variants)
execution_failure_rate()— derives failure percentage from terminal-state transitions- Unit tests for time range construction and failure rate math
- Row types:
-
crates/api/src/dto/analytics.rs— Analytics DTOs:AnalyticsQueryParams(since, until, hours) withto_time_range()conversion- Response types:
DashboardAnalyticsResponse,ExecutionStatusTimeSeriesResponse,ExecutionThroughputResponse,EventVolumeResponse,WorkerStatusTimeSeriesResponse,EnforcementVolumeResponse,FailureRateResponse TimeSeriesPoint— universal (bucket, label, value) data pointFromconversions from all repository bucket types toTimeSeriesPoint- Unit tests for query param defaults, clamping, explicit ranges, and conversions
-
crates/api/src/routes/analytics.rs— 7 API endpoints:GET /api/v1/analytics/dashboard— combined payload (all metrics in one call, concurrent queries viatokio::try_join!)GET /api/v1/analytics/executions/status— status transitions over timeGET /api/v1/analytics/executions/throughput— creation throughput over timeGET /api/v1/analytics/executions/failure-rate— failure rate summaryGET /api/v1/analytics/events/volume— event creation volumeGET /api/v1/analytics/workers/status— worker status transitionsGET /api/v1/analytics/enforcements/volume— enforcement creation volume- All endpoints: authenticated, utoipa-documented, accept
since/until/hoursquery params
-
web/src/hooks/useAnalytics.ts— React Query hooks:useDashboardAnalytics()— fetches combined dashboard payload (1-min stale, 2-min auto-refresh)- Individual hooks:
useExecutionStatusAnalytics(),useExecutionThroughputAnalytics(),useFailureRateAnalytics(),useEventVolumeAnalytics(),useWorkerStatusAnalytics(),useEnforcementVolumeAnalytics() - Types:
DashboardAnalytics,TimeSeriesPoint,FailureRateSummary,TimeSeriesResponse,AnalyticsQueryParams
-
web/src/components/common/AnalyticsWidgets.tsx— Dashboard visualization components:AnalyticsDashboard— composite widget rendering all charts and metricsMiniBarChart— pure-CSS bar chart with hover tooltips and adaptive x-axis labelsStackedBarChart— stacked bar chart for status breakdowns with auto-generated legendFailureRateCard— SVG ring gauge showing success/failure/timeout breakdownStatCard— simple metric card with icon, label, and valueTimeRangeSelector— segmented button group (6h, 12h, 24h, 2d, 7d)- No external chart library dependency — all visualization is pure CSS/SVG
Modified Files
-
crates/common/src/repositories/mod.rs— Registeredanalyticsmodule, re-exportedAnalyticsRepository -
crates/api/src/dto/mod.rs— Registeredanalyticsmodule, re-exported key DTO types -
crates/api/src/routes/mod.rs— Registeredanalyticsmodule, re-exportedanalytics_routes -
crates/api/src/server.rs— Mergedanalytics_routes()into the API v1 router -
web/src/pages/dashboard/DashboardPage.tsx— AddedAnalyticsDashboardwidget below existing metrics/activity sections withTimeRangeHoursstate management -
docs/plans/timescaledb-entity-history.md— Marked Phase 2 continuous aggregates and Phase 3 analytics items as ✅ complete -
AGENTS.md— Updated development status (continuous aggregates + analytics in Complete, removed from Planned)
Design Decisions
- Combined dashboard endpoint:
GET /analytics/dashboardfetches all 6 aggregate queries concurrently withtokio::try_join!, returning everything in one response. This avoids 6+ waterfall requests from the dashboard page. - No chart library: All visualization uses pure CSS (flex-based bars) and inline SVG (ring gauge). This avoids adding a heavy chart dependency for what are essentially bar charts and a donut. A dedicated charting library can be introduced later if more sophisticated visualizations are needed.
- Time range selector: The dashboard defaults to 24 hours and offers 6h/12h/24h/2d/7d presets. The
hoursquery param provides a simpler interface than specifying ISO timestamps for the common case. - Auto-refresh: The dashboard analytics hook has
refetchInterval: 120000(2 minutes) so the dashboard stays reasonably current without hammering the API. The continuous aggregates themselves refresh every 30 minutes on the server side. - Stale time: Analytics hooks use 60-second stale time since the underlying aggregates only refresh every 30 minutes — there's no benefit to re-fetching more often.
- Continuous aggregate refresh policies: 30-minute schedule for execution/event/enforcement aggregates (higher expected volume), 1-hour for workers (low volume). All with a 1-hour
end_offsetto avoid refreshing the currently-filling bucket, and 7-daystart_offsetto limit refresh scope.
Remaining (not in scope)
- Configurable retention periods via admin settings — retention policies are set in the migration; admin UI for changing them is deferred
- Export/archival to external storage — deferred to a future phase