- Implement 'Show Problems Only' toggle combining degraded status, high CPU/memory alerts, and needs backup filters - Add 'Investigate with AI' button to filter bar for problematic guests - Fix dashboard column sizing inconsistencies between bars and sparklines view modes - Fix PBS backups display and polling - Refine AI prompt for general-purpose usage - Fix frontend flickering and reload loops during initial load - Integrate persistent SQLite metrics store with Monitor - Fortify AI command routing with improved validation and logging - Fix CSRF token handling for note deletion - Debug and fix AI command execution issues - Various AI reliability improvements and command safety enhancements
6.6 KiB
6.6 KiB
Task: Persistent Metrics Storage for Sparklines
Problem
Currently, metrics history for sparklines is stored in-memory only. When the Pulse backend restarts, all historical metrics are lost. Users expect to see historical trends even after being away for days.
Goal
Implement SQLite-based persistent metrics storage that:
- Survives backend restarts
- Provides historical data for sparklines/trends view
- Supports configurable retention periods
- Minimizes disk I/O and storage footprint
Architecture
Storage Tiers (Data Rollup)
┌─────────────────────────────────────────────────────────┐
│ RAW (5s intervals) → Keep 2 hours → ~1,440 pts │
│ MINUTE (1min avg) → Keep 24 hours → ~1,440 pts │
│ HOURLY (1hr avg) → Keep 7 days → ~168 pts │
│ DAILY (1day avg) → Keep 90 days → ~90 pts │
└─────────────────────────────────────────────────────────┘
Database Schema
-- Main metrics table (partitioned by time for efficient pruning)
CREATE TABLE metrics (
id INTEGER PRIMARY KEY AUTOINCREMENT,
resource_type TEXT NOT NULL, -- 'node', 'vm', 'container', 'storage'
resource_id TEXT NOT NULL,
metric_type TEXT NOT NULL, -- 'cpu', 'memory', 'disk'
value REAL NOT NULL,
timestamp INTEGER NOT NULL, -- Unix timestamp in seconds
tier TEXT DEFAULT 'raw' -- 'raw', 'minute', 'hourly', 'daily'
);
-- Indexes for efficient queries
CREATE INDEX idx_metrics_lookup ON metrics(resource_type, resource_id, metric_type, tier, timestamp);
CREATE INDEX idx_metrics_timestamp ON metrics(timestamp);
CREATE INDEX idx_metrics_tier_time ON metrics(tier, timestamp);
Configuration
metrics:
enabled: true
database_path: "${PULSE_DATA_DIR}/metrics.db"
retention:
raw: 2h # 2 hours of raw data
minute: 24h # 24 hours of 1-minute averages
hourly: 168h # 7 days of hourly averages
daily: 2160h # 90 days of daily averages
write_buffer: 100 # Buffer size before batch write
rollup_interval: 5m # How often to run rollup job
Implementation Steps
Phase 1: SQLite Foundation ✅ COMPLETED
- Add SQLite dependency (
modernc.org/sqlite- pure Go, no CGO) - Create
internal/metrics/store.gowith:StorestructNewStore(config StoreConfig) (*Store, error)Close() error- Schema auto-migration on startup
Phase 2: Write Path ✅ COMPLETED
- Create
Write(resourceType, resourceID, metricType string, value float64, timestamp time.Time) - Implement write buffering (batch inserts every 100 records or 5 seconds)
- Integrate with existing
AddGuestMetric,AddNodeMetriccalls in monitor.go and monitor_polling.go - Add graceful shutdown to flush buffer
Phase 3: Read Path ✅ COMPLETED
- Create
Query(resourceType, resourceID, metricType string, start, end time.Time) ([]MetricPoint, error) - Auto-select appropriate tier based on time range:
- < 2 hours → raw data
- 2-24 hours → minute data
- 1-7 days → hourly data
- 7+ days → daily data
- Add
/api/metrics-store/statsendpoint for monitoring
Phase 4: Rollup & Retention ✅ COMPLETED
- Create background rollup job:
- Runs every 5 minutes
- Aggregates raw → minute (AVG, MIN, MAX)
- Aggregates minute → hourly
- Aggregates hourly → daily
- Create retention pruning job:
- Runs every hour
- Deletes data older than configured retention
- Use SQLite transactions for atomic operations
Phase 5: Integration
- Add configuration to
system.jsonor newmetrics.json - Add Settings UI for metrics retention config
- Add database file size monitoring
- Add vacuum/optimize scheduled job (weekly)
Files to Create/Modify
New Files
internal/metrics/
├── store.go # MetricsStore implementation
├── store_test.go # Unit tests
├── rollup.go # Rollup/aggregation logic
├── retention.go # Retention/pruning logic
└── config.go # Metrics configuration
Files to Modify
internal/monitoring/monitor.go # Initialize MetricsStore, call Write()
internal/monitoring/metrics_history.go # Keep in-memory as cache, backed by SQLite
internal/api/router.go # Update handleCharts to query from store
internal/config/persistence.go # Add metrics config persistence
API Changes
/api/charts Query Parameters
GET /api/charts?range=1h # Last hour (raw/minute data)
GET /api/charts?range=24h # Last 24 hours (minute data)
GET /api/charts?range=7d # Last 7 days (hourly data)
GET /api/charts?range=30d # Last 30 days (daily data)
GET /api/charts?start=...&end=... # Custom range
Response Enhancement
{
"data": { ... },
"nodeData": { ... },
"stats": {
"oldestDataTimestamp": 1699900000000,
"tier": "hourly",
"pointCount": 168
}
}
Performance Considerations
- Write Buffering: Batch inserts to reduce I/O
- WAL Mode: Enable SQLite WAL for concurrent reads/writes
- Prepared Statements: Reuse for repeated queries
- Index Strategy: Composite index on (resource_type, resource_id, metric_type, tier, timestamp)
- Connection Pooling: Single connection with proper locking for SQLite
- Memory Mapping: Use
PRAGMA mmap_sizefor faster reads
Storage Estimates
For a typical Pulse installation (5 nodes, 50 VMs, 20 containers, 10 storage):
- 85 resources × 3 metrics = 255 metric series
- Raw (2h at 5s): ~86,400 rows → ~10 MB
- Minute (24h): ~367,200 rows → ~40 MB
- Hourly (7d): ~42,840 rows → ~5 MB
- Daily (90d): ~22,950 rows → ~3 MB
- Total: ~60-100 MB for comprehensive historical data
Testing Plan
- Unit tests for store CRUD operations
- Unit tests for rollup logic
- Integration tests with mock monitor
- Performance tests with 100+ resources
- Restart resilience tests
Rollout Plan
- Implement as opt-in feature (disable by default initially)
- Add migration path from in-memory to SQLite
- Test in dev environment for 1 week
- Enable by default in next minor release
Definition of Done
- SQLite metrics storage implemented
- Data survives backend restart
- Rollup/retention working correctly
- Charts endpoint serves historical data
- Documentation updated
- Settings UI for retention config
- Performance validated (no noticeable slowdown)