Repository: memgraph/memgraph
Pull request #2812: Improve the failover process in HA when using multitenancy
Author: login: as51340
Labels: name: feature
description: feature
name: Capability - high-availability
name: Docs needed
description: Docs needed
Created: 2025-03-20T20:14:45Z | Merged: 2025-04-11T11:00:01Z

The failover in the MT code now has a different logic. We 1st try to find the instance which has most newest databases. If there are multiple instances that are newest on the same number of databases, the sum of timestamps of all databases decides. The instance which has larger sum it is assumed to be newer. 


Diff:
diff --git a/src/coordination/coordinator_instance.cpp b/src/coordination/coordinator_instance.cpp
index 101131b94d8..f847c3d0d9a 100644
--- a/src/coordination/coordinator_instance.cpp
+++ b/src/coordination/coordinator_instance.cpp
@@ -39,15 +39,14 @@
 #include "coordination/register_main_replica_coordinator_status.hpp"
 #include "coordination/replication_instance_client.hpp"
 #include "coordination/replication_instance_connector.hpp"
-#include "dbms/constants.hpp"
 #include "replication_coordination_glue/role.hpp"
 #include "utils/event_counter.hpp"
-#include "utils/event_histogram.hpp"
 #include "utils/exponential_backoff.hpp"
 #include "utils/functional.hpp"
 #include "utils/logging.hpp"
 #include "utils/metrics_timer.hpp"
 
+#include <fmt/ranges.h>
 #include <spdlog/spdlog.h>
 #include <range/v3/range/conversion.hpp>
 #include <range/v3/view/filter.hpp>
