feat: compute requests_per_second from SQLite in admin metrics snapshot

This commit is contained in:
whit3rabbit
2026-03-27 22:38:45 -05:00
parent cad85745f3
commit 52c6ffa3dd
2 changed files with 56 additions and 1 deletions
+39
View File
@@ -373,6 +373,18 @@ pub fn purge_old_logs(conn: &Connection, retention_days: u32) -> rusqlite::Resul
Ok(changed)
}
/// Count request log entries with a timestamp >= `since_epoch` (Unix seconds).
/// Used to compute requests-per-second for the metrics dashboard.
pub fn count_requests_since(conn: &Connection, since_epoch: u64) -> rusqlite::Result<u64> {
let since_iso = epoch_to_iso8601(since_epoch);
let count: i64 = conn.query_row(
"SELECT COUNT(*) FROM request_log WHERE timestamp >= ?1",
rusqlite::params![since_iso],
|row| row.get(0),
)?;
Ok(count.max(0) as u64)
}
/// Spawn the write buffer background task. Returns the sender for proxy handlers.
/// Flushes every 100ms or 100 rows, whichever comes first.
pub fn spawn_write_buffer(db: Arc<Mutex<Connection>>) -> mpsc::Sender<RequestLogEntry> {
@@ -1052,4 +1064,31 @@ mod tests {
query_request_log(&conn, 10, 0, None, None, Some("garbage"), None).unwrap();
assert_eq!(results.len(), 1);
}
#[test]
fn count_requests_since_returns_zero_on_empty_log() {
let conn = in_memory_db();
let count = count_requests_since(&conn, 0).unwrap();
assert_eq!(count, 0);
}
#[test]
fn count_requests_since_counts_recent_entries() {
let conn = in_memory_db();
// Insert a recent entry (sample_entry uses current time).
let recent = sample_entry();
insert_request_log(&conn, &recent).unwrap();
// Insert an old entry.
let mut old = sample_entry();
old.request_id = "old-req".to_string();
old.timestamp = "2020-01-01T00:00:00Z".to_string();
insert_request_log(&conn, &old).unwrap();
// Count since 2025-01-01 should include only the recent entry.
let since_2025: u64 = 1735689600; // 2025-01-01T00:00:00Z
let count = count_requests_since(&conn, since_2025).unwrap();
assert_eq!(count, 1);
}
}
+17 -1
View File
@@ -400,12 +400,28 @@ async fn main() {
backends.insert(name.clone(), snap);
}
let error_rate = aggregate.error_rate();
// Count requests in the last 60 seconds for RPS.
let rps = {
let db = snapshot_shared.db.clone();
let now_secs = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let since = now_secs.saturating_sub(60);
tokio::task::spawn_blocking(move || {
let conn = db.lock().unwrap_or_else(|e| e.into_inner());
admin::db::count_requests_since(&conn, since).unwrap_or(0)
})
.await
.unwrap_or(0) as f64
/ 60.0
};
let snapshot = admin::state::MetricsSnapshotData {
backends,
latency_p50_ms: None, // Computed on demand by REST endpoint
latency_p95_ms: None,
latency_p99_ms: None,
requests_per_second: 0.0, // TODO: compute from recent request log
requests_per_second: rps,
error_rate,
};
let _ = snapshot_shared