Remove Jinja2 template fallback (1249 lines) — /admin/performance now serves Vue SPA exclusively via send_from_directory. Backend: - Add _SLOW_QUERY_WAITING counter with get_slow_query_waiting_count() - Record slow-path latency in read_sql_df_slow/iter via record_query_latency() - Extend metrics_history schema with slow_query_active, slow_query_waiting, worker_rss_bytes columns + ALTER TABLE migration for existing DBs - Add cleanup_archive_logs() with configurable ARCHIVE_LOG_DIR/KEEP_COUNT - Integrate archive cleanup into MetricsHistoryCollector 50-min cycle Frontend: - Add slow_query_active and slow_query_waiting StatCards to connection pool - Add slow_query_active trend line to pool trend chart - Add Worker memory (RSS MB) trend chart with preprocessing - Update modernization gate check path to frontend style.css Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
3.6 KiB
MODIFIED Requirements
Requirement: SQLite metrics history store
The system SHALL provide a MetricsHistoryStore class in core/metrics_history.py that persists metrics snapshots to a SQLite database (logs/metrics_history.sqlite by default). The store SHALL use thread-local connections and a write lock, following the LogStore pattern in core/log_store.py. The schema SHALL include columns for slow_query_active (INTEGER), slow_query_waiting (INTEGER), and worker_rss_bytes (INTEGER) in addition to the existing pool, Redis, route cache, and latency columns.
Scenario: Write and query snapshots
- WHEN
write_snapshot(data)is called with pool/redis/route_cache/latency/slow_query/memory metrics - THEN a row SHALL be inserted into
metrics_snapshotswith the current ISO 8601 timestamp, worker PID, and all metric columns
Scenario: Query by time range
- WHEN
query_snapshots(minutes=30)is called - THEN it SHALL return all rows from the last 30 minutes, ordered by timestamp ascending, including the new columns
Scenario: Retention cleanup
- WHEN
cleanup()is called - THEN rows older than
METRICS_HISTORY_RETENTION_DAYS(default 3) SHALL be deleted, and total rows SHALL be capped atMETRICS_HISTORY_MAX_ROWS(default 50000)
Scenario: Thread safety
- WHEN multiple threads write snapshots concurrently
- THEN the write lock SHALL serialize writes and prevent database corruption
Scenario: Schema migration for existing databases
- WHEN the store initializes on an existing database without the new columns
- THEN it SHALL execute ALTER TABLE ADD COLUMN for each missing column, tolerating "duplicate column" errors
Requirement: Background metrics collector
The system SHALL provide a MetricsHistoryCollector class that runs a daemon thread collecting metrics snapshots at a configurable interval (default 30 seconds, via METRICS_HISTORY_INTERVAL env var). The collector SHALL include slow_query_active, slow_query_waiting, and worker_rss_bytes in each snapshot.
Scenario: Automatic collection
- WHEN the collector is started via
start_metrics_history(app) - THEN it SHALL collect pool status (including slow_query_active and slow_query_waiting), Redis info, route cache status, query latency metrics, and worker RSS memory every interval and write them to the store
Scenario: Graceful shutdown
- WHEN
stop_metrics_history()is called - THEN the collector thread SHALL stop within one interval period
Scenario: Subsystem unavailability
- WHEN a subsystem (e.g., Redis) is unavailable during collection
- THEN the collector SHALL write null/0 for those fields and continue collecting other metrics
Requirement: Frontend trend charts
The system SHALL display 5 trend chart panels in the admin performance dashboard using vue-echarts VChart line/area charts: connection pool saturation, query latency (P50/P95/P99), Redis memory, cache hit rates, and worker memory.
Scenario: Trend charts with data
- WHEN historical snapshots contain more than 1 data point
- THEN the dashboard SHALL display trend charts for: connection pool saturation (including slow_query_active), query latency (P50/P95/P99), Redis memory, cache hit rates, and worker memory (RSS in MB)
Scenario: Trend charts without data
- WHEN historical snapshots are empty or contain only 1 data point
- THEN the trend charts SHALL NOT be displayed (hidden via
v-if)
Scenario: Auto-refresh
- WHEN the dashboard auto-refreshes
- THEN historical data SHALL also be refreshed alongside real-time metrics