ic_backup/policy/effect_order/
mod.rs1use crate::model::{
4 artifacts::ArtifactChecksumRecord,
5 effect_graph::{EffectGraphError, EffectGraphRecord, EffectNodeRecord, MAX_EFFECT_OPERATIONS},
6};
7use std::collections::BTreeSet;
8use thiserror::Error;
9
10#[derive(Clone, Debug)]
12pub struct EffectProgressRequest {
13 pub completed_operations: Vec<u64>,
15}
16
17#[derive(Clone, Debug)]
19pub struct EffectReadinessView<'a> {
20 pub graph: ArtifactChecksumRecord,
22 pub completed_operations: usize,
24 pub ready: Vec<&'a EffectNodeRecord>,
26 pub blocked: Vec<&'a EffectNodeRecord>,
28}
29
30pub fn readiness<'a>(
38 graph: &'a EffectGraphRecord,
39 request: &EffectProgressRequest,
40) -> Result<EffectReadinessView<'a>, EffectOrderError> {
41 if request.completed_operations.len() > MAX_EFFECT_OPERATIONS {
42 return Err(EffectOrderError::TooManyCompleted);
43 }
44 let mut completed = BTreeSet::new();
45 for sequence in &request.completed_operations {
46 graph.node(*sequence)?;
47 if !completed.insert(*sequence) {
48 return Err(EffectOrderError::DuplicateCompleted(*sequence));
49 }
50 }
51 for sequence in &completed {
52 let node = graph.node(*sequence)?;
53 if let Some(dependency) = node
54 .depends_on()
55 .iter()
56 .find(|dependency| !completed.contains(dependency))
57 {
58 return Err(EffectOrderError::UnmetDependency {
59 operation_sequence: *sequence,
60 dependency: *dependency,
61 });
62 }
63 }
64 let mut ready = Vec::new();
65 let mut blocked = Vec::new();
66 for node in graph
67 .ordered_nodes()
68 .filter(|node| !completed.contains(&node.operation_sequence()))
69 {
70 if node
71 .depends_on()
72 .iter()
73 .all(|dependency| completed.contains(dependency))
74 {
75 ready.push(node);
76 } else {
77 blocked.push(node);
78 }
79 }
80 Ok(EffectReadinessView {
81 graph: graph.digest(),
82 completed_operations: completed.len(),
83 ready,
84 blocked,
85 })
86}
87
88#[derive(Debug, Error)]
90pub enum EffectOrderError {
91 #[error("completed effects exceed {MAX_EFFECT_OPERATIONS} operations")]
93 TooManyCompleted,
94 #[error("duplicate completed effect {0}")]
96 DuplicateCompleted(u64),
97 #[error("completed effect {operation_sequence} has unmet dependency {dependency}")]
99 UnmetDependency {
100 operation_sequence: u64,
102 dependency: u64,
104 },
105 #[error(transparent)]
107 Graph(#[from] EffectGraphError),
108}
109
110#[cfg(test)]
111mod tests;