change capture
This commit is contained in:
88
work-summary/2026-02-26-entity-history-phase3-analytics.md
Normal file
88
work-summary/2026-02-26-entity-history-phase3-analytics.md
Normal file
@@ -0,0 +1,88 @@
|
||||
# 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
|
||||
|
||||
1. **`migrations/20260226200000_continuous_aggregates.sql`** — TimescaleDB continuous aggregates migration:
|
||||
- `execution_status_hourly` — execution status transitions per hour by action_ref and status
|
||||
- `execution_throughput_hourly` — execution creation volume per hour by action_ref
|
||||
- `event_volume_hourly` — event creation volume per hour by trigger_ref
|
||||
- `worker_status_hourly` — worker status transitions per hour by worker name
|
||||
- `enforcement_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
|
||||
|
||||
2. **`crates/common/src/repositories/analytics.rs`** — Analytics repository:
|
||||
- Row types: `ExecutionStatusBucket`, `ExecutionThroughputBucket`, `EventVolumeBucket`, `WorkerStatusBucket`, `EnforcementVolumeBucket`, `FailureRateSummary`
|
||||
- `AnalyticsTimeRange` with `default()` (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
|
||||
|
||||
3. **`crates/api/src/dto/analytics.rs`** — Analytics DTOs:
|
||||
- `AnalyticsQueryParams` (since, until, hours) with `to_time_range()` conversion
|
||||
- Response types: `DashboardAnalyticsResponse`, `ExecutionStatusTimeSeriesResponse`, `ExecutionThroughputResponse`, `EventVolumeResponse`, `WorkerStatusTimeSeriesResponse`, `EnforcementVolumeResponse`, `FailureRateResponse`
|
||||
- `TimeSeriesPoint` — universal (bucket, label, value) data point
|
||||
- `From` conversions from all repository bucket types to `TimeSeriesPoint`
|
||||
- Unit tests for query param defaults, clamping, explicit ranges, and conversions
|
||||
|
||||
4. **`crates/api/src/routes/analytics.rs`** — 7 API endpoints:
|
||||
- `GET /api/v1/analytics/dashboard` — combined payload (all metrics in one call, concurrent queries via `tokio::try_join!`)
|
||||
- `GET /api/v1/analytics/executions/status` — status transitions over time
|
||||
- `GET /api/v1/analytics/executions/throughput` — creation throughput over time
|
||||
- `GET /api/v1/analytics/executions/failure-rate` — failure rate summary
|
||||
- `GET /api/v1/analytics/events/volume` — event creation volume
|
||||
- `GET /api/v1/analytics/workers/status` — worker status transitions
|
||||
- `GET /api/v1/analytics/enforcements/volume` — enforcement creation volume
|
||||
- All endpoints: authenticated, utoipa-documented, accept `since`/`until`/`hours` query params
|
||||
|
||||
5. **`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`
|
||||
|
||||
6. **`web/src/components/common/AnalyticsWidgets.tsx`** — Dashboard visualization components:
|
||||
- `AnalyticsDashboard` — composite widget rendering all charts and metrics
|
||||
- `MiniBarChart` — pure-CSS bar chart with hover tooltips and adaptive x-axis labels
|
||||
- `StackedBarChart` — stacked bar chart for status breakdowns with auto-generated legend
|
||||
- `FailureRateCard` — SVG ring gauge showing success/failure/timeout breakdown
|
||||
- `StatCard` — simple metric card with icon, label, and value
|
||||
- `TimeRangeSelector` — segmented button group (6h, 12h, 24h, 2d, 7d)
|
||||
- No external chart library dependency — all visualization is pure CSS/SVG
|
||||
|
||||
### Modified Files
|
||||
|
||||
7. **`crates/common/src/repositories/mod.rs`** — Registered `analytics` module, re-exported `AnalyticsRepository`
|
||||
|
||||
8. **`crates/api/src/dto/mod.rs`** — Registered `analytics` module, re-exported key DTO types
|
||||
|
||||
9. **`crates/api/src/routes/mod.rs`** — Registered `analytics` module, re-exported `analytics_routes`
|
||||
|
||||
10. **`crates/api/src/server.rs`** — Merged `analytics_routes()` into the API v1 router
|
||||
|
||||
11. **`web/src/pages/dashboard/DashboardPage.tsx`** — Added `AnalyticsDashboard` widget below existing metrics/activity sections with `TimeRangeHours` state management
|
||||
|
||||
12. **`docs/plans/timescaledb-entity-history.md`** — Marked Phase 2 continuous aggregates and Phase 3 analytics items as ✅ complete
|
||||
|
||||
13. **`AGENTS.md`** — Updated development status (continuous aggregates + analytics in Complete, removed from Planned)
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **Combined dashboard endpoint**: `GET /analytics/dashboard` fetches all 6 aggregate queries concurrently with `tokio::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 `hours` query 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_offset` to avoid refreshing the currently-filling bucket, and 7-day `start_offset` to 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
|
||||
51
work-summary/2026-02-26-entity-history-ui-panels.md
Normal file
51
work-summary/2026-02-26-entity-history-ui-panels.md
Normal file
@@ -0,0 +1,51 @@
|
||||
# Entity History UI Panels — Phase 2 Completion
|
||||
|
||||
**Date**: 2026-02-26
|
||||
|
||||
## Summary
|
||||
|
||||
Completed the remaining Phase 2 work for the TimescaleDB entity history feature by building the Web UI history panels and integrating them into entity detail pages.
|
||||
|
||||
## Changes
|
||||
|
||||
### New Files
|
||||
|
||||
1. **`web/src/hooks/useHistory.ts`** — React Query hooks for fetching entity history from the API:
|
||||
- `useEntityHistory()` — generic hook accepting entity type, ID, and query params
|
||||
- `useExecutionHistory()`, `useWorkerHistory()`, `useEnforcementHistory()`, `useEventHistory()` — convenience wrappers
|
||||
- Types: `HistoryRecord`, `PaginatedHistoryResponse`, `HistoryQueryParams`, `HistoryEntityType`
|
||||
- Uses `apiClient` (with auth interceptors) to call `GET /api/v1/{entities}/{id}/history`
|
||||
|
||||
2. **`web/src/components/common/EntityHistoryPanel.tsx`** — Reusable collapsible panel component:
|
||||
- Starts collapsed by default to avoid unnecessary API calls on page load
|
||||
- Fetches history only when expanded (via `enabled` flag on React Query)
|
||||
- **Filters**: Operation type dropdown (INSERT/UPDATE/DELETE) and changed field text input
|
||||
- **Pagination**: First/prev/next/last page navigation with total count
|
||||
- **Timeline rows**: Each record is expandable to show field-level details
|
||||
- **Field diffs**: For UPDATE operations, shows old → new values side by side; simple scalar values use inline red/green format, complex objects use side-by-side JSON blocks
|
||||
- **INSERT/DELETE handling**: Shows initial values or values at deletion respectively
|
||||
- Operation badges color-coded: green (INSERT), blue (UPDATE), red (DELETE)
|
||||
- Relative timestamps with full ISO 8601 tooltip
|
||||
|
||||
### Modified Files
|
||||
|
||||
3. **`web/src/pages/executions/ExecutionDetailPage.tsx`** — Added `EntityHistoryPanel` below the main content grid with `entityType="execution"`
|
||||
|
||||
4. **`web/src/pages/enforcements/EnforcementDetailPage.tsx`** — Added `EntityHistoryPanel` below the main content grid with `entityType="enforcement"`
|
||||
|
||||
5. **`web/src/pages/events/EventDetailPage.tsx`** — Added `EntityHistoryPanel` below the main content grid with `entityType="event"`
|
||||
|
||||
6. **`docs/plans/timescaledb-entity-history.md`** — Marked Phase 2 as ✅ complete with details of the UI implementation
|
||||
|
||||
7. **`AGENTS.md`** — Updated development status: moved "History UI panels" from Planned to Complete
|
||||
|
||||
### Not Modified
|
||||
|
||||
- **Worker detail page** does not exist yet in the web UI, so no worker history panel was added. The `useWorkerHistory()` hook and the `entityType="worker"` option are ready for when a worker detail page is created.
|
||||
|
||||
## Design Decisions
|
||||
|
||||
- **Collapsed by default**: History panels start collapsed to avoid unnecessary API requests on every page load. The query only fires when the user expands the panel.
|
||||
- **Uses `apiClient` directly**: Since the history endpoints aren't part of the generated OpenAPI client (they would need a client regeneration), the hook uses `apiClient` from `lib/api-client.ts` which already handles JWT auth and token refresh.
|
||||
- **Configurable page size**: Defaults to 10 records per page (suitable for a detail-page sidebar), but can be overridden via prop.
|
||||
- **Full-width placement**: The history panel is placed below the main two-column grid layout on each detail page, spanning full width for readability.
|
||||
Reference in New Issue
Block a user