use std::sync::Arc;
use parking_lot::Mutex;
use crate::metrics::{
garnet_server_monitor::GarnetServerMonitor,
info::garnet_info_metrics::{DEFAULT_INFO, GarnetInfoMetrics, InfoProvider},
info_metrics_type::InfoMetricsType,
latency::{
garnet_latency_metrics::GarnetLatencyMetrics, latency_metrics_type::LatencyMetricsType,
},
metrics_item::MetricsItem,
};
pub struct MetricsApi {
monitor: Option<Arc<Mutex<GarnetServerMonitor>>>,
}
impl MetricsApi {
pub fn new(monitor: Option<Arc<Mutex<GarnetServerMonitor>>>) -> Self {
Self { monitor }
}
pub fn get_info_metrics(
&self,
info_metrics_type: InfoMetricsType,
db_id: i32,
provider: &dyn InfoProvider,
) -> Option<Vec<MetricsItem>> {
GarnetInfoMetrics::new().get_metric(info_metrics_type, db_id, provider)
}
pub fn get_info_metrics_all(
&self,
info_metrics_types: Option<&[InfoMetricsType]>,
db_id: i32,
provider: &dyn InfoProvider,
) -> Vec<(InfoMetricsType, Vec<MetricsItem>)> {
let sections = info_metrics_types.unwrap_or(DEFAULT_INFO);
GarnetInfoMetrics::new().get_info_metrics(sections, db_id, provider)
}
pub fn get_header(info_metrics_type: InfoMetricsType, db_id: i32) -> String {
GarnetInfoMetrics::get_section_header(info_metrics_type, db_id)
}
pub fn reset_info_metrics(&self, info_metrics_type: InfoMetricsType) {
if let Some(monitor) = &self.monitor {
monitor.lock().reset_event_flags[info_metrics_type.idx()] = true;
}
}
pub fn reset_info_metrics_all(&self, info_metrics_types: Option<&[InfoMetricsType]>) {
let sections = info_metrics_types.unwrap_or(DEFAULT_INFO);
for §ion in sections {
self.reset_info_metrics(section);
}
}
pub fn get_latency_metrics(&self, latency_metrics_type: LatencyMetricsType) -> Vec<MetricsItem> {
let Some(monitor) = &self.monitor else {
return Vec::new();
};
let Some(global_latency_metrics) = monitor.lock().global_latency_metrics() else {
return Vec::new();
};
global_latency_metrics
.lock()
.get_latency_metrics(latency_metrics_type)
}
pub fn get_latency_metrics_all(
&self,
latency_metrics_types: Option<&[LatencyMetricsType]>,
) -> Vec<(LatencyMetricsType, Vec<MetricsItem>)> {
let Some(monitor) = &self.monitor else {
return Vec::new();
};
if monitor.lock().global_latency_metrics().is_none() {
return Vec::new();
}
let types = latency_metrics_types.unwrap_or(GarnetLatencyMetrics::DEFAULT_LATENCY_TYPES);
types
.iter()
.map(|&latency_metrics_type| {
(
latency_metrics_type,
self.get_latency_metrics(latency_metrics_type),
)
})
.collect()
}
pub fn reset_latency_metrics(&self, latency_metrics_type: LatencyMetricsType) {
if let Some(monitor) = &self.monitor {
monitor.lock().reset_latency_metrics[latency_metrics_type.idx()] = true;
}
}
pub fn reset_latency_metrics_all(&self, latency_metrics_types: Option<&[LatencyMetricsType]>) {
let types = latency_metrics_types.unwrap_or(GarnetLatencyMetrics::DEFAULT_LATENCY_TYPES);
for &latency_metrics_type in types {
self.reset_latency_metrics(latency_metrics_type);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metrics::info::garnet_info_metrics::{DbSnapshot, GlobalMetricsSnapshot, ServerFacts};
struct MockProvider;
impl InfoProvider for MockProvider {
fn server_facts(&self) -> ServerFacts {
ServerFacts {
version: "1.0.0".into(),
run_id: "run123".into(),
redis_protocol_version: "7.0".into(),
enable_cluster: false,
enable_aof: false,
metrics_sampling_frequency: 10,
latency_monitor: false,
command_stats_monitor: false,
startup_timestamp_unix_secs: 0,
log_dir: "/tmp/log".into(),
}
}
fn databases(&self) -> Vec<DbSnapshot> {
vec![DbSnapshot {
id: 0,
current_version: 7,
..DbSnapshot::default()
}]
}
fn max_database_id(&self) -> i32 {
0
}
fn global_metrics(&self) -> Option<GlobalMetricsSnapshot> {
None
}
fn command_stats(&self) -> Vec<(String, u64, u64)> {
Vec::new()
}
fn keyspace_stats(&self, _db_id: i32) -> (u64, u64) {
(0, 0)
}
fn replication_info(&self) -> Option<Vec<MetricsItem>> {
None
}
fn gossip_stats(&self, _metrics_disabled: bool) -> Vec<MetricsItem> {
Vec::new()
}
fn buffer_pool_stats(&self) -> Vec<(String, String)> {
Vec::new()
}
fn checkpoint_info(&self) -> Option<Vec<MetricsItem>> {
None
}
fn hlog_scan_dump(&self) -> Vec<(String, String)> {
Vec::new()
}
fn safe_aof_address(&self) -> i64 {
0
}
}
#[test]
fn header_and_info_metrics_flow_through_provider() {
let api = MetricsApi::new(None);
let header = MetricsApi::get_header(InfoMetricsType::Server, 0);
assert!(!header.is_empty());
let provider = MockProvider;
let items = api.get_info_metrics(InfoMetricsType::Server, 0, &provider);
assert!(items.is_some());
let all = api.get_info_metrics_all(None, 0, &provider);
assert!(!all.is_empty());
}
#[test]
fn reset_flags_without_monitor_are_noop() {
let api = MetricsApi::new(None);
api.reset_info_metrics(InfoMetricsType::Stats);
api.reset_info_metrics_all(None);
api.reset_latency_metrics(LatencyMetricsType::NetRsLat);
api.reset_latency_metrics_all(None);
assert!(
api
.get_latency_metrics(LatencyMetricsType::NetRsLat)
.is_empty()
);
assert!(api.get_latency_metrics_all(None).is_empty());
}
#[test]
fn reset_flags_set_on_monitor() {
let monitor = Arc::new(Mutex::new(GarnetServerMonitor::new(1, true, true, false)));
let api = MetricsApi::new(Some(Arc::clone(&monitor)));
api.reset_info_metrics(InfoMetricsType::Stats);
api.reset_latency_metrics(LatencyMetricsType::NetRsLat);
let monitor = monitor.lock();
assert!(monitor.reset_event_flags[InfoMetricsType::Stats.idx()]);
assert!(monitor.reset_latency_metrics[LatencyMetricsType::NetRsLat.idx()]);
}
}