mirror of
https://github.com/rustfs/rustfs.git
synced 2026-07-26 08:18:18 +00:00
feat(compression): show cluster-level compression stat in grafana (#4112)
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::metrics::report::PrometheusMetric;
|
||||
use crate::metrics::schema::compression::*;
|
||||
|
||||
/// Cluster-level compression statistics.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CompressionClusterStats {
|
||||
pub original_bytes_total: u64,
|
||||
pub compressed_bytes_total: u64,
|
||||
pub bytes_saved_total: u64,
|
||||
pub compression_ratio: f64,
|
||||
pub compression_operations_total: u64,
|
||||
}
|
||||
|
||||
/// Collect cluster-level compression metrics from cluster stats.
|
||||
pub fn collect_compression_cluster_metrics(stats: &CompressionClusterStats) -> Vec<PrometheusMetric> {
|
||||
vec![
|
||||
PrometheusMetric::from_descriptor(&COMPRESSION_OPERATIONS_TOTAL, stats.compression_operations_total as f64),
|
||||
PrometheusMetric::from_descriptor(&COMPRESSION_BYTES_ORIGINAL_TOTAL, stats.original_bytes_total as f64),
|
||||
PrometheusMetric::from_descriptor(&COMPRESSION_BYTES_COMPRESSED_TOTAL, stats.compressed_bytes_total as f64),
|
||||
PrometheusMetric::from_descriptor(&COMPRESSION_BYTES_SAVED_TOTAL, stats.bytes_saved_total as f64),
|
||||
PrometheusMetric::from_descriptor(&COMPRESSION_RATIO, stats.compression_ratio),
|
||||
]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::metrics::report::report_metrics;
|
||||
|
||||
#[test]
|
||||
fn test_collect_compression_cluster_metrics() {
|
||||
let stats = CompressionClusterStats {
|
||||
original_bytes_total: 1000,
|
||||
compressed_bytes_total: 700,
|
||||
bytes_saved_total: 300,
|
||||
compression_ratio: 0.7,
|
||||
compression_operations_total: 10,
|
||||
};
|
||||
|
||||
let metrics = collect_compression_cluster_metrics(&stats);
|
||||
report_metrics(&metrics);
|
||||
|
||||
assert_eq!(metrics.len(), 5);
|
||||
|
||||
// Verify original bytes
|
||||
let name = COMPRESSION_BYTES_ORIGINAL_TOTAL.get_full_metric_name();
|
||||
let m = metrics.iter().find(|m| m.name == name && m.value == 1000.0);
|
||||
assert!(m.is_some(), "missing or wrong value for original_bytes_total");
|
||||
|
||||
// Verify compressed bytes
|
||||
let name = COMPRESSION_BYTES_COMPRESSED_TOTAL.get_full_metric_name();
|
||||
let m = metrics.iter().find(|m| m.name == name && m.value == 700.0);
|
||||
assert!(m.is_some(), "missing or wrong value for compressed_bytes_total");
|
||||
|
||||
// Verify saved bytes
|
||||
let name = COMPRESSION_BYTES_SAVED_TOTAL.get_full_metric_name();
|
||||
let m = metrics.iter().find(|m| m.name == name && m.value == 300.0);
|
||||
assert!(m.is_some(), "missing or wrong value for bytes_saved_total");
|
||||
|
||||
// Verify compression ratio
|
||||
let name = COMPRESSION_RATIO.get_full_metric_name();
|
||||
let m = metrics.iter().find(|m| m.name == name && m.value == 0.7);
|
||||
assert!(m.is_some(), "missing or wrong value for compression_ratio");
|
||||
|
||||
// Verify operations total
|
||||
let name = COMPRESSION_OPERATIONS_TOTAL.get_full_metric_name();
|
||||
let m = metrics.iter().find(|m| m.name == name && m.value == 10.0);
|
||||
assert!(m.is_some(), "missing or wrong value for compression_operations_total");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_collect_compression_cluster_metrics_empty() {
|
||||
let stats = CompressionClusterStats::default();
|
||||
|
||||
let metrics = collect_compression_cluster_metrics(&stats);
|
||||
report_metrics(&metrics);
|
||||
|
||||
assert_eq!(metrics.len(), 5);
|
||||
|
||||
// All values should be zero
|
||||
for metric in &metrics {
|
||||
assert_eq!(metric.value, 0.0, "expected zero value for {}", metric.name);
|
||||
assert!(metric.labels.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_compression_cluster_stats_default() {
|
||||
let stats = CompressionClusterStats::default();
|
||||
assert_eq!(stats.original_bytes_total, 0);
|
||||
assert_eq!(stats.compressed_bytes_total, 0);
|
||||
assert_eq!(stats.bytes_saved_total, 0);
|
||||
assert_eq!(stats.compression_ratio, 0.0);
|
||||
assert_eq!(stats.compression_operations_total, 0);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ pub mod cluster_erasure_set;
|
||||
pub mod cluster_health;
|
||||
pub mod cluster_iam;
|
||||
pub mod cluster_usage;
|
||||
pub mod compression;
|
||||
pub mod dial9;
|
||||
pub mod ilm;
|
||||
pub mod node;
|
||||
@@ -51,6 +52,7 @@ pub use cluster_erasure_set::{ErasureSetStats, collect_erasure_set_metrics};
|
||||
pub use cluster_health::{ClusterHealthStats, collect_cluster_health_metrics};
|
||||
pub use cluster_iam::{IamStats, collect_iam_metrics};
|
||||
pub use cluster_usage::{BucketUsageStats, ClusterUsageStats, collect_bucket_usage_metrics, collect_cluster_usage_metrics};
|
||||
pub use compression::{CompressionClusterStats, collect_compression_cluster_metrics};
|
||||
pub use dial9::{Dial9Stats, collect_dial9_metrics, is_dial9_enabled};
|
||||
pub use ilm::{IlmStats, collect_ilm_metrics};
|
||||
pub use node::{DiskStats, collect_node_metrics};
|
||||
|
||||
@@ -33,6 +33,7 @@ pub use scheduler::{
|
||||
pub(crate) use storage_api::metrics::{
|
||||
BucketOperations, BucketOptions, ObsBucketBandwidthMonitor, ObsEcstoreResult, ObsReplicationStats, ObsStore, StorageAdminApi,
|
||||
obs_expiry_state_handle, obs_get_global_bucket_monitor, obs_get_global_replication_stats, obs_get_quota_config,
|
||||
obs_get_total_usable_capacity, obs_get_total_usable_capacity_free, obs_load_data_usage_from_backend,
|
||||
obs_resolve_object_store_handle, obs_transition_state_handle,
|
||||
obs_get_total_usable_capacity, obs_get_total_usable_capacity_free, obs_is_disk_compression_enabled,
|
||||
obs_load_compression_total_from_memory, obs_load_data_usage_from_backend, obs_resolve_object_store_handle,
|
||||
obs_transition_state_handle,
|
||||
};
|
||||
|
||||
@@ -42,6 +42,7 @@ use crate::metrics::collectors::{
|
||||
collect_cluster_health_metrics,
|
||||
collect_cluster_metrics,
|
||||
collect_cluster_usage_metrics,
|
||||
collect_compression_cluster_metrics,
|
||||
collect_cpu_metrics,
|
||||
collect_drive_count_metrics,
|
||||
collect_drive_detailed_metrics,
|
||||
@@ -70,6 +71,7 @@ use crate::metrics::config::{
|
||||
ENV_BUCKET_REPLICATION_BANDWIDTH_METRICS_INTERVAL, ENV_CLUSTER_METRICS_INTERVAL, ENV_DEFAULT_METRICS_INTERVAL,
|
||||
ENV_NODE_METRICS_INTERVAL, ENV_NOTIFICATION_METRICS_INTERVAL, ENV_RESOURCE_METRICS_INTERVAL,
|
||||
};
|
||||
use crate::metrics::obs_is_disk_compression_enabled;
|
||||
use crate::metrics::report::{PrometheusMetric, report_metrics};
|
||||
use crate::metrics::runtime_sources::bucket_monitor_available;
|
||||
use crate::metrics::schema::bucket_replication::{
|
||||
@@ -78,9 +80,10 @@ use crate::metrics::schema::bucket_replication::{
|
||||
use crate::metrics::stats_collector::{
|
||||
ProcessMetricBundle, collect_bucket_replication_bandwidth_stats, collect_bucket_replication_detail_stats,
|
||||
collect_bucket_stats, collect_cluster_and_health_stats, collect_cluster_config_stats, collect_cluster_usage_metric_stats,
|
||||
collect_disk_and_system_drive_stats, collect_erasure_set_stats, collect_host_network_stats, collect_iam_stats,
|
||||
collect_ilm_metric_stats, collect_internode_network_stats, collect_process_metric_bundle, collect_replication_stats,
|
||||
collect_scanner_metric_stats, collect_system_cpu_and_memory_stats_with,
|
||||
collect_compression_cluster_stats, collect_disk_and_system_drive_stats, collect_erasure_set_stats,
|
||||
collect_host_network_stats, collect_iam_stats, collect_ilm_metric_stats, collect_internode_network_stats,
|
||||
collect_process_metric_bundle, collect_replication_stats, collect_scanner_metric_stats,
|
||||
collect_system_cpu_and_memory_stats_with,
|
||||
};
|
||||
use rustfs_audit::audit_target_metrics;
|
||||
use rustfs_notify::{notification_metrics_snapshot, notification_target_metrics};
|
||||
@@ -801,6 +804,30 @@ pub fn init_metrics_runtime(token: CancellationToken) {
|
||||
}
|
||||
});
|
||||
|
||||
// Spawn task for compression metrics.
|
||||
if obs_is_disk_compression_enabled() {
|
||||
let token_clone = token.clone();
|
||||
tokio::spawn(async move {
|
||||
let mut interval = tokio::time::interval(cluster_interval);
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = interval.tick() => {
|
||||
if let Some(stats) = collect_compression_cluster_stats().await {
|
||||
let metrics = collect_compression_cluster_metrics(&stats);
|
||||
if !metrics.is_empty() {
|
||||
report_metrics(&metrics);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = token_clone.cancelled() => {
|
||||
warn!(event = EVENT_METRICS_RUNTIME_STATE, component = LOG_COMPONENT_OBS, subsystem = LOG_SUBSYSTEM_METRICS_RUNTIME, collector = "compression_cluster_stats", state = "cancelled", "metrics runtime state changed");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Spawn task for internode/system network metrics.
|
||||
let token_clone = token;
|
||||
tokio::spawn(async move {
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2024 RustFS Team
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use crate::{MetricDescriptor, MetricName, new_counter_md, new_gauge_md, subsystems};
|
||||
use std::sync::LazyLock;
|
||||
|
||||
pub static COMPRESSION_BYTES_ORIGINAL_TOTAL: LazyLock<MetricDescriptor> = LazyLock::new(|| {
|
||||
new_counter_md(
|
||||
MetricName::OriginedBytesTotal,
|
||||
"Total bytes before compression (original)",
|
||||
&["compression"],
|
||||
subsystems::COMPRESSION,
|
||||
)
|
||||
});
|
||||
|
||||
pub static COMPRESSION_BYTES_COMPRESSED_TOTAL: LazyLock<MetricDescriptor> = LazyLock::new(|| {
|
||||
new_counter_md(
|
||||
MetricName::CompressedBytesTotal,
|
||||
"Total bytes after compression",
|
||||
&["compression"],
|
||||
subsystems::COMPRESSION,
|
||||
)
|
||||
});
|
||||
|
||||
pub static COMPRESSION_BYTES_SAVED_TOTAL: LazyLock<MetricDescriptor> = LazyLock::new(|| {
|
||||
new_counter_md(
|
||||
MetricName::SavedBytesTotal,
|
||||
"Total bytes saved by compression",
|
||||
&["compression"],
|
||||
subsystems::COMPRESSION,
|
||||
)
|
||||
});
|
||||
|
||||
pub static COMPRESSION_RATIO: LazyLock<MetricDescriptor> = LazyLock::new(|| {
|
||||
new_gauge_md(
|
||||
MetricName::CompressionRatio,
|
||||
"Compression ratio (0.0 - 1.0)",
|
||||
&["compression"],
|
||||
subsystems::COMPRESSION,
|
||||
)
|
||||
});
|
||||
|
||||
pub static COMPRESSION_OPERATIONS_TOTAL: LazyLock<MetricDescriptor> = LazyLock::new(|| {
|
||||
new_counter_md(
|
||||
MetricName::CompressionOperationsTotal,
|
||||
"Total number of compression operations performed",
|
||||
&["compression"],
|
||||
subsystems::COMPRESSION,
|
||||
)
|
||||
});
|
||||
@@ -338,6 +338,13 @@ pub enum MetricName {
|
||||
ScannerPartialCycles,
|
||||
ScannerPartialCyclesByReason,
|
||||
|
||||
// Compression-related metrics
|
||||
CompressedBytesTotal,
|
||||
OriginedBytesTotal,
|
||||
SavedBytesTotal,
|
||||
CompressionRatio,
|
||||
CompressionOperationsTotal,
|
||||
|
||||
// CPU system-related metrics
|
||||
SysCPUAvgIdle,
|
||||
SysCPUAvgIOWait,
|
||||
@@ -737,6 +744,13 @@ impl MetricName {
|
||||
Self::ScannerPartialCycles => "partial_cycles".to_string(),
|
||||
Self::ScannerPartialCyclesByReason => "partial_cycles_by_reason".to_string(),
|
||||
|
||||
// Compression-related metrics
|
||||
Self::CompressedBytesTotal => "compressed_bytes".to_string(),
|
||||
Self::OriginedBytesTotal => "original_bytes".to_string(),
|
||||
Self::SavedBytesTotal => "saved_bytes".to_string(),
|
||||
Self::CompressionRatio => "compression_ratio".to_string(),
|
||||
Self::CompressionOperationsTotal => "compression_operation_total".to_string(),
|
||||
|
||||
// CPU system-related metrics
|
||||
Self::SysCPUAvgIdle => "avg_idle".to_string(),
|
||||
Self::SysCPUAvgIOWait => "avg_iowait".to_string(),
|
||||
|
||||
@@ -51,6 +51,7 @@ pub enum MetricSubsystem {
|
||||
Replication,
|
||||
Notification,
|
||||
Scanner,
|
||||
Compression,
|
||||
|
||||
// Custom paths
|
||||
Custom(String),
|
||||
@@ -93,6 +94,7 @@ impl MetricSubsystem {
|
||||
Self::Replication => "/replication",
|
||||
Self::Notification => "/notification",
|
||||
Self::Scanner => "/scanner",
|
||||
Self::Compression => "/compression",
|
||||
|
||||
// Custom paths
|
||||
Self::Custom(path) => path,
|
||||
@@ -141,6 +143,7 @@ impl MetricSubsystem {
|
||||
"/replication" => Self::Replication,
|
||||
"/notification" => Self::Notification,
|
||||
"/scanner" => Self::Scanner,
|
||||
"/compression" => Self::Compression,
|
||||
|
||||
// Treat other paths as custom subsystems
|
||||
_ => Self::Custom(path.to_string()),
|
||||
@@ -203,6 +206,7 @@ pub mod subsystems {
|
||||
pub const REPLICATION: MetricSubsystem = MetricSubsystem::Replication;
|
||||
pub const NOTIFICATION: MetricSubsystem = MetricSubsystem::Notification;
|
||||
pub const SCANNER: MetricSubsystem = MetricSubsystem::Scanner;
|
||||
pub const COMPRESSION: MetricSubsystem = MetricSubsystem::Compression;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -22,6 +22,7 @@ pub mod cluster_health;
|
||||
pub mod cluster_iam;
|
||||
pub mod cluster_notification;
|
||||
pub mod cluster_usage;
|
||||
pub mod compression;
|
||||
pub mod entry;
|
||||
pub mod ilm;
|
||||
pub mod node_bucket;
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
|
||||
use crate::metrics::collectors::{
|
||||
BucketReplicationBandwidthStats, BucketReplicationStats, BucketReplicationTargetStats, BucketStats, BucketUsageStats,
|
||||
ClusterConfigStats, ClusterHealthStats, ClusterStats, ClusterUsageStats, CpuStats, DiskStats, DriveCountStats,
|
||||
DriveDetailedStats, ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats, ProcessStats,
|
||||
ProcessStatusType, ReplicationStats, ResourceStats, ScannerStats,
|
||||
ClusterConfigStats, ClusterHealthStats, ClusterStats, ClusterUsageStats, CompressionClusterStats, CpuStats, DiskStats,
|
||||
DriveCountStats, DriveDetailedStats, ErasureSetStats, HostNetworkStats, IamStats, IlmStats, MemoryStats, NetworkStats,
|
||||
ProcessStats, ProcessStatusType, ReplicationStats, ResourceStats, ScannerStats,
|
||||
};
|
||||
use crate::metrics::runtime_sources::{
|
||||
ObsIlmRuntimeSnapshot, bucket_monitor_handle, iam_metrics_snapshot, ilm_runtime_snapshot, replication_stats_handle,
|
||||
};
|
||||
use crate::metrics::{
|
||||
BucketOperations, BucketOptions, ObsEcstoreResult, ObsStore, StorageAdminApi, obs_get_quota_config,
|
||||
obs_get_total_usable_capacity, obs_get_total_usable_capacity_free, obs_load_data_usage_from_backend,
|
||||
obs_resolve_object_store_handle,
|
||||
obs_get_total_usable_capacity, obs_get_total_usable_capacity_free, obs_load_compression_total_from_memory,
|
||||
obs_load_data_usage_from_backend, obs_resolve_object_store_handle,
|
||||
};
|
||||
use chrono::Utc;
|
||||
use rustfs_common::heal_channel::HealScanMode;
|
||||
@@ -1100,6 +1100,28 @@ pub async fn collect_scanner_metric_stats() -> Option<ScannerStats> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Collect cluster-level compression statistics.
|
||||
pub async fn collect_compression_cluster_stats() -> Option<CompressionClusterStats> {
|
||||
let compression_data_usage = obs_load_compression_total_from_memory().await?;
|
||||
|
||||
let original_bytes_total = compression_data_usage.original_bytes_total;
|
||||
let compressed_bytes_total = compression_data_usage.compressed_bytes_total;
|
||||
let bytes_saved_total = original_bytes_total.saturating_sub(compressed_bytes_total);
|
||||
let compression_ratio = if original_bytes_total > 0 {
|
||||
compressed_bytes_total as f64 / original_bytes_total as f64
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let compression_operations_total = compression_data_usage.compression_operations_total;
|
||||
|
||||
Some(CompressionClusterStats {
|
||||
original_bytes_total,
|
||||
compressed_bytes_total,
|
||||
bytes_saved_total,
|
||||
compression_ratio,
|
||||
compression_operations_total,
|
||||
})
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -21,6 +21,8 @@ pub(crate) use rustfs_ecstore::api::capacity::{
|
||||
get_total_usable_capacity as obs_get_total_usable_capacity,
|
||||
get_total_usable_capacity_free as obs_get_total_usable_capacity_free,
|
||||
};
|
||||
pub(crate) use rustfs_ecstore::api::compression::is_disk_compression_enabled as obs_is_disk_compression_enabled;
|
||||
pub(crate) use rustfs_ecstore::api::data_usage::load_compression_total_from_memory as obs_load_compression_total_from_memory;
|
||||
pub(crate) use rustfs_ecstore::api::data_usage::load_data_usage_from_backend as obs_load_data_usage_from_backend;
|
||||
pub(crate) use rustfs_ecstore::api::error::Result as ObsEcstoreResult;
|
||||
pub(crate) use rustfs_ecstore::api::runtime::{
|
||||
@@ -36,7 +38,7 @@ pub(crate) mod metrics {
|
||||
pub(crate) use super::{
|
||||
ObsBucketBandwidthMonitor, ObsEcstoreResult, ObsReplicationStats, ObsStore, obs_expiry_state_handle,
|
||||
obs_get_global_bucket_monitor, obs_get_global_replication_stats, obs_get_quota_config, obs_get_total_usable_capacity,
|
||||
obs_get_total_usable_capacity_free, obs_load_data_usage_from_backend, obs_resolve_object_store_handle,
|
||||
obs_transition_state_handle,
|
||||
obs_get_total_usable_capacity_free, obs_is_disk_compression_enabled, obs_load_compression_total_from_memory,
|
||||
obs_load_data_usage_from_backend, obs_resolve_object_store_handle, obs_transition_state_handle,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user