2022-03-10 01:33:29 -08:00
|
|
|
/// Admin database.
|
2022-02-25 18:20:15 -08:00
|
|
|
use bytes::{Buf, BufMut, BytesMut};
|
2022-02-27 10:21:24 -08:00
|
|
|
use log::{info, trace};
|
2022-02-28 08:14:39 -08:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
2022-07-27 21:47:55 -05:00
|
|
|
use crate::config::{get_config, reload_config, VERSION};
|
2022-02-25 18:20:15 -08:00
|
|
|
use crate::errors::Error;
|
2022-03-01 08:47:19 -08:00
|
|
|
use crate::messages::*;
|
2022-07-27 21:47:55 -05:00
|
|
|
use crate::pool::get_all_pools;
|
2022-02-25 18:20:15 -08:00
|
|
|
use crate::stats::get_stats;
|
2022-06-24 14:52:38 -07:00
|
|
|
use crate::ClientServerMap;
|
2022-02-25 18:20:15 -08:00
|
|
|
|
2022-07-31 21:52:23 -05:00
|
|
|
pub fn generate_server_info_for_admin() -> BytesMut {
|
|
|
|
|
let mut server_info = BytesMut::new();
|
|
|
|
|
|
|
|
|
|
server_info.put(server_paramater_message("application_name", ""));
|
|
|
|
|
server_info.put(server_paramater_message("client_encoding", "UTF8"));
|
|
|
|
|
server_info.put(server_paramater_message("server_encoding", "UTF8"));
|
|
|
|
|
server_info.put(server_paramater_message("server_version", VERSION));
|
|
|
|
|
server_info.put(server_paramater_message("DateStyle", "ISO, MDY"));
|
|
|
|
|
|
|
|
|
|
return server_info;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Handle admin client.
|
2022-06-27 15:52:01 -07:00
|
|
|
pub async fn handle_admin<T>(
|
|
|
|
|
stream: &mut T,
|
2022-02-28 17:22:28 -08:00
|
|
|
mut query: BytesMut,
|
2022-06-24 14:52:38 -07:00
|
|
|
client_server_map: ClientServerMap,
|
2022-06-27 15:52:01 -07:00
|
|
|
) -> Result<(), Error>
|
|
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-02-25 18:20:15 -08:00
|
|
|
let code = query.get_u8() as char;
|
|
|
|
|
|
|
|
|
|
if code != 'Q' {
|
|
|
|
|
return Err(Error::ProtocolSyncError);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let len = query.get_i32() as usize;
|
|
|
|
|
let query = String::from_utf8_lossy(&query[..len - 5])
|
|
|
|
|
.to_string()
|
|
|
|
|
.to_ascii_uppercase();
|
|
|
|
|
|
2022-02-28 17:22:28 -08:00
|
|
|
trace!("Admin query: {}", query);
|
|
|
|
|
|
2022-02-25 18:20:15 -08:00
|
|
|
if query.starts_with("SHOW STATS") {
|
|
|
|
|
trace!("SHOW STATS");
|
2022-07-27 21:47:55 -05:00
|
|
|
show_stats(stream).await
|
2022-02-27 10:21:24 -08:00
|
|
|
} else if query.starts_with("RELOAD") {
|
|
|
|
|
trace!("RELOAD");
|
2022-06-24 14:52:38 -07:00
|
|
|
reload(stream, client_server_map).await
|
2022-02-28 08:14:39 -08:00
|
|
|
} else if query.starts_with("SHOW CONFIG") {
|
|
|
|
|
trace!("SHOW CONFIG");
|
|
|
|
|
show_config(stream).await
|
2022-02-28 17:22:28 -08:00
|
|
|
} else if query.starts_with("SHOW DATABASES") {
|
|
|
|
|
trace!("SHOW DATABASES");
|
2022-07-27 21:47:55 -05:00
|
|
|
show_databases(stream).await
|
2022-03-01 22:49:43 -08:00
|
|
|
} else if query.starts_with("SHOW POOLS") {
|
|
|
|
|
trace!("SHOW POOLS");
|
2022-07-27 21:47:55 -05:00
|
|
|
show_pools(stream).await
|
2022-03-01 22:49:43 -08:00
|
|
|
} else if query.starts_with("SHOW LISTS") {
|
|
|
|
|
trace!("SHOW LISTS");
|
2022-07-27 21:47:55 -05:00
|
|
|
show_lists(stream).await
|
2022-03-01 22:49:43 -08:00
|
|
|
} else if query.starts_with("SHOW VERSION") {
|
|
|
|
|
trace!("SHOW VERSION");
|
|
|
|
|
show_version(stream).await
|
2022-02-28 17:22:28 -08:00
|
|
|
} else if query.starts_with("SET ") {
|
|
|
|
|
trace!("SET");
|
|
|
|
|
ignore_set(stream).await
|
2022-02-25 18:20:15 -08:00
|
|
|
} else {
|
2022-02-28 17:22:28 -08:00
|
|
|
error_response(stream, "Unsupported query against the admin database").await
|
2022-02-25 18:20:15 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Column-oriented statistics.
|
2022-07-27 21:47:55 -05:00
|
|
|
async fn show_lists<T>(stream: &mut T) -> Result<(), Error>
|
2022-06-27 15:52:01 -07:00
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-03-01 22:49:43 -08:00
|
|
|
let stats = get_stats();
|
|
|
|
|
|
|
|
|
|
let columns = vec![("list", DataType::Text), ("items", DataType::Int4)];
|
|
|
|
|
|
2022-07-27 21:47:55 -05:00
|
|
|
let mut users = 1;
|
|
|
|
|
let mut databases = 1;
|
|
|
|
|
for (_, pool) in get_all_pools() {
|
|
|
|
|
databases += pool.databases();
|
|
|
|
|
users += 1; // One user per pool
|
|
|
|
|
}
|
2022-03-01 22:49:43 -08:00
|
|
|
let mut res = BytesMut::new();
|
|
|
|
|
res.put(row_description(&columns));
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"databases".to_string(),
|
2022-07-27 21:47:55 -05:00
|
|
|
databases.to_string(),
|
2022-03-01 22:49:43 -08:00
|
|
|
]));
|
2022-07-27 21:47:55 -05:00
|
|
|
res.put(data_row(&vec!["users".to_string(), users.to_string()]));
|
|
|
|
|
res.put(data_row(&vec!["pools".to_string(), databases.to_string()]));
|
2022-03-01 22:49:43 -08:00
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"free_clients".to_string(),
|
2022-03-04 17:04:27 -08:00
|
|
|
stats
|
|
|
|
|
.keys()
|
|
|
|
|
.map(|address_id| stats[&address_id]["cl_idle"])
|
|
|
|
|
.sum::<i64>()
|
|
|
|
|
.to_string(),
|
2022-03-01 22:49:43 -08:00
|
|
|
]));
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"used_clients".to_string(),
|
2022-03-04 17:04:27 -08:00
|
|
|
stats
|
|
|
|
|
.keys()
|
|
|
|
|
.map(|address_id| stats[&address_id]["cl_active"])
|
|
|
|
|
.sum::<i64>()
|
|
|
|
|
.to_string(),
|
2022-03-01 22:49:43 -08:00
|
|
|
]));
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"login_clients".to_string(),
|
|
|
|
|
"0".to_string(),
|
|
|
|
|
]));
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"free_servers".to_string(),
|
2022-03-04 17:04:27 -08:00
|
|
|
stats
|
|
|
|
|
.keys()
|
|
|
|
|
.map(|address_id| stats[&address_id]["sv_idle"])
|
|
|
|
|
.sum::<i64>()
|
|
|
|
|
.to_string(),
|
2022-03-01 22:49:43 -08:00
|
|
|
]));
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
"used_servers".to_string(),
|
2022-03-04 17:04:27 -08:00
|
|
|
stats
|
|
|
|
|
.keys()
|
|
|
|
|
.map(|address_id| stats[&address_id]["sv_active"])
|
|
|
|
|
.sum::<i64>()
|
|
|
|
|
.to_string(),
|
2022-03-01 22:49:43 -08:00
|
|
|
]));
|
|
|
|
|
res.put(data_row(&vec!["dns_names".to_string(), "0".to_string()]));
|
|
|
|
|
res.put(data_row(&vec!["dns_zones".to_string(), "0".to_string()]));
|
|
|
|
|
res.put(data_row(&vec!["dns_queries".to_string(), "0".to_string()]));
|
|
|
|
|
res.put(data_row(&vec!["dns_pending".to_string(), "0".to_string()]));
|
|
|
|
|
|
|
|
|
|
res.put(command_complete("SHOW"));
|
|
|
|
|
|
|
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Show PgCat version.
|
2022-06-27 15:52:01 -07:00
|
|
|
async fn show_version<T>(stream: &mut T) -> Result<(), Error>
|
|
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-03-01 22:49:43 -08:00
|
|
|
let mut res = BytesMut::new();
|
|
|
|
|
|
|
|
|
|
res.put(row_description(&vec![("version", DataType::Text)]));
|
2022-07-27 21:47:55 -05:00
|
|
|
res.put(data_row(&vec![format!("PgCat {}", VERSION).to_string()]));
|
2022-03-01 22:49:43 -08:00
|
|
|
res.put(command_complete("SHOW"));
|
|
|
|
|
|
|
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Show utilization of connection pools for each shard and replicas.
|
2022-07-27 21:47:55 -05:00
|
|
|
async fn show_pools<T>(stream: &mut T) -> Result<(), Error>
|
2022-06-27 15:52:01 -07:00
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-03-01 22:49:43 -08:00
|
|
|
let stats = get_stats();
|
|
|
|
|
|
|
|
|
|
let columns = vec![
|
|
|
|
|
("database", DataType::Text),
|
|
|
|
|
("user", DataType::Text),
|
2022-08-15 22:51:37 -05:00
|
|
|
("cl_idle", DataType::Numeric),
|
2022-03-01 22:49:43 -08:00
|
|
|
("cl_active", DataType::Numeric),
|
|
|
|
|
("cl_waiting", DataType::Numeric),
|
|
|
|
|
("cl_cancel_req", DataType::Numeric),
|
|
|
|
|
("sv_active", DataType::Numeric),
|
|
|
|
|
("sv_idle", DataType::Numeric),
|
|
|
|
|
("sv_used", DataType::Numeric),
|
|
|
|
|
("sv_tested", DataType::Numeric),
|
|
|
|
|
("sv_login", DataType::Numeric),
|
|
|
|
|
("maxwait", DataType::Numeric),
|
|
|
|
|
("maxwait_us", DataType::Numeric),
|
|
|
|
|
("pool_mode", DataType::Text),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let mut res = BytesMut::new();
|
|
|
|
|
res.put(row_description(&columns));
|
2022-07-27 21:47:55 -05:00
|
|
|
for (_, pool) in get_all_pools() {
|
|
|
|
|
let pool_config = &pool.settings;
|
|
|
|
|
for shard in 0..pool.shards() {
|
|
|
|
|
for server in 0..pool.servers(shard) {
|
|
|
|
|
let address = pool.address(shard, server);
|
|
|
|
|
let stats = match stats.get(&address.id) {
|
|
|
|
|
Some(stats) => stats.clone(),
|
|
|
|
|
None => HashMap::new(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let mut row = vec![address.name(), pool_config.user.username.clone()];
|
|
|
|
|
|
|
|
|
|
for column in &columns[2..columns.len() - 1] {
|
|
|
|
|
let value = stats.get(column.0).unwrap_or(&0).to_string();
|
|
|
|
|
row.push(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
row.push(pool_config.pool_mode.to_string());
|
|
|
|
|
res.put(data_row(&row));
|
2022-03-04 17:04:27 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-03-01 22:49:43 -08:00
|
|
|
|
|
|
|
|
res.put(command_complete("SHOW"));
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
// ReadyForQuery
|
2022-03-01 22:49:43 -08:00
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Show shards and replicas.
|
2022-07-27 21:47:55 -05:00
|
|
|
async fn show_databases<T>(stream: &mut T) -> Result<(), Error>
|
2022-06-27 15:52:01 -07:00
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-03-01 08:47:19 -08:00
|
|
|
// Columns
|
|
|
|
|
let columns = vec![
|
|
|
|
|
("name", DataType::Text),
|
|
|
|
|
("host", DataType::Text),
|
|
|
|
|
("port", DataType::Text),
|
|
|
|
|
("database", DataType::Text),
|
|
|
|
|
("force_user", DataType::Text),
|
|
|
|
|
("pool_size", DataType::Int4),
|
|
|
|
|
("min_pool_size", DataType::Int4),
|
|
|
|
|
("reserve_pool", DataType::Int4),
|
|
|
|
|
("pool_mode", DataType::Text),
|
|
|
|
|
("max_connections", DataType::Int4),
|
|
|
|
|
("current_connections", DataType::Int4),
|
|
|
|
|
("paused", DataType::Int4),
|
|
|
|
|
("disabled", DataType::Int4),
|
2022-02-28 17:22:28 -08:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let mut res = BytesMut::new();
|
|
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(row_description(&columns));
|
2022-02-28 17:22:28 -08:00
|
|
|
|
2022-07-27 21:47:55 -05:00
|
|
|
for (_, pool) in get_all_pools() {
|
|
|
|
|
let pool_config = pool.settings.clone();
|
|
|
|
|
for shard in 0..pool.shards() {
|
|
|
|
|
let database_name = &pool_config.shards[&shard.to_string()].database;
|
|
|
|
|
for server in 0..pool.servers(shard) {
|
|
|
|
|
let address = pool.address(shard, server);
|
|
|
|
|
let pool_state = pool.pool_state(shard, server);
|
2022-08-08 09:50:29 -05:00
|
|
|
let banned = pool.is_banned(address, shard, Some(address.role));
|
2022-07-27 21:47:55 -05:00
|
|
|
|
|
|
|
|
res.put(data_row(&vec![
|
|
|
|
|
address.name(), // name
|
|
|
|
|
address.host.to_string(), // host
|
|
|
|
|
address.port.to_string(), // port
|
|
|
|
|
database_name.to_string(), // database
|
|
|
|
|
pool_config.user.username.to_string(), // force_user
|
|
|
|
|
pool_config.user.pool_size.to_string(), // pool_size
|
|
|
|
|
"0".to_string(), // min_pool_size
|
|
|
|
|
"0".to_string(), // reserve_pool
|
|
|
|
|
pool_config.pool_mode.to_string(), // pool_mode
|
|
|
|
|
pool_config.user.pool_size.to_string(), // max_connections
|
|
|
|
|
pool_state.connections.to_string(), // current_connections
|
|
|
|
|
"0".to_string(), // paused
|
2022-08-08 09:50:29 -05:00
|
|
|
match banned {
|
|
|
|
|
// disabled
|
|
|
|
|
true => "1".to_string(),
|
|
|
|
|
false => "0".to_string(),
|
|
|
|
|
},
|
2022-07-27 21:47:55 -05:00
|
|
|
]));
|
|
|
|
|
}
|
2022-02-28 17:22:28 -08:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(command_complete("SHOW"));
|
2022-02-28 17:22:28 -08:00
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
// ReadyForQuery
|
2022-02-28 17:22:28 -08:00
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Ignore any SET commands the client sends.
|
|
|
|
|
/// This is common initialization done by ORMs.
|
2022-06-27 15:52:01 -07:00
|
|
|
async fn ignore_set<T>(stream: &mut T) -> Result<(), Error>
|
|
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-02-28 17:22:28 -08:00
|
|
|
custom_protocol_response_ok(stream, "SET").await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Reload the configuration file without restarting the process.
|
2022-06-27 15:52:01 -07:00
|
|
|
async fn reload<T>(stream: &mut T, client_server_map: ClientServerMap) -> Result<(), Error>
|
|
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-02-27 10:21:24 -08:00
|
|
|
info!("Reloading config");
|
|
|
|
|
|
2022-06-24 14:52:38 -07:00
|
|
|
reload_config(client_server_map).await?;
|
2022-02-27 10:21:24 -08:00
|
|
|
|
2022-06-24 14:52:38 -07:00
|
|
|
get_config().show();
|
2022-02-27 10:21:24 -08:00
|
|
|
|
|
|
|
|
let mut res = BytesMut::new();
|
|
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(command_complete("RELOAD"));
|
2022-02-27 10:21:24 -08:00
|
|
|
|
|
|
|
|
// ReadyForQuery
|
|
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Shows current configuration.
|
2022-06-27 15:52:01 -07:00
|
|
|
async fn show_config<T>(stream: &mut T) -> Result<(), Error>
|
|
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-06-24 14:52:38 -07:00
|
|
|
let config = &get_config();
|
2022-02-28 08:14:39 -08:00
|
|
|
let config: HashMap<String, String> = config.into();
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
// Configs that cannot be changed without restarting.
|
2022-02-28 08:14:39 -08:00
|
|
|
let immutables = ["host", "port", "connect_timeout"];
|
|
|
|
|
|
|
|
|
|
// Columns
|
2022-03-01 08:47:19 -08:00
|
|
|
let columns = vec![
|
|
|
|
|
("key", DataType::Text),
|
|
|
|
|
("value", DataType::Text),
|
|
|
|
|
("default", DataType::Text),
|
|
|
|
|
("changeable", DataType::Text),
|
|
|
|
|
];
|
2022-02-28 08:14:39 -08:00
|
|
|
|
|
|
|
|
// Response data
|
|
|
|
|
let mut res = BytesMut::new();
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(row_description(&columns));
|
2022-02-28 08:14:39 -08:00
|
|
|
|
|
|
|
|
// DataRow rows
|
|
|
|
|
for (key, value) in config {
|
|
|
|
|
let changeable = if immutables.iter().filter(|col| *col == &key).count() == 1 {
|
2022-03-01 08:47:19 -08:00
|
|
|
"no".to_string()
|
2022-02-28 08:14:39 -08:00
|
|
|
} else {
|
2022-03-01 08:47:19 -08:00
|
|
|
"yes".to_string()
|
2022-02-28 08:14:39 -08:00
|
|
|
};
|
|
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
let row = vec![key, value, "-".to_string(), changeable];
|
|
|
|
|
|
|
|
|
|
res.put(data_row(&row));
|
2022-02-28 08:14:39 -08:00
|
|
|
}
|
|
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(command_complete("SHOW"));
|
2022-02-28 08:14:39 -08:00
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
// ReadyForQuery
|
2022-02-28 08:14:39 -08:00
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
/// Show shard and replicas statistics.
|
2022-07-27 21:47:55 -05:00
|
|
|
async fn show_stats<T>(stream: &mut T) -> Result<(), Error>
|
2022-06-27 15:52:01 -07:00
|
|
|
where
|
|
|
|
|
T: tokio::io::AsyncWrite + std::marker::Unpin,
|
|
|
|
|
{
|
2022-03-01 08:47:19 -08:00
|
|
|
let columns = vec![
|
|
|
|
|
("database", DataType::Text),
|
2022-08-03 20:16:53 -05:00
|
|
|
("user", DataType::Text),
|
2022-03-01 08:47:19 -08:00
|
|
|
("total_xact_count", DataType::Numeric),
|
|
|
|
|
("total_query_count", DataType::Numeric),
|
|
|
|
|
("total_received", DataType::Numeric),
|
|
|
|
|
("total_sent", DataType::Numeric),
|
|
|
|
|
("total_xact_time", DataType::Numeric),
|
|
|
|
|
("total_query_time", DataType::Numeric),
|
|
|
|
|
("total_wait_time", DataType::Numeric),
|
|
|
|
|
("avg_xact_count", DataType::Numeric),
|
|
|
|
|
("avg_query_count", DataType::Numeric),
|
|
|
|
|
("avg_recv", DataType::Numeric),
|
|
|
|
|
("avg_sent", DataType::Numeric),
|
|
|
|
|
("avg_xact_time", DataType::Numeric),
|
|
|
|
|
("avg_query_time", DataType::Numeric),
|
|
|
|
|
("avg_wait_time", DataType::Numeric),
|
2022-02-25 18:20:15 -08:00
|
|
|
];
|
|
|
|
|
|
2022-02-26 11:01:52 -08:00
|
|
|
let stats = get_stats();
|
2022-02-25 18:20:15 -08:00
|
|
|
let mut res = BytesMut::new();
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(row_description(&columns));
|
2022-02-25 18:20:15 -08:00
|
|
|
|
2022-08-03 20:16:53 -05:00
|
|
|
for ((_db_name, username), pool) in get_all_pools() {
|
2022-07-27 21:47:55 -05:00
|
|
|
for shard in 0..pool.shards() {
|
|
|
|
|
for server in 0..pool.servers(shard) {
|
|
|
|
|
let address = pool.address(shard, server);
|
|
|
|
|
let stats = match stats.get(&address.id) {
|
|
|
|
|
Some(stats) => stats.clone(),
|
|
|
|
|
None => HashMap::new(),
|
|
|
|
|
};
|
2022-02-25 18:20:15 -08:00
|
|
|
|
2022-07-27 21:47:55 -05:00
|
|
|
let mut row = vec![address.name()];
|
2022-08-03 20:16:53 -05:00
|
|
|
row.push(username.clone());
|
2022-03-04 17:04:27 -08:00
|
|
|
|
2022-08-03 20:16:53 -05:00
|
|
|
for column in &columns[2..] {
|
2022-07-27 21:47:55 -05:00
|
|
|
row.push(stats.get(column.0).unwrap_or(&0).to_string());
|
|
|
|
|
}
|
2022-03-04 17:04:27 -08:00
|
|
|
|
2022-07-27 21:47:55 -05:00
|
|
|
res.put(data_row(&row));
|
|
|
|
|
}
|
2022-03-04 17:04:27 -08:00
|
|
|
}
|
2022-02-25 18:20:15 -08:00
|
|
|
}
|
|
|
|
|
|
2022-03-01 08:47:19 -08:00
|
|
|
res.put(command_complete("SHOW"));
|
2022-02-25 18:20:15 -08:00
|
|
|
|
2022-03-10 01:33:29 -08:00
|
|
|
// ReadyForQuery
|
2022-02-25 18:20:15 -08:00
|
|
|
res.put_u8(b'Z');
|
|
|
|
|
res.put_i32(5);
|
|
|
|
|
res.put_u8(b'I');
|
|
|
|
|
|
|
|
|
|
write_all_half(stream, res).await
|
|
|
|
|
}
|