mirror of
https://github.com/postgresml/pgcat.git
synced 2026-03-27 10:46:30 +00:00
70
src/stats.rs
70
src/stats.rs
@@ -1,4 +1,4 @@
|
|||||||
use log::{error, info};
|
use log::{debug, error, info};
|
||||||
use once_cell::sync::OnceCell;
|
use once_cell::sync::OnceCell;
|
||||||
use statsd::Client;
|
use statsd::Client;
|
||||||
/// Events collector and publisher.
|
/// Events collector and publisher.
|
||||||
@@ -10,6 +10,7 @@ use std::sync::{Arc, Mutex};
|
|||||||
use crate::config::get_config;
|
use crate::config::get_config;
|
||||||
|
|
||||||
static LATEST_STATS: OnceCell<Arc<Mutex<HashMap<String, i64>>>> = OnceCell::new();
|
static LATEST_STATS: OnceCell<Arc<Mutex<HashMap<String, i64>>>> = OnceCell::new();
|
||||||
|
static STAT_PERIOD: u64 = 15000; //15 seconds
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum EventName {
|
enum EventName {
|
||||||
@@ -228,7 +229,15 @@ impl Collector {
|
|||||||
("total_xact_count", 0),
|
("total_xact_count", 0),
|
||||||
("total_sent", 0),
|
("total_sent", 0),
|
||||||
("total_received", 0),
|
("total_received", 0),
|
||||||
|
("total_xact_time", 0),
|
||||||
|
("total_query_time", 0),
|
||||||
("total_wait_time", 0),
|
("total_wait_time", 0),
|
||||||
|
("avg_xact_time", 0),
|
||||||
|
("avg_query_time", 0),
|
||||||
|
("avg_xact_count", 0),
|
||||||
|
("avg_sent", 0),
|
||||||
|
("avg_received", 0),
|
||||||
|
("avg_wait_time", 0),
|
||||||
("maxwait_us", 0),
|
("maxwait_us", 0),
|
||||||
("maxwait", 0),
|
("maxwait", 0),
|
||||||
("cl_waiting", 0),
|
("cl_waiting", 0),
|
||||||
@@ -240,11 +249,18 @@ impl Collector {
|
|||||||
("sv_tested", 0),
|
("sv_tested", 0),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
let mut client_server_states: HashMap<i32, EventName> = HashMap::new();
|
// Stats saved after each iteration of the flush event. Used in calculation
|
||||||
let tx = self.tx.clone();
|
// of averages in the last flush period.
|
||||||
|
let mut old_stats: HashMap<String, i64> = HashMap::new();
|
||||||
|
|
||||||
|
// Track which state the client and server are at any given time.
|
||||||
|
let mut client_server_states: HashMap<i32, EventName> = HashMap::new();
|
||||||
|
|
||||||
|
// Flush stats to StatsD and calculate averages every 15 seconds.
|
||||||
|
let tx = self.tx.clone();
|
||||||
tokio::task::spawn(async move {
|
tokio::task::spawn(async move {
|
||||||
let mut interval = tokio::time::interval(tokio::time::Duration::from_millis(15000));
|
let mut interval =
|
||||||
|
tokio::time::interval(tokio::time::Duration::from_millis(STAT_PERIOD));
|
||||||
loop {
|
loop {
|
||||||
interval.tick().await;
|
interval.tick().await;
|
||||||
let _ = tx.try_send(Event {
|
let _ = tx.try_send(Event {
|
||||||
@@ -255,6 +271,7 @@ impl Collector {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// The collector loop
|
||||||
loop {
|
loop {
|
||||||
let stat = match self.rx.recv().await {
|
let stat = match self.rx.recv().await {
|
||||||
Some(stat) => stat,
|
Some(stat) => stat,
|
||||||
@@ -291,10 +308,11 @@ impl Collector {
|
|||||||
*counter += stat.value;
|
*counter += stat.value;
|
||||||
|
|
||||||
let counter = stats.entry("maxwait_us").or_insert(0);
|
let counter = stats.entry("maxwait_us").or_insert(0);
|
||||||
|
let mic_part = stat.value % 1_000_000;
|
||||||
|
|
||||||
// Report max time here
|
// Report max time here
|
||||||
if stat.value > *counter {
|
if mic_part > *counter {
|
||||||
*counter = stat.value;
|
*counter = mic_part;
|
||||||
}
|
}
|
||||||
|
|
||||||
let counter = stats.entry("maxwait").or_insert(0);
|
let counter = stats.entry("maxwait").or_insert(0);
|
||||||
@@ -320,6 +338,7 @@ impl Collector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
EventName::FlushStatsToStatsD => {
|
EventName::FlushStatsToStatsD => {
|
||||||
|
// Calculate connection states
|
||||||
for (_, state) in &client_server_states {
|
for (_, state) in &client_server_states {
|
||||||
match state {
|
match state {
|
||||||
EventName::ClientActive => {
|
EventName::ClientActive => {
|
||||||
@@ -361,8 +380,26 @@ impl Collector {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("{:?}", stats);
|
// Calculate averages
|
||||||
|
for stat in &[
|
||||||
|
"avg_query_count",
|
||||||
|
"avgxact_count",
|
||||||
|
"avg_sent",
|
||||||
|
"avg_received",
|
||||||
|
"avg_wait_time",
|
||||||
|
] {
|
||||||
|
let total_name = stat.replace("avg_", "total_");
|
||||||
|
let old_value = old_stats.entry(total_name.clone()).or_insert(0);
|
||||||
|
let new_value = stats.get(total_name.as_str()).unwrap_or(&0).to_owned();
|
||||||
|
let avg = (new_value - *old_value) / (STAT_PERIOD as i64 / 1_000); // Avg / second
|
||||||
|
|
||||||
|
stats.insert(stat, avg);
|
||||||
|
*old_value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("{:?}", stats);
|
||||||
|
|
||||||
|
// Update latest stats used in SHOW STATS
|
||||||
match LATEST_STATS.get() {
|
match LATEST_STATS.get() {
|
||||||
Some(arc) => {
|
Some(arc) => {
|
||||||
let mut guard = arc.lock().unwrap();
|
let mut guard = arc.lock().unwrap();
|
||||||
@@ -376,9 +413,24 @@ impl Collector {
|
|||||||
|
|
||||||
let mut pipeline = self.client.pipeline();
|
let mut pipeline = self.client.pipeline();
|
||||||
|
|
||||||
for (key, value) in stats.iter_mut() {
|
for (key, value) in stats.iter() {
|
||||||
pipeline.gauge(key, *value as f64);
|
pipeline.gauge(key, *value as f64);
|
||||||
*value = 0;
|
}
|
||||||
|
|
||||||
|
// These are re-calculated every iteration of the loop, so we don't want to add values
|
||||||
|
// from the last iteration.
|
||||||
|
for stat in &[
|
||||||
|
"cl_active",
|
||||||
|
"cl_waiting",
|
||||||
|
"cl_idle",
|
||||||
|
"sv_idle",
|
||||||
|
"sv_active",
|
||||||
|
"sv_tested",
|
||||||
|
"sv_login",
|
||||||
|
"maxwait",
|
||||||
|
"maxwait_us",
|
||||||
|
] {
|
||||||
|
stats.insert(stat, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pipeline.send(&self.client);
|
pipeline.send(&self.client);
|
||||||
|
|||||||
Reference in New Issue
Block a user