From 3e3224f0a248da67250892df10f2f92251424cd6 Mon Sep 17 00:00:00 2001
From: Andi <andi8647@gmail.com>
Date: Fri, 16 Feb 2024 12:41:15 +0100
Subject: [PATCH] Forbid having multiple mains in the cluster (#1727)

---
 src/coordination/coordinator_instance.cpp         |  4 ++++
 .../register_main_replica_coordinator_status.hpp  |  1 +
 src/query/interpreter.cpp                         |  2 ++
 .../single_coordinator.py                         | 15 +++++++++++++++
 4 files changed, 22 insertions(+)

diff --git a/src/coordination/coordinator_instance.cpp b/src/coordination/coordinator_instance.cpp
index b8afbb6a3..1bbcf4f8f 100644
--- a/src/coordination/coordinator_instance.cpp
+++ b/src/coordination/coordinator_instance.cpp
@@ -206,6 +206,10 @@ auto CoordinatorInstance::SetReplicationInstanceToMain(std::string instance_name
     -> SetInstanceToMainCoordinatorStatus {
   auto lock = std::lock_guard{coord_instance_lock_};
 
+  if (std::ranges::any_of(repl_instances_, &ReplicationInstance::IsMain)) {
+    return SetInstanceToMainCoordinatorStatus::MAIN_ALREADY_EXISTS;
+  }
+
   auto const is_new_main = [&instance_name](ReplicationInstance const &instance) {
     return instance.InstanceName() == instance_name;
   };
diff --git a/src/coordination/include/coordination/register_main_replica_coordinator_status.hpp b/src/coordination/include/coordination/register_main_replica_coordinator_status.hpp
index 5fcb93921..3aa7e3ca1 100644
--- a/src/coordination/include/coordination/register_main_replica_coordinator_status.hpp
+++ b/src/coordination/include/coordination/register_main_replica_coordinator_status.hpp
@@ -39,6 +39,7 @@ enum class UnregisterInstanceCoordinatorStatus : uint8_t {
 
 enum class SetInstanceToMainCoordinatorStatus : uint8_t {
   NO_INSTANCE_WITH_NAME,
+  MAIN_ALREADY_EXISTS,
   NOT_COORDINATOR,
   SUCCESS,
   COULD_NOT_PROMOTE_TO_MAIN,
diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp
index f0fed2357..c7ccbb1ef 100644
--- a/src/query/interpreter.cpp
+++ b/src/query/interpreter.cpp
@@ -563,6 +563,8 @@ class CoordQueryHandler final : public query::CoordinatorQueryHandler {
       using enum memgraph::coordination::SetInstanceToMainCoordinatorStatus;
       case NO_INSTANCE_WITH_NAME:
         throw QueryRuntimeException("No instance with such name!");
+      case MAIN_ALREADY_EXISTS:
+        throw QueryRuntimeException("Couldn't set instance to main since there is already a main instance in cluster!");
       case NOT_COORDINATOR:
         throw QueryRuntimeException("SET INSTANCE TO MAIN query can only be run on a coordinator!");
       case COULD_NOT_PROMOTE_TO_MAIN:
diff --git a/tests/e2e/high_availability_experimental/single_coordinator.py b/tests/e2e/high_availability_experimental/single_coordinator.py
index d1df05b4f..8e620e7e4 100644
--- a/tests/e2e/high_availability_experimental/single_coordinator.py
+++ b/tests/e2e/high_availability_experimental/single_coordinator.py
@@ -515,5 +515,20 @@ def test_automatic_failover_main_back_as_main():
     mg_sleep_and_assert([("main",)], retrieve_data_show_repl_role_instance3)
 
 
+def test_disable_multiple_mains():
+    safe_execute(shutil.rmtree, TEMP_DIR)
+    interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
+
+    coord_cursor = connect(host="localhost", port=7690).cursor()
+
+    try:
+        execute_and_fetch_all(
+            coord_cursor,
+            "SET INSTANCE instance_1 TO MAIN;",
+        )
+    except Exception as e:
+        assert str(e) == "Couldn't set instance to main since there is already a main instance in cluster!"
+
+
 if __name__ == "__main__":
     sys.exit(pytest.main([__file__, "-rA"]))