Skip to main content

HierarchicalQuorumSet

Struct HierarchicalQuorumSet 

Source
pub struct HierarchicalQuorumSet<ID> { /* private fields */ }
Expand description

A quorum rule over a tree of groups: it accepts an ID set that reaches the root group.

A Group holds IDs, child groups, or both: its members. A set reaches a group when the IDs of the group that it holds, plus the child groups that it reaches, number at least the quorum size of the group.

Each group has a name that is stable among its siblings, such as the name of a zone, and each ID belongs to exactly one group. new() checks that every group of the tree holds a member and has 1 <= quorum_size <= member_count, and that no ID appears twice in the tree. So every group can be reached, and the set of all IDs is a quorum.

The intersection check proves Some(true) for two rules over the same tree when their quorum sizes sum to more than the member count of every group, as a rule and its dual() do. It answers None for every other pair.

§Examples

use std::collections::BTreeMap;
use std::collections::BTreeSet;

use quorum_set::{Group, HierarchicalQuorumSet, QuorumIntersection, QuorumSet};

// Zone a has racks r1 = {1, 2} and r2 = {3, 4}, zone b holds IDs 5 and 6, and ID 7
// sits at the root. A quorum reaches 2 of zone a, zone b and ID 7. It reaches zone a
// through an ID in each rack, and zone b through both of its IDs.
let a = Group::of_groups(2, [
    ("r1", Group::of_ids(1, [1, 2])),
    ("r2", Group::of_ids(1, [3, 4])),
]);
let b = Group::of_ids(2, [5, 6]);
let root = Group {
    quorum_size: 2,
    ids: BTreeSet::from([7]),
    groups: BTreeMap::from([("a".to_string(), a), ("b".to_string(), b)]),
};
let rule = HierarchicalQuorumSet::new(root).unwrap();

let accepts = rule.is_quorum([1, 3, 7].iter());
assert!(accepts);
// Rack r2 is lost, so only zone b is reached.
let accepts = rule.is_quorum([1, 2, 5, 6].iter());
assert!(!accepts);

// The quorums {1, 3, 7} and {2, 4, 5, 6} both reach zone a, through other IDs of each
// rack. The check proves nothing, because the two quorum sizes of a rack sum to only its
// member count.
let intersects = rule.intersects_with(&rule);
assert_eq!(None, intersects);

Implementations§

Source§

impl<ID> HierarchicalQuorumSet<ID>
where ID: Ord + Clone,

Source

pub fn new(root: Group<ID>) -> Result<Self, HierarchicalQuorumSetError<ID>>

Builds a rule that accepts an ID set reaching root.

Returns HierarchicalQuorumSetError for the first problem in a walk of the tree that visits a group before the groups below it, and sibling groups in name order: a group that holds no member, a group without 1 <= quorum_size <= member_count, or an ID that a visited group already holds.

Source

pub fn root(&self) -> &Group<ID>

Returns the root group, which holds every ID of the rule in its tree.

Source

pub fn dual(&self) -> Self

Returns the rule over the same tree that accepts exactly the sets that meet every quorum of self.

A quorum of self that misses a set S takes its IDs from outside S. Because no ID appears twice in the tree, it can reach the members of a group independently: each ID outside S, and each child group that S does not block. S blocks a group when it leaves fewer than quorum_size members reachable, so when it holds or blocks member_count - quorum_size + 1 members: the IDs that it holds and the child groups that it blocks. S meets every quorum exactly when it blocks the root group. So the result keeps the tree and gives every group that number as its quorum size.

Trait Implementations§

Source§

impl<ID: Clone> Clone for HierarchicalQuorumSet<ID>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<ID: Debug> Debug for HierarchicalQuorumSet<ID>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<ID: Eq> Eq for HierarchicalQuorumSet<ID>

Source§

impl<ID: PartialEq> PartialEq for HierarchicalQuorumSet<ID>

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<ID> QuorumIntersection<HierarchicalQuorumSet<ID>> for HierarchicalQuorumSet<ID>
where ID: Ord + Clone,

Sufficient quorum intersection for two rules over the same tree.

A quorum of each rule reaches at least quorum_size members of the root group. When the two quorum sizes sum to more than the member count, two quorums reach a common member. A common ID is shared by both quorums, and a common child group repeats the argument one level down. So two quorums share an ID when the two quorum sizes of every group sum to more than its member count. The dual() of a rule gives every group the quorum size that makes this sum exceed its member count by exactly 1, so the check proves every rule against its dual. The check runs in O(n).

Source§

fn intersects_with(&self, other: &HierarchicalQuorumSet<ID>) -> Option<bool>

Return Some(true) when both rules hold the same IDs and the same child group names in every group, and the two quorum sizes of every group sum to more than its member count.

Return None otherwise, including for rules over different trees, such as after an ID moves to another group: the check proves nothing for them. This check never returns Some(false).

Source§

impl<ID> QuorumSet for HierarchicalQuorumSet<ID>
where ID: Ord + Clone,

Source§

type Id = ID

Node ID type in this quorum set.
Source§

type Iter = IntoIter<ID>

Iterator over every voter ID tracked by this quorum set. Read more
Source§

fn is_quorum<'a, I>(&self, ids: I) -> bool
where ID: 'a, I: Iterator<Item = &'a ID> + Clone,

Return true if the candidate IDs satisfy this quorum rule. Read more
Source§

fn ids(&self) -> Self::Iter

Return all voter IDs in this quorum set.
Source§

impl<ID: PartialEq> StructuralPartialEq for HierarchicalQuorumSet<ID>

Auto Trait Implementations§

§

impl<ID> Forget for HierarchicalQuorumSet<ID>

§

impl<ID> Freeze for HierarchicalQuorumSet<ID>
where Group<ID>: Freeze,

§

impl<ID> RefUnwindSafe for HierarchicalQuorumSet<ID>
where Group<ID>: RefUnwindSafe,

§

impl<ID> Send for HierarchicalQuorumSet<ID>
where Group<ID>: Send,

§

impl<ID> Sync for HierarchicalQuorumSet<ID>
where Group<ID>: Sync,

§

impl<ID> Unpin for HierarchicalQuorumSet<ID>
where Group<ID>: Unpin,

§

impl<ID> UnsafeUnpin for HierarchicalQuorumSet<ID>
where Group<ID>: UnsafeUnpin,

§

impl<ID> UnwindSafe for HierarchicalQuorumSet<ID>
where Group<ID>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.