Skip to main content

rust_supervisor/policy/
group.rs

1//! Group isolation strategy module.
2//!
3//! Implements group-level fault boundary enforcement
4//! with dependency edge propagation rules (US2: group fault stays within boundary).
5//!
6//! [`GroupDependencyEdge`] declares a directed failure propagation relationship
7//! between two groups. [`PropagationPolicy`] controls the propagation semantics:
8//! `None` (fully isolated), `EscalateOnly` (notify parent, don't block children),
9//! or `Full` (mark all children in the dependent group as non-restartable).
10//!
11//! [`GroupIsolationPolicy`] evaluates whether a failure in one group affects
12//! another, based on the declared DAG of dependency edges. Cyclic dependencies
13//! are rejected at config load time.
14
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17
18/// Failure propagation policy across group boundaries.
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
20pub enum PropagationPolicy {
21    /// No propagation — groups are fully isolated.
22    None,
23    /// Escalate to parent supervisor only, do not affect current group.
24    EscalateOnly,
25    /// Full propagation — all children in the current group are marked
26    /// non-restartable and the group enters meltdown.
27    /// Propagation direction: fault flows from `to_group` to `from_group`
28    /// (one-way), never reversed.
29    Full,
30}
31
32/// Declares a failure propagation dependency between groups.
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
34pub struct GroupDependencyEdge {
35    /// The group that depends on another group.
36    pub from_group: String,
37    /// The group that is depended on.
38    pub to_group: String,
39    /// How failures propagate from `to_group` to `from_group`.
40    pub propagation: PropagationPolicy,
41}
42
43/// Evaluates whether a failure in one group affects another group.
44///
45/// Dependency edges form a directed acyclic graph (DAG). Cyclic dependencies
46/// are detected at config load time and rejected with a structured error
47/// listing the group names on the cycle.
48#[derive(Debug, Clone, PartialEq, Eq)]
49pub struct GroupIsolationPolicy {
50    /// Declared cross-group dependency edges.
51    dependencies: Vec<GroupDependencyEdge>,
52}
53
54impl GroupIsolationPolicy {
55    /// Creates an isolation policy from declared dependency edges.
56    pub fn new(dependencies: Vec<GroupDependencyEdge>) -> Self {
57        Self { dependencies }
58    }
59
60    /// Checks whether `my_group` is affected by a failure in `failed_group`.
61    ///
62    /// Returns `true` when a dependency edge explicitly allows propagation,
63    /// or when `my_group` is the same as `failed_group`.
64    pub fn affected_by(&self, my_group: &str, failed_group: &str) -> bool {
65        if my_group == failed_group {
66            return true;
67        }
68        self.dependencies.iter().any(|edge| {
69            edge.from_group == my_group
70                && edge.to_group == failed_group
71                && edge.propagation == PropagationPolicy::Full
72        })
73    }
74}