From d87dd4cabff6ef5ec0af866da96c2eb998427ff0 Mon Sep 17 00:00:00 2001
From: VS <v@s>
Date: Fri, 27 May 2022 17:29:12 +0300
Subject: [PATCH] fix: represent node leader_id as Option

---
 src/discovery.rs     | 19 +++++++++++++------
 src/main.rs          | 36 ++++++++++++++++++++----------------
 src/traft/node.rs    | 15 +++++++++++----
 src/traft/storage.rs |  5 +++--
 4 files changed, 47 insertions(+), 28 deletions(-)

diff --git a/src/discovery.rs b/src/discovery.rs
index 54d945782a..d023f89b78 100644
--- a/src/discovery.rs
+++ b/src/discovery.rs
@@ -203,12 +203,19 @@ fn proc_discover<'a>(
 ) -> Result<Cow<'a, Response>, Box<dyn StdError>> {
     if let Ok(node) = traft::node::global() {
         let status = node.status();
-        let leader = traft::Storage::peer_by_raft_id(status.leader_id)?
-            .ok_or("leader id is present, but it's address is unknown")?;
-        Ok(Cow::Owned(Response::Done(Role::new(
-            leader.peer_address,
-            status.am_leader(),
-        ))))
+        match status.leader_id {
+            Some(leader_id) => {
+                let leader = traft::Storage::peer_by_raft_id(leader_id)?.ok_or(format!(
+                    "leader_id is present ({}) but it's address is unknown for node {}",
+                    leader_id, status.id
+                ))?;
+                Ok(Cow::Owned(Response::Done(Role::new(
+                    leader.peer_address,
+                    status.am_leader(),
+                ))))
+            }
+            None => return Err(format!("leader_id is unknown for node {}", status.id).into()),
+        }
     } else {
         let discovery = discovery.as_mut().ok_or("discovery uninitialized")?;
         Ok(Cow::Borrowed(discovery.handle_request(request, request_to)))
diff --git a/src/main.rs b/src/main.rs
index 367e35d4ed..356cd60ba2 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -561,7 +561,7 @@ fn postjoin(args: &args::Run) {
         ..tarantool::cfg().unwrap()
     });
 
-    while node.status().leader_id == 0 {
+    while node.status().leader_id == None {
         node.wait_status();
     }
 
@@ -594,22 +594,26 @@ fn postjoin(args: &args::Run) {
             advertise_address: args.advertise_address(),
         };
 
-        let leader_id = node.status().leader_id;
-        let leader = traft::Storage::peer_by_raft_id(leader_id).unwrap().unwrap();
-
-        use traft::node::raft_join;
-        let fn_name = stringify_cfunc!(raft_join);
-        let now = Instant::now();
-        match tarantool::net_box_call(&leader.peer_address, fn_name, &req, timeout) {
-            Err(e) => {
-                tlog!(Error, "failed to promote myself: {e}");
-                fiber::sleep(timeout.saturating_sub(now.elapsed()));
-                continue;
-            }
-            Ok(traft::JoinResponse { .. }) => {
-                break;
+        match node.status().leader_id {
+            None => (),
+            Some(leader_id) => {
+                let leader = traft::Storage::peer_by_raft_id(leader_id).unwrap().unwrap();
+
+                use traft::node::raft_join;
+                let fn_name = stringify_cfunc!(raft_join);
+                let now = Instant::now();
+                match tarantool::net_box_call(&leader.peer_address, fn_name, &req, timeout) {
+                    Err(e) => {
+                        tlog!(Error, "failed to promote myself: {e}");
+                        fiber::sleep(timeout.saturating_sub(now.elapsed()));
+                        continue;
+                    }
+                    Ok(traft::JoinResponse { .. }) => {
+                        break;
+                    }
+                };
             }
-        };
+        }
     }
 
     node.mark_as_ready();
diff --git a/src/traft/node.rs b/src/traft/node.rs
index 55a8d2ba1a..0465b51716 100644
--- a/src/traft/node.rs
+++ b/src/traft/node.rs
@@ -8,6 +8,7 @@
 use ::raft::prelude as raft;
 use ::raft::Error as RaftError;
 use ::raft::StateRole as RaftStateRole;
+use ::raft::INVALID_ID;
 use ::tarantool::error::TransactionError;
 use ::tarantool::fiber;
 use ::tarantool::proc;
@@ -54,14 +55,17 @@ pub struct Status {
     /// `raft_id` of the current instance
     pub id: u64,
     /// `raft_id` of the leader instance
-    pub leader_id: u64,
+    pub leader_id: Option<u64>,
     pub raft_state: String,
     pub is_ready: bool,
 }
 
 impl Status {
     pub fn am_leader(&self) -> bool {
-        self.id == self.leader_id
+        match self.leader_id {
+            Some(id) => self.id == id,
+            None => false,
+        }
     }
 }
 
@@ -118,7 +122,7 @@ impl Node {
         let raw_node = RawNode::new(cfg, Storage, &tlog::root())?;
         let status = Rc::new(RefCell::new(Status {
             id: cfg.id,
-            leader_id: 0,
+            leader_id: None,
             raft_state: "Follower".into(),
             is_ready: false,
         }));
@@ -567,7 +571,10 @@ fn raft_main_loop(
 
             if let Some(ss) = ready.ss() {
                 let mut status = status.borrow_mut();
-                status.leader_id = ss.leader_id;
+                status.leader_id = match ss.leader_id {
+                    INVALID_ID => None,
+                    id => Some(id),
+                };
                 status.raft_state = format!("{:?}", ss.raft_state);
                 status_cond.broadcast();
             }
diff --git a/src/traft/storage.rs b/src/traft/storage.rs
index d854310d70..b935ec986c 100644
--- a/src/traft/storage.rs
+++ b/src/traft/storage.rs
@@ -3,6 +3,7 @@ use std::convert::TryFrom;
 use ::raft::prelude as raft;
 use ::raft::Error as RaftError;
 use ::raft::StorageError;
+use ::raft::INVALID_ID;
 use ::tarantool::index::IteratorType;
 use ::tarantool::space::Space;
 use ::tarantool::tuple::Tuple;
@@ -130,8 +131,8 @@ impl Storage {
     }
 
     pub fn peer_by_raft_id(raft_id: u64) -> Result<Option<traft::Peer>, StorageError> {
-        if raft_id == 0 {
-            return Ok(None);
+        if raft_id == INVALID_ID {
+            unreachable!("peer_by_raft_id called with invalid id ({})", INVALID_ID);
         }
 
         let tuple = Storage::space(RAFT_GROUP)?
-- 
GitLab