From 5f245144740322201fd0185ef9b6eb83299555d3 Mon Sep 17 00:00:00 2001
From: Georgy Moshkin <gmoshkin@picodata.io>
Date: Wed, 4 Sep 2024 15:48:10 +0300
Subject: [PATCH] fix: fail to start with an explanation when current instance
 is expelled

---
 src/lib.rs                 |  5 +++++
 test/conftest.py           |  5 +++--
 test/int/test_expelling.py | 10 +++++++++-
 3 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/src/lib.rs b/src/lib.rs
index 1fac71cb85..214e1aec21 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1007,6 +1007,11 @@ fn postjoin(
             fiber::sleep(timeout);
             continue;
         };
+
+        if has_states!(instance, Expelled -> *) {
+            return Err(Error::Expelled);
+        }
+
         let cluster_id = raft_storage
             .cluster_id()
             .expect("storage should never fail");
diff --git a/test/conftest.py b/test/conftest.py
index c87dea6944..0b68a6b879 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -941,7 +941,7 @@ class Instance:
             ).start()
 
     def fail_to_start(self, timeout: int = 10):
-        assert self.process is None
+        assert self.process is None, "process is already running"
         self.start()
         assert self.process
         try:
@@ -1237,7 +1237,8 @@ class Instance:
             self.check_process_alive()
             assert False
         except ProcessDead:
-            pass
+            # Make it so we can call Instance.start later
+            self.process = None
 
     def wait_online(
         self, timeout: int | float = 30, rps: int | float = 5, expected_incarnation=None
diff --git a/test/int/test_expelling.py b/test/int/test_expelling.py
index 7f02364c25..ab99b006c2 100644
--- a/test/int/test_expelling.py
+++ b/test/int/test_expelling.py
@@ -1,5 +1,5 @@
 import pytest
-from conftest import Cluster, Instance, Retriable
+from conftest import Cluster, Instance, Retriable, log_crawler
 
 
 @pytest.fixture
@@ -40,6 +40,10 @@ def test_expel_follower(cluster3: Cluster):
     # assert i3.process
     Retriable(timeout=10).call(i3.assert_process_dead)
 
+    lc = log_crawler(i3, "current instance is expelled from the cluster")
+    i3.fail_to_start()
+    assert lc.matched
+
 
 def test_expel_leader(cluster3: Cluster):
     # Scenario: expel a Leader instance by command to itself
@@ -61,6 +65,10 @@ def test_expel_leader(cluster3: Cluster):
     # assert i1.process
     Retriable(timeout=10).call(i1.assert_process_dead)
 
+    lc = log_crawler(i1, "current instance is expelled from the cluster")
+    i1.fail_to_start()
+    assert lc.matched
+
 
 def test_expel_by_follower(cluster3: Cluster):
     # Scenario: expel an instance by command to a Follower
-- 
GitLab