Monitoring System 0.1.0
System resource monitoring with pluggable collectors and alerting
Loading...
Searching...
No Matches
kcenon::monitoring::health_dependency_graph Class Reference

Directed acyclic graph for health check dependencies. More...

#include <health_monitor.h>

Collaboration diagram for kcenon::monitoring::health_dependency_graph:
Collaboration graph

Public Member Functions

common::Result< bool > add_node (const std::string &name, std::shared_ptr< health_check > check)
 Add a health check node to the graph.
 
common::Result< bool > add_dependency (const std::string &dependent, const std::string &dependency)
 Add a dependency edge: dependent depends on dependency.
 
std::vector< std::string > get_dependencies (const std::string &name) const
 Get the direct dependencies of a node.
 
std::vector< std::string > get_dependents (const std::string &name) const
 Get the nodes that directly depend on the given node.
 
bool would_create_cycle (const std::string &from, const std::string &to) const
 Check whether adding an edge from -> to would create a cycle.
 
std::vector< std::string > topological_sort () const
 Compute a topological ordering of all nodes.
 
health_check_result check_with_dependencies (const std::string &name)
 Execute a health check after verifying all its dependencies are healthy.
 
std::vector< std::string > get_failure_impact (const std::string &name) const
 Compute all nodes that would be impacted if the given node fails.
 

Private Member Functions

bool would_create_cycle_internal (const std::string &from, const std::string &to) const
 

Private Attributes

std::shared_mutex mutex_
 
std::unordered_map< std::string, std::shared_ptr< health_check > > nodes_
 
std::unordered_map< std::string, std::vector< std::string > > dependencies_
 
std::unordered_map< std::string, std::vector< std::string > > dependents_
 

Detailed Description

Directed acyclic graph for health check dependencies.

Models dependency relationships between health checks so that a check's dependencies are verified before the check itself. Prevents circular dependencies and supports topological ordering and impact analysis.

Thread Safety

All public methods are thread-safe using std::shared_mutex (shared lock for reads, exclusive lock for writes).

See also
health_monitor For the primary consumer of this graph

Definition at line 337 of file health_monitor.h.

Member Function Documentation

◆ add_dependency()

common::Result< bool > kcenon::monitoring::health_dependency_graph::add_dependency ( const std::string & dependent,
const std::string & dependency )
inline

Add a dependency edge: dependent depends on dependency.

Parameters
dependentName of the node that depends on another
dependencyName of the node being depended upon
Returns
Ok(true) on success, or error if nodes are not found or a cycle would be created

Definition at line 364 of file health_monitor.h.

364 {
365 std::lock_guard<std::shared_mutex> lock(mutex_);
366
367 if (nodes_.find(dependent) == nodes_.end()) {
368 return common::Result<bool>::err(error_info(monitoring_error_code::not_found, "Dependent '" + dependent + "' not found").to_common_error());
369 }
370 if (nodes_.find(dependency) == nodes_.end()) {
371 return common::Result<bool>::err(error_info(monitoring_error_code::not_found, "Dependency '" + dependency + "' not found").to_common_error());
372 }
373
374 if (would_create_cycle_internal(dependent, dependency)) {
375 return common::Result<bool>::err(error_info(monitoring_error_code::invalid_state, "Adding dependency would create a cycle").to_common_error());
376 }
377
378 dependencies_[dependent].push_back(dependency);
379 dependents_[dependency].push_back(dependent);
380 return common::ok(true);
381 }
std::unordered_map< std::string, std::vector< std::string > > dependencies_
bool would_create_cycle_internal(const std::string &from, const std::string &to) const
std::unordered_map< std::string, std::vector< std::string > > dependents_
std::unordered_map< std::string, std::shared_ptr< health_check > > nodes_

References dependencies_, dependents_, kcenon::monitoring::invalid_state, mutex_, nodes_, kcenon::monitoring::not_found, and would_create_cycle_internal().

Referenced by kcenon::monitoring::health_monitor::add_dependency(), TEST_F(), TEST_F(), TEST_F(), TEST_F(), and TEST_F().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_node()

common::Result< bool > kcenon::monitoring::health_dependency_graph::add_node ( const std::string & name,
std::shared_ptr< health_check > check )
inline

Add a health check node to the graph.

Parameters
nameUnique name for this node
checkThe health check implementation
Returns
Ok(true) on success, or error if the name already exists

Definition at line 345 of file health_monitor.h.

