Skip to main content

krabka_kraft_core/
event.rs

1//! Inputs to the consensus state machine.
2
3use {
4    crate::types::{Epoch, LogOffsetMetadata, NodeId},
5    uuid::Uuid,
6};
7
8/// Where a replica stands in a resigning leader's `EndQuorumEpoch`
9/// `PreferredCandidates` list.
10///
11/// Kafka's `KafkaRaftClient.endEpochElectionBackoff` walks the list for the
12/// local replica key and backs the election off by that position, so the most
13/// caught-up voter elects first. The default, position 0 of an empty list,
14/// elects at once.
15#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct SuccessorRank {
17    /// The index of the local replica in the list, or the list length when the
18    /// list does not name it.
19    pub position: u32,
20    /// The length of the list.
21    pub successors: u32,
22}
23
24/// A peer's view of its log tip, carried in Vote/Fetch requests.
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
26pub struct LogEnd {
27    pub last_epoch: Epoch,
28    pub last_offset: i64,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
32pub enum Event {
33    /// The election timer fired.
34    ElectionTimeout,
35    /// The fetch timer fired.
36    ///
37    /// The follower or observer lost contact with the leader.
38    FetchTimeout,
39    /// The leader's check-quorum timer fired.
40    ///
41    /// A majority of the voters has not fetched within the check-quorum
42    /// window, so this leader has lost the quorum and resigns (KAFKA-15489).
43    CheckQuorumTimeout,
44    /// A peer asks us for our vote.
45    ReceiveVoteRequest {
46        from: NodeId,
47        /// Optional cluster identity from the Vote request.
48        cluster_id: Option<Uuid>,
49        /// The recipient of this Vote, that is, the wire top-level `voterId`.
50        ///
51        /// KIP-595 and Kafka's `KafkaRaftClient` check that an incoming Vote
52        /// targets this node before they consider the grant. They reject a
53        /// request that is addressed to a different voter.
54        voter_id: NodeId,
55        /// KIP-853 directory identity of the receiving voter.
56        voter_directory_id: Uuid,
57        candidate_epoch: Epoch,
58        candidate: NodeId,
59        /// KIP-853 directory identity of the candidate.
60        candidate_directory_id: Uuid,
61        candidate_log_end: LogEnd,
62        pre_vote: bool,
63    },
64    /// A peer answered our Vote.
65    ///
66    /// The wire does NOT carry the round, that is, pre-vote against real vote.
67    /// The candidate infers the round from its own `Prospective` or `Candidate`
68    /// role and epoch (KIP-996). This matches Kafka's field-less
69    /// `VoteResponse`.
70    ReceiveVoteResponse {
71        from: NodeId,
72        epoch: Epoch,
73        vote_granted: bool,
74    },
75    /// A leader announces its epoch to us.
76    ReceiveBeginQuorumEpoch {
77        leader_id: NodeId,
78        leader_epoch: Epoch,
79    },
80    /// A resigning leader tells us to start an election.
81    ReceiveEndQuorumEpoch {
82        leader_id: NodeId,
83        leader_epoch: Epoch,
84        /// Where this replica stands in the leader's preferred candidates.
85        successor_rank: SuccessorRank,
86    },
87    /// Leader side: a follower fetched at this position.
88    ReceiveFetch {
89        from: NodeId,
90        fetch_epoch: Epoch,
91        fetch_offset: i64,
92    },
93    /// Leader side: a follower asked us for a byte window of a snapshot.
94    ///
95    /// KIP-630 catch-up carries no fetch offset the leader can score, but it is
96    /// still contact from that voter, and Kafka counts it toward check-quorum
97    /// exactly as it counts a Fetch.
98    ReceiveFetchSnapshot { from: NodeId },
99    /// Follower side: the leader answered our Fetch.
100    ReceiveFetchResponse {
101        leader_id: NodeId,
102        leader_epoch: Epoch,
103        /// Holds a value when the leader signalled log divergence.
104        diverging: Option<LogOffsetMetadata>,
105    },
106    /// A Fetch or Vote response came from a newer epoch that names no leader
107    /// yet.
108    ///
109    /// Kafka's `KafkaRaftClient.maybeTransition` moves the replica to
110    /// Unattached in that epoch, and an observer then looks for the leader at
111    /// once.
112    ReceiveLeaderlessEpoch { epoch: Epoch },
113}