use crabka_metadata::voters::VoterSet;
use uuid::Uuid;
pub use crate::types::NodeId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct SimInstant(pub u64);
impl SimInstant {
#[must_use]
pub fn saturating_add_ms(self, ms: u64) -> Self {
Self(self.0.saturating_add(ms))
}
}
pub type LeaderEpoch = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ReplicaKey {
pub id: NodeId,
pub directory_id: Uuid,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LogOffsetMetadata {
pub offset: i64,
pub epoch: LeaderEpoch,
}
pub trait LogView {
fn end_offset(&self) -> i64;
fn last_epoch(&self) -> LeaderEpoch;
fn end_offset_for_epoch(&self, epoch: LeaderEpoch) -> Option<i64>;
}
#[derive(Debug, Clone, PartialEq)]
pub struct QuorumState {
pub cluster_id: Uuid,
pub leader_epoch: LeaderEpoch,
pub leader_id: Option<NodeId>,
pub voted_key: Option<ReplicaKey>,
pub voters: VoterSet,
}
impl QuorumState {
#[must_use]
pub fn bootstrap(cluster_id: Uuid, voters: VoterSet) -> Self {
Self {
cluster_id,
leader_epoch: 0,
leader_id: None,
voted_key: None,
voters,
}
}
#[must_use]
pub fn majority(&self) -> usize {
self.voters.len() / 2 + 1
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert2::assert;
#[test]
fn quorum_state_starts_unattached_at_epoch_zero() {
let voters = test_voter_set(&[1, 2, 3]);
let qs = QuorumState::bootstrap(uuid::Uuid::nil(), voters.clone());
assert!(qs.leader_epoch == 0);
assert!(qs.leader_id.is_none());
assert!(qs.voted_key.is_none());
assert!(qs.voters.contains(2));
}
pub(crate) fn test_voter_set(ids: &[NodeId]) -> crabka_metadata::voters::VoterSet {
crabka_metadata::voters::VoterSet::from_voters(ids.iter().map(|&id| {
crabka_metadata::voters::Voter {
id,
directory_id: uuid::Uuid::nil(),
endpoints: Vec::new(),
kraft_version: crabka_metadata::voters::KRaftVersionRange::default(),
}
}))
}
}