345 {
346 std::lock_guard<std::shared_mutex> lock(mutex_);
347
348 if (nodes_.find(name) != nodes_.end()) {
349 return common::Result<bool>::err(error_info(monitoring_error_code::already_exists, "Node '" + name + "' already exists").to_common_error());
350 }
351
352 nodes_[name] = std::move(check);
353 dependencies_[name] = {};
354 dependents_[name] = {};
355 return common::ok(true);
356 }

References kcenon::monitoring::already_exists, dependencies_, dependents_, mutex_, and nodes_.

Referenced by kcenon::monitoring::health_monitor::register_check(), TEST_F(), TEST_F(), TEST_F(), TEST_F(), TEST_F(), and TEST_F().

Here is the caller graph for this function:

◆ check_with_dependencies()

health_check_result kcenon::monitoring::health_dependency_graph::check_with_dependencies ( const std::string & name)
inline

Execute a health check after verifying all its dependencies are healthy.

Parameters
nameNode name to check
Returns
The check result; unhealthy/degraded if any dependency fails

Definition at line 473 of file health_monitor.h.

473 {
474 std::shared_lock<std::shared_mutex> lock(mutex_);
475
476 auto it = nodes_.find(name);
477 if (it == nodes_.end()) {
478 return health_check_result::unhealthy("Node '" + name + "' not found");
479 }
480
481 auto deps_it = dependencies_.find(name);
482 if (deps_it != dependencies_.end()) {
483 for (const auto& dep_name : deps_it->second) {
484 auto dep_it = nodes_.find(dep_name);
485 if (dep_it != nodes_.end()) {
486 auto dep_result = dep_it->second->check();
487 if (dep_result.status == health_status::unhealthy) {
489 "Dependency '" + dep_name + "' is unhealthy: " + dep_result.message);
490 }
491 if (dep_result.status == health_status::degraded) {
493 "Dependency '" + dep_name + "' is degraded: " + dep_result.message);
494 }
495 }
496 }
497 }
498
499 return it->second->check();
500 }
static health_check_result unhealthy(const std::string &msg)
static health_check_result degraded(const std::string &msg)

References kcenon::monitoring::health_check_result::degraded(), kcenon::monitoring::degraded, dependencies_, mutex_, nodes_, kcenon::monitoring::health_check_result::unhealthy(), and kcenon::monitoring::unhealthy.

Referenced by kcenon::monitoring::health_monitor::check(), and TEST_F().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_dependencies()

std::vector< std::string > kcenon::monitoring::health_dependency_graph::get_dependencies ( const std::string & name) const
inline

Get the direct dependencies of a node.

Parameters
nameNode name to query
Returns
List of dependency names, or empty vector if node not found

Definition at line 388 of file health_monitor.h.

388 {
389 std::shared_lock<std::shared_mutex> lock(mutex_);
390
391 auto it = dependencies_.find(name);
392 if (it != dependencies_.end()) {
393 return it->second;
394 }
395 return {};
396 }

References dependencies_, and mutex_.

Referenced by TEST_F().

Here is the caller graph for this function:

◆ get_dependents()

std::vector< std::string > kcenon::monitoring::health_dependency_graph::get_dependents ( const std::string & name) const
inline

Get the nodes that directly depend on the given node.

Parameters
nameNode name to query
Returns
List of dependent names, or empty vector if node not found

Definition at line 403 of file health_monitor.h.

403 {
404 std::shared_lock<std::shared_mutex> lock(mutex_);
405
406 auto it = dependents_.find(name);
407 if (it != dependents_.end()) {
408 return it->second;
409 }
410 return {};
411 }

References dependents_, and mutex_.

Referenced by TEST_F().

Here is the caller graph for this function:

◆ get_failure_impact()

std::vector< std::string > kcenon::monitoring::health_dependency_graph::get_failure_impact ( const std::string & name) const
inline

Compute all nodes that would be impacted if the given node fails.

Parameters
nameNode name to analyze
Returns
List of all transitively dependent node names

Definition at line 507 of file health_monitor.h.

