diff --git a/.env.example b/.env.example index b24e81b..5e10d65 100644 --- a/.env.example +++ b/.env.example @@ -27,4 +27,4 @@ REFERER="https://localhost" # Ping AVG period in hours PING_RANGE_H="3" # enable/disable startup scanning -#DISABLE_STARTUP_SCAN=true \ No newline at end of file +DISABLE_STARTUP_SCAN=false \ No newline at end of file diff --git a/entities/src/state/mod.rs b/entities/src/state/mod.rs index 8debd1f..a887b53 100644 --- a/entities/src/state/mod.rs +++ b/entities/src/state/mod.rs @@ -30,9 +30,9 @@ pub struct CacheHost { pub domain: String, pub points: i32, pub rss: bool, - pub recent_pings: Vec, - pub ping_max: i32, - pub ping_min: i32, + pub recent_pings: Vec>, + pub ping_max: Option, + pub ping_min: Option, pub ping_avg: Option, pub version: Option, pub healthy: bool, diff --git a/scanner/src/cache_update.rs b/scanner/src/cache_update.rs index 7670468..0ef1cf5 100644 --- a/scanner/src/cache_update.rs +++ b/scanner/src/cache_update.rs @@ -39,10 +39,12 @@ pub struct LastPingsQuery { ping: i32, } -#[derive(Debug)] +#[derive(Debug, Default)] struct LastPings { - avg: i32, - pings: Vec, + avg: Option, + min: Option, + max: Option, + pings: Vec>, } #[derive(Debug, FromQueryResult)] @@ -123,9 +125,9 @@ impl Scanner { rss: host.rss, version: host.version, healthy: last_check.healthy, - ping_max: host_ping_data.as_ref().and_then(|v|v.pings.iter().max().cloned()).unwrap_or(0i32), - ping_min: host_ping_data.as_ref().and_then(|v|v.pings.iter().min().cloned()).unwrap_or(0i32), - ping_avg: host_ping_data.as_ref().map(|v|v.avg), + ping_max: host_ping_data.as_ref().and_then(|v|v.max), + ping_min: host_ping_data.as_ref().and_then(|v|v.min), + ping_avg: host_ping_data.as_ref().and_then(|v|v.avg), recent_pings: host_ping_data.map(|v|v.pings).unwrap_or_default(), }) } @@ -149,15 +151,15 @@ impl Scanner { #[derive(Debug, FromQueryResult, Default)] struct PingEntry { host: i32, - ping: i32, + ping: Option, } let last_pings = PingEntry::find_by_statement(Statement::from_sql_and_values( DbBackend::Sqlite, r#" - SELECT u.host,u.resp_time as ping FROM update_check u + SELECT u.host,(CASE u.healthy WHEN true THEN u.resp_time ELSE null END) as ping FROM update_check u JOIN host h ON h.id = u.host - WHERE h.enabled = true AND u.healthy = true AND u.time >= $1 - ORDER BY u.host,u.time DESC + WHERE h.enabled = true AND u.time >= $1 + ORDER BY u.host,u.time ASC "#, [age.into()], )) @@ -169,31 +171,37 @@ impl Scanner { None => {return Ok(HashMap::new());}, Some(v) => v, }; - let mut current_entry = LastPings { - pings: Vec::new(),// TODO: optimize capacity - avg: 0, - }; + let mut current_entry = LastPings::default(); let mut last_host = first.host; + let mut non_null_entries = first.ping.as_ref().map_or(0, |_|1); current_entry.pings.push(first.ping); for ping in iter { if last_host != ping.host { - let mut new_entry = LastPings{ - avg: 0, - pings: Vec::new(), - }; + let mut new_entry = LastPings::default(); // will overflow only if we hit > 1500 days of backlog // when having 5 minutes interval and only 5000ms response times - current_entry.avg = (current_entry.pings.iter().copied().sum::() / (current_entry.pings.len() as i32) ) as i32; + if let Some(sum) = current_entry.avg { + current_entry.avg = Some(sum / non_null_entries); + } + non_null_entries = 0; std::mem::swap(&mut new_entry, &mut current_entry); // insert back the old (swapped) entry assert_eq!(map.insert(last_host, new_entry).is_some(),false); last_host = ping.host; } + if let Some(ping) = ping.ping.as_ref() { + current_entry.avg = Some(current_entry.avg.unwrap_or(0) + ping); + non_null_entries += 1; + current_entry.min = Some(current_entry.min.map_or(*ping,|v|v.min(*ping))); + current_entry.max = Some(current_entry.max.map_or(*ping,|v|v.max(*ping))); + } current_entry.pings.push(ping.ping); } - // Insert last one - current_entry.avg = (current_entry.pings.iter().copied().sum::() / (current_entry.pings.len() as i32) ) as i32; - map.insert(last_host, current_entry); + // Insert last entry + if let Some(sum) = current_entry.avg { + current_entry.avg = Some(sum / non_null_entries); + } + assert_eq!(map.insert(last_host, current_entry).is_some(),false); Ok(map) } diff --git a/server/src/website.rs b/server/src/website.rs index bd1fb97..c08a0f8 100644 --- a/server/src/website.rs +++ b/server/src/website.rs @@ -19,7 +19,6 @@ pub async fn instances( context.insert("instances", &guard.hosts); let time = guard.last_update.format("%Y.%m.%d %H:%M").to_string(); context.insert("last_updated", &time); - let res = Html(template.render("instances.html.j2", &context)?).into_response(); drop(guard); res diff --git a/server/templates/instances.html.j2 b/server/templates/instances.html.j2 index f9b2640..1454c52 100644 --- a/server/templates/instances.html.j2 +++ b/server/templates/instances.html.j2 @@ -26,34 +26,56 @@ - {% for host in instances %} + {% for host in instances -%} {{host.domain}} - {% if host.healthy%} ✅ {% else %} ❌ {% endif %} + {% if host.healthy -%} ✅ {% else -%} ❌ {% endif -%} - {% if host.recent_pings|length > 0 %} - {% set height = 57 %} - {% set width = 182 %} - {% set amount = host.recent_pings|length %} - {% set width_bar = width / amount %} - {% set offset = 5 %} - {% set step_size = (host.ping_max - host.ping_min)/(height-offset) %} + {% if host.recent_pings|length > 0 -%} + {% set height = 57 -%} + {% set width = 182 -%} + {% set amount = host.recent_pings|length -%} + {% set width_bar = width / amount -%} + {% set offset = 5 -%} + {% set error_size = height / 3 %} + {% if host.ping_max and host.ping_min -%} + {# only one entry, so max = min => step_size would be 0 #} + {% if host.ping_max == host.ping_min -%} + {% set step_size = 5/(height-offset) -%} + {% else -%} + {% set step_size = (host.ping_max - host.ping_min)/(height-offset) -%} + {% endif -%} + {% else %} + {# no valid entries, only nulls #} + {% set step_size = 5/(height-offset) -%} + {% endif %} + {# + + #} - {% set avg_pos = (height-(host.ping_avg - host.ping_min )/step_size) - offset %} - Average - {% for ping in host.recent_pings %} - {% set height_bar = (ping - host.ping_min )/step_size + offset%} - {{ping}} - {% endfor %} + {% if host.ping_avg and host.ping_min -%} + {% set avg_pos = (height-(host.ping_avg - host.ping_min )/step_size) - offset -%} + Average + {% endif -%} + {% for ping in host.recent_pings -%} + {% if ping -%} + {% set height_bar = (ping - host.ping_min )/step_size + offset -%} + {% set color_bar = "#a09df2" -%} + {% else %} + {% set height_bar = error_size -%} + {% set color_bar = "#cc0000" -%} + {% endif %} + {{ping | default(value="unhealthy") }} + {% endfor -%} {% endif %} {{host.ping_avg}} - {% if host.rss%} ✅ {% else %} ❌ {% endif %} + {% if host.rss -%} ✅ {% else %} ❌ {% endif %} {{host.version}} {{host.points}} - {% endfor %} + {% endfor -%}