From 74c95920caa4706084609dff6a8d51233e649974 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Tue, 30 Jun 2026 02:08:58 +0800 Subject: [PATCH] fix: dual-write renamed IO metrics (#4077) --- .../grafana/dashboards/rustfs.json | 12 ++--- crates/io-metrics/src/lib.rs | 31 ++++++++++- crates/io-metrics/src/metric_names.rs | 51 +++++++++++++++++++ rustfs/src/app/object_usecase.rs | 6 +++ 4 files changed, 93 insertions(+), 7 deletions(-) diff --git a/.docker/observability/grafana/dashboards/rustfs.json b/.docker/observability/grafana/dashboards/rustfs.json index bdba8a335..fca4ef27e 100644 --- a/.docker/observability/grafana/dashboards/rustfs.json +++ b/.docker/observability/grafana/dashboards/rustfs.json @@ -5211,11 +5211,11 @@ "uid": "${datasource}" }, "expr": "rate(rustfs_s3_put_object_total{job=~\"$job\"}[5m])", - "legendFormat": "PutObject - legacy mmap flag {{zero_copy_enabled}}", + "legendFormat": "PutObject", "refId": "B" } ], - "description": "The zero_copy_enabled label is a legacy mmap-read compatibility flag retained until the metric rename transition.", + "description": "Aggregate S3 operation rate. Data-plane copy mode uses dedicated mmap-copy and buffered-write metrics.", "title": "S3 Operations Rate", "type": "timeseries" }, @@ -5480,13 +5480,13 @@ "type": "prometheus", "uid": "${datasource}" }, - "expr": "sum(increase(rustfs_zero_copy_memory_saved_bytes_total{job=~\"$job\"}[$__rate_interval])) or sum(increase(rustfs_zero_copy_bytes_saved_total{job=~\"$job\"}[$__rate_interval])) or vector(0)", - "legendFormat": "Legacy mmap metric", + "expr": "sum(increase(rustfs_mmap_copy_bytes_copied_total{job=~\"$job\"}[$__rate_interval])) or sum(increase(rustfs_zero_copy_memory_saved_bytes_total{job=~\"$job\"}[$__rate_interval])) or sum(increase(rustfs_zero_copy_bytes_saved_total{job=~\"$job\"}[$__rate_interval])) or vector(0)", + "legendFormat": "Mmap-copy bytes", "refId": "A" } ], - "description": "Legacy zero_copy metric names are retained for dashboard compatibility until the Phase 3 metric rename can dual-write replacement metrics.", - "title": "Mmap Read Memory Savings", + "description": "Prefers rustfs_mmap_copy_bytes_copied_total. Legacy zero_copy counters are retained as compatibility fallbacks during the metric rename transition.", + "title": "Mmap Copy Read Bytes", "type": "timeseries" }, { diff --git a/crates/io-metrics/src/lib.rs b/crates/io-metrics/src/lib.rs index 9da20255a..543048798 100644 --- a/crates/io-metrics/src/lib.rs +++ b/crates/io-metrics/src/lib.rs @@ -1053,6 +1053,11 @@ pub fn record_zero_copy_read(size_bytes: usize, duration_ms: f64) { counter!("rustfs_zero_copy_reads_total").increment(1); histogram!("rustfs_zero_copy_read_size_bytes").record(size_bytes as f64); histogram!("rustfs_zero_copy_read_duration_ms").record(duration_ms); + + counter!(mmap_copy::READS_TOTAL).increment(1); + histogram!(mmap_copy::READ_SIZE_BYTES).record(size_bytes as f64); + histogram!(mmap_copy::READ_DURATION_MS).record(duration_ms); + counter!(mmap_copy::BYTES_COPIED_TOTAL).increment(size_bytes as u64); } /// Record memory copies avoided by using zero-copy. @@ -1076,6 +1081,7 @@ pub fn record_memory_copy_saved(bytes_saved: usize) { #[inline(always)] pub fn record_zero_copy_fallback(reason: &str) { counter!("rustfs_zero_copy_fallback_total", "reason" => reason.to_string()).increment(1); + counter!(mmap_copy::FALLBACK_TOTAL, "reason" => reason.to_string()).increment(1); } // ============================================================================ @@ -1172,6 +1178,11 @@ pub fn record_zero_copy_write(size_bytes: usize, duration_ms: f64) { counter!("rustfs_zero_copy_write_total").increment(1); histogram!("rustfs_zero_copy_write_size_bytes").record(size_bytes as f64); histogram!("rustfs_zero_copy_write_duration_ms").record(duration_ms); + + counter!(buffered_write::WRITES_TOTAL).increment(1); + histogram!(buffered_write::WRITE_SIZE_BYTES).record(size_bytes as f64); + histogram!(buffered_write::WRITE_DURATION_MS).record(duration_ms); + counter!(buffered_write::BYTES_COPIED_TOTAL).increment(size_bytes as u64); } /// Record zero-copy write fallback. @@ -1184,6 +1195,7 @@ pub fn record_zero_copy_write(size_bytes: usize, duration_ms: f64) { #[inline(always)] pub fn record_zero_copy_write_fallback(reason: &str) { counter!("rustfs_zero_copy_write_fallback_total", "reason" => reason.to_string()).increment(1); + counter!(buffered_write::FALLBACK_TOTAL, "reason" => reason.to_string()).increment(1); } /// Record bytes saved from zero-copy. @@ -2016,7 +2028,7 @@ pub mod bandwidth; pub mod global_metrics; pub mod metric_names; -pub use metric_names::zero_copy; +pub use metric_names::{aligned_pread, buffered_write, mmap_copy, zero_copy}; /// Record a zero-copy buffer operation. /// @@ -2095,6 +2107,20 @@ pub fn record_direct_io_operation(operation: &str, size: usize, success: bool) { "status" => status.to_string() ) .increment(size as u64); + + counter!( + aligned_pread::OPERATIONS_TOTAL, + "operation" => operation.to_string(), + "status" => status.to_string() + ) + .increment(1); + + counter!( + aligned_pread::BYTES_TOTAL, + "operation" => operation.to_string(), + "status" => status.to_string() + ) + .increment(size as u64); } /// Update zero-copy performance metrics. @@ -2160,5 +2186,8 @@ mod zero_copy_tests { assert!(!zero_copy::BUFFER_OPERATIONS_TOTAL.is_empty()); assert!(!zero_copy::MEMORY_COPY_TOTAL.is_empty()); assert!(!zero_copy::THROUGHPUT_MBPS.is_empty()); + assert!(!mmap_copy::READS_TOTAL.is_empty()); + assert!(!buffered_write::WRITES_TOTAL.is_empty()); + assert!(!aligned_pread::OPERATIONS_TOTAL.is_empty()); } } diff --git a/crates/io-metrics/src/metric_names.rs b/crates/io-metrics/src/metric_names.rs index 230e634f1..979dac68f 100644 --- a/crates/io-metrics/src/metric_names.rs +++ b/crates/io-metrics/src/metric_names.rs @@ -52,3 +52,54 @@ pub mod zero_copy { /// Current memory saved estimate by zero-copy in bytes pub const MEMORY_SAVED_BYTES: &str = "rustfs_zero_copy_memory_saved_bytes_current"; } + +/// Mmap-then-copy read metric names. +pub mod mmap_copy { + /// Total number of mmap-copy reads. + pub const READS_TOTAL: &str = "rustfs_mmap_copy_reads_total"; + + /// Mmap-copy read size distribution. + pub const READ_SIZE_BYTES: &str = "rustfs_mmap_copy_read_size_bytes"; + + /// Mmap-copy read duration distribution. + pub const READ_DURATION_MS: &str = "rustfs_mmap_copy_read_duration_ms"; + + /// Total bytes copied from mmap regions into owned buffers. + pub const BYTES_COPIED_TOTAL: &str = "rustfs_mmap_copy_bytes_copied_total"; + + /// Total number of mmap-copy read fallbacks. + pub const FALLBACK_TOTAL: &str = "rustfs_mmap_copy_fallback_total"; +} + +/// Buffered eager PUT write metric names. +pub mod buffered_write { + /// Total number of buffered eager PUT attempts. + pub const ATTEMPTS_TOTAL: &str = "rustfs_buffered_write_attempts_total"; + + /// Buffered eager PUT attempt size distribution. + pub const ATTEMPT_SIZE_BYTES: &str = "rustfs_buffered_write_attempt_size_bytes"; + + /// Total number of buffered eager PUT writes. + pub const WRITES_TOTAL: &str = "rustfs_buffered_write_total"; + + /// Buffered eager PUT write size distribution. + pub const WRITE_SIZE_BYTES: &str = "rustfs_buffered_write_size_bytes"; + + /// Buffered eager PUT write duration distribution. + pub const WRITE_DURATION_MS: &str = "rustfs_buffered_write_duration_ms"; + + /// Total bytes copied into buffered eager PUT bodies. + pub const BYTES_COPIED_TOTAL: &str = "rustfs_buffered_write_bytes_copied_total"; + + /// Total number of buffered eager PUT fallbacks. + pub const FALLBACK_TOTAL: &str = "rustfs_buffered_write_fallback_total"; +} + +/// Aligned pread metric names. +pub mod aligned_pread { + /// Total number of aligned pread operations. + pub const OPERATIONS_TOTAL: &str = "rustfs_aligned_pread_operations_total"; + + /// Total bytes processed by aligned pread operations. + pub const BYTES_TOTAL: &str = "rustfs_aligned_pread_bytes_total"; +} diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index a9b1a791f..c44fbe7a0 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -15,6 +15,8 @@ //! Object application use-case contracts. // Performance metrics recording (with zero-copy-metrics integration) +use rustfs_io_metrics::buffered_write; + use super::storage_api::object_usecase::ECStore; use super::storage_api::object_usecase::access::{ PostObjectRequestMarker, authorize_request, has_bypass_governance_header, req_info_mut, @@ -2707,6 +2709,10 @@ impl DefaultObjectUsecase { should_use_small_eager_put_path(size, &req.headers, server_side_encryption_requested, should_compress, false); let use_zero_copy_eager_put_path = should_use_zero_copy_eager_put_path(size, &req.headers, server_side_encryption_requested, should_compress, false); + if use_zero_copy_eager_put_path { + counter!(buffered_write::ATTEMPTS_TOTAL).increment(1); + histogram!(buffered_write::ATTEMPT_SIZE_BYTES).record(size as f64); + } let put_path = if should_compress { "stream_compressed" } else if use_zero_copy_eager_put_path {