507 {
508 std::shared_lock<std::shared_mutex> lock(mutex_);
509
510 std::vector<std::string> impacted;
511 std::unordered_set<std::string> visited;
512 std::queue<std::string> to_visit;
513
514 auto it = dependents_.find(name);
515 if (it != dependents_.end()) {
516 for (const auto& dep : it->second) {
517 to_visit.push(dep);
518 }
519 }
520
521 while (!to_visit.empty()) {
522 std::string current = to_visit.front();
523 to_visit.pop();
524
525 if (visited.find(current) != visited.end()) {
526 continue;
527 }
528 visited.insert(current);
529 impacted.push_back(current);
530
531 auto dep_it = dependents_.find(current);
532 if (dep_it != dependents_.end()) {
533 for (const auto& dep : dep_it->second) {
534 if (visited.find(dep) == visited.end()) {
535 to_visit.push(dep);
536 }
537 }
538 }
539 }
540
541 return impacted;
542 }

References dependents_, and mutex_.

Referenced by TEST_F().

Here is the caller graph for this function:

◆ topological_sort()

std::vector< std::string > kcenon::monitoring::health_dependency_graph::topological_sort ( ) const
inline

Compute a topological ordering of all nodes.

Returns
Node names in dependency order (dependencies before dependents)

Definition at line 428 of file health_monitor.h.

428 {
429 std::shared_lock<std::shared_mutex> lock(mutex_);
430
431 std::unordered_map<std::string, int> in_degree;
432 for (const auto& [name, _] : nodes_) {
433 in_degree[name] = 0;
434 }
435
436 for (const auto& [name, deps] : dependencies_) {
437 in_degree[name] = static_cast<int>(deps.size());
438 }
439
440 std::queue<std::string> queue;
441 for (const auto& [name, degree] : in_degree) {
442 if (degree == 0) {
443 queue.push(name);
444 }
445 }
446
447 std::vector<std::string> result;
448 result.reserve(nodes_.size());
449
450 while (!queue.empty()) {
451 std::string current = queue.front();
452 queue.pop();
453 result.push_back(current);
454
455 auto it = dependents_.find(current);
456 if (it != dependents_.end()) {
457 for (const auto& dep : it->second) {
458 if (--in_degree[dep] == 0) {
459 queue.push(dep);
460 }
461 }
462 }
463 }
464
465 return result;
466 }

References dependencies_, dependents_, mutex_, and nodes_.

Referenced by TEST_F().

Here is the caller graph for this function:

◆ would_create_cycle()

bool kcenon::monitoring::health_dependency_graph::would_create_cycle ( const std::string & from,
const std::string & to ) const
inline

Check whether adding an edge from -> to would create a cycle.

Parameters
fromSource node name
toTarget node name
Returns
true if the edge would create a cycle

Definition at line 419 of file health_monitor.h.

419 {
420 std::shared_lock<std::shared_mutex> lock(mutex_);
421 return would_create_cycle_internal(from, to);
422 }

References mutex_, and would_create_cycle_internal().

Referenced by TEST_F().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ would_create_cycle_internal()

bool kcenon::monitoring::health_dependency_graph::would_create_cycle_internal ( const std::string & from,
const std::string & to ) const
inlineprivate

Definition at line 545 of file health_monitor.h.

545 {
546 if (from == to) {
547 return true;
548 }
549
550 std::unordered_set<std::string> visited;
551 std::queue<std::string> to_visit;
552 to_visit.push(to);
553
554 while (!to_visit.empty()) {
555 std::string current = to_visit.front();
556 to_visit.pop();
557
558 if (current == from) {
559 return true;
560 }
561
562 if (visited.find(current) != visited.end()) {
563 continue;
564 }
565 visited.insert(current);
566
567 auto it = dependencies_.find(current);
568 if (it != dependencies_.end()) {
569 for (const auto& dep : it->second) {
570 if (visited.find(dep) == visited.end()) {
571 to_visit.push(dep);
572 }
573 }
574 }
575 }
576
577 return false;
578 }

References dependencies_.

Referenced by add_dependency(), and would_create_cycle().

Here is the caller graph for this function:

Member Data Documentation

◆ dependencies_

std::unordered_map<std::string, std::vector<std::string> > kcenon::monitoring::health_dependency_graph::dependencies_
private

◆ dependents_

std::unordered_map<std::string, std::vector<std::string> > kcenon::monitoring::health_dependency_graph::dependents_
private

◆ mutex_

std::shared_mutex kcenon::monitoring::health_dependency_graph::mutex_
mutableprivate

◆ nodes_

std::unordered_map<std::string, std::shared_ptr<health_check> > kcenon::monitoring::health_dependency_graph::nodes_
private

The documentation for this class was generated from the following file: