datafusion_physical_plan/metrics/custom.rs
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18//! Custom metric value type.
19
20use std::{any::Any, fmt::Debug, fmt::Display, sync::Arc};
21
22/// A trait for implementing custom metric values.
23///
24/// This trait enables defining application- or operator-specific metric types
25/// that can be aggregated and displayed alongside standard metrics. These
26/// custom metrics integrate with [`MetricValue::Custom`] and support
27/// aggregation logic, introspection, and optional numeric representation.
28///
29/// # Requirements
30/// Implementations of `CustomMetricValue` must satisfy the following:
31///
32/// 1. [`Self::aggregate`]: Defines how two metric values are combined
33/// 2. [`Self::new_empty`]: Returns a new, zero-value instance for accumulation
34/// 3. [`Self::as_any`]: Enables dynamic downcasting for type-specific operations
35/// 4. [`Self::as_usize`]: Optionally maps the value to a `usize` (for sorting, display, etc.)
36/// 5. [`Self::is_eq`]: Implements comparison between two values, this isn't reusing the std
37/// PartialEq trait because this trait is used dynamically in the context of
38/// [`MetricValue::Custom`]
39///
40/// # Examples
41/// ```
42/// # use std::sync::Arc;
43/// # use std::fmt::{Debug, Display};
44/// # use std::any::Any;
45/// # use std::sync::atomic::{AtomicUsize, Ordering};
46///
47/// # use datafusion_physical_plan::metrics::CustomMetricValue;
48///
49/// #[derive(Debug, Default)]
50/// struct MyCounter {
51/// count: AtomicUsize,
52/// }
53///
54/// impl Display for MyCounter {
55/// fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
56/// write!(f, "count: {}", self.count.load(Ordering::Relaxed))
57/// }
58/// }
59///
60/// impl CustomMetricValue for MyCounter {
61/// fn new_empty(&self) -> Arc<dyn CustomMetricValue> {
62/// Arc::new(Self::default())
63/// }
64///
65/// fn aggregate(&self, other: Arc<dyn CustomMetricValue>) {
66/// let other = other.as_any().downcast_ref::<Self>().unwrap();
67/// self.count
68/// .fetch_add(other.count.load(Ordering::Relaxed), Ordering::Relaxed);
69/// }
70///
71/// fn as_any(&self) -> &dyn Any {
72/// self
73/// }
74///
75/// fn as_usize(&self) -> usize {
76/// self.count.load(Ordering::Relaxed)
77/// }
78///
79/// fn is_eq(&self, other: &Arc<dyn CustomMetricValue>) -> bool {
80/// let Some(other) = other.as_any().downcast_ref::<Self>() else {
81/// return false;
82/// };
83///
84/// self.count.load(Ordering::Relaxed) == other.count.load(Ordering::Relaxed)
85/// }
86/// }
87/// ```
88///
89/// [`MetricValue::Custom`]: super::MetricValue::Custom
90pub trait CustomMetricValue: Display + Debug + Send + Sync {
91 /// Returns a new, zero-initialized version of this metric value.
92 ///
93 /// This value is used during metric aggregation to accumulate results.
94 fn new_empty(&self) -> Arc<dyn CustomMetricValue>;
95
96 /// Merges another metric value into this one.
97 ///
98 /// The type of `other` could be of a different custom type as long as it's aggregatable into self.
99 fn aggregate(&self, other: Arc<dyn CustomMetricValue + 'static>);
100
101 /// Returns this value as a [`Any`] to support dynamic downcasting.
102 fn as_any(&self) -> &dyn Any;
103
104 /// Optionally returns a numeric representation of the value, if meaningful.
105 /// Otherwise will default to zero.
106 ///
107 /// This is used for sorting and summarizing metrics.
108 fn as_usize(&self) -> usize {
109 0
110 }
111
112 /// Compares this value with another custom value.
113 fn is_eq(&self, other: &Arc<dyn CustomMetricValue>) -> bool;
114}