feat: integrate global metrics system into AHM scanner

- Add global metrics system to common crate for cross-module usage
- Integrate global metrics collection into AHM scanner operations
- Update ECStore to use common metrics system instead of local implementation
- Add chrono dependency to AHM crate for timestamp handling
- Re-export IlmAction from common metrics in ECStore lifecycle module
- Update scanner methods to use global metrics for cycle, disk, and volume scans
- Maintain backward compatibility with local metrics collector
- Fix clippy warnings and ensure proper code formatting

This change enables unified metrics collection across the entire RustFS system,
allowing better monitoring and observability of scanner operations.

Signed-off-by: junxiang Mu <1948535941@qq.com>
This commit is contained in:
junxiang Mu
2025-07-21 11:32:43 +08:00
parent 7d3b2b774c
commit e7d0a8d4b9
14 changed files with 677 additions and 144 deletions
@@ -22,6 +22,7 @@ use async_channel::{Receiver as A_Receiver, Sender as A_Sender, bounded};
use futures::Future;
use http::HeaderMap;
use lazy_static::lazy_static;
use rustfs_common::metrics::{IlmAction, Metrics};
use s3s::Body;
use sha2::{Digest, Sha256};
use std::any::Any;
@@ -41,7 +42,7 @@ use xxhash_rust::xxh64;
//use rustfs_notify::{BucketNotificationConfig, Event, EventName, LogLevel, NotificationError, init_logger};
//use rustfs_notify::{initialize, notification_system};
use super::bucket_lifecycle_audit::{LcAuditEvent, LcEventSrc};
use super::lifecycle::{self, ExpirationOptions, IlmAction, Lifecycle, TransitionOptions};
use super::lifecycle::{self, ExpirationOptions, Lifecycle, TransitionOptions};
use super::tier_last_day_stats::{DailyAllTierStats, LastDayTierStats};
use super::tier_sweeper::{Jentry, delete_object_from_remote_tier};
use crate::bucket::{metadata_sys::get_lifecycle_config, versioning_sys::BucketVersioningSys};
@@ -54,7 +55,6 @@ use crate::global::GLOBAL_LocalNodeName;
use crate::global::{GLOBAL_LifecycleSys, GLOBAL_TierConfigMgr, get_global_deployment_id};
use crate::heal::{
data_scanner::{apply_expiry_on_non_transitioned_objects, apply_expiry_on_transitioned_object},
data_scanner_metric::ScannerMetrics,
data_usage_cache::TierStats,
};
use crate::store::ECStore;
@@ -631,7 +631,7 @@ pub async fn enqueue_transition_immediate(oi: &ObjectInfo, src: LcEventSrc) {
if !lc.is_none() {
let event = lc.expect("err").eval(&oi.to_lifecycle_opts()).await;
match event.action {
lifecycle::IlmAction::TransitionAction | lifecycle::IlmAction::TransitionVersionAction => {
IlmAction::TransitionAction | IlmAction::TransitionVersionAction => {
if oi.delete_marker || oi.is_dir {
return;
}
@@ -728,7 +728,7 @@ pub fn gen_transition_objname(bucket: &str) -> Result<String, Error> {
}
pub async fn transition_object(api: Arc<ECStore>, oi: &ObjectInfo, lae: LcAuditEvent) -> Result<(), Error> {
let time_ilm = ScannerMetrics::time_ilm(lae.event.action);
let time_ilm = Metrics::time_ilm(lae.event.action);
let opts = ObjectOptions {
transition: TransitionOptions {
@@ -43,49 +43,7 @@ const _ERR_XML_NOT_WELL_FORMED: &str =
const ERR_LIFECYCLE_BUCKET_LOCKED: &str =
"ExpiredObjectAllVersions element and DelMarkerExpiration action cannot be used on an retention bucket";
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IlmAction {
NoneAction = 0,
DeleteAction,
DeleteVersionAction,
TransitionAction,
TransitionVersionAction,
DeleteRestoredAction,
DeleteRestoredVersionAction,
DeleteAllVersionsAction,
DelMarkerDeleteAllVersionsAction,
ActionCount,
}
impl IlmAction {
pub fn delete_restored(&self) -> bool {
*self == Self::DeleteRestoredAction || *self == Self::DeleteRestoredVersionAction
}
pub fn delete_versioned(&self) -> bool {
*self == Self::DeleteVersionAction || *self == Self::DeleteRestoredVersionAction
}
pub fn delete_all(&self) -> bool {
*self == Self::DeleteAllVersionsAction || *self == Self::DelMarkerDeleteAllVersionsAction
}
pub fn delete(&self) -> bool {
if self.delete_restored() {
return true;
}
*self == Self::DeleteVersionAction
|| *self == Self::DeleteAction
|| *self == Self::DeleteAllVersionsAction
|| *self == Self::DelMarkerDeleteAllVersionsAction
}
}
impl Display for IlmAction {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
pub use rustfs_common::metrics::IlmAction;
#[async_trait::async_trait]
pub trait RuleValidate {
+6 -6
View File
@@ -39,13 +39,13 @@ use crate::global::{GLOBAL_IsErasureSD, GLOBAL_RootDiskThreshold};
use crate::heal::data_scanner::{
ScannerItem, ShouldSleepFn, SizeSummary, lc_has_active_rules, rep_has_active_rules, scan_data_folder,
};
use crate::heal::data_scanner_metric::{ScannerMetric, ScannerMetrics};
use crate::heal::data_usage_cache::{DataUsageCache, DataUsageEntry};
use crate::heal::error::{ERR_IGNORE_FILE_CONTRIB, ERR_SKIP_FILE};
use crate::heal::heal_commands::{HealScanMode, HealingTracker};
use crate::heal::heal_ops::HEALING_TRACKER_FILENAME;
use crate::new_object_layer_fn;
use crate::store_api::{ObjectInfo, StorageAPI};
use rustfs_common::metrics::{Metric, Metrics};
use rustfs_utils::path::{
GLOBAL_DIR_SUFFIX, GLOBAL_DIR_SUFFIX_WITH_SLASH, SLASH_SEPARATOR, clean, decode_dir_object, encode_dir_object, has_suffix,
path_join, path_join_buf,
@@ -2323,9 +2323,9 @@ impl DiskAPI for LocalDisk {
if !item.path.ends_with(&format!("{SLASH_SEPARATOR}{STORAGE_FORMAT_FILE}")) {
return Err(Error::other(ERR_SKIP_FILE).into());
}
let stop_fn = ScannerMetrics::log(ScannerMetric::ScanObject);
let stop_fn = Metrics::log(Metric::ScanObject);
let mut res = HashMap::new();
let done_sz = ScannerMetrics::time_size(ScannerMetric::ReadMetadata);
let done_sz = Metrics::time_size(Metric::ReadMetadata);
let buf = match disk.read_metadata(item.path.clone()).await {
Ok(buf) => buf,
Err(err) => {
@@ -2351,7 +2351,7 @@ impl DiskAPI for LocalDisk {
}
};
let mut size_s = SizeSummary::default();
let done = ScannerMetrics::time(ScannerMetric::ApplyAll);
let done = Metrics::time(Metric::ApplyAll);
let obj_infos = match item.apply_versions_actions(&fivs.versions).await {
Ok(obj_infos) => obj_infos,
Err(err) => {
@@ -2369,7 +2369,7 @@ impl DiskAPI for LocalDisk {
let mut obj_deleted = false;
for info in obj_infos.iter() {
let done = ScannerMetrics::time(ScannerMetric::ApplyVersion);
let done = Metrics::time(Metric::ApplyVersion);
let sz: i64;
(obj_deleted, sz) = item.apply_actions(info, &mut size_s).await;
done();
@@ -2405,7 +2405,7 @@ impl DiskAPI for LocalDisk {
&item.object_path().to_string_lossy(),
versioned,
);
let done = ScannerMetrics::time(ScannerMetric::TierObjSweep);
let done = Metrics::time(Metric::TierObjSweep);
done();
}
+75 -75
View File
@@ -29,7 +29,6 @@ use std::{
use time::{self, OffsetDateTime};
use super::{
data_scanner_metric::{ScannerMetric, ScannerMetrics, globalScannerMetrics},
data_usage::{DATA_USAGE_BLOOM_NAME_PATH, DataUsageInfo, store_data_usage_in_backend},
data_usage_cache::{DataUsageCache, DataUsageEntry, DataUsageHash},
heal_commands::{HEAL_DEEP_SCAN, HEAL_NORMAL_SCAN, HealScanMode},
@@ -39,6 +38,7 @@ use crate::bucket::{
utils::is_meta_bucketname,
};
use crate::cmd::bucket_replication::queue_replication_heal;
use crate::disk::local::LocalDisk;
use crate::event::name::EventName;
use crate::{
bucket::{
@@ -57,7 +57,7 @@ use crate::{
bucket::{versioning::VersioningApi, versioning_sys::BucketVersioningSys},
cmd::bucket_replication::ReplicationStatusType,
disk,
heal::data_usage::DATA_USAGE_ROOT,
// heal::data_usage::DATA_USAGE_ROOT,
};
use crate::{
cache_value::metacache_set::{ListPathRawOptions, list_path_raw},
@@ -66,7 +66,7 @@ use crate::{
heal::Config,
},
disk::{DiskInfoOptions, DiskStore},
global::{GLOBAL_BackgroundHealState, GLOBAL_IsErasure, GLOBAL_IsErasureSD},
global::{GLOBAL_BackgroundHealState, GLOBAL_IsErasureSD},
heal::{
data_usage::BACKGROUND_HEAL_INFO_PATH,
data_usage_cache::{DataUsageHashMap, hash_path},
@@ -83,7 +83,6 @@ use crate::{
disk::error::DiskError,
error::{Error, Result},
};
use crate::{disk::local::LocalDisk, heal::data_scanner_metric::current_path_updater};
use chrono::{DateTime, Utc};
use lazy_static::lazy_static;
use rand::Rng;
@@ -300,14 +299,14 @@ async fn run_data_scanner_cycle() {
};
// Start metrics collection for this cycle
let stop_fn = ScannerMetrics::log(ScannerMetric::ScanCycle);
// let stop_fn = ScannerMetrics::log(ScannerMetric::ScanCycle);
// Update cycle information
cycle_info.current = cycle_info.next;
cycle_info.started = Utc::now();
// Update global scanner metrics
globalScannerMetrics.set_cycle(Some(cycle_info.clone())).await;
// globalScannerMetrics.set_cycle(Some(cycle_info.clone())).await;
// Read background healing information and determine scan mode
let bg_heal_info = read_background_heal_info(store.clone()).await;
@@ -357,7 +356,7 @@ async fn run_data_scanner_cycle() {
}
// Update global metrics with completion info
globalScannerMetrics.set_cycle(Some(cycle_info.clone())).await;
// globalScannerMetrics.set_cycle(Some(cycle_info.clone())).await;
// Persist updated cycle information
// ignore error, continue.
@@ -379,7 +378,7 @@ async fn run_data_scanner_cycle() {
}
// Complete metrics collection for this cycle
stop_fn(&scan_result);
// stop_fn(&scan_result);
}
/// Execute namespace scan with cancellation support
@@ -781,7 +780,7 @@ impl ScannerItem {
}
pub async fn apply_actions(&mut self, oi: &ObjectInfo, _size_s: &mut SizeSummary) -> (bool, i64) {
let done = ScannerMetrics::time(ScannerMetric::Ilm);
// let done = ScannerMetrics::time(ScannerMetric::Ilm);
let (action, _size) = self.apply_lifecycle(oi).await;
@@ -807,7 +806,7 @@ impl ScannerItem {
self.heal_replication(&oi, _size_s).await;
}
done();
// done();
if action.delete_all() {
return (true, 0);
@@ -1572,68 +1571,69 @@ pub fn rep_has_active_rules(config: &ReplicationConfiguration, prefix: &str, rec
pub type LocalDrive = Arc<LocalDisk>;
pub async fn scan_data_folder(
disks: &[Option<DiskStore>],
drive: LocalDrive,
cache: &DataUsageCache,
get_size_fn: GetSizeFn,
heal_scan_mode: HealScanMode,
should_sleep: ShouldSleepFn,
_disks: &[Option<DiskStore>],
_drive: LocalDrive,
_cache: &DataUsageCache,
_get_size_fn: GetSizeFn,
_heal_scan_mode: HealScanMode,
_should_sleep: ShouldSleepFn,
) -> disk::error::Result<DataUsageCache> {
if cache.info.name.is_empty() || cache.info.name == DATA_USAGE_ROOT {
return Err(DiskError::other("internal error: root scan attempted"));
}
// if cache.info.name.is_empty() || cache.info.name == DATA_USAGE_ROOT {
// return Err(DiskError::other("internal error: root scan attempted"));
// }
let base_path = drive.to_string();
let (update_path, close_disk) = current_path_updater(&base_path, &cache.info.name);
let skip_heal = if *GLOBAL_IsErasure.read().await || cache.info.skip_healing {
AtomicBool::new(true)
} else {
AtomicBool::new(false)
};
let mut s = FolderScanner {
root: base_path,
get_size: get_size_fn,
old_cache: cache.clone(),
new_cache: DataUsageCache {
info: cache.info.clone(),
..Default::default()
},
update_cache: DataUsageCache {
info: cache.info.clone(),
..Default::default()
},
data_usage_scanner_debug: false,
heal_object_select: 0,
scan_mode: heal_scan_mode,
updates: cache.info.updates.clone().unwrap(),
last_update: SystemTime::now(),
update_current_path: update_path,
disks: disks.to_vec(),
disks_quorum: disks.len() / 2,
skip_heal,
drive: drive.clone(),
we_sleep: should_sleep,
};
// let base_path = drive.to_string();
// // let (update_path, close_disk) = current_path_updater(&base_path, &cache.info.name);
// let skip_heal = if *GLOBAL_IsErasure.read().await || cache.info.skip_healing {
// AtomicBool::new(true)
// } else {
// AtomicBool::new(false)
// };
// let mut s = FolderScanner {
// root: base_path,
// get_size: get_size_fn,
// old_cache: cache.clone(),
// new_cache: DataUsageCache {
// info: cache.info.clone(),
// ..Default::default()
// },
// update_cache: DataUsageCache {
// info: cache.info.clone(),
// ..Default::default()
// },
// data_usage_scanner_debug: false,
// heal_object_select: 0,
// scan_mode: heal_scan_mode,
// updates: cache.info.updates.clone().unwrap(),
// last_update: SystemTime::now(),
// update_current_path: update_path,
// disks: disks.to_vec(),
// disks_quorum: disks.len() / 2,
// skip_heal,
// drive: drive.clone(),
// we_sleep: should_sleep,
// };
if *GLOBAL_IsErasure.read().await || !cache.info.skip_healing {
s.heal_object_select = HEAL_OBJECT_SELECT_PROB as u32;
}
// if *GLOBAL_IsErasure.read().await || !cache.info.skip_healing {
// s.heal_object_select = HEAL_OBJECT_SELECT_PROB as u32;
// }
let mut root = DataUsageEntry::default();
let folder = CachedFolder {
name: cache.info.name.clone(),
object_heal_prob_div: 1,
parent: DataUsageHash("".to_string()),
};
// let mut root = DataUsageEntry::default();
// let folder = CachedFolder {
// name: cache.info.name.clone(),
// object_heal_prob_div: 1,
// parent: DataUsageHash("".to_string()),
// };
if s.scan_folder(&folder, &mut root).await.is_err() {
close_disk().await;
}
s.new_cache.force_compact(DATA_SCANNER_COMPACT_AT_CHILDREN as usize);
s.new_cache.info.last_update = Some(SystemTime::now());
s.new_cache.info.next_cycle = cache.info.next_cycle;
close_disk().await;
Ok(s.new_cache)
// if s.scan_folder(&folder, &mut root).await.is_err() {
// close_disk().await;
// }
// s.new_cache.force_compact(DATA_SCANNER_COMPACT_AT_CHILDREN as usize);
// s.new_cache.info.last_update = Some(SystemTime::now());
// s.new_cache.info.next_cycle = cache.info.next_cycle;
// close_disk().await;
// Ok(s.new_cache)
todo!()
}
pub async fn eval_action_from_lifecycle(
@@ -1695,11 +1695,11 @@ pub async fn apply_expiry_on_transitioned_object(
lc_event: &lifecycle::Event,
src: &LcEventSrc,
) -> bool {
let time_ilm = ScannerMetrics::time_ilm(lc_event.action.clone());
// let time_ilm = ScannerMetrics::time_ilm(lc_event.action.clone());
if let Err(_err) = expire_transitioned_object(api, oi, lc_event, src).await {
return false;
}
let _ = time_ilm(1);
// let _ = time_ilm(1);
true
}
@@ -1727,7 +1727,7 @@ pub async fn apply_expiry_on_non_transitioned_objects(
opts.delete_prefix_object = true;
}
let time_ilm = ScannerMetrics::time_ilm(lc_event.action.clone());
// let time_ilm = ScannerMetrics::time_ilm(lc_event.action.clone());
let mut dobj = api
.delete_object(&oi.bucket, &encode_dir_object(&oi.name), opts)
@@ -1759,11 +1759,11 @@ pub async fn apply_expiry_on_non_transitioned_objects(
});
if lc_event.action != lifecycle::IlmAction::NoneAction {
let mut num_versions = 1_u64;
if lc_event.action.delete_all() {
num_versions = oi.num_versions as u64;
}
let _ = time_ilm(num_versions);
// let mut num_versions = 1_u64;
// if lc_event.action.delete_all() {
// num_versions = oi.num_versions as u64;
// }
// let _ = time_ilm(num_versions);
}
true
+1 -1
View File
@@ -14,7 +14,7 @@
pub mod background_heal_ops;
pub mod data_scanner;
pub mod data_scanner_metric;
// pub mod data_scanner_metric;
pub mod data_usage;
pub mod data_usage_cache;
pub mod error;
+6 -6
View File
@@ -15,7 +15,10 @@
use std::collections::{HashMap, HashSet};
use chrono::Utc;
use rustfs_common::globals::{GLOBAL_Local_Node_Name, GLOBAL_Rustfs_Addr};
use rustfs_common::{
globals::{GLOBAL_Local_Node_Name, GLOBAL_Rustfs_Addr},
metrics::globalMetrics,
};
use rustfs_madmin::metrics::{DiskIOStats, DiskMetric, RealtimeMetrics};
use rustfs_utils::os::get_drive_stats;
use serde::{Deserialize, Serialize};
@@ -23,10 +26,7 @@ use tracing::info;
use crate::{
admin_server_info::get_local_server_property,
heal::{
data_scanner_metric::globalScannerMetrics,
heal_commands::{DRIVE_STATE_OK, DRIVE_STATE_UNFORMATTED},
},
heal::heal_commands::{DRIVE_STATE_OK, DRIVE_STATE_UNFORMATTED},
new_object_layer_fn,
store_api::StorageAPI,
// utils::os::get_drive_stats,
@@ -108,7 +108,7 @@ pub async fn collect_local_metrics(types: MetricType, opts: &CollectMetricsOpts)
if types.contains(&MetricType::SCANNER) {
info!("start get scanner metrics");
let metrics = globalScannerMetrics.report().await;
let metrics = globalMetrics.report().await;
real_time_metrics.aggregated.scanner = Some(metrics);
}