Skip to main content

mini_apm/models/
rollup.rs

1use crate::DbPool;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct HourlyRollup {
6    pub id: i64,
7    pub hour: String,
8    pub path: String,
9    pub method: String,
10    pub request_count: i64,
11    pub error_count: i64,
12    pub total_ms_sum: f64,
13    pub total_ms_p50: Option<f64>,
14    pub total_ms_p95: Option<f64>,
15    pub total_ms_p99: Option<f64>,
16    pub db_ms_sum: f64,
17    pub db_count_sum: i64,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct DailyRollup {
22    pub id: i64,
23    pub date: String,
24    pub path: String,
25    pub method: String,
26    pub request_count: i64,
27    pub error_count: i64,
28    pub total_ms_p50: Option<f64>,
29    pub total_ms_p95: Option<f64>,
30    pub total_ms_p99: Option<f64>,
31    pub avg_db_ms: Option<f64>,
32    pub avg_db_count: Option<f64>,
33}
34
35pub fn insert_hourly(pool: &DbPool, rollup: &HourlyRollup) -> anyhow::Result<()> {
36    let conn = pool.get()?;
37    conn.execute(
38        r#"
39        INSERT OR REPLACE INTO rollups_hourly
40        (hour, path, method, request_count, error_count, total_ms_sum, total_ms_p50, total_ms_p95, total_ms_p99, db_ms_sum, db_count_sum)
41        VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11)
42        "#,
43        (
44            &rollup.hour,
45            &rollup.path,
46            &rollup.method,
47            rollup.request_count,
48            rollup.error_count,
49            rollup.total_ms_sum,
50            rollup.total_ms_p50,
51            rollup.total_ms_p95,
52            rollup.total_ms_p99,
53            rollup.db_ms_sum,
54            rollup.db_count_sum,
55        ),
56    )?;
57    Ok(())
58}
59
60pub fn insert_daily(pool: &DbPool, rollup: &DailyRollup) -> anyhow::Result<()> {
61    let conn = pool.get()?;
62    conn.execute(
63        r#"
64        INSERT OR REPLACE INTO rollups_daily
65        (date, path, method, request_count, error_count, total_ms_p50, total_ms_p95, total_ms_p99, avg_db_ms, avg_db_count)
66        VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)
67        "#,
68        (
69            &rollup.date,
70            &rollup.path,
71            &rollup.method,
72            rollup.request_count,
73            rollup.error_count,
74            rollup.total_ms_p50,
75            rollup.total_ms_p95,
76            rollup.total_ms_p99,
77            rollup.avg_db_ms,
78            rollup.avg_db_count,
79        ),
80    )?;
81    Ok(())
82}
83
84pub fn daily_for_range(
85    pool: &DbPool,
86    start: &str,
87    end: &str,
88    limit: i64,
89) -> anyhow::Result<Vec<DailyRollup>> {
90    let conn = pool.get()?;
91    let mut stmt = conn.prepare(
92        r#"
93        SELECT id, date, path, method, request_count, error_count,
94               total_ms_p50, total_ms_p95, total_ms_p99, avg_db_ms, avg_db_count
95        FROM rollups_daily
96        WHERE date >= ?1 AND date <= ?2
97        ORDER BY request_count DESC
98        LIMIT ?3
99        "#,
100    )?;
101
102    let rollups = stmt
103        .query_map(rusqlite::params![start, end, limit], |row| {
104            Ok(DailyRollup {
105                id: row.get(0)?,
106                date: row.get(1)?,
107                path: row.get(2)?,
108                method: row.get(3)?,
109                request_count: row.get(4)?,
110                error_count: row.get(5)?,
111                total_ms_p50: row.get(6)?,
112                total_ms_p95: row.get(7)?,
113                total_ms_p99: row.get(8)?,
114                avg_db_ms: row.get(9)?,
115                avg_db_count: row.get(10)?,
116            })
117        })?
118        .collect::<Result<Vec<_>, _>>()?;
119
120    Ok(rollups)
121}
122
123pub fn delete_hourly_before(pool: &DbPool, before: &str) -> anyhow::Result<usize> {
124    let conn = pool.get()?;
125    let deleted = conn.execute("DELETE FROM rollups_hourly WHERE hour < ?1", [before])?;
126    Ok(deleted)
127}