mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-16 18:08:21 +00:00
14627a5c2b
Signed-off-by: mujunxiang <1948535941@qq.com>
260 lines
8.2 KiB
Rust
260 lines
8.2 KiB
Rust
use chrono::Utc;
|
|
use common::globals::GLOBAL_Local_Node_Name;
|
|
use common::last_minute::{AccElem, LastMinuteLatency};
|
|
use lazy_static::lazy_static;
|
|
use madmin::metrics::ScannerMetrics as M_ScannerMetrics;
|
|
use std::future::Future;
|
|
use std::pin::Pin;
|
|
use std::sync::atomic::AtomicU64;
|
|
use std::sync::Once;
|
|
use std::time::{Duration, UNIX_EPOCH};
|
|
use std::{
|
|
collections::HashMap,
|
|
sync::{atomic::Ordering, Arc},
|
|
time::SystemTime,
|
|
};
|
|
use tokio::sync::RwLock;
|
|
use tracing::info;
|
|
|
|
use super::data_scanner::{CurrentScannerCycle, UpdateCurrentPathFn};
|
|
|
|
lazy_static! {
|
|
pub static ref globalScannerMetrics: Arc<RwLock<ScannerMetrics>> = Arc::new(RwLock::new(ScannerMetrics::new()));
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, PartialOrd)]
|
|
pub enum ScannerMetric {
|
|
// START Realtime metrics, that only to records
|
|
// last minute latencies and total operation count.
|
|
ReadMetadata = 0,
|
|
CheckMissing,
|
|
SaveUsage,
|
|
ApplyAll,
|
|
ApplyVersion,
|
|
TierObjSweep,
|
|
HealCheck,
|
|
Ilm,
|
|
CheckReplication,
|
|
Yield,
|
|
CleanAbandoned,
|
|
ApplyNonCurrent,
|
|
HealAbandonedVersion,
|
|
|
|
// START Trace metrics:
|
|
StartTrace,
|
|
ScanObject, // Scan object. All operations included.
|
|
HealAbandonedObject,
|
|
|
|
// END realtime metrics:
|
|
LastRealtime,
|
|
|
|
// Trace only metrics:
|
|
ScanFolder, // Scan a folder on disk, recursively.
|
|
ScanCycle, // Full cycle, cluster global.
|
|
ScanBucketDrive, // Single bucket on one drive.
|
|
CompactFolder, // Folder compacted.
|
|
|
|
// Must be last:
|
|
Last,
|
|
}
|
|
|
|
static INIT: Once = Once::new();
|
|
|
|
#[derive(Default)]
|
|
pub struct LockedLastMinuteLatency {
|
|
cached_sec: AtomicU64,
|
|
cached: AccElem,
|
|
mu: RwLock<bool>,
|
|
latency: LastMinuteLatency,
|
|
}
|
|
|
|
impl Clone for LockedLastMinuteLatency {
|
|
fn clone(&self) -> Self {
|
|
Self {
|
|
cached_sec: AtomicU64::new(0),
|
|
cached: self.cached.clone(),
|
|
mu: RwLock::new(true),
|
|
latency: self.latency.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LockedLastMinuteLatency {
|
|
pub async fn add(&mut self, value: &Duration) {
|
|
self.add_size(value, 0).await;
|
|
}
|
|
|
|
pub async fn add_size(&mut self, value: &Duration, sz: u64) {
|
|
let t = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.expect("Time went backwards")
|
|
.as_secs();
|
|
INIT.call_once(|| {
|
|
self.cached = AccElem::default();
|
|
self.cached_sec.store(t, Ordering::SeqCst);
|
|
});
|
|
let last_t = self.cached_sec.load(Ordering::SeqCst);
|
|
if last_t != t
|
|
&& self
|
|
.cached_sec
|
|
.compare_exchange(last_t, t, Ordering::SeqCst, Ordering::SeqCst)
|
|
.is_ok()
|
|
{
|
|
let old = self.cached.clone();
|
|
self.cached = AccElem::default();
|
|
let a = AccElem {
|
|
size: old.size,
|
|
total: old.total,
|
|
n: old.n,
|
|
};
|
|
let _ = self.mu.write().await;
|
|
self.latency.add_all(t - 1, &a);
|
|
}
|
|
self.cached.n += 1;
|
|
self.cached.total += value.as_secs();
|
|
self.cached.size += sz;
|
|
}
|
|
|
|
pub async fn total(&mut self) -> AccElem {
|
|
let _ = self.mu.read().await;
|
|
self.latency.get_total()
|
|
}
|
|
}
|
|
|
|
pub type LogFn = Arc<dyn Fn(&HashMap<String, String>) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static>;
|
|
pub type TimeSizeFn = Arc<dyn Fn(u64) -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static>;
|
|
pub type TimeFn = Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static>;
|
|
|
|
pub struct ScannerMetrics {
|
|
operations: Vec<AtomicU64>,
|
|
latency: Vec<LockedLastMinuteLatency>,
|
|
cycle_info: RwLock<Option<CurrentScannerCycle>>,
|
|
current_paths: HashMap<String, String>,
|
|
}
|
|
|
|
impl Default for ScannerMetrics {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
impl ScannerMetrics {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
operations: (0..ScannerMetric::Last as usize).map(|_| AtomicU64::new(0)).collect(),
|
|
latency: vec![LockedLastMinuteLatency::default(); ScannerMetric::LastRealtime as usize],
|
|
cycle_info: RwLock::new(None),
|
|
current_paths: HashMap::new(),
|
|
}
|
|
}
|
|
|
|
pub async fn set_cycle(&mut self, c: Option<CurrentScannerCycle>) {
|
|
info!("ScannerMetrics set_cycle {c:?}");
|
|
*self.cycle_info.write().await = c;
|
|
}
|
|
|
|
pub fn log(s: ScannerMetric) -> LogFn {
|
|
let start = SystemTime::now();
|
|
let s_clone = s as usize;
|
|
Arc::new(move |_custom: &HashMap<String, String>| {
|
|
Box::pin(async move {
|
|
let duration = SystemTime::now().duration_since(start).unwrap_or(Duration::from_secs(0));
|
|
let mut sm_w = globalScannerMetrics.write().await;
|
|
sm_w.operations[s_clone].fetch_add(1, Ordering::SeqCst);
|
|
if s_clone < ScannerMetric::LastRealtime as usize {
|
|
sm_w.latency[s_clone].add(&duration).await;
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
pub async fn time_size(s: ScannerMetric) -> TimeSizeFn {
|
|
let start = SystemTime::now();
|
|
let s_clone = s as usize;
|
|
Arc::new(move |sz: u64| {
|
|
Box::pin(async move {
|
|
let duration = SystemTime::now().duration_since(start).unwrap_or(Duration::from_secs(0));
|
|
let mut sm_w = globalScannerMetrics.write().await;
|
|
sm_w.operations[s_clone].fetch_add(1, Ordering::SeqCst);
|
|
if s_clone < ScannerMetric::LastRealtime as usize {
|
|
sm_w.latency[s_clone].add_size(&duration, sz).await;
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
pub fn time(s: ScannerMetric) -> TimeFn {
|
|
let start = SystemTime::now();
|
|
let s_clone = s as usize;
|
|
Arc::new(move || {
|
|
Box::pin(async move {
|
|
let duration = SystemTime::now().duration_since(start).unwrap_or(Duration::from_secs(0));
|
|
let mut sm_w = globalScannerMetrics.write().await;
|
|
sm_w.operations[s_clone].fetch_add(1, Ordering::SeqCst);
|
|
if s_clone < ScannerMetric::LastRealtime as usize {
|
|
sm_w.latency[s_clone].add(&duration).await;
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
pub async fn get_cycle(&self) -> Option<CurrentScannerCycle> {
|
|
let r = self.cycle_info.read().await;
|
|
if let Some(c) = r.as_ref() {
|
|
return Some(c.clone());
|
|
}
|
|
None
|
|
}
|
|
|
|
pub async fn get_current_paths(&self) -> Vec<String> {
|
|
let mut res = Vec::new();
|
|
let prefix = format!("{}/", GLOBAL_Local_Node_Name.read().await);
|
|
self.current_paths.iter().for_each(|(k, v)| {
|
|
res.push(format!("{}/{}/{}", prefix, k, v));
|
|
});
|
|
res
|
|
}
|
|
|
|
pub async fn report(&self) -> M_ScannerMetrics {
|
|
let mut m = M_ScannerMetrics::default();
|
|
if let Some(cycle) = self.get_cycle().await {
|
|
info!("cycle: {cycle:?}");
|
|
m.current_cycle = cycle.current;
|
|
m.cycles_completed_at = cycle.cycle_completed;
|
|
m.current_started = cycle.started;
|
|
}
|
|
m.collected_at = Utc::now();
|
|
m.active_paths = self.get_current_paths().await;
|
|
for (i, v) in self.operations.iter().enumerate() {
|
|
m.life_time_ops.insert(i.to_string(), v.load(Ordering::SeqCst));
|
|
}
|
|
|
|
m
|
|
}
|
|
}
|
|
|
|
pub type CloseDiskFn = Arc<dyn Fn() -> Pin<Box<dyn Future<Output = ()> + Send>> + Send + Sync + 'static>;
|
|
pub fn current_path_updater(disk: &str, _initial: &str) -> (UpdateCurrentPathFn, CloseDiskFn) {
|
|
let disk_1 = disk.to_string();
|
|
let disk_2 = disk.to_string();
|
|
(
|
|
Arc::new(move |path: &str| {
|
|
let disk_inner = disk_1.clone();
|
|
let path = path.to_string();
|
|
Box::pin(async move {
|
|
globalScannerMetrics
|
|
.write()
|
|
.await
|
|
.current_paths
|
|
.insert(disk_inner, path.to_string());
|
|
})
|
|
}),
|
|
Arc::new(move || {
|
|
let disk_inner = disk_2.clone();
|
|
Box::pin(async move {
|
|
globalScannerMetrics.write().await.current_paths.remove(&disk_inner);
|
|
})
|
|
}),
|
|
)
|
|
}
|