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}