@@ -3,16 +3,14 @@ use std::pin::Pin;
3
3
use std:: sync:: atomic:: { AtomicU64 , AtomicU8 , Ordering } ;
4
4
use std:: sync:: Arc ;
5
5
use std:: task:: { Context , Poll } ;
6
- use std:: time:: { Duration , Instant } ;
7
-
8
- use crate :: service:: registry:: ServiceId ;
6
+ use tokio:: time:: { Duration , Instant } ;
9
7
10
8
/// Minimum interval when utilization is recalculated.
11
9
const UTILIZATION_UPDATE_THRESHOLD : Duration = Duration :: from_secs ( 5 ) ;
12
10
13
11
pin_project_lite:: pin_project! {
12
+ /// A service monitor tracks service metrics.
14
13
pub struct ServiceMonitor <F > {
15
- id: ServiceId ,
16
14
#[ pin]
17
15
inner: F ,
18
16
@@ -24,9 +22,9 @@ pin_project_lite::pin_project! {
24
22
}
25
23
26
24
impl < F > ServiceMonitor < F > {
27
- pub fn wrap ( id : ServiceId , inner : F ) -> Self {
25
+ /// Wraps a service future with a monitor.
26
+ pub fn wrap ( inner : F ) -> Self {
28
27
Self {
29
- id,
30
28
inner,
31
29
metrics : Arc :: new ( RawMetrics {
32
30
poll_count : AtomicU64 :: new ( 0 ) ,
@@ -38,6 +36,7 @@ impl<F> ServiceMonitor<F> {
38
36
}
39
37
}
40
38
39
+ /// Provides access to the raw metrics tracked in this monitor.
41
40
pub fn metrics ( & self ) -> & Arc < RawMetrics > {
42
41
& self . metrics
43
42
}
87
86
}
88
87
}
89
88
89
+ /// The raw metrics extracted from a [`ServiceMonitor`].
90
+ ///
91
+ /// All access outside the [`ServiceMonitor`] must be *read* only.
90
92
#[ derive( Debug ) ]
91
93
pub struct RawMetrics {
94
+ /// Amount of times the service was polled.
92
95
pub poll_count : AtomicU64 ,
96
+ /// The total time the service spent in its poll function.
93
97
pub total_duration_ns : AtomicU64 ,
98
+ /// Estimated utilization percentage `[0-100]`
94
99
pub utilization : AtomicU8 ,
95
100
}
101
+
102
+ #[ cfg( test) ]
103
+ mod tests {
104
+ use super :: * ;
105
+
106
+ #[ tokio:: test( start_paused = true ) ]
107
+ async fn test_monitor ( ) {
108
+ let mut monitor = ServiceMonitor :: wrap ( Box :: pin ( async {
109
+ loop {
110
+ tokio:: time:: advance ( Duration :: from_millis ( 500 ) ) . await ;
111
+ }
112
+ } ) ) ;
113
+ let metrics = Arc :: clone ( monitor. metrics ( ) ) ;
114
+
115
+ assert_eq ! ( metrics. poll_count. load( Ordering :: Relaxed ) , 0 ) ;
116
+ let _ = futures:: poll!( & mut monitor) ;
117
+ assert_eq ! ( metrics. poll_count. load( Ordering :: Relaxed ) , 1 ) ;
118
+ let _ = futures:: poll!( & mut monitor) ;
119
+ assert_eq ! ( metrics. poll_count. load( Ordering :: Relaxed ) , 2 ) ;
120
+ let _ = futures:: poll!( & mut monitor) ;
121
+ assert_eq ! ( metrics. poll_count. load( Ordering :: Relaxed ) , 3 ) ;
122
+
123
+ assert_eq ! ( metrics. utilization. load( Ordering :: Relaxed ) , 0 ) ;
124
+ assert_eq ! (
125
+ metrics. total_duration_ns. load( Ordering :: Relaxed ) ,
126
+ 1500000000
127
+ ) ;
128
+
129
+ // Advance time just enough to perfectly hit the update threshold.
130
+ tokio:: time:: advance ( UTILIZATION_UPDATE_THRESHOLD - Duration :: from_secs ( 2 ) ) . await ;
131
+
132
+ let _ = futures:: poll!( & mut monitor) ;
133
+ assert_eq ! ( metrics. poll_count. load( Ordering :: Relaxed ) , 4 ) ;
134
+ assert_eq ! ( metrics. utilization. load( Ordering :: Relaxed ) , 40 ) ;
135
+ assert_eq ! (
136
+ metrics. total_duration_ns. load( Ordering :: Relaxed ) ,
137
+ 2000000000
138
+ ) ;
139
+ }
140
+ }
0 commit comments