@@ -28,10 +28,15 @@ use crate::subscribers::subscriber::EventSubscriber;
28
28
29
29
const EVENT_CHANNEL_SIZE : usize = 100 ;
30
30
31
+ struct HealthCheckEventStats {
32
+ excess_cache_miss_reported : bool ,
33
+ }
34
+
31
35
/// This subscriber is responsible for forwarding events to the health check client
32
36
pub struct HealthCheckSubscriber {
33
37
health_check_client : Option < Box < dyn HealthCheckClient > > ,
34
38
event_sender : Option < Sender < HealthCheckEvent > > ,
39
+ event_stats : HealthCheckEventStats ,
35
40
}
36
41
37
42
#[ async_trait]
@@ -62,6 +67,9 @@ impl HealthCheckSubscriber {
62
67
Box :: new ( Self {
63
68
health_check_client,
64
69
event_sender : Some ( events_tx) ,
70
+ event_stats : HealthCheckEventStats {
71
+ excess_cache_miss_reported : false ,
72
+ } ,
65
73
} )
66
74
}
67
75
@@ -96,17 +104,20 @@ impl HealthCheckSubscriber {
96
104
)
97
105
} ) ,
98
106
ActionExecution ( action_execution_end) => {
99
- let has_excess_cache_miss = action_execution_end
100
- . invalidation_info
101
- . as_ref ( )
102
- . is_some_and ( |v| v. changed_file . is_none ( ) ) ;
103
-
104
- if has_excess_cache_miss {
105
- Some ( HealthCheckEvent :: HealthCheckContextEvent (
106
- HealthCheckContextEvent :: HasExcessCacheMisses ( ) ,
107
- ) )
108
- } else {
107
+ if self . event_stats . excess_cache_miss_reported {
109
108
None
109
+ } else {
110
+ let has_excess_cache_miss = action_execution_end
111
+ . invalidation_info
112
+ . as_ref ( )
113
+ . is_some_and ( |v| v. changed_file . is_none ( ) ) ;
114
+
115
+ has_excess_cache_miss. then ( || {
116
+ self . event_stats . excess_cache_miss_reported = true ;
117
+ HealthCheckEvent :: HealthCheckContextEvent (
118
+ HealthCheckContextEvent :: HasExcessCacheMisses ( ) ,
119
+ )
120
+ } )
110
121
}
111
122
}
112
123
_ => None ,
@@ -168,6 +179,8 @@ impl HealthCheckSubscriber {
168
179
mod tests {
169
180
use std:: time:: SystemTime ;
170
181
182
+ use buck2_data:: ActionExecutionEnd ;
183
+ use buck2_data:: ActionKind ;
171
184
use buck2_health_check:: interface:: HealthCheckType ;
172
185
use buck2_health_check:: report:: DisplayReport ;
173
186
use buck2_health_check:: report:: HealthIssue ;
@@ -211,6 +224,8 @@ mod tests {
211
224
}
212
225
213
226
impl HealthCheckClient for TestHealthCheckClient { }
227
+ struct NoOpHealthCheckClient { }
228
+ impl HealthCheckClient for NoOpHealthCheckClient { }
214
229
215
230
fn test_reports ( ) -> Vec < DisplayReport > {
216
231
vec ! [
@@ -243,6 +258,23 @@ mod tests {
243
258
) )
244
259
}
245
260
261
+ fn event_for_excess_cache_miss ( ) -> buck2_data:: buck_event:: Data {
262
+ let action_end = ActionExecutionEnd {
263
+ kind : ActionKind :: Run as i32 ,
264
+ invalidation_info : Some ( buck2_data:: CommandInvalidationInfo {
265
+ changed_file : None ,
266
+ changed_any : None ,
267
+ } ) ,
268
+ ..Default :: default ( )
269
+ } ;
270
+ buck2_data:: buck_event:: Data :: SpanEnd ( buck2_data:: SpanEndEvent {
271
+ data : Some ( buck2_data:: span_end_event:: Data :: ActionExecution (
272
+ Box :: new ( action_end) . into ( ) ,
273
+ ) ) ,
274
+ ..buck2_data:: SpanEndEvent :: default ( )
275
+ } )
276
+ }
277
+
246
278
#[ tokio:: test]
247
279
async fn test_health_check_subscriber ( ) -> buck2_error:: Result < ( ) > {
248
280
let ( tags_tx, mut tags_rx) = mpsc:: channel :: < Vec < String > > ( 10 ) ;
@@ -281,4 +313,24 @@ mod tests {
281
313
282
314
Ok ( ( ) )
283
315
}
316
+
317
+ #[ tokio:: test]
318
+ async fn test_excess_cache_miss_reporting ( ) {
319
+ let ( events_tx, mut events_rx) = mpsc:: channel :: < HealthCheckEvent > ( EVENT_CHANNEL_SIZE ) ;
320
+
321
+ let mut subscriber = HealthCheckSubscriber :: new_with_client (
322
+ Some ( Box :: new ( NoOpHealthCheckClient { } ) ) ,
323
+ events_tx,
324
+ ) ;
325
+
326
+ let event1 = test_event ( event_for_excess_cache_miss ( ) . into ( ) ) ;
327
+ let event2 = test_event ( event_for_excess_cache_miss ( ) . into ( ) ) ;
328
+
329
+ subscriber. handle_event ( & event1) . await . unwrap ( ) ;
330
+ subscriber. handle_event ( & event2) . await . unwrap ( ) ;
331
+
332
+ // Verify that only one HealthCheckContextEvent::HasExcessCacheMisses is sent
333
+ let mut buffer: Vec < HealthCheckEvent > = Vec :: with_capacity ( 2 ) ;
334
+ assert_eq ! ( 1 , events_rx. recv_many( & mut buffer, 2 ) . await ) ;
335
+ }
284
336
}
0 commit comments