@@ -496,7 +495,7 @@ auto CoordinatorInstance::TryFailover() const -> FailoverStatus {
   // TODO: (andi) Remove has replica state.
   spdlog::trace("Trying failover in thread {}.", std::this_thread::get_id());
 
-  auto const maybe_most_up_to_date_instance = GetMostUpToDateInstanceFromHistories(repl_instances_);
+  auto const maybe_most_up_to_date_instance = GetInstanceForFailover();
   if (!maybe_most_up_to_date_instance.has_value()) {
     spdlog::error("Couldn't choose instance for failover, check logs for more details.");
     metrics::IncrementCounter(metrics::NoAliveInstanceFailedFailovers);
@@ -1033,104 +1032,143 @@ void CoordinatorInstance::InstanceFailCallback(std::string_view instance_name,
   spdlog::trace("Instance {} finished fail callback.", instance_name);
 }
 
-auto CoordinatorInstance::ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
-    -> std::optional<NewMainRes> {
+auto CoordinatorInstance::ChooseMostUpToDateInstance(
+    std::map<std::string, replication_coordination_glue::InstanceInfo> const &instances_info)
+    -> std::optional<std::string> {
   utils::MetricsTimer const timer{metrics::ChooseMostUpToDateInstance_us};
 
-  std::optional<NewMainRes> new_main_res;
-
-  for (auto const &instance_res_pair : instance_database_histories) {
-    const auto &[instance_name, instance_db_histories] = instance_res_pair;
-
-    // Find default db for instance and its history
-    auto default_db_history_data = std::ranges::find_if(
-        instance_db_histories,
-        [default_db = memgraph::dbms::kDefaultDB](auto const &db_history) { return db_history.name == default_db; });
+  // Find the instance with the largest system committed timestamp
+  auto const largest_sys_ts_instance = std::ranges::max_element(instances_info, [](auto const &lhs, auto const &rhs) {
+    return lhs.second.last_committed_system_timestamp < rhs.second.last_committed_system_timestamp;
+  });
 
-    std::ranges::for_each(instance_db_histories, [&instance_name](auto &&db_history) {
-      spdlog::debug("Instance {}: db_history_name {}, default db {}.", instance_name, db_history.name,
-                    dbms::kDefaultDB);
-    });
+  if (largest_sys_ts_instance == instances_info.end()) [[unlikely]] {
+    spdlog::error("Couldn't retrieve history for any instance. Failover won't be performed.");
+    return std::nullopt;
+  }
 
-    MG_ASSERT(default_db_history_data != instance_db_histories.end(), "No history for instance");
+  spdlog::trace("The instance with the newest system committed timestamp is {}", largest_sys_ts_instance->first);
 
-    const auto &instance_default_db_history = default_db_history_data->history;
+  // db_uuid -> vector<std::pair<instance_name, latest_durable_timestamp>>
+  std::map<std::string, std::vector<std::pair<std::string, uint64_t>>> dbs_info;
 
-    std::ranges::for_each(instance_default_db_history | ranges::views::reverse,
-                          [&instance_name](auto &&epoch_history_it) {
-                            spdlog::debug("Instance {}: epoch {}, last_durable_timestamp: {}.", instance_name,
-                                          std::get<0>(epoch_history_it), std::get<1>(epoch_history_it));
-                          });
+  // Use only DBs from the instance with the largest committed system timestamps
+  for (auto const &largest_sys_ts_instance_dbs_info = largest_sys_ts_instance->second.dbs_info;
+       auto const &[db_uuid, _] : largest_sys_ts_instance_dbs_info) {
+    dbs_info.try_emplace(db_uuid);
+  }
 
-    if (!new_main_res) {
-      const auto &[epoch, latest_commit_timestamp] = *instance_default_db_history.crbegin();
-      new_main_res = std::make_optional<NewMainRes>({instance_name, epoch, latest_commit_timestamp});
-      spdlog::debug("Currently the most up to date instance is {} with epoch {} and latest commit timestamp {}.",
-                    instance_name, epoch, latest_commit_timestamp);
-      continue;
+  // Pre-process received failover data
+  for (auto const &[instance_name, instance_info] : instances_info) {
+    for (auto const &[db_uuid, ldt] : instance_info.dbs_info) {
+      if (auto db_it = dbs_info.find(db_uuid); db_it != dbs_info.end()) {
+        db_it->second.emplace_back(instance_name, ldt);
+      }
     }
+  }
 
-    bool found_same_point{false};
-    std::string last_most_up_to_date_epoch{new_main_res->latest_epoch};
-    for (auto [epoch, timestamp] : ranges::reverse_view(instance_default_db_history)) {
-      if (new_main_res->latest_commit_timestamp < timestamp) {
-        new_main_res = std::make_optional<NewMainRes>({instance_name, epoch, timestamp});
-        spdlog::trace("Found the new most up to date instance {} with epoch {} and {} latest commit timestamp",
-                      instance_name, epoch, timestamp);
+  // Instance name -> cnt on how many DBs is instance the newest
+  std::map<std::string, uint64_t> total_instances_counter;
+  // Instance name -> sum of timestamps on all DBs
+  std::map<std::string, uint64_t> total_instances_sum;
+
+  auto const find_newest_instances_for_db =
+      [&total_instances_sum](std::vector<std::pair<std::string, uint64_t>> const &db_info)
+      -> std::pair<std::vector<std::string>, uint64_t> {
+    // There could be multiple instances with the same timestamp, that's why we are returning vector
+    std::vector<std::string> newest_db_instances;
+    uint64_t curr_ldt{0};
+    // Loop through instances
+    for (auto const &[instance_name, latest_durable_timestamp] : db_info) {
+      // Sum timestamps for each instance
+      if (auto [instance_it, inserted] = total_instances_sum.try_emplace(instance_name, latest_durable_timestamp);
+          !inserted) {
+        instance_it->second += latest_durable_timestamp;
       }
 
-      // we found point at which they were same
-      if (epoch == last_most_up_to_date_epoch) {
-        found_same_point = true;
-        break;
+      // If no new instance exists, or instance has newer timestamp -> do the update
+      if (newest_db_instances.empty() || curr_ldt < latest_durable_timestamp) {
+        newest_db_instances.clear();
+        newest_db_instances.emplace_back(instance_name);
+        curr_ldt = latest_durable_timestamp;
+      } else if (curr_ldt == latest_durable_timestamp) {
+        // Otherwise, we have more instances with the max timestamp, add it to the vector
+        newest_db_instances.emplace_back(instance_name);
+      }
+    }
+    return {newest_db_instances, curr_ldt};
+  };
+
+  auto const update_instances_counter = [&total_instances_counter](
+                                            std::vector<std::string> const &newest_db_instances) {
+    for (auto const &db_newest_instance_name : newest_db_instances) {
+      if (auto [instance_it, inserted] = total_instances_counter.try_emplace(db_newest_instance_name, 1); !inserted) {
+        instance_it->second++;
       }
     }
+  };
 
-    if (!found_same_point) {
-      spdlog::error("Didn't find same history epoch {} for instance {} and instance {}", last_most_up_to_date_epoch,
-                    new_main_res->most_up_to_date_instance, instance_name);
+  // Process each DB
+  for (auto const &[db_uuid, db_info] : dbs_info) {
+    spdlog::trace("Trying to find newest instance for db with uuid {}", db_uuid);
+    if (auto const [newest_db_instances, curr_ldt] = find_newest_instances_for_db(db_info);
+        newest_db_instances.empty()) {
+      spdlog::error("Couldn't find newest instance for db with uuid {}", db_uuid);
+    } else {
+      spdlog::info("The latest durable timestamp is {} for db with uuid {}. The following instances have it {}",
+                   curr_ldt, db_uuid, fmt::join(newest_db_instances, ", "));
+      update_instances_counter(newest_db_instances);
     }
   }
 
-  return new_main_res;
+  std::optional<std::pair<std::string, uint64_t>> newest_instance;
+  for (auto const &[instance_name, cnt_newest_dbs] : total_instances_counter) {
+    // If better on more DBs, update currently the best
+    if (!newest_instance.has_value() || newest_instance->second < cnt_newest_dbs) {
+      newest_instance.emplace(instance_name, cnt_newest_dbs);
+    } else if (newest_instance->second == cnt_newest_dbs) {
+      // Instances are the best over the same number of instances, let the sum of timestamps decide
+      spdlog::info("Instances {} and {} are most up to date on the same number of instances.", instance_name,
+                   newest_instance->first);
+      if (total_instances_sum[instance_name] > total_instances_sum[newest_instance->first]) {
+        spdlog::info("Instance {} has the total sum of timestamps larger than {}. It will be considered newer.",
+                     instance_name, newest_instance->first);
+        newest_instance.emplace(instance_name, cnt_newest_dbs);
+      }
+    }
+  }
+  if (newest_instance.has_value()) {
+    spdlog::info("The newest instance is {}", newest_instance->first);
+    return newest_instance->first;
+  }
+  return std::nullopt;
 }
 
 auto CoordinatorInstance::GetRoutingTable() const -> RoutingTable { return raft_state_->GetRoutingTable(); }
 
-auto CoordinatorInstance::GetMostUpToDateInstanceFromHistories(const std::list<ReplicationInstanceConnector> &instances)
-    -> std::optional<std::string> {
+auto CoordinatorInstance::GetInstanceForFailover() const -> std::optional<std::string> {
   utils::MetricsTimer const timer{metrics::GetHistories_us};
 
-  if (instances.empty()) {
+  if (repl_instances_.empty()) {
     return std::nullopt;
   }
 
-  spdlog::trace("{} data instances can become new main.", instances.size());
-
-  auto const get_ts = [](auto const &instance) {
-    spdlog::trace("Sending get db histories to {}.", instance.InstanceName());
+  auto const get_instance_info = [](auto const &instance) {
     return instance.GetClient().SendGetDatabaseHistoriesRpc();
   };
 
-  std::vector<std::pair<std::string, replication_coordination_glue::DatabaseHistories>> instance_db_histories;
+  // instance_name -> InstanceInfo
+  std::map<std::string, replication_coordination_glue::InstanceInfo> instances_info;
 
-  for (auto const &instance : instances) {
-    if (auto maybe_history = get_ts(instance); maybe_history.has_value()) {
-      spdlog::trace("Received history for instance {}.", instance.InstanceName());
-      instance_db_histories.emplace_back(instance.InstanceName(), *maybe_history);
+  for (auto const &instance : repl_instances_) {
+    if (auto maybe_instance_info = get_instance_info(instance); maybe_instance_info.has_value()) {
+      instances_info.emplace(instance.InstanceName(), std::move(*maybe_instance_info));
     } else {
-      spdlog::trace("Couldn't receive history for instance {}.", instance.InstanceName());
+      spdlog::error("Couldn't retrieve failover info for instance {}", instance.InstanceName());
     }
   }
 
-  if (auto maybe_newest_instance = CoordinatorInstance::ChooseMostUpToDateInstance(instance_db_histories);
-      maybe_newest_instance.has_value()) {
-    auto const [most_up_to_date_instance, latest_epoch, latest_commit_timestamp] = *maybe_newest_instance;
-    spdlog::trace("The most up to date instance is {} with epoch {} and {} latest commit timestamp.",
-                  most_up_to_date_instance, latest_epoch, latest_commit_timestamp);  // NOLINT
-    return most_up_to_date_instance;
-  }
-  return std::nullopt;
+  return ChooseMostUpToDateInstance(instances_info);
 }
 
 }  // namespace memgraph::coordination
diff --git a/src/coordination/coordinator_rpc.cpp b/src/coordination/coordinator_rpc.cpp
index 894ac8e3961..8eb03c2691d 100644
--- a/src/coordination/coordinator_rpc.cpp
+++ b/src/coordination/coordinator_rpc.cpp
@@ -264,11 +264,11 @@ void Load(memgraph::coordination::EnableWritingOnMainRes *self, memgraph::slk::R
 // GetDatabaseHistoriesRpc
 
 void Save(const memgraph::coordination::GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder) {
-  memgraph::slk::Save(self.database_histories, builder);
+  memgraph::slk::Save(self.instance_info, builder);
 }
 
 void Load(memgraph::coordination::GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader) {
-  memgraph::slk::Load(&self->database_histories, reader);
+  memgraph::slk::Load(&self->instance_info, reader);
 }
 
 // RegisterReplicaOnMainRpc
diff --git a/src/coordination/include/coordination/coordinator_instance.hpp b/src/coordination/include/coordination/coordinator_instance.hpp
index b4dca455019..500a0827f35 100644
--- a/src/coordination/include/coordination/coordinator_instance.hpp
+++ b/src/coordination/include/coordination/coordinator_instance.hpp
@@ -36,9 +36,8 @@
 namespace memgraph::coordination {
 
 struct NewMainRes {
-  std::string most_up_to_date_instance;
-  std::string latest_epoch;
-  uint64_t latest_commit_timestamp;
+  std::string instance_name;
+  uint64_t latest_durable_timestamp;
 };
 
 enum class FailoverStatus : uint8_t {
@@ -49,7 +48,7 @@ enum class FailoverStatus : uint8_t {
 
 enum class CoordinatorStatus : uint8_t { FOLLOWER, LEADER_NOT_READY, LEADER_READY };
 
-using InstanceNameDbHistories = std::pair<std::string, replication_coordination_glue::DatabaseHistories>;
+using InstanceNameDbHistories = std::pair<std::string, replication_coordination_glue::InstanceInfo>;
 
 class CoordinatorInstance {
  public:
@@ -98,11 +97,11 @@ class CoordinatorInstance {
 
   auto GetRoutingTable() const -> RoutingTable;
 
-  static auto GetMostUpToDateInstanceFromHistories(const std::list<ReplicationInstanceConnector> &instances)
-      -> std::optional<std::string>;
+  auto GetInstanceForFailover() const -> std::optional<std::string>;
 
-  static auto ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
-      -> std::optional<NewMainRes>;
+  static auto ChooseMostUpToDateInstance(
+      std::map<std::string, replication_coordination_glue::InstanceInfo> const &instances_info)
+      -> std::optional<std::string>;
 
   auto GetLeaderCoordinatorData() const -> std::optional<LeaderCoordinatorData>;
 
diff --git a/src/coordination/include/coordination/coordinator_rpc.hpp b/src/coordination/include/coordination/coordinator_rpc.hpp
index 124980e1dbc..7ae9e4e3b2b 100644
--- a/src/coordination/include/coordination/coordinator_rpc.hpp
+++ b/src/coordination/include/coordination/coordinator_rpc.hpp
@@ -19,6 +19,7 @@
 #include "coordination/instance_status.hpp"
 #include "replication_coordination_glue/common.hpp"
 #include "rpc/messages.hpp"
+#include "utils/typeinfo.hpp"
 
 namespace memgraph::coordination {
 
@@ -184,11 +185,11 @@ struct GetDatabaseHistoriesRes {
   static void Load(GetDatabaseHistoriesRes *self, memgraph::slk::Reader *reader);
   static void Save(const GetDatabaseHistoriesRes &self, memgraph::slk::Builder *builder);
 
-  explicit GetDatabaseHistoriesRes(replication_coordination_glue::DatabaseHistories db_histories)
-      : database_histories(std::move(db_histories)) {}
+  explicit GetDatabaseHistoriesRes(replication_coordination_glue::InstanceInfo instance_info)
+      : instance_info(std::move(instance_info)) {}
   GetDatabaseHistoriesRes() = default;
 
-  replication_coordination_glue::DatabaseHistories database_histories;
+  replication_coordination_glue::InstanceInfo instance_info;
 };
 
 using GetDatabaseHistoriesRpc = rpc::RequestResponse<GetDatabaseHistoriesReq, GetDatabaseHistoriesRes>;
diff --git a/src/coordination/include/coordination/coordinator_slk.hpp b/src/coordination/include/coordination/coordinator_slk.hpp
index 6cbc3bcdfca..caa14fa4a5e 100644
--- a/src/coordination/include/coordination/coordinator_slk.hpp
+++ b/src/coordination/include/coordination/coordinator_slk.hpp
@@ -1,4 +1,4 @@
-// Copyright 2024 Memgraph Ltd.
+// Copyright 2025 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -47,16 +47,24 @@ inline void Load(ReplicationClientInfo *obj, Reader *reader) {
   Load(&obj->replication_server, reader);
 }
 
-inline void Save(const replication_coordination_glue::DatabaseHistory &obj, Builder *builder) {
+inline void Save(const replication_coordination_glue::InstanceInfo &obj, Builder *builder) {
+  Save(obj.last_committed_system_timestamp, builder);
+  Save(obj.dbs_info, builder);
+}
+
+inline void Load(replication_coordination_glue::InstanceInfo *obj, Reader *reader) {
+  Load(&obj->last_committed_system_timestamp, reader);
+  Load(&obj->dbs_info, reader);
+}
+
+inline void Save(const replication_coordination_glue::InstanceDBInfo &obj, Builder *builder) {
   Save(obj.db_uuid, builder);
-  Save(obj.history, builder);
-  Save(obj.name, builder);
+  Save(obj.latest_durable_timestamp, builder);
 }
 
-inline void Load(replication_coordination_glue::DatabaseHistory *obj, Reader *reader) {
+inline void Load(replication_coordination_glue::InstanceDBInfo *obj, Reader *reader) {
   Load(&obj->db_uuid, reader);
-  Load(&obj->history, reader);
-  Load(&obj->name, reader);
+  Load(&obj->latest_durable_timestamp, reader);
 }
 
 inline void Save(const InstanceStatus &obj, Builder *builder) {
diff --git a/src/coordination/include/coordination/replication_instance_client.hpp b/src/coordination/include/coordination/replication_instance_client.hpp
index 6b5394f5e93..dd7a6540b1c 100644
--- a/src/coordination/include/coordination/replication_instance_client.hpp
+++ b/src/coordination/include/coordination/replication_instance_client.hpp
@@ -75,7 +75,7 @@ class ReplicationInstanceClient {
   auto ManagementSocketAddress() const -> std::string;
   auto ReplicationSocketAddress() const -> std::string;
 
-  auto SendGetDatabaseHistoriesRpc() const -> std::optional<replication_coordination_glue::DatabaseHistories>;
+  auto SendGetDatabaseHistoriesRpc() const -> std::optional<replication_coordination_glue::InstanceInfo>;
   auto GetReplicationClientInfo() const -> ReplicationClientInfo;
   auto RpcClient() const -> rpc::Client & { return rpc_client_; }
 
diff --git a/src/coordination/replication_instance_client.cpp b/src/coordination/replication_instance_client.cpp
index d9879bc3ce1..f53f5ac64f4 100644
--- a/src/coordination/replication_instance_client.cpp
+++ b/src/coordination/replication_instance_client.cpp
@@ -100,13 +100,13 @@ auto ReplicationInstanceClient::SendStateCheckRpc() const -> std::optional<Insta
 }
 
 auto ReplicationInstanceClient::SendGetDatabaseHistoriesRpc() const
-    -> std::optional<replication_coordination_glue::DatabaseHistories> {
+    -> std::optional<replication_coordination_glue::InstanceInfo> {
   try {
     utils::MetricsTimer const timer{metrics::GetDatabaseHistoriesRpc_us};
     auto stream{rpc_client_.Stream<GetDatabaseHistoriesRpc>()};
     auto res = stream.AwaitResponse();
     metrics::IncrementCounter(metrics::GetDatabaseHistoriesRpcSuccess);
-    return res.database_histories;
+    return res.instance_info;
 
   } catch (const rpc::RpcFailedException &e) {
     spdlog::error("Failed to receive response to GetDatabaseHistoriesReq. Error occurred: {}", e.what());
diff --git a/src/dbms/replication_handlers.cpp b/src/dbms/replication_handlers.cpp
index 762867ab909..9b3b5d4f201 100644
--- a/src/dbms/replication_handlers.cpp
+++ b/src/dbms/replication_handlers.cpp
@@ -22,10 +22,10 @@ namespace memgraph::dbms {
 
 #ifdef MG_ENTERPRISE
 
-void CreateDatabaseHandler(memgraph::system::ReplicaHandlerAccessToState &system_state_access,
+void CreateDatabaseHandler(system::ReplicaHandlerAccessToState &system_state_access,
                            const std::optional<utils::UUID> &current_main_uuid, DbmsHandler &dbms_handler,
                            slk::Reader *req_reader, slk::Builder *res_builder) {
-  using memgraph::storage::replication::CreateDatabaseRes;
+  using storage::replication::CreateDatabaseRes;
   CreateDatabaseRes res(CreateDatabaseRes::Result::FAILURE);
 
   // Ignore if no license
@@ -37,17 +37,17 @@ void CreateDatabaseHandler(memgraph::system::ReplicaHandlerAccessToState &system
     return;
   }
 
-  memgraph::storage::replication::CreateDatabaseReq req;
-  memgraph::slk::Load(&req, req_reader);
+  storage::replication::CreateDatabaseReq req;
+  Load(&req, req_reader);
 
   if (!current_main_uuid.has_value() || req.main_uuid != current_main_uuid) [[unlikely]] {
-    LogWrongMain(current_main_uuid, req.main_uuid, memgraph::storage::replication::CreateDatabaseReq::kType.name);
+    LogWrongMain(current_main_uuid, req.main_uuid, storage::replication::CreateDatabaseReq::kType.name);
     rpc::SendFinalResponse(res, res_builder);
     return;
   }
 
   // Note: No need to check epoch, recovery mechanism is done by a full uptodate snapshot
-  //       of the set of databases. Hence no history exists to maintain regarding epoch change.
+  //       of the set of databases. Hence, no history exists to maintain regarding epoch change.
   //       If MAIN has changed we need to check this new group_timestamp is consistent with
   //       what we have so far.
 
@@ -60,8 +60,7 @@ void CreateDatabaseHandler(memgraph::system::ReplicaHandlerAccessToState &system
 
   try {
     // Create new
-    auto new_db = dbms_handler.Update(req.config);
-    if (new_db.HasValue()) {
+    if (auto const new_db = dbms_handler.Update(req.config); new_db.HasValue()) {
       // Successfully create db
       system_state_access.SetLastCommitedTS(req.new_group_timestamp);
       res = CreateDatabaseRes(CreateDatabaseRes::Result::SUCCESS);
diff --git a/src/replication_coordination_glue/common.hpp b/src/replication_coordination_glue/common.hpp
index 97f31a9473a..a23d1a14ca6 100644
--- a/src/replication_coordination_glue/common.hpp
+++ b/src/replication_coordination_glue/common.hpp
@@ -1,4 +1,4 @@
-// Copyright 2024 Memgraph Ltd.
+// Copyright 2025 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,21 +11,18 @@
 
 #pragma once
 
-#include "rpc/client.hpp"
-#include "utils/uuid.hpp"
-
-#include "messages.hpp"
-#include "rpc/messages.hpp"
 #include "utils/uuid.hpp"
 
 namespace memgraph::replication_coordination_glue {
 
-struct DatabaseHistory {
-  memgraph::utils::UUID db_uuid;
-  std::vector<std::pair<std::string, uint64_t>> history;
-  std::string name;  // db name
+struct InstanceDBInfo {
+  std::string db_uuid;
+  uint64_t latest_durable_timestamp;
 };
 
-using DatabaseHistories = std::vector<DatabaseHistory>;
+struct InstanceInfo {
+  std::vector<InstanceDBInfo> dbs_info;
+  uint64_t last_committed_system_timestamp;
+};
 
 }  // namespace memgraph::replication_coordination_glue
diff --git a/src/replication_handler/include/replication_handler/replication_handler.hpp b/src/replication_handler/include/replication_handler/replication_handler.hpp
index a5dd1f0081d..02a18fe32b5 100644
--- a/src/replication_handler/include/replication_handler/replication_handler.hpp
+++ b/src/replication_handler/include/replication_handler/replication_handler.hpp
@@ -158,7 +158,9 @@ struct ReplicationHandler : public query::ReplicationQueryHandler {
   auto GetReplState() const { return repl_state_.ReadLock(); }
   auto GetReplState() { return repl_state_.Lock(); }
 
-  auto GetDatabasesHistories() const -> replication_coordination_glue::DatabaseHistories;
+#ifdef MG_ENTERPRISE
+  auto GetDatabasesHistories() const -> replication_coordination_glue::InstanceInfo;
+#endif
 
  private:
   void ClientsShutdown(auto &locked_repl_state) const {
diff --git a/src/replication_handler/replication_handler.cpp b/src/replication_handler/replication_handler.cpp
index 27302a6531c..fa08a9e49ad 100644
--- a/src/replication_handler/replication_handler.cpp
+++ b/src/replication_handler/replication_handler.cpp
@@ -363,22 +363,19 @@ auto ReplicationHandler::GetRole() const -> replication_coordination_glue::Repli
   return repl_state_.ReadLock()->GetRole();
 }
 
-auto ReplicationHandler::GetDatabasesHistories() const -> replication_coordination_glue::DatabaseHistories {
-  replication_coordination_glue::DatabaseHistories results;
+#ifdef MG_ENTERPRISE
+auto ReplicationHandler::GetDatabasesHistories() const -> replication_coordination_glue::InstanceInfo {
+  replication_coordination_glue::InstanceInfo results;
+  results.last_committed_system_timestamp = system_.LastCommittedSystemTimestamp();
   dbms_handler_.ForEach([&results](dbms::DatabaseAccess db_acc) {
-    auto &repl_storage_state = db_acc->storage()->repl_storage_state_;
-
-    std::vector<std::pair<std::string, uint64_t>> history = utils::fmap(repl_storage_state.history);
-
-    history.emplace_back(std::string(repl_storage_state.epoch_.id()),
-                         repl_storage_state.last_durable_timestamp_.load());
-    replication_coordination_glue::DatabaseHistory repl{
-        .db_uuid = utils::UUID{db_acc->storage()->uuid()}, .history = history, .name = std::string(db_acc->name())};
-    results.emplace_back(repl);
+    auto const &repl_storage_state = db_acc->storage()->repl_storage_state_;
+    results.dbs_info.emplace_back(std::string{db_acc->storage()->uuid()},
+                                  repl_storage_state.last_durable_timestamp_.load(std::memory_order_acquire));
   });
 
   return results;
 }
+#endif
 
 bool ReplicationHandler::IsMain() const { return repl_state_.ReadLock()->IsMain(); }
 
diff --git a/src/storage/v2/inmemory/storage.cpp b/src/storage/v2/inmemory/storage.cpp
index e4ca4551cf1..f6cec7696b5 100644
--- a/src/storage/v2/inmemory/storage.cpp
+++ b/src/storage/v2/inmemory/storage.cpp
@@ -796,6 +796,8 @@ utils::BasicResult<StorageManipulationError, void> InMemoryStorage::InMemoryAcce
           // Replica can only update the last durable timestamp with
           // the commits received from main.
           // Update the last durable timestamp
+          auto prev = mem_storage->repl_storage_state_.last_durable_timestamp_.load(std::memory_order_acquire);
+          DMG_ASSERT(durability_commit_timestamp >= prev, "LDT not monotonically increasing");
           mem_storage->repl_storage_state_.last_durable_timestamp_.store(durability_commit_timestamp);
         }
 
diff --git a/src/system/include/system/state.hpp b/src/system/include/system/state.hpp
index 4ef699c1e38..8b4159e9dab 100644
--- a/src/system/include/system/state.hpp
+++ b/src/system/include/system/state.hpp
@@ -1,4 +1,4 @@
-// Copyright 2024 Memgraph Ltd.
+// Copyright 2025 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -33,7 +33,7 @@ struct State {
     last_committed_system_timestamp_.store(timestamp);
   }
 
-  auto LastCommittedSystemTimestamp() -> uint64_t { return last_committed_system_timestamp_.load(); }
+  auto LastCommittedSystemTimestamp() const -> uint64_t { return last_committed_system_timestamp_.load(); }
 
  private:
   friend struct ReplicaHandlerAccessToState;
diff --git a/src/system/include/system/system.hpp b/src/system/include/system/system.hpp
index b9614223efd..7c48ccbdf8c 100644
--- a/src/system/include/system/system.hpp
+++ b/src/system/include/system/system.hpp
@@ -39,7 +39,7 @@ struct System {
 
   // TODO: this and LastCommittedSystemTimestamp maybe not needed
   auto GenTransactionGuard() -> TransactionGuard { return TransactionGuard{std::unique_lock{mtx_}}; }
-  auto LastCommittedSystemTimestamp() -> uint64_t { return state_.LastCommittedSystemTimestamp(); }
+  auto LastCommittedSystemTimestamp() const -> uint64_t { return state_.LastCommittedSystemTimestamp(); }
 
   auto CreateSystemStateAccess() -> ReplicaHandlerAccessToState { return ReplicaHandlerAccessToState{state_}; }
 
diff --git a/tests/e2e/high_availability/single_coordinator.py b/tests/e2e/high_availability/single_coordinator.py
index 42ebbd84e74..51356119532 100644
--- a/tests/e2e/high_availability/single_coordinator.py
+++ b/tests/e2e/high_availability/single_coordinator.py
@@ -748,9 +748,7 @@ def test_replication_correct_replica_chosen_up_to_date_data(data_recovery, test_
     # 8. Instance_2 commits and replicates data
     # 9. Instance_4 up, instance_2 will replicate data to instance4
     # 10. instance_2 down (MAIN), instance 1 up (missed epoch) and
-    # instance 4 up (In this case we should always choose instance_4 because it has up-to-date data)
-    # 11 Instance 4 new main
-    # 12 instance_1 gets up-to-date data
+    # instance 4 up (In this case we should always
 
     # 1
 
@@ -851,10 +849,19 @@ def test_replication_correct_replica_chosen_up_to_date_data(data_recovery, test_
     # 9
 
     interactive_mg_runner.kill(memgraph_instances_description, "instance_2")
-    interactive_mg_runner.start(memgraph_instances_description, "instance_1")
+
+    expected_data_on_coord = [
+        ("coordinator_1", "localhost:7690", "localhost:10111", "localhost:10121", "up", "leader"),
+        ("instance_1", "localhost:7688", "", "localhost:10011", "down", "unknown"),
+        ("instance_2", "localhost:7689", "", "localhost:10012", "down", "unknown"),
+        ("instance_3", "localhost:7687", "", "localhost:10013", "down", "unknown"),
+        ("instance_4", "localhost:7691", "", "localhost:10014", "up", "main"),
+    ]
+    mg_sleep_and_assert(expected_data_on_coord, partial(show_instances, coord_cursor))
 
     # 10
 
+    interactive_mg_runner.start(memgraph_instances_description, "instance_1")
     expected_data_on_coord = [
         ("coordinator_1", "localhost:7690", "localhost:10111", "localhost:10121", "up", "leader"),
         ("instance_1", "localhost:7688", "", "localhost:10011", "up", "replica"),
diff --git a/tests/unit/coordinator_utils.cpp b/tests/unit/coordinator_utils.cpp
index 38facd5272e..20636b7cac2 100644
--- a/tests/unit/coordinator_utils.cpp
+++ b/tests/unit/coordinator_utils.cpp
@@ -12,265 +12,455 @@
 #include <gflags/gflags.h>
 #include <gtest/gtest.h>
 #include "coordination/coordinator_instance.hpp"
-#include "dbms/constants.hpp"
-#include "replication_coordination_glue/common.hpp"
 #include "utils/functional.hpp"
 
-using memgraph::coordination::CoordinatorInstanceInitConfig;
+using memgraph::coordination::CoordinatorInstance;
+using memgraph::replication_coordination_glue::InstanceDBInfo;
+using memgraph::replication_coordination_glue::InstanceInfo;
+using memgraph::utils::UUID;
 
-class CoordinationUtils : public ::testing::Test {
- protected:
-  void SetUp() override {
-    if (!std::filesystem::exists(test_folder_)) {
-      std::filesystem::create_directories(test_folder_);
-    }
-  }
+TEST(CoordinationUtils, FailoverFirstInstanceNewest) {
+  // Choose any if everything is same
 
-  void TearDown() override {
-    if (!std::filesystem::exists(test_folder_)) return;
-    std::filesystem::remove_all(test_folder_);
-  }
+  auto const default_db_uuid = std::string{UUID()};
 
-  std::filesystem::path test_folder_{std::filesystem::temp_directory_path() / "MG_tests_unit_coordination"};
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1
 
-  int const bolt_port{8688};
-  int const coordinator_port{20111};
-  int const management_port{30111};
-  int32_t const coordinator_id{11};
-};
+       }},
+      {"instance_3",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1}},
 
-// Networking is used in this test, be careful with ports used.
-TEST_F(CoordinationUtils, MemgraphDbHistorySimple) {
-  // Choose any if everything is same
-  // X = dead
-  // Main      : A(24)  B(36)  C(48) D(50) E(51) X
-  // replica  1: A(24)  B(36)  C(48) D(50) E(51)
-  // replica  2: A(24)  B(36)  C(48) D(50) E(51)
-  // replica  3: A(24)  B(36)  C(48) D(50) E(51)
-  std::vector<std::pair<std::string, memgraph::replication_coordination_glue::DatabaseHistories>>
-      instance_database_histories;
-
-  std::vector<std::pair<memgraph::utils::UUID, uint64_t>> histories;
-  histories.emplace_back(memgraph::utils::UUID{}, 24);
-  histories.emplace_back(memgraph::utils::UUID{}, 36);
-  histories.emplace_back(memgraph::utils::UUID{}, 48);
-  histories.emplace_back(memgraph::utils::UUID{}, 50);
-  histories.emplace_back(memgraph::utils::UUID{}, 51);
-
-  memgraph::utils::UUID db_uuid;
-  std::string default_name = std::string(memgraph::dbms::kDefaultDB);
-
-  auto db_histories = memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-    return std::make_pair(std::string(pair.first), pair.second);
-  });
-
-  memgraph::replication_coordination_glue::DatabaseHistory history{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history};
-  instance_database_histories.emplace_back("instance_1", instance_1_db_histories_);
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history};
-  instance_database_histories.emplace_back("instance_2", instance_2_db_histories_);
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history};
-  instance_database_histories.emplace_back("instance_3", instance_3_db_histories_);
-
-  CoordinatorInstanceInitConfig const init_config1{.coordinator_id = coordinator_id,
-                                                   .coordinator_port = coordinator_port,
-                                                   .bolt_port = bolt_port,
-                                                   .management_port = management_port,
-                                                   .durability_dir = test_folder_ / "high_availability" / "coordinator",
-                                                   .coordinator_hostname = "localhost"};
-  memgraph::coordination::CoordinatorInstance instance{init_config1};
-
-  auto [instance_name, latest_epoch, latest_commit_timestamp] =
-      *instance.ChooseMostUpToDateInstance(instance_database_histories);
-  ASSERT_TRUE(instance_name == "instance_1" || instance_name == "instance_2" || instance_name == "instance_3");
-  ASSERT_TRUE(latest_epoch == db_histories.back().first);
-  ASSERT_TRUE(latest_commit_timestamp == db_histories.back().second);
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_1" || *maybe_instance_name == "instance_2" ||
+              *maybe_instance_name == "instance_3");
+}
+
+TEST(CoordinationUtils, FailoverSecondInstanceNewest) {
+  // Instanc2 newest
+
+  auto const default_db_uuid = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 53}},
+                    .last_committed_system_timestamp = 1
+
+       }},
+      {"instance_3",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
 }
 
-TEST_F(CoordinationUtils, MemgraphDbHistoryLastEpochDifferent) {
-  // Prioritize one with the biggest last durable timestamp on last epoch
-  // X = dead
-  // Main      : A(24)  B(36)  C(48) D(50) E(59) X
-  // replica  1: A(24)  B(12)  C(15) D(17) E(51)
-  // replica  2: A(24)  B(12)  C(15) D(17) E(57)
-  // replica  3: A(24)  B(12)  C(15) D(17) E(59)
-  std::vector<std::pair<std::string, memgraph::replication_coordination_glue::DatabaseHistories>>
-      instance_database_histories;
-
-  std::vector<std::pair<memgraph::utils::UUID, uint64_t>> histories;
-  histories.emplace_back(memgraph::utils::UUID{}, 24);
-  histories.emplace_back(memgraph::utils::UUID{}, 36);
-  histories.emplace_back(memgraph::utils::UUID{}, 48);
-  histories.emplace_back(memgraph::utils::UUID{}, 50);
-  histories.emplace_back(memgraph::utils::UUID{}, 59);
-
-  memgraph::utils::UUID db_uuid;
-  std::string default_name = std::string(memgraph::dbms::kDefaultDB);
-
-  auto db_histories = memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-    return std::make_pair(std::string(pair.first), pair.second);
-  });
-
-  db_histories.back().second = 51;
-  memgraph::replication_coordination_glue::DatabaseHistory history1{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history1};
-  instance_database_histories.emplace_back("instance_1", instance_1_db_histories_);
-
-  db_histories.back().second = 57;
-  memgraph::replication_coordination_glue::DatabaseHistory history2{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-  memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history2};
-  instance_database_histories.emplace_back("instance_2", instance_2_db_histories_);
-
-  db_histories.back().second = 59;
-  memgraph::replication_coordination_glue::DatabaseHistory history3{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-  memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history3};
-  instance_database_histories.emplace_back("instance_3", instance_3_db_histories_);
-
-  CoordinatorInstanceInitConfig const init_config1{.coordinator_id = coordinator_id,
-                                                   .coordinator_port = coordinator_port,
-                                                   .bolt_port = bolt_port,
-                                                   .management_port = management_port,
-                                                   .durability_dir = test_folder_ / "high_availability" / "coordinator",
-                                                   .coordinator_hostname = "localhost"};
-  memgraph::coordination::CoordinatorInstance instance{init_config1};
-  auto [instance_name, latest_epoch, latest_commit_timestamp] =
-      *instance.ChooseMostUpToDateInstance(instance_database_histories);
-
-  ASSERT_TRUE(instance_name == "instance_3");
-  ASSERT_TRUE(latest_epoch == db_histories.back().first);
-  ASSERT_TRUE(latest_commit_timestamp == db_histories.back().second);
+TEST(CoordinationUtils, FailoverLastInstanceNewest) {
+  // Prioritize one with the largest last durable timestamp on
+
+  auto const default_db_uuid = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 51}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 57}},
+                    .last_committed_system_timestamp = 1
+
+       }},
+      {"instance_3",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 59}},
+                    .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_3");
 }
 
-TEST_F(CoordinationUtils, MemgraphDbHistoryOneInstanceAheadFewEpochs) {
-  // Prioritize one biggest commit timestamp
-  // X = dead
-  // Main      : A(24)  B(36)  C(48) D(50) E(51)   X    X     X  X
-  // replica  1: A(24)  B(36)  C(48) D(50) E(51) F(60) G(65)  X  up
-  // replica  2: A(24)  B(36)  C(48) D(50) E(51)  X     X     X  up
-  // replica  3: A(24)  B(36)  C(48) D(50) E(51)  X     X     X  up
-  std::vector<std::pair<std::string, memgraph::replication_coordination_glue::DatabaseHistories>>
-      instance_database_histories;
-
-  std::vector<std::pair<memgraph::utils::UUID, uint64_t>> histories;
-  histories.emplace_back(memgraph::utils::UUID{}, 24);
-  histories.emplace_back(memgraph::utils::UUID{}, 36);
-  histories.emplace_back(memgraph::utils::UUID{}, 48);
-  histories.emplace_back(memgraph::utils::UUID{}, 50);
-  histories.emplace_back(memgraph::utils::UUID{}, 51);
-
-  memgraph::utils::UUID db_uuid;
-  std::string default_name = std::string(memgraph::dbms::kDefaultDB);
-
-  auto db_histories = memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-    return std::make_pair(std::string(pair.first), pair.second);
-  });
-
-  memgraph::replication_coordination_glue::DatabaseHistory history{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history};
-  instance_database_histories.emplace_back("instance_1", instance_1_db_histories_);
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history};
-  instance_database_histories.emplace_back("instance_2", instance_2_db_histories_);
-
-  histories.emplace_back(memgraph::utils::UUID{}, 60);
-  histories.emplace_back(memgraph::utils::UUID{}, 65);
-  auto db_histories_longest =
-      memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-        return std::make_pair(std::string(pair.first), pair.second);
-      });
-
-  memgraph::replication_coordination_glue::DatabaseHistory history_longest{
-      .db_uuid = db_uuid, .history = db_histories_longest, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history_longest};
-  instance_database_histories.emplace_back("instance_3", instance_3_db_histories_);
-
-  CoordinatorInstanceInitConfig const init_config1{.coordinator_id = coordinator_id,
-                                                   .coordinator_port = coordinator_port,
-                                                   .bolt_port = bolt_port,
-                                                   .management_port = management_port,
-                                                   .durability_dir = test_folder_ / "high_availability" / "coordinator",
-                                                   .coordinator_hostname = "localhost"};
-  memgraph::coordination::CoordinatorInstance instance{init_config1};
-  auto [instance_name, latest_epoch, latest_commit_timestamp] =
-      *instance.ChooseMostUpToDateInstance(instance_database_histories);
-
-  ASSERT_TRUE(instance_name == "instance_3");
-  ASSERT_TRUE(latest_epoch == db_histories_longest.back().first);
-  ASSERT_TRUE(latest_commit_timestamp == db_histories_longest.back().second);
+TEST(CoordinationUtils, MTFailover1) {
+  // Instance 2 newest for all DBs
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+  auto const db_b = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 11},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                            InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 57},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 33},
+                                      },
+                                  .last_committed_system_timestamp = 1
+
+                     }},
+      {"instance_3", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 12},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 56},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 31},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
 }
 
-TEST_F(CoordinationUtils, MemgraphDbHistoryInstancesHistoryDiverged) {
-  // When history diverged, also prioritize one with the biggest last durable timestamp
-  // Main      : A(1)  B(2)   C(3)    X
-  // replica  1: A(1)  B(2)   C(3)    X     X up
-  // replica  2: A(1)  B(2)    X     D(5)   X up
-  // replica  3: A(1)  B(2)    X     D(4)   X up
-  std::vector<std::pair<std::string, memgraph::replication_coordination_glue::DatabaseHistories>>
-      instance_database_histories;
-
-  std::vector<std::pair<memgraph::utils::UUID, uint64_t>> histories;
-  histories.emplace_back(memgraph::utils::UUID{}, 1);
-  histories.emplace_back(memgraph::utils::UUID{}, 2);
-  histories.emplace_back(memgraph::utils::UUID{}, 3);
-
-  memgraph::utils::UUID db_uuid;
-  std::string default_name = std::string(memgraph::dbms::kDefaultDB);
-
-  auto db_histories = memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-    return std::make_pair(std::string(pair.first), pair.second);
-  });
-
-  memgraph::replication_coordination_glue::DatabaseHistory history{
-      .db_uuid = db_uuid, .history = db_histories, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_1_db_histories_{history};
-  instance_database_histories.emplace_back("instance_1", instance_1_db_histories_);
-
-  db_histories.pop_back();
-
-  auto oldest_commit_timestamp{5};
-  auto newest_different_epoch = memgraph::utils::UUID{};
-  histories.emplace_back(newest_different_epoch, oldest_commit_timestamp);
-  auto db_histories_different =
-      memgraph::utils::fmap(histories, [](const std::pair<memgraph::utils::UUID, uint64_t> &pair) {
-        return std::make_pair(std::string(pair.first), pair.second);
-      });
-
-  memgraph::replication_coordination_glue::DatabaseHistory history_3{
-      .db_uuid = db_uuid, .history = db_histories_different, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_3_db_histories_{history_3};
-  instance_database_histories.emplace_back("instance_3", instance_3_db_histories_);
-
-  db_histories_different.back().second = 4;
-  memgraph::replication_coordination_glue::DatabaseHistory history_2{
-      .db_uuid = db_uuid, .history = db_histories_different, .name = default_name};
-
-  memgraph::replication_coordination_glue::DatabaseHistories instance_2_db_histories_{history_2};
-  instance_database_histories.emplace_back("instance_2", instance_2_db_histories_);
-
-  CoordinatorInstanceInitConfig const init_config1{.coordinator_id = coordinator_id,
-                                                   .coordinator_port = coordinator_port,
-                                                   .bolt_port = bolt_port,
-                                                   .management_port = management_port,
-                                                   .durability_dir = test_folder_ / "high_availability" / "coordinator",
-                                                   .coordinator_hostname = "localhost"};
-  memgraph::coordination::CoordinatorInstance instance{init_config1};
-  auto [instance_name, latest_epoch, latest_commit_timestamp] =
-      *instance.ChooseMostUpToDateInstance(instance_database_histories);
-
-  ASSERT_TRUE(instance_name == "instance_3");
-  ASSERT_TRUE(latest_epoch == std::string(newest_different_epoch));
-  ASSERT_TRUE(latest_commit_timestamp == oldest_commit_timestamp);
+TEST(CoordinationUtils, MTFailover2) {
+  // Instance 3 newest for 2 DBs
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+  auto const db_b = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 11},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                            InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 57},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 33},
+                                      },
+                                  .last_committed_system_timestamp = 1
+
+                     }},
+      {"instance_3", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 0},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 58},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 36},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_3");
+}
+
+TEST(CoordinationUtils, MTFailover3) {
+  // Instance 2 best for default db, best for db_a
+  // Instance 1 best for default db
+  // Instance 3 best for db_b
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+  auto const db_b = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                            InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 57},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 33},
+                                      },
+                                  .last_committed_system_timestamp = 1
+
+                     }},
+      {"instance_3", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 0},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 55},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 34},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name = CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
+}
+
+TEST(CoordinationUtils, MTFailover4) {
+  // Instance 1 best for all 3 DBs
+  // Instance 2 and 3 for default_db_uuid and db_a
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+  auto const db_b = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                            InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 29},
+                                      },
+                                  .last_committed_system_timestamp = 1
+
+                     }},
+      {"instance_3", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 51},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 28},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_1");
+}
+
+TEST(CoordinationUtils, MTFailover5) {
+  // The decision needs to be done by summing timestamps
+  // All 3 instances are up to date on db_b
+  // Instance 1 is the best on default_db_uuid and has +5 over instance_2
+  // Instance 2 is the best on db_a and has +6 over instance 1 -> therefore the winner
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+  auto const db_b = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 19},
+                                            InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 10},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 25},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30},
+                                      },
+                                  .last_committed_system_timestamp = 1
+
+                     }},
+      {"instance_3", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 8},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 8},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 30},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
+}
+
+TEST(CoordinationUtils, MTFailover6) {
+  // The decision needs to be done by summing timestamps
+  // Instance 1 is the best on default_db_uuid and has +6 over instance_1
+  // Instance 2 is the best on db_a and has +5 over instance 1
+  // --> Instance 1 is the winner
+
+  auto const default_db_uuid = std::string{UUID()};
+  auto const db_a = std::string{UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 16},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 19}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 10},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 24},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_1");
+}
+
+TEST(CoordinationUtils, MTFailover7) {
+  // Instances are equally good on the same number of instances and they have the same total sum of timestamps.
+  // Take the 1st one as winner
+
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+  auto const db_a = std::string{memgraph::utils::UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 16},
+                                            InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 10}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 10},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 16},
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_1");
+}
+
+TEST(CoordinationUtils, MTFailover8) {
+  // Both instances are equally good on default DB.
+  // Instance has larger sys timstamp so db_b and default_db_uuid are the relevant ones
+
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+  auto const db_a = std::string{memgraph::utils::UUID()};
+  auto const db_b = std::string{memgraph::utils::UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 16},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 20},
+
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 16},
+                                          InstanceDBInfo{.db_uuid = db_b, .latest_durable_timestamp = 20},
+                                      },
+                                  .last_committed_system_timestamp = 2}},
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
+}
+
+TEST(CoordinationUtils, MTFailover9) {
+  // Only take into account default DB for instance_2 on which it is better
+
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+  auto const db_a = std::string{memgraph::utils::UUID()};
+  auto const db_b = std::string{memgraph::utils::UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 16},
+                                          InstanceDBInfo{.db_uuid = db_a, .latest_durable_timestamp = 20},
+
+                                      },
+                                  .last_committed_system_timestamp = 1}},
+      {"instance_2", InstanceInfo{.dbs_info =
+                                      std::vector{
+                                          InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 17},
+                                      },
+                                  .last_committed_system_timestamp = 2}},
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_TRUE(*maybe_instance_name == "instance_2");
+}
+
+TEST(CoordinationUtils, FailoverNoInstancesAvailable) {
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+  std::map<std::string, std::vector<std::pair<std::string, uint64_t>>> instances_nfo{{default_db_uuid, {}}};
+
+  std::map<std::string, InstanceInfo> instances_info{};
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_FALSE(maybe_instance_name.has_value());
+}
+
+TEST(CoordinationUtils, FailoverSomeInstancesMissingTimestamps) {
+  // missing timestamps
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+  std::map<std::string, std::vector<std::pair<std::string, uint64_t>>> instnces_info{
+      {default_db_uuid, {{"instance_1", 0}, {"instance_2", 15}, {"instance_3", 0}}}};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 0}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_2",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 15}},
+                    .last_committed_system_timestamp = 1}},
+      {"instance_3",
+       InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid, .latest_durable_timestamp = 0}},
+                    .last_committed_system_timestamp = 1}},
+
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_EQ(*maybe_instance_name, "instance_2");
+}
+
+TEST(CoordinationUtils, FailoverSingleInstanceOnly) {
+  auto const default_db_uuid = std::string{memgraph::utils::UUID()};
+
+  std::map<std::string, InstanceInfo> instances_info{
+      {"instance_1", InstanceInfo{.dbs_info = std::vector{InstanceDBInfo{.db_uuid = default_db_uuid,
+                                                                         .latest_durable_timestamp = 100}},
+                                  .last_committed_system_timestamp = 1}},
+  };
+
+  auto const maybe_instance_name =
+      memgraph::coordination::CoordinatorInstance::ChooseMostUpToDateInstance(instances_info);
+  ASSERT_TRUE(maybe_instance_name.has_value());
+  // NOLINTNEXTLINE
+  ASSERT_EQ(*maybe_instance_name, "instance_1");
 }


