Skip to content

Commit afe2134

Browse files
committed
Refactor: update gRPC example to adapt to LeaderId changes
- Update the `raft-kv-memstore-grpc` example to use the protobuf-defined `LeaderId`. - Automatically implement `CommittedLeaderId` for all types. - Add `openraft::vote::LeaderIdCompare` to provide comparison functions for both single-leader-per-term and multi-leader-per-term implementations.
1 parent 6908a3c commit afe2134

File tree

14 files changed

+243
-80
lines changed

14 files changed

+243
-80
lines changed

examples/raft-kv-memstore-grpc/build.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
77
"proto/management_service.proto",
88
"proto/api_service.proto",
99
];
10+
11+
// TODO: remove serde
12+
1013
tonic_build::configure()
1114
.type_attribute("openraftpb.Node", "#[derive(Eq, serde::Serialize, serde::Deserialize)]")
1215
.type_attribute(
@@ -17,6 +20,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
1720
"openraftpb.Response",
1821
"#[derive(Eq, serde::Serialize, serde::Deserialize)]",
1922
)
23+
.type_attribute(
24+
"openraftpb.LeaderId",
25+
"#[derive(Eq, serde::Serialize, serde::Deserialize)]",
26+
)
2027
.compile_protos_with_config(config, &proto_files, &["proto"])?;
2128
Ok(())
2229
}

