More statistics (#20)

* cleaner stats

* remove shard selection there
This commit is contained in:
Lev Kokotov
2022-02-15 08:18:01 -08:00
committed by GitHub
parent 659b1e00b8
commit 05b4cccb97
5 changed files with 136 additions and 51 deletions

View File

@@ -57,6 +57,7 @@ pub struct Client {
default_server_role: Option<Role>, default_server_role: Option<Role>,
// Client parameters, e.g. user, client_encoding, etc. // Client parameters, e.g. user, client_encoding, etc.
#[allow(dead_code)]
parameters: HashMap<String, String>, parameters: HashMap<String, String>,
// Statistics // Statistics
@@ -302,8 +303,11 @@ impl Client {
// Release server // Release server
if self.transaction_mode { if self.transaction_mode {
self.stats.client_idle();
shard = None; shard = None;
role = self.default_server_role; role = self.default_server_role;
break; break;
} }
} }
@@ -371,8 +375,11 @@ impl Client {
self.stats.transaction(); self.stats.transaction();
if self.transaction_mode { if self.transaction_mode {
self.stats.client_idle();
shard = None; shard = None;
role = self.default_server_role; role = self.default_server_role;
break; break;
} }
} }

View File

@@ -17,6 +17,7 @@ pub enum Role {
pub struct Address { pub struct Address {
pub host: String, pub host: String,
pub port: String, pub port: String,
pub shard: usize,
pub role: Role, pub role: Role,
} }

View File

@@ -57,7 +57,7 @@ impl ConnectionPool {
let mut pools = Vec::new(); let mut pools = Vec::new();
let mut replica_addresses = Vec::new(); let mut replica_addresses = Vec::new();
for server in &shard.servers { for (idx, server) in shard.servers.iter().enumerate() {
let role = match server.2.as_ref() { let role = match server.2.as_ref() {
"primary" => Role::Primary, "primary" => Role::Primary,
"replica" => Role::Replica, "replica" => Role::Replica,
@@ -71,6 +71,7 @@ impl ConnectionPool {
host: server.0.clone(), host: server.0.clone(),
port: server.1.to_string(), port: server.1.to_string(),
role: role, role: role,
shard: idx,
}; };
let manager = ServerPool::new( let manager = ServerPool::new(
@@ -165,6 +166,9 @@ impl ConnectionPool {
None => 0, // TODO: pick a shard at random None => 0, // TODO: pick a shard at random
}; };
// We are waiting for a server now.
self.stats.client_waiting();
let addresses = &self.addresses[shard]; let addresses = &self.addresses[shard];
// Make sure if a specific role is requested, it's available in the pool. // Make sure if a specific role is requested, it's available in the pool.
@@ -237,7 +241,8 @@ impl ConnectionPool {
}; };
if !with_health_check { if !with_health_check {
self.stats.checkout_time(now.elapsed().as_millis()); self.stats.checkout_time(now.elapsed().as_micros());
self.stats.client_active();
return Ok((conn, address.clone())); return Ok((conn, address.clone()));
} }
@@ -253,7 +258,8 @@ impl ConnectionPool {
// Check if health check succeeded // Check if health check succeeded
Ok(res) => match res { Ok(res) => match res {
Ok(_) => { Ok(_) => {
self.stats.checkout_time(now.elapsed().as_millis()); self.stats.checkout_time(now.elapsed().as_micros());
self.stats.client_active();
return Ok((conn, address.clone())); return Ok((conn, address.clone()));
} }
Err(_) => { Err(_) => {
@@ -385,13 +391,10 @@ impl ManageConnection for ServerPool {
println!(">> Creating a new connection for the pool"); println!(">> Creating a new connection for the pool");
Server::startup( Server::startup(
&self.address.host, &self.address,
&self.address.port, &self.user,
&self.user.name,
&self.user.password,
&self.database, &self.database,
self.client_server_map.clone(), self.client_server_map.clone(),
self.address.role,
self.stats.clone(), self.stats.clone(),
) )
.await .await

View File

@@ -8,7 +8,7 @@ use tokio::io::{AsyncReadExt, BufReader};
use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf};
use tokio::net::TcpStream; use tokio::net::TcpStream;
use crate::config::{Address, Role}; use crate::config::{Address, User};
use crate::errors::Error; use crate::errors::Error;
use crate::messages::*; use crate::messages::*;
use crate::stats::Reporter; use crate::stats::Reporter;
@@ -16,11 +16,9 @@ use crate::ClientServerMap;
/// Server state. /// Server state.
pub struct Server { pub struct Server {
// Server host, e.g. localhost // Server host, e.g. localhost,
host: String, // port, e.g. 5432, and role, e.g. primary or replica.
address: Address,
// Server port: e.g. 5432
port: String,
// Buffered read socket // Buffered read socket
read: BufReader<OwnedReadHalf>, read: BufReader<OwnedReadHalf>,
@@ -50,9 +48,6 @@ pub struct Server {
// Mapping of clients and servers used for query cancellation. // Mapping of clients and servers used for query cancellation.
client_server_map: ClientServerMap, client_server_map: ClientServerMap,
// Server role, e.g. primary or replica.
role: Role,
// Server connected at // Server connected at
connected_at: chrono::naive::NaiveDateTime, connected_at: chrono::naive::NaiveDateTime,
@@ -64,25 +59,23 @@ impl Server {
/// Pretend to be the Postgres client and connect to the server given host, port and credentials. /// Pretend to be the Postgres client and connect to the server given host, port and credentials.
/// Perform the authentication and return the server in a ready-for-query mode. /// Perform the authentication and return the server in a ready-for-query mode.
pub async fn startup( pub async fn startup(
host: &str, address: &Address,
port: &str, user: &User,
user: &str,
password: &str,
database: &str, database: &str,
client_server_map: ClientServerMap, client_server_map: ClientServerMap,
role: Role,
stats: Reporter, stats: Reporter,
) -> Result<Server, Error> { ) -> Result<Server, Error> {
let mut stream = match TcpStream::connect(&format!("{}:{}", host, port)).await { let mut stream =
Ok(stream) => stream, match TcpStream::connect(&format!("{}:{}", &address.host, &address.port)).await {
Err(err) => { Ok(stream) => stream,
println!(">> Could not connect to server: {}", err); Err(err) => {
return Err(Error::SocketError); println!(">> Could not connect to server: {}", err);
} return Err(Error::SocketError);
}; }
};
// Send the startup packet. // Send the startup packet.
startup(&mut stream, user, database).await?; startup(&mut stream, &user.name, database).await?;
let mut server_info = BytesMut::with_capacity(25); let mut server_info = BytesMut::with_capacity(25);
let mut backend_id: i32 = 0; let mut backend_id: i32 = 0;
@@ -117,7 +110,8 @@ impl Server {
Err(_) => return Err(Error::SocketError), Err(_) => return Err(Error::SocketError),
}; };
md5_password(&mut stream, user, password, &salt[..]).await?; md5_password(&mut stream, &user.name, &user.password, &salt[..])
.await?;
} }
// Authentication handshake complete. // Authentication handshake complete.
@@ -189,8 +183,7 @@ impl Server {
let (read, write) = stream.into_split(); let (read, write) = stream.into_split();
return Ok(Server { return Ok(Server {
host: host.to_string(), address: address.clone(),
port: port.to_string(),
read: BufReader::new(read), read: BufReader::new(read),
write: write, write: write,
buffer: BytesMut::with_capacity(8196), buffer: BytesMut::with_capacity(8196),
@@ -201,7 +194,6 @@ impl Server {
data_available: false, data_available: false,
bad: false, bad: false,
client_server_map: client_server_map, client_server_map: client_server_map,
role: role,
connected_at: chrono::offset::Utc::now().naive_utc(), connected_at: chrono::offset::Utc::now().naive_utc(),
stats: stats, stats: stats,
}); });
@@ -382,8 +374,8 @@ impl Server {
( (
self.backend_id, self.backend_id,
self.secret_key, self.secret_key,
self.host.clone(), self.address.host.clone(),
self.port.clone(), self.address.port.clone(),
), ),
); );
} }
@@ -422,11 +414,7 @@ impl Server {
} }
pub fn address(&self) -> Address { pub fn address(&self) -> Address {
Address { self.address.clone()
host: self.host.to_string(),
port: self.port.to_string(),
role: self.role,
}
} }
} }

View File

@@ -14,6 +14,9 @@ pub enum StatisticName {
Transactions, Transactions,
DataSent, DataSent,
DataReceived, DataReceived,
ClientsWaiting,
ClientsActive,
ClientsIdle,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -76,6 +79,54 @@ impl Reporter {
let _ = self.tx.try_send(statistic); let _ = self.tx.try_send(statistic);
} }
pub fn client_waiting(&mut self) {
let statistic = Statistic {
name: StatisticName::ClientsWaiting,
value: 1,
};
let _ = self.tx.try_send(statistic);
let statistic = Statistic {
name: StatisticName::ClientsIdle,
value: -1,
};
let _ = self.tx.try_send(statistic);
}
pub fn client_active(&mut self) {
let statistic = Statistic {
name: StatisticName::ClientsWaiting,
value: -1,
};
let _ = self.tx.try_send(statistic);
let statistic = Statistic {
name: StatisticName::ClientsActive,
value: 1,
};
let _ = self.tx.try_send(statistic);
}
pub fn client_idle(&mut self) {
let statistic = Statistic {
name: StatisticName::ClientsActive,
value: -1,
};
let _ = self.tx.try_send(statistic);
let statistic = Statistic {
name: StatisticName::ClientsIdle,
value: 1,
};
let _ = self.tx.try_send(statistic);
}
} }
pub struct Collector { pub struct Collector {
@@ -93,12 +144,18 @@ impl Collector {
pub async fn collect(&mut self) { pub async fn collect(&mut self) {
let mut stats = HashMap::from([ let mut stats = HashMap::from([
("queries", 0), ("total_query_count", 0),
("transactions", 0), ("total_xact_count", 0),
("data_sent", 0), ("total_sent", 0),
("data_received", 0), ("total_received", 0),
("checkout_time", 0), ("total_wait_time", 0),
("maxwait_us", 0),
("maxwait", 0),
("cl_waiting", 0),
("cl_active", 0),
("cl_idle", 0),
]); ]);
let mut now = Instant::now(); let mut now = Instant::now();
loop { loop {
@@ -113,32 +170,61 @@ impl Collector {
// Some are counters, some are gauges... // Some are counters, some are gauges...
match stat.name { match stat.name {
StatisticName::Queries => { StatisticName::Queries => {
let counter = stats.entry("queries").or_insert(0); let counter = stats.entry("total_query_count").or_insert(0);
*counter += stat.value; *counter += stat.value;
} }
StatisticName::Transactions => { StatisticName::Transactions => {
let counter = stats.entry("transactions").or_insert(0); let counter = stats.entry("total_xact_count").or_insert(0);
*counter += stat.value; *counter += stat.value;
} }
StatisticName::DataSent => { StatisticName::DataSent => {
let counter = stats.entry("data_sent").or_insert(0); let counter = stats.entry("total_sent").or_insert(0);
*counter += stat.value; *counter += stat.value;
} }
StatisticName::DataReceived => { StatisticName::DataReceived => {
let counter = stats.entry("data_received").or_insert(0); let counter = stats.entry("total_received").or_insert(0);
*counter += stat.value; *counter += stat.value;
} }
StatisticName::CheckoutTime => { StatisticName::CheckoutTime => {
let counter = stats.entry("checkout_time").or_insert(0); let counter = stats.entry("total_wait_time").or_insert(0);
*counter += stat.value;
let counter = stats.entry("maxwait_us").or_insert(0);
// Report max time here // Report max time here
if stat.value > *counter { if stat.value > *counter {
*counter = stat.value; *counter = stat.value;
} }
let counter = stats.entry("maxwait").or_insert(0);
let seconds = *counter / 1_000_000;
if seconds > *counter {
*counter = seconds;
}
}
StatisticName::ClientsActive => {
let counter = stats.entry("cl_active").or_insert(0);
*counter += stat.value;
*counter = std::cmp::max(*counter, 0);
}
StatisticName::ClientsWaiting => {
let counter = stats.entry("cl_waiting").or_insert(0);
*counter += stat.value;
*counter = std::cmp::max(*counter, 0);
}
StatisticName::ClientsIdle => {
let counter = stats.entry("cl_idle").or_insert(0);
*counter += stat.value;
*counter = std::cmp::max(*counter, 0);
} }
}; };