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
+1
View File
@@ -34,6 +34,7 @@ url = { workspace = true }
rustfs-lock = { workspace = true }
lazy_static = { workspace = true }
chrono = { workspace = true }
[dev-dependencies]
rmp-serde = { workspace = true }
+33 -1
View File
@@ -37,6 +37,7 @@ use crate::{
error::{Error, Result},
get_ahm_services_cancel_token, HealRequest,
};
use rustfs_common::metrics::{globalMetrics, Metric, Metrics};
use rustfs_ecstore::disk::RUSTFS_META_BUCKET;
@@ -117,7 +118,7 @@ pub struct Scanner {
config: Arc<RwLock<ScannerConfig>>,
/// Scanner state
state: Arc<RwLock<ScannerState>>,
/// Metrics collector
/// Local metrics collector (for backward compatibility)
metrics: Arc<MetricsCollector>,
/// Bucket metrics cache
bucket_metrics: Arc<Mutex<HashMap<String, BucketMetrics>>>,
@@ -286,10 +287,18 @@ impl Scanner {
metrics
}
/// Get global metrics from common crate
pub async fn get_global_metrics(&self) -> rustfs_madmin::metrics::ScannerMetrics {
globalMetrics.report().await
}
/// Perform a single scan cycle
pub async fn scan_cycle(&self) -> Result<()> {
let start_time = SystemTime::now();
// Start global metrics collection for this cycle
let stop_fn = Metrics::time(Metric::ScanCycle);
info!("Starting scan cycle {} for all EC sets", self.metrics.get_metrics().current_cycle + 1);
// Update state
@@ -301,6 +310,14 @@ impl Scanner {
state.scanning_disks.clear();
}
// Update global metrics cycle information
let cycle_info = rustfs_common::metrics::CurrentCycle {
current: self.state.read().await.current_cycle,
cycle_completed: vec![chrono::Utc::now()],
started: chrono::Utc::now(),
};
globalMetrics.set_cycle(Some(cycle_info)).await;
self.metrics.set_current_cycle(self.state.read().await.current_cycle);
self.metrics.increment_total_cycles();
@@ -392,6 +409,9 @@ impl Scanner {
state.current_scan_duration = Some(scan_duration);
}
// Complete global metrics collection for this cycle
stop_fn();
info!(
"Completed scan cycle in {:?} ({} successful, {} failed)",
scan_duration, successful_scans, failed_scans
@@ -475,6 +495,9 @@ impl Scanner {
async fn scan_disk(&self, disk: &DiskStore) -> Result<HashMap<String, HashMap<String, rustfs_filemeta::FileMeta>>> {
let disk_path = disk.path().to_string_lossy().to_string();
// Start global metrics collection for disk scan
let stop_fn = Metrics::time(Metric::ScanBucketDrive);
info!("Scanning disk: {}", disk_path);
// Update disk metrics
@@ -638,6 +661,9 @@ impl Scanner {
state.scanning_disks.retain(|d| d != &disk_path);
}
// Complete global metrics collection for disk scan
stop_fn();
Ok(disk_objects)
}
@@ -646,6 +672,9 @@ impl Scanner {
/// This method collects all objects from a disk for a specific bucket.
/// It returns a map of object names to their metadata for later analysis.
async fn scan_volume(&self, disk: &DiskStore, bucket: &str) -> Result<HashMap<String, rustfs_filemeta::FileMeta>> {
// Start global metrics collection for volume scan
let stop_fn = Metrics::time(Metric::ScanObject);
info!("Scanning bucket: {} on disk: {}", bucket, disk.to_string());
// Initialize bucket metrics if not exists
@@ -785,6 +814,9 @@ impl Scanner {
state.scanning_buckets.retain(|b| b != bucket);
}
// Complete global metrics collection for volume scan
stop_fn();
debug!(
"Completed scanning bucket: {} on disk {} ({} objects, {} issues)",
bucket,
+7 -7
View File
@@ -318,13 +318,13 @@ async fn test_heal_format_with_data() {
let obj_dir = disk_paths[0].join(bucket_name).join(object_name);
let target_part = WalkDir::new(&obj_dir)
.min_depth(2)
.max_depth(2)
.into_iter()
.filter_map(Result::ok)
.find(|e| e.file_type().is_file() && e.file_name().to_str().map(|n| n.starts_with("part.")).unwrap_or(false))
.map(|e| e.into_path())
.expect("Failed to locate part file to delete");
.min_depth(2)
.max_depth(2)
.into_iter()
.filter_map(Result::ok)
.find(|e| e.file_type().is_file() && e.file_name().to_str().map(|n| n.starts_with("part.")).unwrap_or(false))
.map(|e| e.into_path())
.expect("Failed to locate part file to delete");
// ─── 1️⃣ delete format.json on one disk ──────────────
let format_path = disk_paths[0].join(".rustfs.sys").join("format.json");