Reviews:
user: login: andrejtonev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T09:53:05Z
user: login: as51340
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:08:20Z
user: login: as51340
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:53:55Z
user: login: as51340
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-02T06:56:12Z
user: login: andrejtonev
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-11T07:16:02Z

Review comments:
diff_hunk: @@ -7,7 +7,7 @@ on:
 
 env:
   ARCH: 'amd'
-  BUILD_TYPE: 'RelWithDebInfo'
path: .github/workflows/stress_jepsen.yaml
user: login: andrejtonev
body: Is there a particular reason we should switch to Debug?
created_at: 2025-03-27T13:22:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1032,104 +1031,126 @@ void CoordinatorInstance::InstanceFailCallback(std::string_view instance_name,
   spdlog::trace("Instance {} finished fail callback.", instance_name);
 }
 
-auto CoordinatorInstance::ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
-    -> std::optional<NewMainRes> {
+auto CoordinatorInstance::ChooseMostUpToDateInstance(
+    std::map<std::string, replication_coordination_glue::InstanceInfo> const &instances_info)
+    -> std::optional<std::string> {
   utils::MetricsTimer const timer{metrics::ChooseMostUpToDateInstance_us};
 
-  std::optional<NewMainRes> new_main_res;
+  // Find the instance with the largest system committed timestamp
path: src/coordination/coordinator_instance.cpp
user: login: andrejtonev
body: This is too hard to follow.
Can we just make it simpler even if less optimal?
More logging will also be useful
created_at: 2025-04-01T09:46:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,21 +11,18 @@
 
 #pragma once
 
-#include "rpc/client.hpp"
-#include "utils/uuid.hpp"
-
-#include "messages.hpp"
-#include "rpc/messages.hpp"
 #include "utils/uuid.hpp"
 
 namespace memgraph::replication_coordination_glue {
 
-struct DatabaseHistory {
-  memgraph::utils::UUID db_uuid;
-  std::vector<std::pair<std::string, uint64_t>> history;
-  std::string name;  // db name
+struct InstanceDBInfo {
+  std::string db_uuid;
+  uint64_t latest_durable_timestamp;
 };
 
-using DatabaseHistories = std::vector<DatabaseHistory>;
+struct InstanceInfo {
+  std::vector<InstanceDBInfo> dbs_info;
path: src/replication_coordination_glue/common.hpp
user: login: andrejtonev
body: Making this a map between db and ts might make some processes easier down the line.
created_at: 2025-04-01T09:50:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1032,104 +1031,126 @@ void CoordinatorInstance::InstanceFailCallback(std::string_view instance_name,
   spdlog::trace("Instance {} finished fail callback.", instance_name);
 }
 
-auto CoordinatorInstance::ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
-    -> std::optional<NewMainRes> {
+auto CoordinatorInstance::ChooseMostUpToDateInstance(
+    std::map<std::string, replication_coordination_glue::InstanceInfo> const &instances_info)
+    -> std::optional<std::string> {
   utils::MetricsTimer const timer{metrics::ChooseMostUpToDateInstance_us};
 
-  std::optional<NewMainRes> new_main_res;
+  // Find the instance with the largest system committed timestamp
path: src/coordination/coordinator_instance.cpp
user: login: andrejtonev
body: There should always be very few instance and perhaps many tenants
I my mind we can break it like so:
1. find newest system ts and log
2. filter instances based on the ts and log
2.a. if one instance left, we are done
3. use the latest system instance to loop through dbs and their ts
3.a. loop through instances, find the db and ++ for the max one
3.b. since looping through everything, we can also do the ts sum here as well
4. check which instance has the most up to date dbs and log
4.a. if one instance, we are done
5. check which instance has the most sum ts and log
5.a. if one instance, we are done
6. pick one
created_at: 2025-04-01T09:52:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -7,7 +7,7 @@ on:
 
 env:
   ARCH: 'amd'
-  BUILD_TYPE: 'RelWithDebInfo'
path: .github/workflows/stress_jepsen.yaml
user: login: as51340
body: No, I don't know what was I trying
created_at: 2025-04-02T06:08:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -1032,104 +1031,126 @@ void CoordinatorInstance::InstanceFailCallback(std::string_view instance_name,
   spdlog::trace("Instance {} finished fail callback.", instance_name);
 }
 
-auto CoordinatorInstance::ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> instance_database_histories)
-    -> std::optional<NewMainRes> {
+auto CoordinatorInstance::ChooseMostUpToDateInstance(
+    std::map<std::string, replication_coordination_glue::InstanceInfo> const &instances_info)
+    -> std::optional<std::string> {
   utils::MetricsTimer const timer{metrics::ChooseMostUpToDateInstance_us};
 
-  std::optional<NewMainRes> new_main_res;
+  // Find the instance with the largest system committed timestamp
path: src/coordination/coordinator_instance.cpp
user: login: as51340
body: Partially agree. I added some lambdas to make it simpler.
1. :white_check_mark: 
2. and 3. It is much easier to preprocess and to have outer loop going over DBs for me. In this way for each DB I have a set of steps:
i.) find instances with max ts
ii) update_counters for max_instances


For steps 4 and 5 this is the code that does exactly this:
```
std::optional<std::pair<std::string, uint64_t>> newest_instance;
  for (auto const &[instance_name, cnt_newest_dbs] : total_instances_counter) {
    // If better on more DBs, update currently the best
    if (!newest_instance.has_value() || newest_instance->second < cnt_newest_dbs) {
      newest_instance.emplace(instance_name, cnt_newest_dbs);
    } else if (newest_instance->second == cnt_newest_dbs) {
      // Instances are the best over the same number of instances, let the sum of timestamps decide
      spdlog::info("Instances {} and {} are most up to date on the same number of instances.", instance_name,
                   newest_instance->first);
      if (total_instances_sum[instance_name] > total_instances_sum[newest_instance->first]) {
        spdlog::info("Instance {} has the total sum of timestamps larger than {}. It will be considered newer.",
                     instance_name, newest_instance->first);
        newest_instance.emplace(instance_name, cnt_newest_dbs);
      }
    }
  }
```

Compares counters 1st and if counters are the same, uses sum to decide. I think in your case you would have the same amount of code or more.
created_at: 2025-04-02T06:53:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -11,21 +11,18 @@
 
 #pragma once
 
-#include "rpc/client.hpp"
-#include "utils/uuid.hpp"
-
-#include "messages.hpp"
-#include "rpc/messages.hpp"
 #include "utils/uuid.hpp"
 
 namespace memgraph::replication_coordination_glue {
 
-struct DatabaseHistory {
-  memgraph::utils::UUID db_uuid;
-  std::vector<std::pair<std::string, uint64_t>> history;
-  std::string name;  // db name
+struct InstanceDBInfo {
+  std::string db_uuid;
+  uint64_t latest_durable_timestamp;
 };
 
-using DatabaseHistories = std::vector<DatabaseHistory>;
+struct InstanceInfo {
+  std::vector<InstanceDBInfo> dbs_info;
path: src/replication_coordination_glue/common.hpp
user: login: as51340
body: I am not searching anywhere for db's timestamp because I am looping over instances
created_at: 2025-04-02T06:56:12Z
author_association: CONTRIBUTOR

PR comments:
user: login: as51340
created_at: 2025-03-31T13:05:29Z
body: ### Tracking
- [ ] **[Link to Epic/Issue]**


### Standard development
- [x] Update unit/E2E tests
- [ ] Compare the [benchmarking results](https://bench-graph.memgraph.com/) between the master branch this branch


### CI Testing Labels
- [x] Select the appropriate CI test labels _(CI -build=**build-name** -test=**test-suite**)_


### Documentation checklist
- [x] Add the documentation label
- [x] Add the bug / feature label
- [x] Add the milestone for which this feature is intended
    - If not known, set for a later milestone
- [x] Write a release note, including added/changed clauses
    - **There is now an improved logic for selecting the new MAIN instance during the failover in the MT environment. Instance which has most newest databases is considered the newest. Users will now have a smaller chance of experiencing the loss of committed data. [#2812](https://github.com/memgraph/memgraph/pull/2812),**
- [x] **[ Documentation PR link memgraph/documentation ](https://github.com/memgraph/documentation/pull/1234)**
    - [x] Is back linked to this development PR
- [x] **[ Tag someone from docs team ]** @katarinasupe 

author_association: CONTRIBUTOR
user: login: sonarqubecloud[bot]
created_at: 2025-04-11T08:20:11Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=memgraph_memgraph&pullRequest=2812) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [1 New issue](https://sonarcloud.io/project/issues?id=memgraph_memgraph&pullRequest=2812&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=memgraph_memgraph&pullRequest=2812&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=memgraph_memgraph&pullRequest=2812&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=memgraph_memgraph&pullRequest=2812&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=memgraph_memgraph&pullRequest=2812&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=memgraph_memgraph&pullRequest=2812)


author_association: NONE