Skip to content

Commit 4ab21c9

Browse files
committed
hmon: add logic monitor
Add logic monitor to HMON.
1 parent 542ad99 commit 4ab21c9

6 files changed

Lines changed: 794 additions & 6 deletions

File tree

src/health_monitoring_lib/rust/common.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use crate::deadline::DeadlineEvaluationError;
1515
use crate::heartbeat::HeartbeatEvaluationError;
1616
use crate::log::ScoreDebug;
17+
use crate::logic::LogicEvaluationError;
1718
use crate::tag::MonitorTag;
1819
use core::hash::Hash;
1920
use core::time::Duration;
@@ -51,7 +52,7 @@ pub(crate) trait Monitor {
5152
pub(crate) enum MonitorEvaluationError {
5253
Deadline(DeadlineEvaluationError),
5354
Heartbeat(HeartbeatEvaluationError),
54-
Logic,
55+
Logic(LogicEvaluationError),
5556
}
5657

5758
impl From<DeadlineEvaluationError> for MonitorEvaluationError {
@@ -66,6 +67,12 @@ impl From<HeartbeatEvaluationError> for MonitorEvaluationError {
6667
}
6768
}
6869

70+
impl From<LogicEvaluationError> for MonitorEvaluationError {
71+
fn from(value: LogicEvaluationError) -> Self {
72+
MonitorEvaluationError::Logic(value)
73+
}
74+
}
75+
6976
/// Trait for evaluating monitors and reporting errors to be used by HealthMonitor.
7077
pub(crate) trait MonitorEvaluator {
7178
/// Run monitor evaluation.

src/health_monitoring_lib/rust/lib.rs

Lines changed: 125 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ mod worker;
2121

2222
pub mod deadline;
2323
pub mod heartbeat;
24+
pub mod logic;
2425

2526
use crate::common::{Monitor, MonitorEvalHandle};
2627
use crate::deadline::{DeadlineMonitor, DeadlineMonitorBuilder};
2728
use crate::heartbeat::{HeartbeatMonitor, HeartbeatMonitorBuilder};
2829
use crate::log::{error, ScoreDebug};
30+
use crate::logic::{LogicMonitor, LogicMonitorBuilder};
2931
pub use common::TimeRange;
3032
use containers::fixed_capacity::FixedCapacityVec;
3133
use core::time::Duration;
@@ -48,6 +50,7 @@ pub enum HealthMonitorError {
4850
pub struct HealthMonitorBuilder {
4951
deadline_monitor_builders: HashMap<MonitorTag, DeadlineMonitorBuilder>,
5052
heartbeat_monitor_builders: HashMap<MonitorTag, HeartbeatMonitorBuilder>,
53+
logic_monitor_builders: HashMap<MonitorTag, LogicMonitorBuilder>,
5154
supervisor_api_cycle: Duration,
5255
internal_processing_cycle: Duration,
5356
}
@@ -58,6 +61,7 @@ impl HealthMonitorBuilder {
5861
Self {
5962
deadline_monitor_builders: HashMap::new(),
6063
heartbeat_monitor_builders: HashMap::new(),
64+
logic_monitor_builders: HashMap::new(),
6165
supervisor_api_cycle: Duration::from_millis(500),
6266
internal_processing_cycle: Duration::from_millis(100),
6367
}
@@ -89,6 +93,19 @@ impl HealthMonitorBuilder {
8993
self
9094
}
9195

96+
/// Add a [`LogicMonitor`] for the given [`MonitorTag`].
97+
///
98+
/// - `monitor_tag` - unique tag for the [`LogicMonitor`].
99+
/// - `monitor_builder` - monitor builder to finalize.
100+
///
101+
/// # Note
102+
///
103+
/// If a logic monitor with the same tag already exists, it will be overwritten.
104+
pub fn add_logic_monitor(mut self, monitor_tag: MonitorTag, monitor_builder: LogicMonitorBuilder) -> Self {
105+
self.add_logic_monitor_internal(monitor_tag, monitor_builder);
106+
self
107+
}
108+
92109
/// Set the interval between supervisor API notifications.
93110
/// This duration determines how often the health monitor notifies the supervisor about system liveness.
94111
///
@@ -121,7 +138,9 @@ impl HealthMonitorBuilder {
121138
}
122139

123140
// Check number of monitors.
124-
let num_monitors = self.deadline_monitor_builders.len() + self.heartbeat_monitor_builders.len();
141+
let num_monitors = self.deadline_monitor_builders.len()
142+
+ self.heartbeat_monitor_builders.len()
143+
+ self.logic_monitor_builders.len();
125144
if num_monitors == 0 {
126145
error!("No monitors have been added. HealthMonitor cannot be created.");
127146
return Err(HealthMonitorError::WrongState);
@@ -144,9 +163,17 @@ impl HealthMonitorBuilder {
144163
heartbeat_monitors.insert(tag, Some(MonitorState::Available(monitor)));
145164
}
146165

166+
// Create logic monitors.
167+
let mut logic_monitors = HashMap::new();
168+
for (tag, builder) in self.logic_monitor_builders {
169+
let monitor = builder.build(tag, &allocator)?;
170+
logic_monitors.insert(tag, Some(MonitorState::Available(monitor)));
171+
}
172+
147173
Ok(HealthMonitor {
148174
deadline_monitors,
149175
heartbeat_monitors,
176+
logic_monitors,
150177
worker: worker::UniqueThreadRunner::new(self.internal_processing_cycle),
151178
supervisor_api_cycle: self.supervisor_api_cycle,
152179
})
@@ -170,6 +197,10 @@ impl HealthMonitorBuilder {
170197
self.heartbeat_monitor_builders.insert(monitor_tag, monitor_builder);
171198
}
172199

200+
pub(crate) fn add_logic_monitor_internal(&mut self, monitor_tag: MonitorTag, monitor_builder: LogicMonitorBuilder) {
201+
self.logic_monitor_builders.insert(monitor_tag, monitor_builder);
202+
}
203+
173204
pub(crate) fn with_supervisor_api_cycle_internal(&mut self, cycle_duration: Duration) {
174205
self.supervisor_api_cycle = cycle_duration;
175206
}
@@ -196,6 +227,7 @@ type MonitorContainer<M> = Option<MonitorState<M>>;
196227
pub struct HealthMonitor {
197228
deadline_monitors: HashMap<MonitorTag, MonitorContainer<DeadlineMonitor>>,
198229
heartbeat_monitors: HashMap<MonitorTag, MonitorContainer<HeartbeatMonitor>>,
230+
logic_monitors: HashMap<MonitorTag, MonitorContainer<LogicMonitor>>,
199231
worker: worker::UniqueThreadRunner,
200232
supervisor_api_cycle: Duration,
201233
}
@@ -241,6 +273,16 @@ impl HealthMonitor {
241273
Self::get_monitor(&mut self.heartbeat_monitors, monitor_tag)
242274
}
243275

276+
/// Get and pass ownership of a [`LogicMonitor`] for the given [`MonitorTag`].
277+
///
278+
/// - `monitor_tag` - unique tag for the [`LogicMonitor`].
279+
///
280+
/// Returns [`Some`] containing [`LogicMonitor`] if found and not taken.
281+
/// Otherwise returns [`None`].
282+
pub fn get_logic_monitor(&mut self, monitor_tag: MonitorTag) -> Option<LogicMonitor> {
283+
Self::get_monitor(&mut self.logic_monitors, monitor_tag)
284+
}
285+
244286
fn collect_given_monitors<M>(
245287
monitors_to_collect: &mut HashMap<MonitorTag, MonitorContainer<M>>,
246288
collected_monitors: &mut FixedCapacityVec<MonitorEvalHandle>,
@@ -287,10 +329,11 @@ impl HealthMonitor {
287329
/// Health monitoring logic stops when the [`HealthMonitor`] is dropped.
288330
pub fn start(&mut self) -> Result<(), HealthMonitorError> {
289331
// Collect all monitors.
290-
let num_monitors = self.deadline_monitors.len() + self.heartbeat_monitors.len();
332+
let num_monitors = self.deadline_monitors.len() + self.heartbeat_monitors.len() + self.logic_monitors.len();
291333
let mut collected_monitors = FixedCapacityVec::new(num_monitors);
292334
Self::collect_given_monitors(&mut self.deadline_monitors, &mut collected_monitors)?;
293335
Self::collect_given_monitors(&mut self.heartbeat_monitors, &mut collected_monitors)?;
336+
Self::collect_given_monitors(&mut self.logic_monitors, &mut collected_monitors)?;
294337

295338
// Start monitoring logic.
296339
let monitoring_logic = worker::MonitoringLogic::new(
@@ -315,7 +358,8 @@ mod tests {
315358
use crate::common::TimeRange;
316359
use crate::deadline::DeadlineMonitorBuilder;
317360
use crate::heartbeat::HeartbeatMonitorBuilder;
318-
use crate::tag::MonitorTag;
361+
use crate::logic::LogicMonitorBuilder;
362+
use crate::tag::{MonitorTag, StateTag};
319363
use crate::{HealthMonitorBuilder, HealthMonitorError};
320364
use core::time::Duration;
321365

@@ -324,11 +368,20 @@ mod tests {
324368
HeartbeatMonitorBuilder::new(range)
325369
}
326370

371+
fn def_logic_monitor_builder() -> LogicMonitorBuilder {
372+
let state1 = StateTag::from("state1");
373+
let state2 = StateTag::from("state2");
374+
LogicMonitorBuilder::new(state1)
375+
.add_state(state2)
376+
.add_transition((state1, state2))
377+
}
378+
327379
#[test]
328380
fn health_monitor_builder_new_succeeds() {
329381
let health_monitor_builder = HealthMonitorBuilder::new();
330382
assert!(health_monitor_builder.deadline_monitor_builders.is_empty());
331383
assert!(health_monitor_builder.heartbeat_monitor_builders.is_empty());
384+
assert!(health_monitor_builder.logic_monitor_builders.is_empty());
332385
assert_eq!(health_monitor_builder.supervisor_api_cycle, Duration::from_millis(500));
333386
assert_eq!(
334387
health_monitor_builder.internal_processing_cycle,
@@ -342,10 +395,13 @@ mod tests {
342395
let deadline_monitor_builder = DeadlineMonitorBuilder::new();
343396
let heartbeat_monitor_tag = MonitorTag::from("heartbeat_monitor");
344397
let heartbeat_monitor_builder = def_heartbeat_monitor_builder();
398+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
399+
let logic_monitor_builder = def_logic_monitor_builder();
345400

346401
let result = HealthMonitorBuilder::new()
347402
.add_deadline_monitor(deadline_monitor_tag, deadline_monitor_builder)
348403
.add_heartbeat_monitor(heartbeat_monitor_tag, heartbeat_monitor_builder)
404+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
349405
.build();
350406
assert!(result.is_ok());
351407
}
@@ -475,21 +531,80 @@ mod tests {
475531
assert!(result.is_none());
476532
}
477533

534+
#[test]
535+
fn health_monitor_get_logic_monitor_available() {
536+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
537+
let logic_monitor_builder = def_logic_monitor_builder();
538+
let mut health_monitor = HealthMonitorBuilder::new()
539+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
540+
.build()
541+
.unwrap();
542+
543+
let result = health_monitor.get_logic_monitor(logic_monitor_tag);
544+
assert!(result.is_some());
545+
}
546+
547+
#[test]
548+
fn health_monitor_get_logic_monitor_taken() {
549+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
550+
let logic_monitor_builder = def_logic_monitor_builder();
551+
let mut health_monitor = HealthMonitorBuilder::new()
552+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
553+
.build()
554+
.unwrap();
555+
556+
let _ = health_monitor.get_logic_monitor(logic_monitor_tag);
557+
let result = health_monitor.get_logic_monitor(logic_monitor_tag);
558+
assert!(result.is_none());
559+
}
560+
561+
#[test]
562+
fn health_monitor_get_logic_monitor_unknown() {
563+
let logic_monitor_builder = def_logic_monitor_builder();
564+
let mut health_monitor = HealthMonitorBuilder::new()
565+
.add_logic_monitor(MonitorTag::from("logic_monitor"), logic_monitor_builder)
566+
.build()
567+
.unwrap();
568+
569+
let result = health_monitor.get_logic_monitor(MonitorTag::from("undefined_monitor"));
570+
assert!(result.is_none());
571+
}
572+
573+
#[test]
574+
fn health_monitor_get_logic_monitor_invalid_state() {
575+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
576+
let logic_monitor_builder = def_logic_monitor_builder();
577+
let mut health_monitor = HealthMonitorBuilder::new()
578+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
579+
.build()
580+
.unwrap();
581+
582+
// Inject broken state - unreachable otherwise.
583+
health_monitor.logic_monitors.insert(logic_monitor_tag, None);
584+
585+
let result = health_monitor.get_logic_monitor(logic_monitor_tag);
586+
assert!(result.is_none());
587+
}
588+
478589
#[test]
479590
fn health_monitor_start_succeeds() {
480591
let deadline_monitor_tag = MonitorTag::from("deadline_monitor");
481592
let deadline_monitor_builder = DeadlineMonitorBuilder::new();
482593
let heartbeat_monitor_tag = MonitorTag::from("heartbeat_monitor");
483594
let heartbeat_monitor_builder = def_heartbeat_monitor_builder();
595+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
596+
let logic_monitor_builder = def_logic_monitor_builder();
484597

485598
let mut health_monitor = HealthMonitorBuilder::new()
486599
.add_deadline_monitor(deadline_monitor_tag, deadline_monitor_builder)
487600
.add_heartbeat_monitor(heartbeat_monitor_tag, heartbeat_monitor_builder)
601+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
488602
.build()
489603
.unwrap();
490604

491605
let _deadline_monitor = health_monitor.get_deadline_monitor(deadline_monitor_tag).unwrap();
492606
let _heartbeat_monitor = health_monitor.get_heartbeat_monitor(heartbeat_monitor_tag).unwrap();
607+
let _logic_monitor = health_monitor.get_logic_monitor(logic_monitor_tag).unwrap();
493608

494609
let result = health_monitor.start();
495610
assert!(result.is_ok());
@@ -499,10 +614,12 @@ mod tests {
499614
fn health_monitor_start_monitors_not_taken() {
500615
let deadline_monitor_builder = DeadlineMonitorBuilder::new();
501616
let heartbeat_monitor_builder = def_heartbeat_monitor_builder();
617+
let logic_monitor_builder = def_logic_monitor_builder();
502618

503619
let mut health_monitor = HealthMonitorBuilder::new()
504620
.add_deadline_monitor(MonitorTag::from("deadline_monitor"), deadline_monitor_builder)
505621
.add_heartbeat_monitor(MonitorTag::from("heartbeat_monitor"), heartbeat_monitor_builder)
622+
.add_logic_monitor(MonitorTag::from("logic_monitor"), logic_monitor_builder)
506623
.build()
507624
.unwrap();
508625

@@ -516,10 +633,13 @@ mod tests {
516633
let deadline_monitor_builder = DeadlineMonitorBuilder::new();
517634
let heartbeat_monitor_tag = MonitorTag::from("heartbeat_monitor");
518635
let heartbeat_monitor_builder = def_heartbeat_monitor_builder();
636+
let logic_monitor_tag = MonitorTag::from("logic_monitor");
637+
let logic_monitor_builder = def_logic_monitor_builder();
519638

520639
let mut health_monitor = HealthMonitorBuilder::new()
521640
.add_deadline_monitor(deadline_monitor_tag, deadline_monitor_builder)
522641
.add_heartbeat_monitor(heartbeat_monitor_tag, heartbeat_monitor_builder)
642+
.add_logic_monitor(logic_monitor_tag, logic_monitor_builder)
523643
.build()
524644
.unwrap();
525645

@@ -532,6 +652,8 @@ mod tests {
532652
assert!(get_deadline_monitor_result.is_some());
533653
let get_heartbeat_monitor_result = health_monitor.get_heartbeat_monitor(heartbeat_monitor_tag);
534654
assert!(get_heartbeat_monitor_result.is_some());
655+
let get_logic_monitor_result = health_monitor.get_logic_monitor(logic_monitor_tag);
656+
assert!(get_logic_monitor_result.is_some());
535657

536658
// Try to start again, this time should be successful.
537659
let start_result = health_monitor.start();

0 commit comments

Comments
 (0)