exocore_core/cell/
cell_nodes.rs

1use std::{
2    collections::{HashMap, HashSet},
3    sync::{RwLockReadGuard, RwLockWriteGuard},
4};
5
6use exocore_protos::generated::exocore_core::cell_node_config;
7
8use super::{Cell, Error, LocalNode, Node, NodeId};
9
10/// Common methods collection of nodes of a `Cell`
11pub trait CellNodes {
12    fn cell(&self) -> &Cell;
13    fn nodes_map(&self) -> &HashMap<NodeId, CellNode>;
14
15    fn local_node(&self) -> &LocalNode {
16        self.cell().local_node()
17    }
18
19    fn local_cell_node(&self) -> &CellNode {
20        let local_node = self.cell().local_node();
21        self.nodes_map()
22            .get(local_node.id())
23            .expect("Local node couldn't be found in cell nodes")
24    }
25
26    fn count(&self) -> usize {
27        self.nodes_map().len()
28    }
29
30    fn count_with_role(&self, role: CellNodeRole) -> usize {
31        self.nodes_map()
32            .values()
33            .filter(|cn| cn.has_role(role))
34            .count()
35    }
36
37    fn is_empty(&self) -> bool {
38        self.count() == 0
39    }
40
41    fn get(&self, node_id: &NodeId) -> Option<&CellNode> {
42        self.nodes_map().get(node_id)
43    }
44
45    fn has_quorum(&self, count: usize, role: Option<CellNodeRole>) -> bool {
46        let nb_nodes = if let Some(role) = role {
47            self.count_with_role(role)
48        } else {
49            self.count()
50        };
51
52        if nb_nodes == 0 {
53            false
54        } else {
55            count > nb_nodes / 2
56        }
57    }
58
59    fn to_owned(&self) -> CellNodesOwned {
60        CellNodesOwned {
61            cell: self.cell().clone(),
62            nodes: self.nodes_map().clone(),
63        }
64    }
65}
66
67/// Node that is part of a cell.
68#[derive(Clone)]
69pub struct CellNode {
70    node: Node,
71    roles: HashSet<CellNodeRole>,
72}
73
74impl CellNode {
75    pub fn new(node: Node) -> CellNode {
76        CellNode {
77            node,
78            roles: HashSet::new(),
79        }
80    }
81
82    pub fn node(&self) -> &Node {
83        &self.node
84    }
85
86    pub fn add_role(&mut self, role: CellNodeRole) {
87        self.roles.insert(role);
88    }
89
90    pub fn remove_role(&mut self, role: CellNodeRole) {
91        self.roles.remove(&role);
92    }
93
94    pub fn roles(&self) -> Vec<CellNodeRole> {
95        self.roles.iter().cloned().collect()
96    }
97
98    pub fn has_role(&self, role: CellNodeRole) -> bool {
99        self.roles.contains(&role)
100    }
101}
102
103/// Wraps a `CellNodes` to expose iterator methods. This is needed because of
104/// the complexity of return types of iterators which require `impl` to be used,
105/// but cannot be used in traits.
106pub struct CellNodesIter<'cn, N: CellNodes> {
107    nodes: &'cn N,
108}
109
110impl<'cn, N: CellNodes> CellNodesIter<'cn, N> {
111    pub fn all(&self) -> impl Iterator<Item = &CellNode> {
112        self.nodes.nodes_map().values()
113    }
114
115    pub fn all_except<'a>(
116        &'a self,
117        node_id: &'a NodeId,
118    ) -> impl Iterator<Item = &'a CellNode> + 'a {
119        self.nodes
120            .nodes_map()
121            .values()
122            .filter(move |n| n.node.id() != node_id)
123    }
124
125    pub fn all_except_local(&self) -> impl Iterator<Item = &CellNode> {
126        let local_node = self.nodes.cell().local_node();
127        self.all_except(local_node.id())
128    }
129
130    pub fn with_role(&self, role: CellNodeRole) -> impl Iterator<Item = &CellNode> {
131        self.nodes
132            .nodes_map()
133            .values()
134            .filter(move |cn| cn.has_role(role))
135    }
136}
137
138/// Read reference to nodes of a `Cell`
139pub struct CellNodesRead<'cell> {
140    pub(crate) cell: &'cell Cell,
141    pub(crate) nodes: RwLockReadGuard<'cell, HashMap<NodeId, CellNode>>,
142}
143
144impl<'cell> CellNodesRead<'cell> {
145    pub fn iter(&self) -> CellNodesIter<CellNodesRead> {
146        CellNodesIter { nodes: self }
147    }
148}
149
150impl<'cell> CellNodes for CellNodesRead<'cell> {
151    fn cell(&self) -> &Cell {
152        self.cell
153    }
154
155    fn nodes_map(&self) -> &HashMap<NodeId, CellNode> {
156        &self.nodes
157    }
158}
159
160/// Write reference to nodes of a `Cell`
161pub struct CellNodesWrite<'cell> {
162    pub(crate) cell: &'cell Cell,
163    pub(crate) nodes: RwLockWriteGuard<'cell, HashMap<NodeId, CellNode>>,
164}
165
166impl<'cell> CellNodesWrite<'cell> {
167    pub fn iter(&self) -> CellNodesIter<CellNodesWrite> {
168        CellNodesIter { nodes: self }
169    }
170
171    pub fn add(&mut self, node: Node) {
172        self.add_cell_node(CellNode {
173            node,
174            roles: HashSet::new(),
175        });
176    }
177
178    pub fn add_cell_node(&mut self, cell_node: CellNode) {
179        self.nodes.insert(cell_node.node.id().clone(), cell_node);
180    }
181
182    pub fn get_mut(&mut self, node_id: &NodeId) -> Option<&mut CellNode> {
183        self.nodes.get_mut(node_id)
184    }
185
186    pub fn local_cell_node_mut(&mut self) -> &mut CellNode {
187        let id = {
188            let local_node = self.cell().local_node();
189            local_node.id().clone()
190        };
191        self.nodes
192            .get_mut(&id)
193            .expect("Local node couldn't be found in cell nodes")
194    }
195}
196
197impl<'cell> CellNodes for CellNodesWrite<'cell> {
198    fn cell(&self) -> &Cell {
199        self.cell
200    }
201
202    fn nodes_map(&self) -> &HashMap<NodeId, CellNode> {
203        &self.nodes
204    }
205}
206
207/// Owned copy of nodes of a `Cell`
208pub struct CellNodesOwned {
209    pub(crate) cell: Cell,
210    pub(crate) nodes: HashMap<NodeId, CellNode>,
211}
212
213impl CellNodesOwned {
214    pub fn iter(&self) -> CellNodesIter<CellNodesOwned> {
215        CellNodesIter { nodes: self }
216    }
217}
218
219impl CellNodes for CellNodesOwned {
220    fn cell(&self) -> &Cell {
221        &self.cell
222    }
223
224    fn nodes_map(&self) -> &HashMap<NodeId, CellNode> {
225        &self.nodes
226    }
227}
228
229/// Role of node in a cell, indicating the capabilities of a node in the cell.
230#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
231pub enum CellNodeRole {
232    /// Indicates that the node participates in the chain storage & replication.
233    Chain,
234
235    /// Indicates that the node is running a full entities store.
236    Store,
237
238    /// Indicates that the node is an applications host.
239    AppHost,
240}
241
242impl CellNodeRole {
243    pub fn from_config(config: cell_node_config::Role) -> Result<CellNodeRole, Error> {
244        match config {
245            cell_node_config::Role::ChainRole => Ok(CellNodeRole::Chain),
246            cell_node_config::Role::StoreRole => Ok(CellNodeRole::Store),
247            cell_node_config::Role::AppHostRole => Ok(CellNodeRole::AppHost),
248            cell_node_config::Role::InvalidRole => {
249                Err(Error::Cell(anyhow!("Invalid cell node role")))
250            }
251        }
252    }
253
254    pub fn to_config(&self) -> cell_node_config::Role {
255        match self {
256            CellNodeRole::Chain => cell_node_config::Role::ChainRole,
257            CellNodeRole::Store => cell_node_config::Role::StoreRole,
258            CellNodeRole::AppHost => cell_node_config::Role::AppHostRole,
259        }
260    }
261}
262
263#[cfg(test)]
264mod tests {
265    use super::{super::FullCell, *};
266
267    #[test]
268    fn nodes_add_get() {
269        let local_node = LocalNode::generate();
270        let full_cell = FullCell::generate(local_node.clone()).unwrap();
271
272        {
273            let nodes = full_cell.cell().nodes();
274            assert!(!nodes.is_empty());
275            assert_eq!(nodes.count(), 1); // self
276        }
277
278        {
279            let mut nodes = full_cell.cell().nodes_mut();
280            nodes.add(Node::generate_temporary());
281            assert_eq!(nodes.count(), 2);
282            assert_eq!(nodes.iter().all().count(), 2);
283        }
284
285        {
286            let nodes = full_cell.cell().nodes();
287            assert_eq!(nodes.count(), 2);
288            assert_eq!(nodes.iter().all().count(), 2);
289            assert_eq!(nodes.iter().all_except(local_node.id()).count(), 1);
290            assert_ne!(
291                nodes.iter().all_except_local().next().unwrap().node.id(),
292                local_node.id()
293            );
294
295            assert!(nodes.get(local_node.id()).is_some());
296
297            let other_node = Node::generate_temporary();
298            assert!(nodes.get(other_node.id()).is_none());
299        }
300
301        {
302            let nodes = full_cell.cell().nodes().to_owned();
303            assert_eq!(nodes.count(), 2);
304        }
305    }
306
307    #[test]
308    fn nodes_quorum() {
309        let local_node = LocalNode::generate();
310        let full_cell = FullCell::generate(local_node).unwrap();
311
312        {
313            // 1 node
314            let nodes = full_cell.cell().nodes();
315            assert!(!nodes.has_quorum(0, None));
316            assert!(nodes.has_quorum(1, None));
317            assert!(!nodes.has_quorum(0, Some(CellNodeRole::Chain)));
318            assert!(!nodes.has_quorum(1, Some(CellNodeRole::Chain)));
319        }
320
321        {
322            // 2 nodes
323            let mut nodes = full_cell.cell().nodes_mut();
324            nodes.add(Node::generate_temporary());
325            assert!(!nodes.has_quorum(1, None));
326            assert!(nodes.has_quorum(2, None));
327        }
328
329        {
330            // 3 nodes
331            let mut nodes = full_cell.cell().nodes_mut();
332            nodes.add(Node::generate_temporary());
333            assert!(!nodes.has_quorum(1, None));
334            assert!(nodes.has_quorum(2, None));
335        }
336
337        {
338            // 3 nodes with roles
339            let mut nodes = full_cell.cell().nodes_mut();
340            let ids = nodes
341                .iter()
342                .all()
343                .map(|n| n.node.id())
344                .cloned()
345                .collect::<Vec<_>>();
346            nodes
347                .get_mut(&ids[0])
348                .unwrap()
349                .add_role(CellNodeRole::Chain);
350            nodes
351                .get_mut(&ids[1])
352                .unwrap()
353                .add_role(CellNodeRole::Chain);
354
355            assert!(!nodes.has_quorum(1, Some(CellNodeRole::Chain)));
356            assert!(nodes.has_quorum(2, Some(CellNodeRole::Chain)));
357        }
358    }
359}