From 80e79ea0cdb3b8222a6e8689905a4c6aa99f1f40 Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Sun, 9 Nov 2025 16:10:58 -0800 Subject: [PATCH 1/7] Implement 'scan_efficiency_ratio' for parquet reading --- datafusion/core/tests/sql/explain_analyze.rs | 1 + datafusion/datasource-parquet/src/metrics.rs | 11 ++++++++- datafusion/datasource-parquet/src/reader.rs | 24 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/datafusion/core/tests/sql/explain_analyze.rs b/datafusion/core/tests/sql/explain_analyze.rs index 26b71b5496f2..2a83b17e275d 100644 --- a/datafusion/core/tests/sql/explain_analyze.rs +++ b/datafusion/core/tests/sql/explain_analyze.rs @@ -872,6 +872,7 @@ async fn parquet_explain_analyze() { &formatted, "row_groups_pruned_statistics=1 total \u{2192} 1 matched" ); + assert_contains!(&formatted, "scan_efficiency_ratio=14% (259/1851)"); // The order of metrics is expected to be the same as the actual pruning order // (file-> row-group -> page) diff --git a/datafusion/datasource-parquet/src/metrics.rs b/datafusion/datasource-parquet/src/metrics.rs index 306bc9e6b013..18fda4bc4af3 100644 --- a/datafusion/datasource-parquet/src/metrics.rs +++ b/datafusion/datasource-parquet/src/metrics.rs @@ -16,7 +16,8 @@ // under the License. use datafusion_physical_plan::metrics::{ - Count, ExecutionPlanMetricsSet, MetricBuilder, MetricType, PruningMetrics, Time, + Count, ExecutionPlanMetricsSet, MetricBuilder, MetricType, PruningMetrics, + RatioMetrics, Time, }; /// Stores metrics about the parquet execution for a particular parquet file. @@ -66,6 +67,8 @@ pub struct ParquetFileMetrics { pub page_index_eval_time: Time, /// Total time spent reading and parsing metadata from the footer pub metadata_load_time: Time, + /// Scan Efficiency Ratio, calculated as bytes_scanned / total_file_size + pub scan_efficiency_ratio: RatioMetrics, /// Predicate Cache: number of records read directly from the inner reader. /// This is the number of rows decoded while evaluating predicates pub predicate_cache_inner_records: Count, @@ -114,6 +117,11 @@ impl ParquetFileMetrics { .with_type(MetricType::SUMMARY) .pruning_metrics("files_ranges_pruned_statistics", partition); + let scan_efficiency_ratio = MetricBuilder::new(metrics) + .with_new_label("filename", filename.to_string()) + .with_type(MetricType::SUMMARY) + .ratio_metrics("scan_efficiency_ratio", partition); + // ----------------------- // 'dev' level metrics // ----------------------- @@ -164,6 +172,7 @@ impl ParquetFileMetrics { bloom_filter_eval_time, page_index_eval_time, metadata_load_time, + scan_efficiency_ratio, predicate_cache_inner_records, predicate_cache_records, } diff --git a/datafusion/datasource-parquet/src/reader.rs b/datafusion/datasource-parquet/src/reader.rs index 88a3cea5623b..bbb2bef86ade 100644 --- a/datafusion/datasource-parquet/src/reader.rs +++ b/datafusion/datasource-parquet/src/reader.rs @@ -97,6 +97,7 @@ impl DefaultParquetFileReaderFactory { pub struct ParquetFileReader { pub file_metrics: ParquetFileMetrics, pub inner: ParquetObjectReader, + partitioned_file: PartitionedFile, } impl AsyncFileReader for ParquetFileReader { @@ -129,6 +130,17 @@ impl AsyncFileReader for ParquetFileReader { } } +impl Drop for ParquetFileReader { + fn drop(&mut self) { + self.file_metrics + .scan_efficiency_ratio + .add_part(self.file_metrics.bytes_scanned.value() as usize); + self.file_metrics + .scan_efficiency_ratio + .add_total(self.partitioned_file.object_meta.size as usize); + } +} + impl ParquetFileReaderFactory for DefaultParquetFileReaderFactory { fn create_reader( &self, @@ -156,6 +168,7 @@ impl ParquetFileReaderFactory for DefaultParquetFileReaderFactory { Ok(Box::new(ParquetFileReader { inner, file_metrics, + partitioned_file, })) } } @@ -286,6 +299,17 @@ impl AsyncFileReader for CachedParquetFileReader { } } +impl Drop for CachedParquetFileReader { + fn drop(&mut self) { + self.file_metrics + .scan_efficiency_ratio + .add_part(self.file_metrics.bytes_scanned.value() as usize); + self.file_metrics + .scan_efficiency_ratio + .add_total(self.partitioned_file.object_meta.size as usize); + } +} + /// Wrapper to implement [`FileMetadata`] for [`ParquetMetaData`]. pub struct CachedParquetMetaData(Arc); From 212b1240ecd01388157a748b7a409d20d0aaaf39 Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Sun, 9 Nov 2025 16:18:01 -0800 Subject: [PATCH 2/7] Fix clippy --- datafusion/datasource-parquet/src/reader.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/datafusion/datasource-parquet/src/reader.rs b/datafusion/datasource-parquet/src/reader.rs index bbb2bef86ade..3af492915af6 100644 --- a/datafusion/datasource-parquet/src/reader.rs +++ b/datafusion/datasource-parquet/src/reader.rs @@ -134,7 +134,7 @@ impl Drop for ParquetFileReader { fn drop(&mut self) { self.file_metrics .scan_efficiency_ratio - .add_part(self.file_metrics.bytes_scanned.value() as usize); + .add_part(self.file_metrics.bytes_scanned.value()); self.file_metrics .scan_efficiency_ratio .add_total(self.partitioned_file.object_meta.size as usize); @@ -303,7 +303,7 @@ impl Drop for CachedParquetFileReader { fn drop(&mut self) { self.file_metrics .scan_efficiency_ratio - .add_part(self.file_metrics.bytes_scanned.value() as usize); + .add_part(self.file_metrics.bytes_scanned.value()); self.file_metrics .scan_efficiency_ratio .add_total(self.partitioned_file.object_meta.size as usize); From 981940183cdc2f5ae9a165f75d646a9a70ef85ce Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Sun, 9 Nov 2025 16:18:46 -0800 Subject: [PATCH 3/7] Fix dependent test that creates a 'ParquetFileReader' object --- datafusion/datasource-parquet/src/reader.rs | 2 +- .../datasource-parquet/src/row_group_filter.rs | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/datafusion/datasource-parquet/src/reader.rs b/datafusion/datasource-parquet/src/reader.rs index 3af492915af6..1a202830dad9 100644 --- a/datafusion/datasource-parquet/src/reader.rs +++ b/datafusion/datasource-parquet/src/reader.rs @@ -97,7 +97,7 @@ impl DefaultParquetFileReaderFactory { pub struct ParquetFileReader { pub file_metrics: ParquetFileMetrics, pub inner: ParquetObjectReader, - partitioned_file: PartitionedFile, + pub partitioned_file: PartitionedFile, } impl AsyncFileReader for ParquetFileReader { diff --git a/datafusion/datasource-parquet/src/row_group_filter.rs b/datafusion/datasource-parquet/src/row_group_filter.rs index 2043f75070b5..873cd81fc5a2 100644 --- a/datafusion/datasource-parquet/src/row_group_filter.rs +++ b/datafusion/datasource-parquet/src/row_group_filter.rs @@ -1533,6 +1533,7 @@ mod tests { data: bytes::Bytes, pruning_predicate: &PruningPredicate, ) -> Result { + use datafusion_datasource::PartitionedFile; use object_store::{ObjectMeta, ObjectStore}; let object_meta = ObjectMeta { @@ -1551,12 +1552,22 @@ mod tests { let metrics = ExecutionPlanMetricsSet::new(); let file_metrics = ParquetFileMetrics::new(0, object_meta.location.as_ref(), &metrics); - let inner = ParquetObjectReader::new(Arc::new(in_memory), object_meta.location) + let inner = ParquetObjectReader::new(Arc::new(in_memory), object_meta.location.clone()) .with_file_size(object_meta.size); + let partitioned_file = PartitionedFile { + object_meta, + partition_values: vec![], + range: None, + statistics: None, + extensions: None, + metadata_size_hint: None, + }; + let reader = ParquetFileReader { inner, file_metrics: file_metrics.clone(), + partitioned_file, }; let mut builder = ParquetRecordBatchStreamBuilder::new(reader).await.unwrap(); From 9375ad8a5f3f36c8c522ec9ab3570427cc2b847e Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Sun, 9 Nov 2025 16:28:36 -0800 Subject: [PATCH 4/7] cargo fmt --- datafusion/datasource-parquet/src/row_group_filter.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/datafusion/datasource-parquet/src/row_group_filter.rs b/datafusion/datasource-parquet/src/row_group_filter.rs index 873cd81fc5a2..90e4e10d5ae8 100644 --- a/datafusion/datasource-parquet/src/row_group_filter.rs +++ b/datafusion/datasource-parquet/src/row_group_filter.rs @@ -1552,8 +1552,9 @@ mod tests { let metrics = ExecutionPlanMetricsSet::new(); let file_metrics = ParquetFileMetrics::new(0, object_meta.location.as_ref(), &metrics); - let inner = ParquetObjectReader::new(Arc::new(in_memory), object_meta.location.clone()) - .with_file_size(object_meta.size); + let inner = + ParquetObjectReader::new(Arc::new(in_memory), object_meta.location.clone()) + .with_file_size(object_meta.size); let partitioned_file = PartitionedFile { object_meta, From d4231f87e49cd6cb86b68a7216dfca2703ed99d1 Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Tue, 11 Nov 2025 11:14:09 -0800 Subject: [PATCH 5/7] Support different merge strategies for RatioMetrics and use AddPartSetTotal for 'scan_efficiency_ratio' --- datafusion/datasource-parquet/src/metrics.rs | 4 +- datafusion/datasource-parquet/src/reader.rs | 6 ++- .../physical-plan/src/metrics/builder.rs | 14 +++++- datafusion/physical-plan/src/metrics/mod.rs | 2 +- datafusion/physical-plan/src/metrics/value.rs | 50 +++++++++++++++++-- 5 files changed, 64 insertions(+), 12 deletions(-) diff --git a/datafusion/datasource-parquet/src/metrics.rs b/datafusion/datasource-parquet/src/metrics.rs index 18fda4bc4af3..531907a59ce1 100644 --- a/datafusion/datasource-parquet/src/metrics.rs +++ b/datafusion/datasource-parquet/src/metrics.rs @@ -17,7 +17,7 @@ use datafusion_physical_plan::metrics::{ Count, ExecutionPlanMetricsSet, MetricBuilder, MetricType, PruningMetrics, - RatioMetrics, Time, + RatioMergeStrategy, RatioMetrics, Time, }; /// Stores metrics about the parquet execution for a particular parquet file. @@ -120,7 +120,7 @@ impl ParquetFileMetrics { let scan_efficiency_ratio = MetricBuilder::new(metrics) .with_new_label("filename", filename.to_string()) .with_type(MetricType::SUMMARY) - .ratio_metrics("scan_efficiency_ratio", partition); + .ratio_metrics_with_strategy("scan_efficiency_ratio", partition, RatioMergeStrategy::AddPartSetTotal); // ----------------------- // 'dev' level metrics diff --git a/datafusion/datasource-parquet/src/reader.rs b/datafusion/datasource-parquet/src/reader.rs index 1a202830dad9..1d1024a5f321 100644 --- a/datafusion/datasource-parquet/src/reader.rs +++ b/datafusion/datasource-parquet/src/reader.rs @@ -135,9 +135,10 @@ impl Drop for ParquetFileReader { self.file_metrics .scan_efficiency_ratio .add_part(self.file_metrics.bytes_scanned.value()); + // Multiple ParquetFileReaders may run, so we set_total to avoid adding the total multiple times self.file_metrics .scan_efficiency_ratio - .add_total(self.partitioned_file.object_meta.size as usize); + .set_total(self.partitioned_file.object_meta.size as usize); } } @@ -304,9 +305,10 @@ impl Drop for CachedParquetFileReader { self.file_metrics .scan_efficiency_ratio .add_part(self.file_metrics.bytes_scanned.value()); + // Multiple ParquetFileReaders may run, so we set_total to avoid adding the total multiple times self.file_metrics .scan_efficiency_ratio - .add_total(self.partitioned_file.object_meta.size as usize); + .set_total(self.partitioned_file.object_meta.size as usize); } } diff --git a/datafusion/physical-plan/src/metrics/builder.rs b/datafusion/physical-plan/src/metrics/builder.rs index 6ea947b6d21b..4de34b5c7aa5 100644 --- a/datafusion/physical-plan/src/metrics/builder.rs +++ b/datafusion/physical-plan/src/metrics/builder.rs @@ -20,7 +20,7 @@ use std::{borrow::Cow, sync::Arc}; use crate::metrics::{ - value::{PruningMetrics, RatioMetrics}, + value::{PruningMetrics, RatioMergeStrategy, RatioMetrics}, MetricType, }; @@ -275,7 +275,17 @@ impl<'a> MetricBuilder<'a> { name: impl Into>, partition: usize, ) -> RatioMetrics { - let ratio_metrics = RatioMetrics::new(); + self.ratio_metrics_with_strategy(name, partition, RatioMergeStrategy::default()) + } + + /// Consumes self and creates a new [`RatioMetrics`] with a specific merge strategy + pub fn ratio_metrics_with_strategy( + self, + name: impl Into>, + partition: usize, + merge_strategy: RatioMergeStrategy, + ) -> RatioMetrics { + let ratio_metrics = RatioMetrics::new().with_merge_strategy(merge_strategy); self.with_partition(partition).build(MetricValue::Ratio { name: name.into(), ratio_metrics: ratio_metrics.clone(), diff --git a/datafusion/physical-plan/src/metrics/mod.rs b/datafusion/physical-plan/src/metrics/mod.rs index 4e98af722d4e..8b3feb4bf4db 100644 --- a/datafusion/physical-plan/src/metrics/mod.rs +++ b/datafusion/physical-plan/src/metrics/mod.rs @@ -36,7 +36,7 @@ pub use baseline::{BaselineMetrics, RecordOutput, SpillMetrics, SplitMetrics}; pub use builder::MetricBuilder; pub use custom::CustomMetricValue; pub use value::{ - Count, Gauge, MetricValue, PruningMetrics, RatioMetrics, ScopedTimerGuard, Time, + Count, Gauge, MetricValue, PruningMetrics, RatioMergeStrategy, RatioMetrics, ScopedTimerGuard, Time, Timestamp, }; diff --git a/datafusion/physical-plan/src/metrics/value.rs b/datafusion/physical-plan/src/metrics/value.rs index 298d63e5e216..f826178f1c0f 100644 --- a/datafusion/physical-plan/src/metrics/value.rs +++ b/datafusion/physical-plan/src/metrics/value.rs @@ -437,6 +437,15 @@ impl PruningMetrics { pub struct RatioMetrics { part: Arc, total: Arc, + merge_strategy: RatioMergeStrategy, +} + +#[derive(Debug, Clone, Default)] +pub enum RatioMergeStrategy { + #[default] + AddPartAddTotal, + AddPartSetTotal, + SetPartAddTotal, } impl RatioMetrics { @@ -445,9 +454,15 @@ impl RatioMetrics { Self { part: Arc::new(AtomicUsize::new(0)), total: Arc::new(AtomicUsize::new(0)), + merge_strategy: RatioMergeStrategy::AddPartAddTotal, } } + pub fn with_merge_strategy(mut self, merge_strategy: RatioMergeStrategy) -> Self { + self.merge_strategy = merge_strategy; + self + } + /// Add `n` to the numerator (`part`) value pub fn add_part(&self, n: usize) { self.part.fetch_add(n, Ordering::Relaxed); @@ -458,10 +473,32 @@ impl RatioMetrics { self.total.fetch_add(n, Ordering::Relaxed); } + /// Set the numerator (`part`) value to `n`, overwriting any existing value + pub fn set_part(&self, n: usize) { + self.part.store(n, Ordering::Relaxed); + } + + /// Set the denominator (`total`) value to `n`, overwriting any existing value + pub fn set_total(&self, n: usize) { + self.total.store(n, Ordering::Relaxed); + } + /// Merge the value from `other` into `self` pub fn merge(&self, other: &Self) { - self.add_part(other.part()); - self.add_total(other.total()); + match self.merge_strategy { + RatioMergeStrategy::AddPartAddTotal => { + self.add_part(other.part()); + self.add_total(other.total()); + } + RatioMergeStrategy::AddPartSetTotal => { + self.add_part(other.part()); + self.set_total(other.total()); + } + RatioMergeStrategy::SetPartAddTotal => { + self.set_part(other.part()); + self.add_total(other.total()); + } + } } /// Return the numerator (`part`) value @@ -776,9 +813,12 @@ impl MetricValue { name: name.clone(), pruning_metrics: PruningMetrics::new(), }, - Self::Ratio { name, .. } => Self::Ratio { - name: name.clone(), - ratio_metrics: RatioMetrics::new(), + Self::Ratio { name, ratio_metrics } => { + let merge_strategy = ratio_metrics.merge_strategy.clone(); + Self::Ratio { + name: name.clone(), + ratio_metrics: RatioMetrics::new().with_merge_strategy(merge_strategy), + } }, Self::Custom { name, value } => Self::Custom { name: name.clone(), From 62935c821f12e10094b337bc6a3f2a36ba93229b Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Tue, 11 Nov 2025 11:27:08 -0800 Subject: [PATCH 6/7] Add tests for ratio merge strategy --- datafusion/physical-plan/src/metrics/value.rs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/datafusion/physical-plan/src/metrics/value.rs b/datafusion/physical-plan/src/metrics/value.rs index f826178f1c0f..413af8fb1fa3 100644 --- a/datafusion/physical-plan/src/metrics/value.rs +++ b/datafusion/physical-plan/src/metrics/value.rs @@ -1169,6 +1169,64 @@ mod tests { assert_eq!("0.033% (1/3000)", tiny_ratio.to_string()); } + #[test] + fn test_ratio_set_methods() { + let ratio_metrics = RatioMetrics::new(); + + // Ensure set methods don't increment + ratio_metrics.set_part(10); + ratio_metrics.set_part(10); + ratio_metrics.set_total(40); + ratio_metrics.set_total(40); + assert_eq!("25% (10/40)", ratio_metrics.to_string()); + + let ratio_metrics = RatioMetrics::new(); + + // Calling set should change the value + ratio_metrics.set_part(10); + ratio_metrics.set_part(30); + ratio_metrics.set_total(40); + ratio_metrics.set_total(50); + assert_eq!("60% (30/50)", ratio_metrics.to_string()); + } + + #[test] + fn test_ratio_merge_strategy() { + // Test AddPartSetTotal strategy + let ratio_metrics1 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); + + ratio_metrics1.set_part(10); + ratio_metrics1.set_total(40); + assert_eq!("25% (10/40)", ratio_metrics1.to_string()); + let ratio_metrics2 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); + ratio_metrics2.set_part(20); + ratio_metrics2.set_total(40); + assert_eq!("50% (20/40)", ratio_metrics2.to_string()); + + ratio_metrics1.merge(&ratio_metrics2); + assert_eq!("75% (30/40)", ratio_metrics1.to_string()); + + // Test SetPartAddTotal strategy + let ratio_metrics1 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::SetPartAddTotal); + ratio_metrics1.set_part(20); + ratio_metrics1.set_total(50); + let ratio_metrics2 = RatioMetrics::new(); + ratio_metrics2.set_part(20); + ratio_metrics2.set_total(50); + ratio_metrics1.merge(&ratio_metrics2); + assert_eq!("20% (20/100)", ratio_metrics1.to_string()); + + // Test AddPartAddTotal strategy (default) + let ratio_metrics1 = RatioMetrics::new(); + ratio_metrics1.set_part(20); + ratio_metrics1.set_total(50); + let ratio_metrics2 = RatioMetrics::new(); + ratio_metrics2.set_part(20); + ratio_metrics2.set_total(50); + ratio_metrics1.merge(&ratio_metrics2); + assert_eq!("40% (40/100)", ratio_metrics1.to_string()); + } + #[test] fn test_display_timestamp() { let timestamp = Timestamp::new(); From 25ae3c1bcc5311f05d5ab29413d64fda98228eaf Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Tue, 11 Nov 2025 12:19:25 -0800 Subject: [PATCH 7/7] cargo fmt --- datafusion/datasource-parquet/src/metrics.rs | 6 +++++- datafusion/physical-plan/src/metrics/mod.rs | 4 ++-- datafusion/physical-plan/src/metrics/value.rs | 19 +++++++++++++------ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/datafusion/datasource-parquet/src/metrics.rs b/datafusion/datasource-parquet/src/metrics.rs index 531907a59ce1..5eaa137e9a45 100644 --- a/datafusion/datasource-parquet/src/metrics.rs +++ b/datafusion/datasource-parquet/src/metrics.rs @@ -120,7 +120,11 @@ impl ParquetFileMetrics { let scan_efficiency_ratio = MetricBuilder::new(metrics) .with_new_label("filename", filename.to_string()) .with_type(MetricType::SUMMARY) - .ratio_metrics_with_strategy("scan_efficiency_ratio", partition, RatioMergeStrategy::AddPartSetTotal); + .ratio_metrics_with_strategy( + "scan_efficiency_ratio", + partition, + RatioMergeStrategy::AddPartSetTotal, + ); // ----------------------- // 'dev' level metrics diff --git a/datafusion/physical-plan/src/metrics/mod.rs b/datafusion/physical-plan/src/metrics/mod.rs index 8b3feb4bf4db..53dfc849c47f 100644 --- a/datafusion/physical-plan/src/metrics/mod.rs +++ b/datafusion/physical-plan/src/metrics/mod.rs @@ -36,8 +36,8 @@ pub use baseline::{BaselineMetrics, RecordOutput, SpillMetrics, SplitMetrics}; pub use builder::MetricBuilder; pub use custom::CustomMetricValue; pub use value::{ - Count, Gauge, MetricValue, PruningMetrics, RatioMergeStrategy, RatioMetrics, ScopedTimerGuard, Time, - Timestamp, + Count, Gauge, MetricValue, PruningMetrics, RatioMergeStrategy, RatioMetrics, + ScopedTimerGuard, Time, Timestamp, }; /// Something that tracks a value of interest (metric) of a DataFusion diff --git a/datafusion/physical-plan/src/metrics/value.rs b/datafusion/physical-plan/src/metrics/value.rs index 413af8fb1fa3..3503ff224f31 100644 --- a/datafusion/physical-plan/src/metrics/value.rs +++ b/datafusion/physical-plan/src/metrics/value.rs @@ -813,13 +813,17 @@ impl MetricValue { name: name.clone(), pruning_metrics: PruningMetrics::new(), }, - Self::Ratio { name, ratio_metrics } => { + Self::Ratio { + name, + ratio_metrics, + } => { let merge_strategy = ratio_metrics.merge_strategy.clone(); Self::Ratio { name: name.clone(), - ratio_metrics: RatioMetrics::new().with_merge_strategy(merge_strategy), + ratio_metrics: RatioMetrics::new() + .with_merge_strategy(merge_strategy), } - }, + } Self::Custom { name, value } => Self::Custom { name: name.clone(), value: value.new_empty(), @@ -1193,12 +1197,14 @@ mod tests { #[test] fn test_ratio_merge_strategy() { // Test AddPartSetTotal strategy - let ratio_metrics1 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); + let ratio_metrics1 = + RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); ratio_metrics1.set_part(10); ratio_metrics1.set_total(40); assert_eq!("25% (10/40)", ratio_metrics1.to_string()); - let ratio_metrics2 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); + let ratio_metrics2 = + RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::AddPartSetTotal); ratio_metrics2.set_part(20); ratio_metrics2.set_total(40); assert_eq!("50% (20/40)", ratio_metrics2.to_string()); @@ -1207,7 +1213,8 @@ mod tests { assert_eq!("75% (30/40)", ratio_metrics1.to_string()); // Test SetPartAddTotal strategy - let ratio_metrics1 = RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::SetPartAddTotal); + let ratio_metrics1 = + RatioMetrics::new().with_merge_strategy(RatioMergeStrategy::SetPartAddTotal); ratio_metrics1.set_part(20); ratio_metrics1.set_total(50); let ratio_metrics2 = RatioMetrics::new();