20#include <unordered_map>
21#include <unordered_set>
72 return {
error_info{1,
"Node name cannot be empty",
"health_dependency_graph"}};
75 return {
error_info{2,
"Health check cannot be null",
"health_dependency_graph"}};
78 std::lock_guard<std::mutex> lock(
mutex_);
81 return {
error_info{3,
"Node already exists: " + name,
"health_dependency_graph"}};
84 nodes_[name] = std::move(check);
97 std::lock_guard<std::mutex> lock(
mutex_);
100 return {
error_info{1,
"Node not found: " + name,
"health_dependency_graph"}};
125 std::lock_guard<std::mutex> lock(
mutex_);
128 return {
error_info{1,
"Dependent node not found: " + dependent,
129 "health_dependency_graph"}};
133 "health_dependency_graph"}};
138 "Adding dependency would create a cycle: " + dependent +
" -> " +
140 "health_dependency_graph"}};
156 std::lock_guard<std::mutex> lock(
mutex_);
159 return {
error_info{1,
"Dependent node not found: " + dependent,
160 "health_dependency_graph"}};
177 std::lock_guard<std::mutex> lock(
mutex_);
191 [[nodiscard]] std::set<std::string>
get_dependents(
const std::string& name)
const {
192 std::lock_guard<std::mutex> lock(
mutex_);
208 std::lock_guard<std::mutex> lock(
mutex_);
217 std::lock_guard<std::mutex> lock(
mutex_);
219 std::unordered_map<std::string, int> in_degree;
220 for (
const auto& [name, _] :
nodes_) {
225 for (
const auto& dep : deps) {
228 in_degree[name] =
static_cast<int>(deps.size());
231 std::queue<std::string> zero_in_degree;
232 for (
const auto& [name, degree] : in_degree) {
234 zero_in_degree.push(name);
238 std::vector<std::string> result;
239 result.reserve(
nodes_.size());
241 while (!zero_in_degree.empty()) {
242 std::string current = zero_in_degree.front();
243 zero_in_degree.pop();
244 result.push_back(current);
246 for (
const auto& dependent :
dependents_.at(current)) {
247 --in_degree[dependent];
248 if (in_degree[dependent] == 0) {
249 zero_in_degree.push(dependent);
254 if (result.size() !=
nodes_.size()) {
255 return {
error_info{1,
"Cycle detected in dependency graph",
256 "health_dependency_graph"}};
272 std::lock_guard<std::mutex> lock(
mutex_);
274 auto node_it =
nodes_.find(name);
275 if (node_it ==
nodes_.end()) {
276 return {
error_info{1,
"Node not found: " + name,
"health_dependency_graph"}};
279 std::unordered_map<std::string, health_check_result> results;
289 std::lock_guard<std::mutex> lock(
mutex_);
291 std::set<std::string> impacted;
292 std::queue<std::string> to_visit;
295 while (!to_visit.empty()) {
296 std::string current = to_visit.front();
301 for (
const auto& dependent : it->second) {
302 if (impacted.find(dependent) == impacted.end()) {
303 impacted.insert(dependent);
304 to_visit.push(dependent);
318 [[nodiscard]]
bool has_node(
const std::string& name)
const {
319 std::lock_guard<std::mutex> lock(
mutex_);
327 [[nodiscard]] std::size_t
size()
const {
328 std::lock_guard<std::mutex> lock(
mutex_);
337 std::lock_guard<std::mutex> lock(
mutex_);
345 std::lock_guard<std::mutex> lock(
mutex_);
356 std::lock_guard<std::mutex> lock(
mutex_);
357 std::vector<std::string> names;
358 names.reserve(
nodes_.size());
359 for (
const auto& [name, _] :
nodes_) {
360 names.push_back(name);
367 const std::string& to)
const {
371 std::unordered_set<std::string> visited;
372 std::queue<std::string> queue;
375 while (!queue.empty()) {
376 std::string current = queue.front();
379 if (current == from) {
383 if (visited.find(current) != visited.end()) {
386 visited.insert(current);
391 for (
const auto& dep : it->second) {
401 const std::string& name,
402 std::unordered_map<std::string, health_check_result>& results) {
404 auto result_it = results.find(name);
405 if (result_it != results.end()) {
406 return ok(result_it->second);
409 auto node_it =
nodes_.find(name);
410 if (node_it ==
nodes_.end()) {
411 return {
error_info{1,
"Node not found: " + name,
"health_dependency_graph"}};
415 bool dependency_failed =
false;
416 std::string failure_reason;
420 if (dep_result.is_err()) {
421 dependency_failed =
true;
422 failure_reason =
"Dependency check failed: " + dep;
427 dependency_failed =
true;
428 failure_reason =
"Dependency unhealthy: " + dep;
434 if (dependency_failed) {
436 result.
message = failure_reason;
437 result.
metadata[
"skipped"] =
"true";
438 result.
metadata[
"reason"] =
"dependency_failure";
440 result = node_it->second->check();
443 results[name] = result;
447 std::unordered_map<std::string, std::shared_ptr<health_check>>
nodes_;
449 std::unordered_map<std::string, std::set<std::string>>
dependents_;
Result type for error handling with member function support.
Manages dependencies between health checks as a DAG.
Result< bool > add_dependency(const std::string &dependent, const std::string &dependency)
Add a dependency between two nodes.
std::size_t size() const
Get number of nodes.
bool would_create_cycle_internal(const std::string &from, const std::string &to) const
health_dependency_graph(health_dependency_graph &&)=delete
Result< bool > add_node(const std::string &name, std::shared_ptr< health_check > check)
Add a health check node to the graph.
Result< health_check_result > check_with_dependencies(const std::string &name)
Execute health check with its dependencies.
Result< bool > remove_dependency(const std::string &dependent, const std::string &dependency)
Remove a dependency between two nodes.
bool has_node(const std::string &name) const
Check if a node exists.
~health_dependency_graph()=default
std::set< std::string > get_dependencies(const std::string &name) const
Get all dependencies of a node.
Result< bool > remove_node(const std::string &name)
Remove a health check node from the graph.
std::set< std::string > get_failure_impact(const std::string &name) const
Get the impact of a node failure.
health_dependency_graph(const health_dependency_graph &)=delete
Result< health_check_result > check_with_dependencies_internal(const std::string &name, std::unordered_map< std::string, health_check_result > &results)
std::unordered_map< std::string, std::set< std::string > > dependencies_
std::set< std::string > get_dependents(const std::string &name) const
Get all nodes that depend on a given node.
void clear()
Clear all nodes and dependencies.
std::unordered_map< std::string, std::shared_ptr< health_check > > nodes_
Result< std::vector< std::string > > topological_sort() const
Get topological sort of all nodes.
health_dependency_graph & operator=(health_dependency_graph &&)=delete
std::unordered_map< std::string, std::set< std::string > > dependents_
bool would_create_cycle(const std::string &from, const std::string &to) const
Check if adding a dependency would create a cycle.
health_dependency_graph()=default
std::vector< std::string > get_all_nodes() const
Get all node names.
bool empty() const
Check if graph is empty.
health_dependency_graph & operator=(const health_dependency_graph &)=delete
Base classes and types for health checking functionality.
VoidResult ok()
Create a successful void result.
Standard error information used by Result<T>.
Result of a health check operation.
std::unordered_map< std::string, std::string > metadata