examples/raft-kv-memstore-grpc/proto/internal_service.proto

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ message Vote {
1515

1616
// LogId represents the log identifier in Raft
1717
message LogId {
18-
uint64 index = 1;
19-
LeaderId leader_id = 2;
18+
uint64 term = 1;
19+
uint64 index = 2;
2020
}
2121

2222
// VoteRequest represents a request for votes during leader election

examples/raft-kv-memstore-grpc/src/grpc/management_service.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ use tonic::Response;
55
use tonic::Status;
66
use tracing::debug;
77

8+
use crate::pb;
89
use crate::protobuf::management_service_server::ManagementService;
910
use crate::protobuf::AddLearnerRequest;
1011
use crate::protobuf::ChangeMembershipRequest;
1112
use crate::protobuf::InitRequest;
1213
use crate::protobuf::RaftReplyString;
1314
use crate::protobuf::RaftRequestString;
1415
use crate::typ::*;
15-
use crate::Node;
1616

1717
/// Management service implementation for Raft cluster administration.
1818
/// Handles cluster initialization, membership changes, and metrics collection.
@@ -62,11 +62,11 @@ impl ManagementService for ManagementServiceImpl {
6262
let req = request.into_inner();
6363

6464
// Convert nodes into required format
65-
let nodes_map: BTreeMap<u64, Node> = req
65+
let nodes_map: BTreeMap<u64, pb::Node> = req
6666
.nodes
6767
.into_iter()
6868
.map(|node| {
69-
(node.node_id, Node {
69+
(node.node_id, pb::Node {
7070
rpc_addr: node.rpc_addr,
7171
node_id: node.node_id,
7272
})

examples/raft-kv-memstore-grpc/src/lib.rs

Lines changed: 31 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#![allow(clippy::uninlined_format_args)]
22

3-
use crate::protobuf::Node;
4-
use crate::protobuf::Response;
5-
use crate::protobuf::SetRequest;
3+
use crate::protobuf as pb;
64
use crate::store::StateMachineData;
75
use crate::typ::*;
86

@@ -12,14 +10,17 @@ pub mod store;
1210
#[cfg(test)]
1311
mod test;
1412

13+
mod pb_impl;
14+
1515
pub type NodeId = u64;
1616

1717
openraft::declare_raft_types!(
1818
/// Declare the type configuration for example K/V store.
1919
pub TypeConfig:
20-
D = SetRequest,
21-
R = Response,
22-
Node = Node,
20+
D = pb::SetRequest,
21+
R = pb::Response,
22+
LeaderId = pb::LeaderId,
23+
Node = pb::Node,
2324
SnapshotData = StateMachineData,
2425
);
2526

@@ -33,87 +34,71 @@ pub mod protobuf {
3334
#[path = "../../utils/declare_types.rs"]
3435
pub mod typ;
3536

36-
impl From<protobuf::LeaderId> for LeaderId {
37-
fn from(proto_leader_id: protobuf::LeaderId) -> Self {
38-
LeaderId::new(proto_leader_id.term, proto_leader_id.node_id)
39-
}
40-
}
41-
42-
impl From<protobuf::Vote> for typ::Vote {
43-
fn from(proto_vote: protobuf::Vote) -> Self {
37+
impl From<pb::Vote> for Vote {
38+
fn from(proto_vote: pb::Vote) -> Self {
4439
let leader_id: LeaderId = proto_vote.leader_id.unwrap().into();
4540
if proto_vote.committed {
46-
typ::Vote::new_committed(leader_id.term, leader_id.node_id)
41+
Vote::new_committed(leader_id.term, leader_id.node_id)
4742
} else {
48-
typ::Vote::new(leader_id.term, leader_id.node_id)
43+
Vote::new(leader_id.term, leader_id.node_id)
4944
}
5045
}
5146
}
5247

53-
impl From<protobuf::LogId> for LogId {
54-
fn from(proto_log_id: protobuf::LogId) -> Self {
55-
let leader_id: LeaderId = proto_log_id.leader_id.unwrap().into();
56-
LogId::new(leader_id, proto_log_id.index)
48+
impl From<pb::LogId> for LogId {
49+
fn from(proto_log_id: pb::LogId) -> Self {
50+
LogId::new(proto_log_id.term, proto_log_id.index)
5751
}
5852
}
5953

60-
impl From<protobuf::VoteRequest> for VoteRequest {
61-
fn from(proto_vote_req: protobuf::VoteRequest) -> Self {
62-
let vote: typ::Vote = proto_vote_req.vote.unwrap().into();
54+
impl From<pb::VoteRequest> for VoteRequest {
55+
fn from(proto_vote_req: pb::VoteRequest) -> Self {
56+
let vote: Vote = proto_vote_req.vote.unwrap().into();
6357
let last_log_id = proto_vote_req.last_log_id.map(|log_id| log_id.into());
6458
VoteRequest::new(vote, last_log_id)
6559
}
6660
}
6761

68-
impl From<protobuf::VoteResponse> for VoteResponse {
69-
fn from(proto_vote_resp: protobuf::VoteResponse) -> Self {
70-
let vote: typ::Vote = proto_vote_resp.vote.unwrap().into();
62+
impl From<pb::VoteResponse> for VoteResponse {
63+
fn from(proto_vote_resp: pb::VoteResponse) -> Self {
64+
let vote: Vote = proto_vote_resp.vote.unwrap().into();
7165
let last_log_id = proto_vote_resp.last_log_id.map(|log_id| log_id.into());
7266
VoteResponse::new(vote, last_log_id, proto_vote_resp.vote_granted)
7367
}
7468
}
7569

76-
impl From<LeaderId> for protobuf::LeaderId {
77-
fn from(leader_id: LeaderId) -> Self {
78-
protobuf::LeaderId {
79-
term: leader_id.term,
80-
node_id: leader_id.node_id,
81-
}
82-
}
83-
}
84-
85-
impl From<typ::Vote> for protobuf::Vote {
86-
fn from(vote: typ::Vote) -> Self {
87-
protobuf::Vote {
88-
leader_id: Some(protobuf::LeaderId {
70+
impl From<Vote> for pb::Vote {
71+
fn from(vote: Vote) -> Self {
72+
pb::Vote {
73+
leader_id: Some(pb::LeaderId {
8974
term: vote.leader_id().term,
9075
node_id: vote.leader_id().node_id,
9176
}),
9277
committed: vote.is_committed(),
9378
}
9479
}
9580
}
96-
impl From<LogId> for protobuf::LogId {
81+
impl From<LogId> for pb::LogId {
9782
fn from(log_id: LogId) -> Self {
98-
protobuf::LogId {
83+
pb::LogId {
84+
term: log_id.leader_id,
9985
index: log_id.index,
100-
leader_id: Some(log_id.leader_id.into()),
10186
}
10287
}
10388
}
10489

105-
impl From<VoteRequest> for protobuf::VoteRequest {
90+
impl From<VoteRequest> for pb::VoteRequest {
10691
fn from(vote_req: VoteRequest) -> Self {
107-
protobuf::VoteRequest {
92+
pb::VoteRequest {
10893
vote: Some(vote_req.vote.into()),
10994
last_log_id: vote_req.last_log_id.map(|log_id| log_id.into()),
11095
}
11196
}
11297
}
11398

114-
impl From<VoteResponse> for protobuf::VoteResponse {
99+
impl From<VoteResponse> for pb::VoteResponse {
115100
fn from(vote_resp: VoteResponse) -> Self {
116-
protobuf::VoteResponse {
101+
pb::VoteResponse {
117102
vote: Some(vote_resp.vote.into()),
118103
vote_granted: vote_resp.vote_granted,
119104
last_log_id: vote_resp.last_log_id.map(|log_id| log_id.into()),

examples/raft-kv-memstore-grpc/src/network/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ use openraft::network::RPCOption;
77
use openraft::RaftNetworkFactory;
88
use tonic::transport::Channel;
99

10+
use crate::protobuf as pb;
1011
use crate::protobuf::internal_service_client::InternalServiceClient;
1112
use crate::protobuf::RaftRequestBytes;
1213
use crate::protobuf::SnapshotRequest;
1314
use crate::protobuf::VoteRequest as PbVoteRequest;
1415
use crate::protobuf::VoteResponse as PbVoteResponse;
1516
use crate::typ::RPCError;
1617
use crate::typ::*;
17-
use crate::Node;
1818
use crate::NodeId;
1919
use crate::TypeConfig;
2020

@@ -38,7 +38,7 @@ impl RaftNetworkFactory<TypeConfig> for Network {
3838
/// Represents an active network connection to a remote Raft node.
3939
/// Handles serialization and deserialization of Raft messages over gRPC.
4040
pub struct NetworkConnection {
41-
target_node: Node,
41+
target_node: pb::Node,
4242
}
4343

4444
impl NetworkConnection {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//! Implement [`RaftLeaderId`] for protobuf defined LeaderId, so that it can be used in OpenRaft
2+
3+
use std::cmp::Ordering;
4+
use std::fmt;
5+
6+
use openraft::vote::LeaderIdCompare;
7+
use openraft::vote::RaftLeaderId;
8+
9+
use crate::protobuf as pb;
10+
use crate::TypeConfig;
11+
12+
/// Implements PartialOrd for LeaderId to enforce the standard Raft behavior of at most one leader
13+
/// per term.
14+
///
15+
/// In standard Raft, each term can have at most one leader. This is enforced by making leader IDs
16+
/// with the same term incomparable (returning None), unless they refer to the same node.
17+
///
18+
/// This differs from the [`PartialOrd`] default implementation which would allow multiple leaders
19+
/// in the same term by comparing node IDs.
20+
impl PartialOrd for pb::LeaderId {
21+
#[inline]
22+
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
23+
LeaderIdCompare::std(self, other)
24+
}
25+
}
26+
27+
impl fmt::Display for pb::LeaderId {
28+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
29+
write!(f, "T{}-N{}", self.term, self.node_id)
30+
}
31+
}
32+
33+
impl RaftLeaderId<TypeConfig> for pb::LeaderId {
34+
type Committed = u64;
35+
36+
fn new(term: u64, node_id: u64) -> Self {
37+
Self { term, node_id }
38+
}
39+
40+
fn term(&self) -> u64 {
41+
self.term
42+
}
43+
44+
fn node_id_ref(&self) -> Option<&u64> {
45+
Some(&self.node_id)
46+
}
47+
48+
fn to_committed(&self) -> Self::Committed {
49+
self.term
50+
}
51+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
//! Implements traits for protobuf types
2+
3+
mod impl_leader_id;

examples/utils/declare_types.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ pub type Entry = openraft::Entry<TypeConfig>;
1010
pub type EntryPayload = openraft::EntryPayload<TypeConfig>;
1111
pub type StoredMembership = openraft::StoredMembership<TypeConfig>;
1212

13+
pub type Node = <TypeConfig as openraft::RaftTypeConfig>::Node;
14+
1315
pub type LogState = openraft::storage::LogState<TypeConfig>;
1416

1517
pub type SnapshotMeta = openraft::SnapshotMeta<TypeConfig>;

openraft/src/vote/leader_id/leader_id_adv.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
//! [`RaftLeaderId`] implementation that allows multiple leaders per term.
2+
13
use std::fmt;
24

3-
use crate::vote::RaftCommittedLeaderId;
45
use crate::vote::RaftLeaderId;
56
use crate::RaftTypeConfig;
67

@@ -42,7 +43,7 @@ where C: RaftTypeConfig
4243
pub type CommittedLeaderId<C> = LeaderId<C>;
4344

4445
impl<C> RaftLeaderId<C> for LeaderId<C>
45-
where C: RaftTypeConfig
46+
where C: RaftTypeConfig<LeaderId = Self>
4647
{
4748
type Committed = Self;
4849

@@ -63,8 +64,6 @@ where C: RaftTypeConfig
6364
}
6465
}
6566

66-
impl<C> RaftCommittedLeaderId<C> for LeaderId<C> where C: RaftTypeConfig {}
67-
6867
#[cfg(test)]
6968
mod tests {
7069
use super::LeaderId;
@@ -89,7 +88,7 @@ mod tests {
8988
}
9089

9190
#[test]
92-
fn test_leader_id_partial_order() -> anyhow::Result<()> {
91+
fn test_adv_leader_id_partial_order() -> anyhow::Result<()> {
9392
#[allow(clippy::redundant_closure)]
9493
let lid = |term, node_id| LeaderId::<UTConfig>::new(term, node_id);
9594

0 commit comments

Comments
 (0)