Compare commits
45 Commits
MG_auth_re
...
support-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccabbe7a63 | ||
|
|
aac169c5b3 | ||
|
|
d0cb85e642 | ||
|
|
34a7fed59a | ||
|
|
8124226ba9 | ||
|
|
eea21dd73e | ||
|
|
a6483fc6a7 | ||
|
|
45def388c7 | ||
|
|
9f7118d893 | ||
|
|
da3db9f3bc | ||
|
|
34b2e360a9 | ||
|
|
9bc0d9425e | ||
|
|
62d1b68c2f | ||
|
|
ec46cca7a3 | ||
|
|
326a04c6a3 | ||
|
|
b21f9e71ed | ||
|
|
c2e0b310f5 | ||
|
|
b492c7d34f | ||
|
|
303608982a | ||
|
|
c0f979fdcb | ||
|
|
5242427686 | ||
|
|
d3168ded5a | ||
|
|
0a28fee34b | ||
|
|
dc9a2c45c4 | ||
|
|
1133bb8ecb | ||
|
|
e9c5cc3b82 | ||
|
|
3f4ac0dd58 | ||
|
|
a0ecea7d1c | ||
|
|
567e1fa1cb | ||
|
|
ef37c44149 | ||
|
|
ab34b060c0 | ||
|
|
67c1874e81 | ||
|
|
9d457eafa8 | ||
|
|
986ea37ead | ||
|
|
afe7d47a5c | ||
|
|
8884a0ea78 | ||
|
|
38ade99652 | ||
|
|
6706ebfa2b | ||
|
|
e7f6a5f4f4 | ||
|
|
071df2f439 | ||
|
|
7f10636470 | ||
|
|
76589903a4 | ||
|
|
a8b625d861 | ||
|
|
9c89fce249 | ||
|
|
5e5f215be4 |
50
.github/workflows/diff.yaml
vendored
50
.github/workflows/diff.yaml
vendored
@@ -96,7 +96,7 @@ jobs:
|
||||
|
||||
- name: Python code analysis
|
||||
run: |
|
||||
CHANGED_FILES=$(git diff -U0 ${{ env.BASE_BRANCH }}... --name-only)
|
||||
CHANGED_FILES=$(git diff -U0 ${{ env.BASE_BRANCH }}... --name-only --diff-filter=d)
|
||||
for file in ${CHANGED_FILES}; do
|
||||
echo ${file}
|
||||
if [[ ${file} == *.py ]]; then
|
||||
@@ -337,7 +337,53 @@ jobs:
|
||||
# multiple paths could be defined
|
||||
build/logs
|
||||
|
||||
experimental_build:
|
||||
experimental_build_ha:
|
||||
name: "High availability build"
|
||||
runs-on: [self-hosted, Linux, X64, Diff]
|
||||
env:
|
||||
THREADS: 24
|
||||
MEMGRAPH_ENTERPRISE_LICENSE: ${{ secrets.MEMGRAPH_ENTERPRISE_LICENSE }}
|
||||
MEMGRAPH_ORGANIZATION_NAME: ${{ secrets.MEMGRAPH_ORGANIZATION_NAME }}
|
||||
|
||||
steps:
|
||||
- name: Set up repository
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Number of commits to fetch. `0` indicates all history for all
|
||||
# branches and tags. (default: 1)
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Build release binaries
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
./init
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DMG_EXPERIMENTAL_HIGH_AVAILABILITY=ON ..
|
||||
make -j$THREADS
|
||||
- name: Run unit tests
|
||||
run: |
|
||||
source /opt/toolchain-v4/activate
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure -j$THREADS
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
./setup.sh /opt/toolchain-v4/activate
|
||||
source ve3/bin/activate_e2e
|
||||
cd e2e
|
||||
./run.sh "Coordinator"
|
||||
./run.sh "Client initiated failover"
|
||||
./run.sh "Uninitialized cluster"
|
||||
- name: Save test data
|
||||
uses: actions/upload-artifact@v3
|
||||
if: always()
|
||||
with:
|
||||
name: "Test data"
|
||||
path: |
|
||||
# multiple paths could be defined
|
||||
build/logs
|
||||
|
||||
experimental_build_mt:
|
||||
name: "MultiTenancy replication build"
|
||||
runs-on: [self-hosted, Linux, X64, Diff]
|
||||
env:
|
||||
|
||||
15
.github/workflows/release_debian10.yaml
vendored
15
.github/workflows/release_debian10.yaml
vendored
@@ -281,6 +281,13 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Ensure Kafka and Pulsar are up
|
||||
run: |
|
||||
cd tests/e2e/streams/kafka
|
||||
docker-compose up -d
|
||||
cd ../pulsar
|
||||
docker-compose up -d
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
@@ -289,6 +296,14 @@ jobs:
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
- name: Ensure Kafka and Pulsar are down
|
||||
if: always()
|
||||
run: |
|
||||
cd tests/e2e/streams/kafka
|
||||
docker-compose down
|
||||
cd ../pulsar
|
||||
docker-compose down
|
||||
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
|
||||
15
.github/workflows/release_ubuntu2004.yaml
vendored
15
.github/workflows/release_ubuntu2004.yaml
vendored
@@ -281,6 +281,13 @@ jobs:
|
||||
cd build
|
||||
ctest -R memgraph__unit --output-on-failure
|
||||
|
||||
- name: Ensure Kafka and Pulsar are up
|
||||
run: |
|
||||
cd tests/e2e/streams/kafka
|
||||
docker-compose up -d
|
||||
cd ../pulsar
|
||||
docker-compose up -d
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
cd tests
|
||||
@@ -289,6 +296,14 @@ jobs:
|
||||
cd e2e
|
||||
./run.sh
|
||||
|
||||
- name: Ensure Kafka and Pulsar are down
|
||||
if: always()
|
||||
run: |
|
||||
cd tests/e2e/streams/kafka
|
||||
docker-compose down
|
||||
cd ../pulsar
|
||||
docker-compose down
|
||||
|
||||
- name: Run stress test (plain)
|
||||
run: |
|
||||
cd tests/stress
|
||||
|
||||
34
ADRs/002_nuraft.md
Normal file
34
ADRs/002_nuraft.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# NuRaft ADR
|
||||
|
||||
**Author**
|
||||
Marko Budiselic (github.com/gitbuda)
|
||||
|
||||
**Status**
|
||||
PROPOSED
|
||||
|
||||
**Date**
|
||||
January 10, 2024
|
||||
|
||||
**Problem**
|
||||
|
||||
In order to enhance Memgraph to have High Availability features as requested by
|
||||
customers, we want to have reliable coordinators backed by RAFT consensus algorithm. Implementing
|
||||
RAFT to be correct and performant is a very challenging task. Skillful Memgraph
|
||||
engineers already tried 3 times and failed to deliver in a reasonable timeframe
|
||||
all three times (approximately 4 person-weeks of engineering work each time).
|
||||
|
||||
**Criteria**
|
||||
|
||||
- easy integration with our C++ codebase
|
||||
- heavily tested in production environments
|
||||
- implementation of performance optimizations on top of the canonical Raft
|
||||
implementation
|
||||
|
||||
**Decision**
|
||||
|
||||
There are a few, robust C++ implementations of Raft but as a part of other
|
||||
projects or bigger libraries. **We select
|
||||
[NuRaft](https://github.com/eBay/NuRaft)** because it focuses on delivering
|
||||
Raft without bloatware, and it's used by
|
||||
[Clickhouse](https://github.com/ClickHouse/ClickHouse) (an comparable peer to
|
||||
Memgraph, a very well-established product).
|
||||
@@ -271,6 +271,17 @@ endif()
|
||||
set(libs_dir ${CMAKE_SOURCE_DIR}/libs)
|
||||
add_subdirectory(libs EXCLUDE_FROM_ALL)
|
||||
|
||||
option(MG_EXPERIMENTAL_HIGH_AVAILABILITY "Feature flag for experimental high availability" OFF)
|
||||
|
||||
if (NOT MG_ENTERPRISE AND MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
set(MG_EXPERIMENTAL_HIGH_AVAILABILITY OFF)
|
||||
message(FATAL_ERROR "MG_EXPERIMENTAL_HIGH_AVAILABILITY must be used with enterpise version of the code.")
|
||||
endif ()
|
||||
|
||||
if (MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
add_compile_definitions(MG_EXPERIMENTAL_HIGH_AVAILABILITY)
|
||||
endif ()
|
||||
|
||||
# Optional subproject configuration -------------------------------------------
|
||||
option(TEST_COVERAGE "Generate coverage reports from running memgraph" OFF)
|
||||
option(TOOLS "Build tools binaries" ON)
|
||||
|
||||
@@ -36,7 +36,7 @@ ADDITIONAL USE GRANT: You may use the Licensed Work in accordance with the
|
||||
3. using the Licensed Work to create a work or solution
|
||||
which competes (or might reasonably be expected to
|
||||
compete) with the Licensed Work.
|
||||
CHANGE DATE: 2027-08-12
|
||||
CHANGE DATE: 2028-21-01
|
||||
CHANGE LICENSE: Apache License, Version 2.0
|
||||
|
||||
For information about alternative licensing arrangements, please visit: https://memgraph.com/legal.
|
||||
|
||||
@@ -22,6 +22,8 @@ add_subdirectory(dbms)
|
||||
add_subdirectory(flags)
|
||||
add_subdirectory(distributed)
|
||||
add_subdirectory(replication)
|
||||
add_subdirectory(coordination)
|
||||
add_subdirectory(replication_coordination_glue)
|
||||
|
||||
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -195,7 +195,6 @@ void Auth::SaveUser(const User &user) {
|
||||
if (!success) {
|
||||
throw AuthException("Couldn't save user '{}'!", user.username());
|
||||
}
|
||||
// TODO system delta
|
||||
}
|
||||
|
||||
std::optional<User> Auth::AddUser(const std::string &username, const std::optional<std::string> &password) {
|
||||
@@ -216,7 +215,6 @@ bool Auth::RemoveUser(const std::string &username_orig) {
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove user '{}'!", username);
|
||||
}
|
||||
// TODO system delta
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -254,7 +252,6 @@ void Auth::SaveRole(const Role &role) {
|
||||
if (!storage_.Put(kRolePrefix + role.rolename(), role.Serialize().dump())) {
|
||||
throw AuthException("Couldn't save role '{}'!", role.rolename());
|
||||
}
|
||||
// TODO system delta
|
||||
}
|
||||
|
||||
std::optional<Role> Auth::AddRole(const std::string &rolename) {
|
||||
@@ -278,7 +275,6 @@ bool Auth::RemoveRole(const std::string &rolename_orig) {
|
||||
if (!storage_.DeleteMultiple(keys)) {
|
||||
throw AuthException("Couldn't remove role '{}'!", rolename);
|
||||
}
|
||||
// TODO system delta
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -62,7 +62,8 @@ const std::vector<Permission> kPermissionsAll = {Permission::MATCH,
|
||||
Permission::TRANSACTION_MANAGEMENT,
|
||||
Permission::STORAGE_MODE,
|
||||
Permission::MULTI_DATABASE_EDIT,
|
||||
Permission::MULTI_DATABASE_USE};
|
||||
Permission::MULTI_DATABASE_USE,
|
||||
Permission::COORDINATOR};
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -118,6 +119,8 @@ std::string PermissionToString(Permission permission) {
|
||||
return "MULTI_DATABASE_EDIT";
|
||||
case Permission::MULTI_DATABASE_USE:
|
||||
return "MULTI_DATABASE_USE";
|
||||
case Permission::COORDINATOR:
|
||||
return "COORDINATOR";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 Memgraph Ltd.
|
||||
//
|
||||
// Licensed as a Memgraph Enterprise file under the Memgraph Enterprise
|
||||
// License (the "License"); by using this file, you agree to be bound by the terms of the License, and you may not use
|
||||
@@ -48,6 +48,7 @@ enum class Permission : uint64_t {
|
||||
STORAGE_MODE = 1U << 22U,
|
||||
MULTI_DATABASE_EDIT = 1U << 23U,
|
||||
MULTI_DATABASE_USE = 1U << 24U,
|
||||
COORDINATOR = 1U << 25U,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
|
||||
30
src/coordination/CMakeLists.txt
Normal file
30
src/coordination/CMakeLists.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
add_library(mg-coordination STATIC)
|
||||
add_library(mg::coordination ALIAS mg-coordination)
|
||||
target_sources(mg-coordination
|
||||
PUBLIC
|
||||
include/coordination/coordinator_client.hpp
|
||||
include/coordination/coordinator_state.hpp
|
||||
include/coordination/coordinator_rpc.hpp
|
||||
include/coordination/coordinator_server.hpp
|
||||
include/coordination/coordinator_config.hpp
|
||||
include/coordination/coordinator_exceptions.hpp
|
||||
include/coordination/coordinator_instance.hpp
|
||||
include/coordination/coordinator_slk.hpp
|
||||
include/coordination/coordinator_data.hpp
|
||||
include/coordination/constants.hpp
|
||||
include/coordination/failover_status.hpp
|
||||
include/coordination/coordinator_cluster_config.hpp
|
||||
|
||||
PRIVATE
|
||||
coordinator_client.cpp
|
||||
coordinator_state.cpp
|
||||
coordinator_rpc.cpp
|
||||
coordinator_server.cpp
|
||||
coordinator_data.cpp
|
||||
coordinator_instance.cpp
|
||||
)
|
||||
target_include_directories(mg-coordination PUBLIC include)
|
||||
|
||||
target_link_libraries(mg-coordination
|
||||
PUBLIC mg::utils mg::rpc mg::slk mg::io mg::repl_coord_glue lib::rangev3
|
||||
)
|
||||
106
src/coordination/coordinator_client.cpp
Normal file
106
src/coordination/coordinator_client.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_client.hpp"
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_rpc.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
namespace {
|
||||
auto CreateClientContext(const memgraph::coordination::CoordinatorClientConfig &config)
|
||||
-> communication::ClientContext {
|
||||
return (config.ssl) ? communication::ClientContext{config.ssl->key_file, config.ssl->cert_file}
|
||||
: communication::ClientContext{};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CoordinatorClient::CoordinatorClient(CoordinatorData *coord_data, CoordinatorClientConfig config,
|
||||
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||
: rpc_context_{CreateClientContext(config)},
|
||||
rpc_client_{io::network::Endpoint(io::network::Endpoint::needs_resolving, config.ip_address, config.port),
|
||||
&rpc_context_},
|
||||
config_{std::move(config)},
|
||||
coord_data_{coord_data},
|
||||
succ_cb_{std::move(succ_cb)},
|
||||
fail_cb_{std::move(fail_cb)} {}
|
||||
|
||||
auto CoordinatorClient::InstanceName() const -> std::string { return config_.instance_name; }
|
||||
auto CoordinatorClient::SocketAddress() const -> std::string { return rpc_client_.Endpoint().SocketAddress(); }
|
||||
|
||||
void CoordinatorClient::StartFrequentCheck() {
|
||||
MG_ASSERT(config_.health_check_frequency_sec > std::chrono::seconds(0),
|
||||
"Health check frequency must be greater than 0");
|
||||
|
||||
instance_checker_.Run(
|
||||
config_.instance_name, config_.health_check_frequency_sec, [this, instance_name = config_.instance_name] {
|
||||
try {
|
||||
spdlog::trace("Sending frequent heartbeat to machine {} on {}", instance_name,
|
||||
rpc_client_.Endpoint().SocketAddress());
|
||||
auto stream{rpc_client_.Stream<memgraph::replication_coordination_glue::FrequentHeartbeatRpc>()};
|
||||
stream.AwaitResponse();
|
||||
succ_cb_(coord_data_, instance_name);
|
||||
} catch (const rpc::RpcFailedException &) {
|
||||
fail_cb_(coord_data_, instance_name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void CoordinatorClient::StopFrequentCheck() { instance_checker_.Stop(); }
|
||||
void CoordinatorClient::PauseFrequentCheck() { instance_checker_.Pause(); }
|
||||
void CoordinatorClient::ResumeFrequentCheck() { instance_checker_.Resume(); }
|
||||
|
||||
auto CoordinatorClient::SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void {
|
||||
succ_cb_ = std::move(succ_cb);
|
||||
fail_cb_ = std::move(fail_cb);
|
||||
}
|
||||
|
||||
auto CoordinatorClient::ReplicationClientInfo() const -> CoordinatorClientConfig::ReplicationClientInfo {
|
||||
return config_.replication_client_info;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::SendPromoteReplicaToMainRpc(
|
||||
std::vector<CoordinatorClientConfig::ReplicationClientInfo> replication_clients_info) const -> bool {
|
||||
try {
|
||||
auto stream{rpc_client_.Stream<PromoteReplicaToMainRpc>(std::move(replication_clients_info))};
|
||||
if (!stream.AwaitResponse().success) {
|
||||
spdlog::error("Failed to receive successful RPC failover response!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch (const rpc::RpcFailedException &) {
|
||||
spdlog::error("RPC error occurred while sending failover RPC!");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::DemoteToReplica() const -> bool {
|
||||
const auto instance_name = config_.instance_name;
|
||||
try {
|
||||
auto stream{rpc_client_.Stream<SetMainToReplicaRpc>(config_.replication_client_info)};
|
||||
if (!stream.AwaitResponse().success) {
|
||||
spdlog::error("Failed to receive successful RPC response for setting instance {} to replica!", instance_name);
|
||||
return false;
|
||||
}
|
||||
spdlog::info("Sent request RPC from coordinator to instance to set it as replica!");
|
||||
return true;
|
||||
} catch (const rpc::RpcFailedException &) {
|
||||
spdlog::error("Failed to set instance {} to replica!", instance_name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
224
src/coordination/coordinator_data.cpp
Normal file
224
src/coordination/coordinator_data.cpp
Normal file
@@ -0,0 +1,224 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_data.hpp"
|
||||
|
||||
#include <range/v3/view.hpp>
|
||||
#include <shared_mutex>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
CoordinatorData::CoordinatorData() {
|
||||
auto find_instance = [](CoordinatorData *coord_data, std::string_view instance_name) -> CoordinatorInstance & {
|
||||
auto instance = std::ranges::find_if(
|
||||
coord_data->registered_instances_,
|
||||
[instance_name](const CoordinatorInstance &instance) { return instance.InstanceName() == instance_name; });
|
||||
|
||||
MG_ASSERT(instance != coord_data->registered_instances_.end(), "Instance {} not found during callback!",
|
||||
instance_name);
|
||||
return *instance;
|
||||
};
|
||||
|
||||
replica_succ_cb_ = [find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing replica successful callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
instance.OnSuccessPing();
|
||||
};
|
||||
|
||||
replica_fail_cb_ = [find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing replica failure callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
instance.OnFailPing();
|
||||
};
|
||||
|
||||
main_succ_cb_ = [this, find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing main successful callback", instance_name);
|
||||
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
|
||||
if (instance.IsAlive()) {
|
||||
instance.OnSuccessPing();
|
||||
} else {
|
||||
auto const new_role = coord_data->ClusterHasAliveMain() ? replication_coordination_glue::ReplicationRole::REPLICA
|
||||
: replication_coordination_glue::ReplicationRole::MAIN;
|
||||
if (new_role == replication_coordination_glue::ReplicationRole::REPLICA) {
|
||||
thread_pool_.AddTask([&instance, coord_data, instance_name]() {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::info("Demoting instance {} to replica", instance_name);
|
||||
instance.PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&instance] { instance.ResumeFrequentCheck(); }};
|
||||
auto const status = instance.DemoteToReplica(coord_data->replica_succ_cb_, coord_data->replica_fail_cb_);
|
||||
if (!status) {
|
||||
spdlog::error("Instance {} failed to demote to replica", instance_name);
|
||||
} else {
|
||||
spdlog::info("Instance {} demoted to replica", instance_name);
|
||||
instance.OnSuccessPing();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
instance.OnSuccessPing();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
main_fail_cb_ = [this, find_instance](CoordinatorData *coord_data, std::string_view instance_name) -> void {
|
||||
auto lock = std::lock_guard{coord_data->coord_data_lock_};
|
||||
spdlog::trace("Instance {} performing main failure callback", instance_name);
|
||||
auto &instance = find_instance(coord_data, instance_name);
|
||||
instance.OnFailPing();
|
||||
|
||||
if (!ClusterHasAliveMain()) {
|
||||
spdlog::info("Cluster without main instance, starting automatic failover");
|
||||
switch (auto failover_status = DoFailover(); failover_status) {
|
||||
using enum DoFailoverStatus;
|
||||
case ALL_REPLICAS_DOWN:
|
||||
spdlog::warn("Failover aborted since all replicas are down!");
|
||||
break;
|
||||
case MAIN_ALIVE:
|
||||
spdlog::warn("Failover aborted since main is alive!");
|
||||
break;
|
||||
case RPC_FAILED:
|
||||
spdlog::warn("Failover aborted since promoting replica to main failed!");
|
||||
break;
|
||||
case SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
auto CoordinatorData::ClusterHasAliveMain() const -> bool {
|
||||
auto const alive_main = [](const CoordinatorInstance &instance) { return instance.IsMain() && instance.IsAlive(); };
|
||||
return std::ranges::any_of(registered_instances_, alive_main);
|
||||
}
|
||||
|
||||
auto CoordinatorData::DoFailover() -> DoFailoverStatus {
|
||||
auto replica_instances = registered_instances_ | ranges::views::filter(&CoordinatorInstance::IsReplica);
|
||||
|
||||
auto chosen_replica_instance = std::ranges::find_if(replica_instances, &CoordinatorInstance::IsAlive);
|
||||
if (chosen_replica_instance == replica_instances.end()) {
|
||||
return DoFailoverStatus::ALL_REPLICAS_DOWN;
|
||||
}
|
||||
|
||||
chosen_replica_instance->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&chosen_replica_instance] { chosen_replica_instance->ResumeFrequentCheck(); }};
|
||||
|
||||
std::vector<ReplClientInfo> repl_clients_info;
|
||||
repl_clients_info.reserve(std::ranges::distance(replica_instances));
|
||||
|
||||
auto const not_chosen_replica_instance = [&chosen_replica_instance](const CoordinatorInstance &instance) {
|
||||
return instance != *chosen_replica_instance;
|
||||
};
|
||||
|
||||
std::ranges::transform(replica_instances | ranges::views::filter(not_chosen_replica_instance),
|
||||
std::back_inserter(repl_clients_info),
|
||||
[](const CoordinatorInstance &instance) { return instance.ReplicationClientInfo(); });
|
||||
|
||||
if (!chosen_replica_instance->PromoteToMain(std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
|
||||
return DoFailoverStatus::RPC_FAILED;
|
||||
}
|
||||
return DoFailoverStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorData::ShowInstances() const -> std::vector<CoordinatorInstanceStatus> {
|
||||
std::vector<CoordinatorInstanceStatus> instances_status;
|
||||
instances_status.reserve(registered_instances_.size());
|
||||
|
||||
auto const stringify_repl_role = [](const CoordinatorInstance &instance) -> std::string {
|
||||
if (!instance.IsAlive()) return "unknown";
|
||||
if (instance.IsMain()) return "main";
|
||||
return "replica";
|
||||
};
|
||||
|
||||
auto const instance_to_status =
|
||||
[&stringify_repl_role](const CoordinatorInstance &instance) -> CoordinatorInstanceStatus {
|
||||
return {.instance_name = instance.InstanceName(),
|
||||
.socket_address = instance.SocketAddress(),
|
||||
.replication_role = stringify_repl_role(instance),
|
||||
.is_alive = instance.IsAlive()};
|
||||
};
|
||||
|
||||
{
|
||||
auto lock = std::shared_lock{coord_data_lock_};
|
||||
std::ranges::transform(registered_instances_, std::back_inserter(instances_status), instance_to_status);
|
||||
}
|
||||
|
||||
return instances_status;
|
||||
}
|
||||
|
||||
auto CoordinatorData::SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_data_lock_};
|
||||
|
||||
auto const is_new_main = [&instance_name](const CoordinatorInstance &instance) {
|
||||
return instance.InstanceName() == instance_name;
|
||||
};
|
||||
auto new_main = std::ranges::find_if(registered_instances_, is_new_main);
|
||||
|
||||
if (new_main == registered_instances_.end()) {
|
||||
spdlog::error("Instance {} not registered. Please register it using REGISTER INSTANCE {}", instance_name,
|
||||
instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::NO_INSTANCE_WITH_NAME;
|
||||
}
|
||||
|
||||
new_main->PauseFrequentCheck();
|
||||
utils::OnScopeExit scope_exit{[&new_main] { new_main->ResumeFrequentCheck(); }};
|
||||
|
||||
std::vector<CoordinatorClientConfig::ReplicationClientInfo> repl_clients_info;
|
||||
repl_clients_info.reserve(registered_instances_.size() - 1);
|
||||
|
||||
auto const is_not_new_main = [&instance_name](const CoordinatorInstance &instance) {
|
||||
return instance.InstanceName() != instance_name;
|
||||
};
|
||||
std::ranges::transform(registered_instances_ | ranges::views::filter(is_not_new_main),
|
||||
std::back_inserter(repl_clients_info),
|
||||
[](const CoordinatorInstance &instance) { return instance.ReplicationClientInfo(); });
|
||||
|
||||
if (!new_main->PromoteToMain(std::move(repl_clients_info), main_succ_cb_, main_fail_cb_)) {
|
||||
return SetInstanceToMainCoordinatorStatus::COULD_NOT_PROMOTE_TO_MAIN;
|
||||
}
|
||||
|
||||
spdlog::info("Instance {} promoted to main", instance_name);
|
||||
return SetInstanceToMainCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorData::RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_data_lock_};
|
||||
if (std::ranges::any_of(registered_instances_, [&config](const CoordinatorInstance &instance) {
|
||||
return instance.InstanceName() == config.instance_name;
|
||||
})) {
|
||||
return RegisterInstanceCoordinatorStatus::NAME_EXISTS;
|
||||
}
|
||||
|
||||
if (std::ranges::any_of(registered_instances_, [&config](const CoordinatorInstance &instance) {
|
||||
return instance.SocketAddress() == config.SocketAddress();
|
||||
})) {
|
||||
return RegisterInstanceCoordinatorStatus::END_POINT_EXISTS;
|
||||
}
|
||||
|
||||
try {
|
||||
registered_instances_.emplace_back(this, std::move(config), replica_succ_cb_, replica_fail_cb_);
|
||||
return RegisterInstanceCoordinatorStatus::SUCCESS;
|
||||
|
||||
} catch (CoordinatorRegisterInstanceException const &) {
|
||||
return RegisterInstanceCoordinatorStatus::RPC_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
#endif
|
||||
84
src/coordination/coordinator_instance.cpp
Normal file
84
src/coordination/coordinator_instance.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
CoordinatorInstance::CoordinatorInstance(CoordinatorData *data, CoordinatorClientConfig config,
|
||||
HealthCheckCallback succ_cb, HealthCheckCallback fail_cb)
|
||||
: client_(data, std::move(config), std::move(succ_cb), std::move(fail_cb)),
|
||||
replication_role_(replication_coordination_glue::ReplicationRole::REPLICA),
|
||||
is_alive_(true) {
|
||||
if (!client_.DemoteToReplica()) {
|
||||
throw CoordinatorRegisterInstanceException("Failed to demote instance {} to replica", client_.InstanceName());
|
||||
}
|
||||
client_.StartFrequentCheck();
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::OnSuccessPing() -> void {
|
||||
last_response_time_ = std::chrono::system_clock::now();
|
||||
is_alive_ = true;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::OnFailPing() -> bool {
|
||||
is_alive_ =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - last_response_time_).count() <
|
||||
CoordinatorClusterConfig::alive_response_time_difference_sec_;
|
||||
return is_alive_;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::InstanceName() const -> std::string { return client_.InstanceName(); }
|
||||
auto CoordinatorInstance::SocketAddress() const -> std::string { return client_.SocketAddress(); }
|
||||
auto CoordinatorInstance::IsAlive() const -> bool { return is_alive_; }
|
||||
|
||||
auto CoordinatorInstance::IsReplica() const -> bool {
|
||||
return replication_role_ == replication_coordination_glue::ReplicationRole::REPLICA;
|
||||
}
|
||||
auto CoordinatorInstance::IsMain() const -> bool {
|
||||
return replication_role_ == replication_coordination_glue::ReplicationRole::MAIN;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::PromoteToMain(ReplicationClientsInfo repl_clients_info, HealthCheckCallback main_succ_cb,
|
||||
HealthCheckCallback main_fail_cb) -> bool {
|
||||
if (!client_.SendPromoteReplicaToMainRpc(std::move(repl_clients_info))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
replication_role_ = replication_coordination_glue::ReplicationRole::MAIN;
|
||||
client_.SetCallbacks(std::move(main_succ_cb), std::move(main_fail_cb));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::DemoteToReplica(HealthCheckCallback replica_succ_cb, HealthCheckCallback replica_fail_cb)
|
||||
-> bool {
|
||||
if (!client_.DemoteToReplica()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
replication_role_ = replication_coordination_glue::ReplicationRole::REPLICA;
|
||||
client_.SetCallbacks(std::move(replica_succ_cb), std::move(replica_fail_cb));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::PauseFrequentCheck() -> void { client_.PauseFrequentCheck(); }
|
||||
auto CoordinatorInstance::ResumeFrequentCheck() -> void { client_.ResumeFrequentCheck(); }
|
||||
|
||||
auto CoordinatorInstance::ReplicationClientInfo() const -> CoordinatorClientConfig::ReplicationClientInfo {
|
||||
return client_.ReplicationClientInfo();
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
107
src/coordination/coordinator_rpc.cpp
Normal file
107
src/coordination/coordinator_rpc.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_rpc.hpp"
|
||||
|
||||
#include "coordination/coordinator_slk.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
|
||||
namespace memgraph {
|
||||
|
||||
namespace coordination {
|
||||
|
||||
void PromoteReplicaToMainReq::Save(const PromoteReplicaToMainReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void PromoteReplicaToMainReq::Load(PromoteReplicaToMainReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void PromoteReplicaToMainRes::Save(const PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void PromoteReplicaToMainRes::Load(PromoteReplicaToMainRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void SetMainToReplicaReq::Save(const SetMainToReplicaReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void SetMainToReplicaReq::Load(SetMainToReplicaReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void SetMainToReplicaRes::Save(const SetMainToReplicaRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
|
||||
void SetMainToReplicaRes::Load(SetMainToReplicaRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
} // namespace coordination
|
||||
|
||||
constexpr utils::TypeInfo coordination::PromoteReplicaToMainReq::kType{utils::TypeId::COORD_FAILOVER_REQ,
|
||||
"CoordPromoteReplicaToMainReq", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::PromoteReplicaToMainRes::kType{utils::TypeId::COORD_FAILOVER_RES,
|
||||
"CoordPromoteReplicaToMainRes", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::SetMainToReplicaReq::kType{utils::TypeId::COORD_SET_REPL_MAIN_REQ,
|
||||
"CoordDemoteToReplicaReq", nullptr};
|
||||
|
||||
constexpr utils::TypeInfo coordination::SetMainToReplicaRes::kType{utils::TypeId::COORD_SET_REPL_MAIN_RES,
|
||||
"CoordDemoteToReplicaRes", nullptr};
|
||||
|
||||
namespace slk {
|
||||
|
||||
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.success, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::PromoteReplicaToMainRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->success, reader);
|
||||
}
|
||||
|
||||
void Save(const memgraph::coordination::PromoteReplicaToMainReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.replication_clients_info, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::PromoteReplicaToMainReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->replication_clients_info, reader);
|
||||
}
|
||||
|
||||
void Save(const memgraph::coordination::SetMainToReplicaReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.replication_client_info, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::SetMainToReplicaReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->replication_client_info, reader);
|
||||
}
|
||||
|
||||
void Save(const memgraph::coordination::SetMainToReplicaRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.success, builder);
|
||||
}
|
||||
|
||||
void Load(memgraph::coordination::SetMainToReplicaRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(&self->success, reader);
|
||||
}
|
||||
|
||||
} // namespace slk
|
||||
|
||||
} // namespace memgraph
|
||||
|
||||
#endif
|
||||
57
src/coordination/coordinator_server.cpp
Normal file
57
src/coordination/coordinator_server.cpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
namespace {
|
||||
|
||||
auto CreateServerContext(const memgraph::coordination::CoordinatorServerConfig &config)
|
||||
-> communication::ServerContext {
|
||||
return (config.ssl) ? communication::ServerContext{config.ssl->key_file, config.ssl->cert_file, config.ssl->ca_file,
|
||||
config.ssl->verify_peer}
|
||||
: communication::ServerContext{};
|
||||
}
|
||||
|
||||
// NOTE: The coordinator server doesn't more than 1 processing thread - each replica can
|
||||
// have only a single coordinator server. Also, the single-threaded guarantee
|
||||
// simplifies the rest of the implementation.
|
||||
constexpr auto kCoordinatorServerThreads = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
CoordinatorServer::CoordinatorServer(const CoordinatorServerConfig &config)
|
||||
: rpc_server_context_{CreateServerContext(config)},
|
||||
rpc_server_{io::network::Endpoint{config.ip_address, config.port}, &rpc_server_context_,
|
||||
kCoordinatorServerThreads} {
|
||||
rpc_server_.Register<replication_coordination_glue::FrequentHeartbeatRpc>([](auto *req_reader, auto *res_builder) {
|
||||
spdlog::debug("Received FrequentHeartbeatRpc on coordinator server");
|
||||
replication_coordination_glue::FrequentHeartbeatHandler(req_reader, res_builder);
|
||||
});
|
||||
}
|
||||
|
||||
CoordinatorServer::~CoordinatorServer() {
|
||||
if (rpc_server_.IsRunning()) {
|
||||
auto const &endpoint = rpc_server_.endpoint();
|
||||
spdlog::trace("Closing coordinator server on {}:{}", endpoint.address, endpoint.port);
|
||||
rpc_server_.Shutdown();
|
||||
}
|
||||
rpc_server_.AwaitShutdown();
|
||||
}
|
||||
|
||||
bool CoordinatorServer::Start() { return rpc_server_.Start(); }
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
89
src/coordination/coordinator_state.cpp
Normal file
89
src/coordination/coordinator_state.cpp
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_state.hpp"
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "flags/replication.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "utils/logging.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
CoordinatorState::CoordinatorState() {
|
||||
MG_ASSERT(!(FLAGS_coordinator && FLAGS_coordinator_server_port),
|
||||
"Instance cannot be a coordinator and have registered coordinator server.");
|
||||
|
||||
spdlog::info("Executing coordinator constructor");
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
spdlog::info("Coordinator server port set");
|
||||
auto const config = CoordinatorServerConfig{
|
||||
.ip_address = kDefaultReplicationServerIp,
|
||||
.port = static_cast<uint16_t>(FLAGS_coordinator_server_port),
|
||||
};
|
||||
spdlog::info("Executing coordinator constructor main replica");
|
||||
|
||||
data_ = CoordinatorMainReplicaData{.coordinator_server_ = std::make_unique<CoordinatorServer>(config)};
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorState::RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{
|
||||
[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return RegisterInstanceCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[config](CoordinatorData &coordinator_data) { return coordinator_data.RegisterInstance(config); }},
|
||||
data_);
|
||||
}
|
||||
|
||||
auto CoordinatorState::SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
|
||||
return std::visit(
|
||||
memgraph::utils::Overloaded{[](const CoordinatorMainReplicaData & /*coordinator_main_replica_data*/) {
|
||||
return SetInstanceToMainCoordinatorStatus::NOT_COORDINATOR;
|
||||
},
|
||||
[&instance_name](CoordinatorData &coordinator_data) {
|
||||
return coordinator_data.SetInstanceToMain(instance_name);
|
||||
}},
|
||||
data_);
|
||||
}
|
||||
|
||||
auto CoordinatorState::ShowInstances() const -> std::vector<CoordinatorInstanceStatus> {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_),
|
||||
"Can't call show instances on data_, as variant holds wrong alternative");
|
||||
return std::get<CoordinatorData>(data_).ShowInstances();
|
||||
}
|
||||
|
||||
[[nodiscard]] auto CoordinatorState::DoFailover() -> DoFailoverStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorData>(data_), "Cannot do failover since variant holds wrong alternative");
|
||||
auto &coord_state = std::get<CoordinatorData>(data_);
|
||||
return coord_state.DoFailover();
|
||||
}
|
||||
|
||||
auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorMainReplicaData>(data_),
|
||||
"Cannot get coordinator server since variant holds wrong alternative");
|
||||
return *std::get<CoordinatorMainReplicaData>(data_).coordinator_server_;
|
||||
}
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
22
src/coordination/include/coordination/constants.hpp
Normal file
22
src/coordination/include/coordination/constants.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
#ifdef MG_EXPERIMENTAL_HIGH_AVAILABILITY
|
||||
constexpr bool allow_ha = true;
|
||||
#else
|
||||
constexpr bool allow_ha = false;
|
||||
#endif
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
72
src/coordination/include/coordination/coordinator_client.hpp
Normal file
72
src/coordination/include/coordination/coordinator_client.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "rpc/client.hpp"
|
||||
#include "utils/scheduler.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorData;
|
||||
using HealthCheckCallback = std::function<void(CoordinatorData *, std::string_view)>;
|
||||
using ReplicationClientsInfo = std::vector<ReplClientInfo>;
|
||||
|
||||
class CoordinatorClient {
|
||||
public:
|
||||
explicit CoordinatorClient(CoordinatorData *coord_data_, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||
HealthCheckCallback fail_cb);
|
||||
|
||||
~CoordinatorClient() = default;
|
||||
|
||||
CoordinatorClient(CoordinatorClient &) = delete;
|
||||
CoordinatorClient &operator=(CoordinatorClient const &) = delete;
|
||||
|
||||
CoordinatorClient(CoordinatorClient &&) noexcept = delete;
|
||||
CoordinatorClient &operator=(CoordinatorClient &&) noexcept = delete;
|
||||
|
||||
void StartFrequentCheck();
|
||||
void StopFrequentCheck();
|
||||
void PauseFrequentCheck();
|
||||
void ResumeFrequentCheck();
|
||||
|
||||
auto InstanceName() const -> std::string;
|
||||
auto SocketAddress() const -> std::string;
|
||||
|
||||
[[nodiscard]] auto SendPromoteReplicaToMainRpc(ReplicationClientsInfo replication_clients_info) const -> bool;
|
||||
[[nodiscard]] auto DemoteToReplica() const -> bool;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
|
||||
auto SetCallbacks(HealthCheckCallback succ_cb, HealthCheckCallback fail_cb) -> void;
|
||||
|
||||
friend bool operator==(CoordinatorClient const &first, CoordinatorClient const &second) {
|
||||
return first.config_ == second.config_;
|
||||
}
|
||||
|
||||
private:
|
||||
utils::Scheduler instance_checker_;
|
||||
|
||||
// TODO: (andi) Pimpl?
|
||||
communication::ClientContext rpc_context_;
|
||||
mutable rpc::Client rpc_client_;
|
||||
|
||||
CoordinatorClientConfig config_;
|
||||
CoordinatorData *coord_data_;
|
||||
HealthCheckCallback succ_cb_;
|
||||
HealthCheckCallback fail_cb_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
namespace memgraph::coordination {
|
||||
|
||||
struct CoordinatorClusterConfig {
|
||||
static constexpr int alive_response_time_difference_sec_{5};
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
77
src/coordination/include/coordination/coordinator_config.hpp
Normal file
77
src/coordination/include/coordination/coordinator_config.hpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "replication_coordination_glue/mode.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
inline constexpr auto *kDefaultReplicationServerIp = "0.0.0.0";
|
||||
|
||||
struct CoordinatorClientConfig {
|
||||
std::string instance_name;
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
std::chrono::seconds health_check_frequency_sec{1};
|
||||
|
||||
auto SocketAddress() const -> std::string { return ip_address + ":" + std::to_string(port); }
|
||||
|
||||
struct ReplicationClientInfo {
|
||||
std::string instance_name;
|
||||
replication_coordination_glue::ReplicationMode replication_mode{};
|
||||
std::string replication_ip_address;
|
||||
uint16_t replication_port{};
|
||||
|
||||
friend bool operator==(ReplicationClientInfo const &, ReplicationClientInfo const &) = default;
|
||||
};
|
||||
|
||||
ReplicationClientInfo replication_client_info;
|
||||
|
||||
struct SSL {
|
||||
std::string key_file;
|
||||
std::string cert_file;
|
||||
|
||||
friend bool operator==(const SSL &, const SSL &) = default;
|
||||
};
|
||||
|
||||
std::optional<SSL> ssl;
|
||||
|
||||
friend bool operator==(CoordinatorClientConfig const &, CoordinatorClientConfig const &) = default;
|
||||
};
|
||||
|
||||
using ReplClientInfo = CoordinatorClientConfig::ReplicationClientInfo;
|
||||
|
||||
struct CoordinatorServerConfig {
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
struct SSL {
|
||||
std::string key_file;
|
||||
std::string cert_file;
|
||||
std::string ca_file;
|
||||
bool verify_peer{};
|
||||
friend bool operator==(SSL const &, SSL const &) = default;
|
||||
};
|
||||
|
||||
std::optional<SSL> ssl;
|
||||
|
||||
friend bool operator==(CoordinatorServerConfig const &, CoordinatorServerConfig const &) = default;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
53
src/coordination/include/coordination/coordinator_data.hpp
Normal file
53
src/coordination/include/coordination/coordinator_data.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
#include "coordination/coordinator_instance_status.hpp"
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "coordination/failover_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "utils/rw_lock.hpp"
|
||||
#include "utils/thread_pool.hpp"
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
class CoordinatorData {
|
||||
public:
|
||||
CoordinatorData();
|
||||
|
||||
[[nodiscard]] auto DoFailover() -> DoFailoverStatus;
|
||||
|
||||
[[nodiscard]] auto RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<CoordinatorInstanceStatus>;
|
||||
|
||||
private:
|
||||
auto ClusterHasAliveMain() const -> bool;
|
||||
|
||||
mutable utils::RWLock coord_data_lock_{utils::RWLock::Priority::READ};
|
||||
HealthCheckCallback main_succ_cb_, main_fail_cb_, replica_succ_cb_, replica_fail_cb_;
|
||||
// Must be std::list because we rely on pointer stability
|
||||
std::list<CoordinatorInstance> registered_instances_;
|
||||
utils::ThreadPool thread_pool_{1};
|
||||
};
|
||||
|
||||
struct CoordinatorMainReplicaData {
|
||||
std::unique_ptr<CoordinatorServer> coordinator_server_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
class CoordinatorRegisterInstanceException final : public utils::BasicException {
|
||||
public:
|
||||
explicit CoordinatorRegisterInstanceException(const std::string_view what) noexcept
|
||||
: BasicException("Failed to create instance: " + std::string(what)) {}
|
||||
|
||||
template <class... Args>
|
||||
explicit CoordinatorRegisterInstanceException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
||||
: CoordinatorRegisterInstanceException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(CoordinatorRegisterInstanceException)
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -0,0 +1,68 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_client.hpp"
|
||||
#include "coordination/coordinator_cluster_config.hpp"
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorData;
|
||||
|
||||
class CoordinatorInstance {
|
||||
public:
|
||||
CoordinatorInstance(CoordinatorData *data, CoordinatorClientConfig config, HealthCheckCallback succ_cb,
|
||||
HealthCheckCallback fail_cb);
|
||||
|
||||
CoordinatorInstance(CoordinatorInstance const &other) = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance const &other) = delete;
|
||||
CoordinatorInstance(CoordinatorInstance &&other) noexcept = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance &&other) noexcept = delete;
|
||||
~CoordinatorInstance() = default;
|
||||
|
||||
auto OnSuccessPing() -> void;
|
||||
auto OnFailPing() -> bool;
|
||||
|
||||
auto IsAlive() const -> bool;
|
||||
|
||||
auto InstanceName() const -> std::string;
|
||||
auto SocketAddress() const -> std::string;
|
||||
|
||||
auto IsReplica() const -> bool;
|
||||
auto IsMain() const -> bool;
|
||||
|
||||
auto PromoteToMain(ReplicationClientsInfo repl_clients_info, HealthCheckCallback main_succ_cb,
|
||||
HealthCheckCallback main_fail_cb) -> bool;
|
||||
auto DemoteToReplica(HealthCheckCallback replica_succ_cb, HealthCheckCallback replica_fail_cb) -> bool;
|
||||
|
||||
auto PauseFrequentCheck() -> void;
|
||||
auto ResumeFrequentCheck() -> void;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
|
||||
private:
|
||||
CoordinatorClient client_;
|
||||
replication_coordination_glue::ReplicationRole replication_role_;
|
||||
std::chrono::system_clock::time_point last_response_time_{};
|
||||
bool is_alive_{false};
|
||||
|
||||
friend bool operator==(CoordinatorInstance const &first, CoordinatorInstance const &second) {
|
||||
return first.client_ == second.client_ && first.replication_role_ == second.replication_role_;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "io/network/endpoint.hpp"
|
||||
|
||||
#include <string_view>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
struct CoordinatorInstanceStatus {
|
||||
std::string instance_name;
|
||||
std::string socket_address;
|
||||
std::string replication_role;
|
||||
bool is_alive;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
#endif
|
||||
104
src/coordination/include/coordination/coordinator_rpc.hpp
Normal file
104
src/coordination/include/coordination/coordinator_rpc.hpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "rpc/messages.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
struct PromoteReplicaToMainReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(PromoteReplicaToMainReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const PromoteReplicaToMainReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit PromoteReplicaToMainReq(std::vector<CoordinatorClientConfig::ReplicationClientInfo> replication_clients_info)
|
||||
: replication_clients_info(std::move(replication_clients_info)) {}
|
||||
PromoteReplicaToMainReq() = default;
|
||||
|
||||
std::vector<CoordinatorClientConfig::ReplicationClientInfo> replication_clients_info;
|
||||
};
|
||||
|
||||
struct PromoteReplicaToMainRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(PromoteReplicaToMainRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit PromoteReplicaToMainRes(bool success) : success(success) {}
|
||||
PromoteReplicaToMainRes() = default;
|
||||
|
||||
bool success;
|
||||
};
|
||||
|
||||
using PromoteReplicaToMainRpc = rpc::RequestResponse<PromoteReplicaToMainReq, PromoteReplicaToMainRes>;
|
||||
|
||||
struct SetMainToReplicaReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(SetMainToReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const SetMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit SetMainToReplicaReq(CoordinatorClientConfig::ReplicationClientInfo replication_client_info)
|
||||
: replication_client_info(std::move(replication_client_info)) {}
|
||||
|
||||
SetMainToReplicaReq() = default;
|
||||
|
||||
CoordinatorClientConfig::ReplicationClientInfo replication_client_info;
|
||||
};
|
||||
|
||||
struct SetMainToReplicaRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(SetMainToReplicaRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const SetMainToReplicaRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit SetMainToReplicaRes(bool success) : success(success) {}
|
||||
SetMainToReplicaRes() = default;
|
||||
|
||||
bool success;
|
||||
};
|
||||
|
||||
using SetMainToReplicaRpc = rpc::RequestResponse<SetMainToReplicaReq, SetMainToReplicaRes>;
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
||||
// SLK serialization declarations
|
||||
namespace memgraph::slk {
|
||||
|
||||
void Save(const memgraph::coordination::PromoteReplicaToMainRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
void Load(memgraph::coordination::PromoteReplicaToMainRes *self, memgraph::slk::Reader *reader);
|
||||
|
||||
void Save(const memgraph::coordination::PromoteReplicaToMainReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
void Load(memgraph::coordination::PromoteReplicaToMainReq *self, memgraph::slk::Reader *reader);
|
||||
|
||||
void Save(const memgraph::coordination::SetMainToReplicaRes &self, memgraph::slk::Builder *builder);
|
||||
|
||||
void Load(memgraph::coordination::SetMainToReplicaRes *self, memgraph::slk::Reader *reader);
|
||||
|
||||
void Save(const memgraph::coordination::SetMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
void Load(memgraph::coordination::SetMainToReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
|
||||
} // namespace memgraph::slk
|
||||
|
||||
#endif
|
||||
44
src/coordination/include/coordination/coordinator_server.hpp
Normal file
44
src/coordination/include/coordination/coordinator_server.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "rpc/server.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorServer {
|
||||
public:
|
||||
explicit CoordinatorServer(const CoordinatorServerConfig &config);
|
||||
CoordinatorServer(const CoordinatorServer &) = delete;
|
||||
CoordinatorServer(CoordinatorServer &&) = delete;
|
||||
CoordinatorServer &operator=(const CoordinatorServer &) = delete;
|
||||
CoordinatorServer &operator=(CoordinatorServer &&) = delete;
|
||||
|
||||
virtual ~CoordinatorServer();
|
||||
|
||||
bool Start();
|
||||
|
||||
template <typename TRequestResponse, typename F>
|
||||
void Register(F &&callback) {
|
||||
rpc_server_.Register<TRequestResponse>(std::forward<F>(callback));
|
||||
}
|
||||
|
||||
private:
|
||||
communication::ServerContext rpc_server_context_;
|
||||
rpc::Server rpc_server_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
38
src/coordination/include/coordination/coordinator_slk.hpp
Normal file
38
src/coordination/include/coordination/coordinator_slk.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
#include "slk/streams.hpp"
|
||||
|
||||
namespace memgraph::slk {
|
||||
|
||||
using ReplicationClientInfo = coordination::CoordinatorClientConfig::ReplicationClientInfo;
|
||||
|
||||
inline void Save(const ReplicationClientInfo &obj, Builder *builder) {
|
||||
Save(obj.instance_name, builder);
|
||||
Save(obj.replication_mode, builder);
|
||||
Save(obj.replication_ip_address, builder);
|
||||
Save(obj.replication_port, builder);
|
||||
}
|
||||
|
||||
inline void Load(ReplicationClientInfo *obj, Reader *reader) {
|
||||
Load(&obj->instance_name, reader);
|
||||
Load(&obj->replication_mode, reader);
|
||||
Load(&obj->replication_ip_address, reader);
|
||||
Load(&obj->replication_port, reader);
|
||||
}
|
||||
} // namespace memgraph::slk
|
||||
#endif
|
||||
53
src/coordination/include/coordination/coordinator_state.hpp
Normal file
53
src/coordination/include/coordination/coordinator_state.hpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_data.hpp"
|
||||
#include "coordination/coordinator_instance_status.hpp"
|
||||
#include "coordination/coordinator_server.hpp"
|
||||
#include "coordination/failover_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
|
||||
#include <variant>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorState {
|
||||
public:
|
||||
CoordinatorState();
|
||||
~CoordinatorState() = default;
|
||||
|
||||
CoordinatorState(const CoordinatorState &) = delete;
|
||||
CoordinatorState &operator=(const CoordinatorState &) = delete;
|
||||
|
||||
CoordinatorState(CoordinatorState &&) noexcept = delete;
|
||||
CoordinatorState &operator=(CoordinatorState &&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] auto RegisterInstance(CoordinatorClientConfig config) -> RegisterInstanceCoordinatorStatus;
|
||||
|
||||
[[nodiscard]] auto SetInstanceToMain(std::string instance_name) -> SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<CoordinatorInstanceStatus>;
|
||||
|
||||
// The client code must check that the server exists before calling this method.
|
||||
auto GetCoordinatorServer() const -> CoordinatorServer &;
|
||||
|
||||
[[nodiscard]] auto DoFailover() -> DoFailoverStatus;
|
||||
|
||||
private:
|
||||
std::variant<CoordinatorData, CoordinatorMainReplicaData> data_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
21
src/coordination/include/coordination/failover_status.hpp
Normal file
21
src/coordination/include/coordination/failover_status.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
enum class DoFailoverStatus : uint8_t { SUCCESS, ALL_REPLICAS_DOWN, MAIN_ALIVE, RPC_FAILED };
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -0,0 +1,36 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
enum class RegisterInstanceCoordinatorStatus : uint8_t {
|
||||
NAME_EXISTS,
|
||||
END_POINT_EXISTS,
|
||||
NOT_COORDINATOR,
|
||||
RPC_FAILED,
|
||||
SUCCESS
|
||||
};
|
||||
|
||||
enum class SetInstanceToMainCoordinatorStatus : uint8_t {
|
||||
NO_INSTANCE_WITH_NAME,
|
||||
NOT_COORDINATOR,
|
||||
SUCCESS,
|
||||
COULD_NOT_PROMOTE_TO_MAIN,
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
@@ -1,3 +1,2 @@
|
||||
|
||||
add_library(mg-dbms STATIC dbms_handler.cpp database.cpp replication_handler.cpp replication_client.cpp inmemory/replication_handlers.cpp)
|
||||
target_link_libraries(mg-dbms mg-utils mg-storage-v2 mg-query)
|
||||
add_library(mg-dbms STATIC dbms_handler.cpp database.cpp replication_handler.cpp coordinator_handler.cpp replication_client.cpp inmemory/replication_handlers.cpp coordinator_handlers.cpp)
|
||||
target_link_libraries(mg-dbms mg-utils mg-storage-v2 mg-query mg-replication mg-coordination)
|
||||
|
||||
38
src/dbms/coordinator_handler.cpp
Normal file
38
src/dbms/coordinator_handler.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "dbms/coordinator_handler.hpp"
|
||||
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
CoordinatorHandler::CoordinatorHandler(DbmsHandler &dbms_handler) : dbms_handler_(dbms_handler) {}
|
||||
|
||||
auto CoordinatorHandler::RegisterInstance(memgraph::coordination::CoordinatorClientConfig config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus {
|
||||
return dbms_handler_.CoordinatorState().RegisterInstance(config);
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::SetInstanceToMain(std::string instance_name)
|
||||
-> coordination::SetInstanceToMainCoordinatorStatus {
|
||||
return dbms_handler_.CoordinatorState().SetInstanceToMain(std::move(instance_name));
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::ShowInstances() const -> std::vector<coordination::CoordinatorInstanceStatus> {
|
||||
return dbms_handler_.CoordinatorState().ShowInstances();
|
||||
}
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
#endif
|
||||
47
src/dbms/coordinator_handler.hpp
Normal file
47
src/dbms/coordinator_handler.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "utils/result.hpp"
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_instance_status.hpp"
|
||||
#include "coordination/failover_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
class DbmsHandler;
|
||||
|
||||
class CoordinatorHandler {
|
||||
public:
|
||||
explicit CoordinatorHandler(DbmsHandler &dbms_handler);
|
||||
|
||||
auto RegisterInstance(coordination::CoordinatorClientConfig config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus;
|
||||
|
||||
auto SetInstanceToMain(std::string instance_name) -> coordination::SetInstanceToMainCoordinatorStatus;
|
||||
|
||||
auto ShowInstances() const -> std::vector<coordination::CoordinatorInstanceStatus>;
|
||||
|
||||
private:
|
||||
DbmsHandler &dbms_handler_;
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
#endif
|
||||
155
src/dbms/coordinator_handlers.cpp
Normal file
155
src/dbms/coordinator_handlers.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "dbms/coordinator_handlers.hpp"
|
||||
#include "dbms/utils.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "coordination/coordinator_rpc.hpp"
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
#include "dbms/replication_client.hpp"
|
||||
|
||||
#include "range/v3/view.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
void CoordinatorHandlers::Register(DbmsHandler &dbms_handler) {
|
||||
auto &server = dbms_handler.CoordinatorState().GetCoordinatorServer();
|
||||
|
||||
server.Register<coordination::PromoteReplicaToMainRpc>(
|
||||
[&dbms_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
||||
spdlog::info("Received PromoteReplicaToMainRpc");
|
||||
CoordinatorHandlers::PromoteReplicaToMainHandler(dbms_handler, req_reader, res_builder);
|
||||
});
|
||||
|
||||
server.Register<coordination::SetMainToReplicaRpc>(
|
||||
[&dbms_handler](slk::Reader *req_reader, slk::Builder *res_builder) -> void {
|
||||
spdlog::info("Received SetMainToReplicaRpc from coordinator server");
|
||||
CoordinatorHandlers::SetMainToReplicaHandler(dbms_handler, req_reader, res_builder);
|
||||
});
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::SetMainToReplicaHandler(DbmsHandler &dbms_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder) {
|
||||
auto &repl_state = dbms_handler.ReplicationState();
|
||||
spdlog::info("Executing SetMainToReplicaHandler");
|
||||
|
||||
if (repl_state.IsReplica()) {
|
||||
spdlog::error("Setting to replica must be performed on main.");
|
||||
slk::Save(coordination::SetMainToReplicaRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
coordination::SetMainToReplicaReq req;
|
||||
slk::Load(&req, req_reader);
|
||||
|
||||
const replication::ReplicationServerConfig clients_config{
|
||||
.ip_address = req.replication_client_info.replication_ip_address,
|
||||
.port = req.replication_client_info.replication_port};
|
||||
|
||||
if (bool success = memgraph::dbms::SetReplicationRoleReplica(dbms_handler, clients_config); !success) {
|
||||
spdlog::error("Setting main to replica failed!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder);
|
||||
}
|
||||
|
||||
void CoordinatorHandlers::PromoteReplicaToMainHandler(DbmsHandler &dbms_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder) {
|
||||
auto &repl_state = dbms_handler.ReplicationState();
|
||||
|
||||
if (!repl_state.IsReplica()) {
|
||||
spdlog::error("Only replica can be promoted to main!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
auto repl_server_config = std::get<replication::RoleReplicaData>(repl_state.ReplicationData()).config;
|
||||
|
||||
// This can fail because of disk. If it does, the cluster state could get inconsistent.
|
||||
// We don't handle disk issues.
|
||||
if (bool success = memgraph::dbms::DoReplicaToMainPromotion(dbms_handler); !success) {
|
||||
spdlog::error("Promoting replica to main failed!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
}
|
||||
|
||||
coordination::PromoteReplicaToMainReq req;
|
||||
slk::Load(&req, req_reader);
|
||||
|
||||
auto const converter = [](const auto &repl_info_config) {
|
||||
return replication::ReplicationClientConfig{
|
||||
.name = repl_info_config.instance_name,
|
||||
.mode = repl_info_config.replication_mode,
|
||||
.ip_address = repl_info_config.replication_ip_address,
|
||||
.port = repl_info_config.replication_port,
|
||||
};
|
||||
};
|
||||
|
||||
MG_ASSERT(
|
||||
std::get<replication::RoleMainData>(repl_state.ReplicationData()).registered_replicas_.empty(),
|
||||
"No replicas should be registered after promoting replica to main and before registering replication clients!");
|
||||
|
||||
// registering replicas
|
||||
for (auto const &config : req.replication_clients_info | ranges::views::transform(converter)) {
|
||||
auto instance_client = repl_state.RegisterReplica(config);
|
||||
if (instance_client.HasError()) {
|
||||
switch (instance_client.GetError()) {
|
||||
// Can't happen, we are already replica
|
||||
case memgraph::replication::RegisterReplicaError::NOT_MAIN:
|
||||
spdlog::error("Failover must be performed to main!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
// Can't happen, checked on the coordinator side
|
||||
case memgraph::replication::RegisterReplicaError::NAME_EXISTS:
|
||||
spdlog::error("Replica with the same name already exists!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
// Can't happen, checked on the coordinator side
|
||||
case memgraph::replication::RegisterReplicaError::ENDPOINT_EXISTS:
|
||||
spdlog::error("Replica with the same endpoint already exists!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
// We don't handle disk issues
|
||||
case memgraph::replication::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
|
||||
spdlog::error("Registered replica could not be persisted!");
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{false}, res_builder);
|
||||
return;
|
||||
case memgraph::replication::RegisterReplicaError::SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allow_mt_repl && dbms_handler.All().size() > 1) {
|
||||
spdlog::warn("Multi-tenant replication is currently not supported!");
|
||||
}
|
||||
|
||||
auto &instance_client_ref = *instance_client.GetValue();
|
||||
|
||||
// Update system before enabling individual storage <-> replica clients
|
||||
dbms_handler.SystemRestore(instance_client_ref);
|
||||
|
||||
// TODO: (andi) Policy for register all databases
|
||||
// Will be resolved after deciding about choosing new replica
|
||||
const bool all_clients_good = memgraph::dbms::RegisterAllDatabasesClients(dbms_handler, instance_client_ref);
|
||||
MG_ASSERT(all_clients_good, "Failed to register one or more databases to the REPLICA \"{}\".", config.name);
|
||||
|
||||
StartReplicaClient(dbms_handler, instance_client_ref);
|
||||
}
|
||||
|
||||
slk::Save(coordination::PromoteReplicaToMainRes{true}, res_builder);
|
||||
}
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
#endif
|
||||
34
src/dbms/coordinator_handlers.hpp
Normal file
34
src/dbms/coordinator_handlers.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "slk/serialization.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
class DbmsHandler;
|
||||
|
||||
class CoordinatorHandlers {
|
||||
public:
|
||||
static void Register(DbmsHandler &dbms_handler);
|
||||
|
||||
private:
|
||||
static void PromoteReplicaToMainHandler(DbmsHandler &dbms_handler, slk::Reader *req_reader,
|
||||
slk::Builder *res_builder);
|
||||
static void SetMainToReplicaHandler(DbmsHandler &dbms_handler, slk::Reader *req_reader, slk::Builder *res_builder);
|
||||
};
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
||||
#endif
|
||||
@@ -110,7 +110,7 @@ class Database {
|
||||
* @param force_directory Use the configured directory, do not try to decipher the multi-db version
|
||||
* @return DatabaseInfo
|
||||
*/
|
||||
DatabaseInfo GetInfo(bool force_directory, replication::ReplicationRole replication_role) const {
|
||||
DatabaseInfo GetInfo(bool force_directory, replication_coordination_glue::ReplicationRole replication_role) const {
|
||||
DatabaseInfo info;
|
||||
info.storage_info = storage_->GetInfo(force_directory, replication_role);
|
||||
info.triggers = trigger_store_.GetTriggerInfo().size();
|
||||
|
||||
@@ -11,6 +11,9 @@
|
||||
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
|
||||
#include "dbms/coordinator_handlers.hpp"
|
||||
#include "flags/replication.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
|
||||
@@ -225,6 +228,12 @@ DbmsHandler::DbmsHandler(
|
||||
"enabling durability by using --storage-snapshot-interval-sec and --storage-wal-enabled flags because "
|
||||
"without write-ahead logs this instance is not replicating any data.");
|
||||
}
|
||||
|
||||
// MAIN or REPLICA instance
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
CoordinatorHandlers::Register(*this);
|
||||
MG_ASSERT(coordinator_state_.GetCoordinatorServer().Start(), "Failed to start coordinator server!");
|
||||
}
|
||||
}
|
||||
|
||||
DbmsHandler::DeleteResult DbmsHandler::TryDelete(std::string_view db_name) {
|
||||
@@ -359,7 +368,7 @@ AllSyncReplicaStatus DbmsHandler::Commit() {
|
||||
|
||||
auto sync_status = AllSyncReplicaStatus::AllCommitsConfirmed;
|
||||
// TODO Create a system client that can handle all of this automatically
|
||||
switch (delta.action) { // TODO for
|
||||
switch (delta.action) {
|
||||
using enum SystemTransaction::Delta::Action;
|
||||
case CREATE_DATABASE: {
|
||||
// Replication
|
||||
@@ -375,7 +384,7 @@ AllSyncReplicaStatus DbmsHandler::Commit() {
|
||||
std::string(main_data.epoch_.id()), last_commited_system_timestamp_,
|
||||
system_transaction_->system_timestamp, delta.config);
|
||||
// TODO: reduce duplicate code
|
||||
if (!completed && client.mode_ == replication::ReplicationMode::SYNC) {
|
||||
if (!completed && client.mode_ == replication_coordination_glue::ReplicationMode::SYNC) {
|
||||
sync_status = AllSyncReplicaStatus::SomeCommitsUnconfirmed;
|
||||
}
|
||||
}
|
||||
@@ -399,7 +408,7 @@ AllSyncReplicaStatus DbmsHandler::Commit() {
|
||||
std::string(main_data.epoch_.id()), last_commited_system_timestamp_,
|
||||
system_transaction_->system_timestamp, delta.uuid);
|
||||
// TODO: reduce duplicate code
|
||||
if (!completed && client.mode_ == replication::ReplicationMode::SYNC) {
|
||||
if (!completed && client.mode_ == replication_coordination_glue::ReplicationMode::SYNC) {
|
||||
sync_status = AllSyncReplicaStatus::SomeCommitsUnconfirmed;
|
||||
}
|
||||
}
|
||||
@@ -407,9 +416,6 @@ AllSyncReplicaStatus DbmsHandler::Commit() {
|
||||
auto replica_handler = [](memgraph::replication::RoleReplicaData &) { /* Nothing to do */ };
|
||||
std::visit(utils::Overloaded{main_handler, replica_handler}, repl_state_.ReplicationData());
|
||||
} break;
|
||||
case UPDATE_AUTH_DATA: // TODO
|
||||
case DROP_AUTH_DATA: // TODO
|
||||
break;
|
||||
}
|
||||
|
||||
durability_->Put(kLastCommitedSystemTsKey, std::to_string(system_transaction_->system_timestamp));
|
||||
@@ -430,8 +436,6 @@ AllSyncReplicaStatus DbmsHandler::Commit() {
|
||||
using enum SystemTransaction::Delta::Action;
|
||||
case CREATE_DATABASE:
|
||||
case DROP_DATABASE:
|
||||
case UPDATE_AUTH_DATA: // TODO Do we want to have auth replication under community?
|
||||
case DROP_AUTH_DATA: // TODO Do we want to have auth replication under community?
|
||||
/* Community edition doesn't support multi-tenant replication */
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "storage/v2/transaction.hpp"
|
||||
#include "utils/thread_pool.hpp"
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "coordination/coordinator_state.hpp"
|
||||
#include "dbms/database_handler.hpp"
|
||||
#endif
|
||||
#include "dbms/transaction.hpp"
|
||||
@@ -272,6 +273,10 @@ class DbmsHandler {
|
||||
bool IsMain() const { return repl_state_.IsMain(); }
|
||||
bool IsReplica() const { return repl_state_.IsReplica(); }
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
coordination::CoordinatorState &CoordinatorState() { return coordinator_state_; }
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Return the statistics all databases.
|
||||
*
|
||||
@@ -436,7 +441,7 @@ class DbmsHandler {
|
||||
return false;
|
||||
};
|
||||
|
||||
if (client.mode_ == memgraph::replication::ReplicationMode::ASYNC) {
|
||||
if (client.mode_ == memgraph::replication_coordination_glue::ReplicationMode::ASYNC) {
|
||||
client.thread_pool_.AddTask([task = utils::CopyMovableFunctionWrapper{std::move(task)}]() mutable { task(); });
|
||||
return true;
|
||||
}
|
||||
@@ -672,6 +677,7 @@ class DbmsHandler {
|
||||
storage::Config default_config_; //!< Storage configuration used when creating new databases
|
||||
DatabaseHandler db_handler_; //!< multi-tenancy storage handler
|
||||
std::unique_ptr<kvstore::KVStore> durability_; //!< list of active dbs (pointer so we can postpone its creation)
|
||||
coordination::CoordinatorState coordinator_state_; //!< Replication coordinator
|
||||
#endif
|
||||
// TODO: Make an api
|
||||
public:
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "storage/v2/inmemory/storage.hpp"
|
||||
#include "storage/v2/inmemory/unique_constraints.hpp"
|
||||
|
||||
using memgraph::replication::ReplicationRole;
|
||||
using memgraph::replication_coordination_glue::ReplicationRole;
|
||||
using memgraph::storage::Delta;
|
||||
using memgraph::storage::EdgeAccessor;
|
||||
using memgraph::storage::EdgeRef;
|
||||
|
||||
@@ -18,14 +18,14 @@
|
||||
#include "dbms/global.hpp"
|
||||
#include "dbms/inmemory/replication_handlers.hpp"
|
||||
#include "dbms/replication_client.hpp"
|
||||
#include "dbms/utils.hpp"
|
||||
#include "replication/messages.hpp"
|
||||
#include "replication/state.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "storage/v2/config.hpp"
|
||||
#include "storage/v2/replication/rpc.hpp"
|
||||
#include "utils/on_scope_exit.hpp"
|
||||
|
||||
using memgraph::replication::ReplicationClientConfig;
|
||||
using memgraph::replication::ReplicationState;
|
||||
using memgraph::replication::RoleMainData;
|
||||
using memgraph::replication::RoleReplicaData;
|
||||
|
||||
@@ -38,8 +38,8 @@ std::string RegisterReplicaErrorToString(RegisterReplicaError error) {
|
||||
using enum RegisterReplicaError;
|
||||
case NAME_EXISTS:
|
||||
return "NAME_EXISTS";
|
||||
case END_POINT_EXISTS:
|
||||
return "END_POINT_EXISTS";
|
||||
case ENDPOINT_EXISTS:
|
||||
return "ENDPOINT_EXISTS";
|
||||
case CONNECTION_FAILED:
|
||||
return "CONNECTION_FAILED";
|
||||
case COULD_NOT_BE_PERSISTED:
|
||||
@@ -51,34 +51,13 @@ std::string RegisterReplicaErrorToString(RegisterReplicaError error) {
|
||||
ReplicationHandler::ReplicationHandler(DbmsHandler &dbms_handler) : dbms_handler_(dbms_handler) {}
|
||||
|
||||
bool ReplicationHandler::SetReplicationRoleMain() {
|
||||
auto const main_handler = [](RoleMainData const &) {
|
||||
auto const main_handler = [](RoleMainData &) {
|
||||
// If we are already MAIN, we don't want to change anything
|
||||
return false;
|
||||
};
|
||||
|
||||
auto const replica_handler = [this](RoleReplicaData const &) {
|
||||
// STEP 1) bring down all REPLICA servers
|
||||
dbms_handler_.ForEach([](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
// Remember old epoch + storage timestamp association
|
||||
storage->PrepareForNewEpoch();
|
||||
});
|
||||
|
||||
// STEP 2) Change to MAIN
|
||||
// TODO: restore replication servers if false?
|
||||
if (!dbms_handler_.ReplicationState().SetReplicationRoleMain()) {
|
||||
// TODO: Handle recovery on failure???
|
||||
return false;
|
||||
}
|
||||
|
||||
// STEP 3) We are now MAIN, update storage local epoch
|
||||
const auto &epoch =
|
||||
std::get<RoleMainData>(std::as_const(dbms_handler_.ReplicationState()).ReplicationData()).epoch_;
|
||||
dbms_handler_.ForEach([&](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
storage->repl_storage_state_.epoch_ = epoch;
|
||||
});
|
||||
|
||||
return true;
|
||||
return memgraph::dbms::DoReplicaToMainPromotion(dbms_handler_);
|
||||
};
|
||||
|
||||
// TODO: under lock
|
||||
@@ -121,16 +100,16 @@ auto ReplicationHandler::RegisterReplica(const memgraph::replication::Replicatio
|
||||
-> memgraph::utils::BasicResult<RegisterReplicaError> {
|
||||
MG_ASSERT(dbms_handler_.ReplicationState().IsMain(), "Only main instance can register a replica!");
|
||||
|
||||
auto instance_client = dbms_handler_.ReplicationState().RegisterReplica(config);
|
||||
if (instance_client.HasError()) {
|
||||
switch (instance_client.GetError()) {
|
||||
auto maybe_client = dbms_handler_.ReplicationState().RegisterReplica(config);
|
||||
if (maybe_client.HasError()) {
|
||||
switch (maybe_client.GetError()) {
|
||||
case memgraph::replication::RegisterReplicaError::NOT_MAIN:
|
||||
MG_ASSERT(false, "Only main instance can register a replica!");
|
||||
return {};
|
||||
case memgraph::replication::RegisterReplicaError::NAME_EXISTS:
|
||||
return memgraph::dbms::RegisterReplicaError::NAME_EXISTS;
|
||||
case memgraph::replication::RegisterReplicaError::END_POINT_EXISTS:
|
||||
return memgraph::dbms::RegisterReplicaError::END_POINT_EXISTS;
|
||||
case memgraph::replication::RegisterReplicaError::ENDPOINT_EXISTS:
|
||||
return memgraph::dbms::RegisterReplicaError::ENDPOINT_EXISTS;
|
||||
case memgraph::replication::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
|
||||
return memgraph::dbms::RegisterReplicaError::COULD_NOT_BE_PERSISTED;
|
||||
case memgraph::replication::RegisterReplicaError::SUCCESS:
|
||||
@@ -144,35 +123,15 @@ auto ReplicationHandler::RegisterReplica(const memgraph::replication::Replicatio
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// Update system before enabling individual storage <-> replica clients
|
||||
dbms_handler_.SystemRestore(*instance_client.GetValue());
|
||||
dbms_handler_.SystemRestore(*maybe_client.GetValue());
|
||||
#endif
|
||||
|
||||
bool all_clients_good = true;
|
||||
|
||||
// Add database specific clients (NOTE Currently all databases are connected to each replica)
|
||||
dbms_handler_.ForEach([&](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
if (!allow_mt_repl && storage->name() != kDefaultDB) {
|
||||
return;
|
||||
}
|
||||
// TODO: ATM only IN_MEMORY_TRANSACTIONAL, fix other modes
|
||||
if (storage->storage_mode_ != storage::StorageMode::IN_MEMORY_TRANSACTIONAL) return;
|
||||
|
||||
all_clients_good &= storage->repl_storage_state_.replication_clients_.WithLock(
|
||||
[storage, &instance_client, db_acc = std::move(db_acc)](auto &storage_clients) mutable { // NOLINT
|
||||
auto client = std::make_unique<storage::ReplicationStorageClient>(*instance_client.GetValue());
|
||||
// All good, start replica client
|
||||
client->Start(storage, std::move(db_acc));
|
||||
// After start the storage <-> replica state should be READY or RECOVERING (if correctly started)
|
||||
// MAYBE_BEHIND isn't a statement of the current state, this is the default value
|
||||
// Failed to start due an error like branching of MAIN and REPLICA
|
||||
if (client->State() == storage::replication::ReplicaState::MAYBE_BEHIND) {
|
||||
return false;
|
||||
}
|
||||
storage_clients.push_back(std::move(client));
|
||||
return true;
|
||||
});
|
||||
});
|
||||
const auto dbms_error = memgraph::dbms::HandleRegisterReplicaStatus(maybe_client);
|
||||
if (dbms_error.has_value()) {
|
||||
return *dbms_error;
|
||||
}
|
||||
auto &instance_client_ptr = maybe_client.GetValue();
|
||||
const bool all_clients_good = memgraph::dbms::RegisterAllDatabasesClients(dbms_handler_, *instance_client_ptr);
|
||||
|
||||
// NOTE Currently if any databases fails, we revert back
|
||||
if (!all_clients_good) {
|
||||
@@ -182,7 +141,7 @@ auto ReplicationHandler::RegisterReplica(const memgraph::replication::Replicatio
|
||||
}
|
||||
|
||||
// No client error, start instance level client
|
||||
StartReplicaClient(dbms_handler_, *instance_client.GetValue());
|
||||
StartReplicaClient(dbms_handler_, *instance_client_ptr);
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -210,7 +169,7 @@ auto ReplicationHandler::UnregisterReplica(std::string_view name) -> UnregisterR
|
||||
dbms_handler_.ReplicationState().ReplicationData());
|
||||
}
|
||||
|
||||
auto ReplicationHandler::GetRole() const -> memgraph::replication::ReplicationRole {
|
||||
auto ReplicationHandler::GetRole() const -> memgraph::replication_coordination_glue::ReplicationRole {
|
||||
return dbms_handler_.ReplicationState().GetRole();
|
||||
}
|
||||
|
||||
@@ -331,7 +290,7 @@ void DropDatabaseHandler(DbmsHandler &dbms_handler, slk::Reader *req_reader, slk
|
||||
}
|
||||
|
||||
try {
|
||||
// NOTE: Single communication channel can exist at a time, no other database can be deleted/created aat the moment.
|
||||
// NOTE: Single communication channel can exist at a time, no other database can be deleted/created at the moment.
|
||||
auto new_db = dbms_handler.Delete(req.uuid);
|
||||
if (new_db.HasError()) {
|
||||
if (new_db.GetError() == DeleteError::NON_EXISTENT) {
|
||||
|
||||
@@ -11,11 +11,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
#include "dbms/database.hpp"
|
||||
#include "replication/role.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
// BEGIN fwd declares
|
||||
namespace memgraph::replication {
|
||||
struct ReplicationState;
|
||||
struct ReplicationServerConfig;
|
||||
@@ -23,9 +22,11 @@ struct ReplicationClientConfig;
|
||||
} // namespace memgraph::replication
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
class DbmsHandler;
|
||||
|
||||
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, END_POINT_EXISTS, CONNECTION_FAILED, COULD_NOT_BE_PERSISTED };
|
||||
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, ENDPOINT_EXISTS, CONNECTION_FAILED, COULD_NOT_BE_PERSISTED };
|
||||
|
||||
enum class UnregisterReplicaResult : uint8_t {
|
||||
NOT_MAIN,
|
||||
COULD_NOT_BE_PERSISTED,
|
||||
@@ -52,7 +53,7 @@ struct ReplicationHandler {
|
||||
auto UnregisterReplica(std::string_view name) -> UnregisterReplicaResult;
|
||||
|
||||
// Helper pass-through (TODO: remove)
|
||||
auto GetRole() const -> memgraph::replication::ReplicationRole;
|
||||
auto GetRole() const -> memgraph::replication_coordination_glue::ReplicationRole;
|
||||
bool IsMain() const;
|
||||
bool IsReplica() const;
|
||||
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include "auth/models.hpp"
|
||||
#include "storage/v2/config.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
@@ -22,64 +20,17 @@ struct SystemTransaction {
|
||||
enum class Action {
|
||||
CREATE_DATABASE,
|
||||
DROP_DATABASE,
|
||||
UPDATE_AUTH_DATA,
|
||||
DROP_AUTH_DATA,
|
||||
/**
|
||||
*
|
||||
* CREATE USER user_name [IDENTIFIED BY 'password'];
|
||||
* SET PASSWORD FOR user_name TO 'new_password';
|
||||
* ^ SaveUser
|
||||
*
|
||||
* DROP USER user_name;
|
||||
* ^ Directly on KVStore
|
||||
*
|
||||
* CREATE ROLE role_name;
|
||||
* ^ SaveRole
|
||||
*
|
||||
* DROP ROLE
|
||||
* ^ RemoveRole
|
||||
*
|
||||
* SET ROLE FOR user_name TO role_name;
|
||||
* CLEAR ROLE FOR user_name;
|
||||
* ^ Do stuff then do SaveUser
|
||||
*
|
||||
* GRANT privilege_list TO user_or_role;
|
||||
* DENY AUTH, INDEX TO moderator:
|
||||
* REVOKE AUTH, INDEX TO moderator:
|
||||
* GRANT permission_level ON (LABELS | EDGE_TYPES) label_list TO user_or_role;
|
||||
* REVOKE (LABELS | EDGE_TYPES) label_or_edge_type_list FROM user_or_role
|
||||
* DENY (LABELS | EDGE_TYPES) label_or_edge_type_list TO user_or_role
|
||||
* ^ all of these are EditPermissions <-> SaveUser/Role
|
||||
*
|
||||
* Multi-tenant TODO Doc;
|
||||
* ^ Should all call SaveUser
|
||||
*
|
||||
*/
|
||||
};
|
||||
|
||||
static constexpr struct CreateDatabase {
|
||||
} create_database;
|
||||
static constexpr struct DropDatabase {
|
||||
} drop_database;
|
||||
static constexpr struct UpdateAuthData {
|
||||
} update_auth_data;
|
||||
static constexpr struct DropAuthData {
|
||||
} drop_auth_data;
|
||||
|
||||
// Multi-tenancy
|
||||
Delta(CreateDatabase /*tag*/, storage::SalientConfig config)
|
||||
: action(Action::CREATE_DATABASE), config(std::move(config)) {}
|
||||
Delta(DropDatabase /*tag*/, const utils::UUID &uuid) : action(Action::DROP_DATABASE), uuid(uuid) {}
|
||||
|
||||
// Auth
|
||||
Delta(UpdateAuthData /*tag*/, std::optional<auth::User> user)
|
||||
: action(Action::UPDATE_AUTH_DATA), auth_data{std::move(user), std::nullopt} {}
|
||||
Delta(UpdateAuthData /*tag*/, std::optional<auth::Role> role)
|
||||
: action(Action::UPDATE_AUTH_DATA), auth_data{std::nullopt, std::move(role)} {}
|
||||
Delta(DropAuthData /*tag*/, std::string_view auth_data_key)
|
||||
: action(Action::DROP_AUTH_DATA), auth_data_key{auth_data_key} {}
|
||||
|
||||
// Generic
|
||||
Delta(const Delta &) = delete;
|
||||
Delta(Delta &&) = delete;
|
||||
Delta &operator=(const Delta &) = delete;
|
||||
@@ -91,14 +42,8 @@ struct SystemTransaction {
|
||||
std::destroy_at(&config);
|
||||
break;
|
||||
case Action::DROP_DATABASE:
|
||||
std::destroy_at(&uuid);
|
||||
break;
|
||||
case Action::UPDATE_AUTH_DATA:
|
||||
std::destroy_at(&auth_data);
|
||||
break;
|
||||
case Action::DROP_AUTH_DATA:
|
||||
std::destroy_at(&auth_data_key);
|
||||
break;
|
||||
// Some deltas might have special destructor handling
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,18 +51,13 @@ struct SystemTransaction {
|
||||
union {
|
||||
storage::SalientConfig config;
|
||||
utils::UUID uuid;
|
||||
struct {
|
||||
std::optional<auth::User> user;
|
||||
std::optional<auth::Role> role;
|
||||
} auth_data;
|
||||
std::string auth_data_key;
|
||||
};
|
||||
};
|
||||
|
||||
explicit SystemTransaction(uint64_t timestamp) : system_timestamp(timestamp) {}
|
||||
|
||||
// Currently system transitions support a single delta
|
||||
std::optional<Delta> delta{}; // TODO Vector
|
||||
std::optional<Delta> delta{};
|
||||
uint64_t system_timestamp;
|
||||
};
|
||||
|
||||
|
||||
133
src/dbms/utils.hpp
Normal file
133
src/dbms/utils.hpp
Normal file
@@ -0,0 +1,133 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
#pragma once
|
||||
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
#include "dbms/replication_handler.hpp"
|
||||
#include "replication/include/replication/state.hpp"
|
||||
#include "utils/result.hpp"
|
||||
|
||||
namespace memgraph::dbms {
|
||||
|
||||
inline bool DoReplicaToMainPromotion(dbms::DbmsHandler &dbms_handler) {
|
||||
auto &repl_state = dbms_handler.ReplicationState();
|
||||
// STEP 1) bring down all REPLICA servers
|
||||
dbms_handler.ForEach([](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
// Remember old epoch + storage timestamp association
|
||||
storage->PrepareForNewEpoch();
|
||||
});
|
||||
|
||||
// STEP 2) Change to MAIN
|
||||
// TODO: restore replication servers if false?
|
||||
if (!repl_state.SetReplicationRoleMain()) {
|
||||
// TODO: Handle recovery on failure???
|
||||
return false;
|
||||
}
|
||||
|
||||
// STEP 3) We are now MAIN, update storage local epoch
|
||||
const auto &epoch =
|
||||
std::get<replication::RoleMainData>(std::as_const(dbms_handler.ReplicationState()).ReplicationData()).epoch_;
|
||||
dbms_handler.ForEach([&](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
storage->repl_storage_state_.epoch_ = epoch;
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
inline bool SetReplicationRoleReplica(dbms::DbmsHandler &dbms_handler,
|
||||
const memgraph::replication::ReplicationServerConfig &config) {
|
||||
if (dbms_handler.ReplicationState().IsReplica()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO StorageState needs to be synched. Could have a dangling reference if someone adds a database as we are
|
||||
// deleting the replica.
|
||||
// Remove database specific clients
|
||||
dbms_handler.ForEach([&](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
storage->repl_storage_state_.replication_clients_.WithLock([](auto &clients) { clients.clear(); });
|
||||
});
|
||||
// Remove instance level clients
|
||||
std::get<replication::RoleMainData>(dbms_handler.ReplicationState().ReplicationData()).registered_replicas_.clear();
|
||||
|
||||
// Creates the server
|
||||
dbms_handler.ReplicationState().SetReplicationRoleReplica(config);
|
||||
|
||||
// Start
|
||||
const auto success = std::visit(utils::Overloaded{[](replication::RoleMainData const &) {
|
||||
// ASSERT
|
||||
return false;
|
||||
},
|
||||
[&dbms_handler](replication::RoleReplicaData const &data) {
|
||||
return StartRpcServer(dbms_handler, data);
|
||||
}},
|
||||
dbms_handler.ReplicationState().ReplicationData());
|
||||
// TODO Handle error (restore to main?)
|
||||
return success;
|
||||
}
|
||||
|
||||
inline bool RegisterAllDatabasesClients(dbms::DbmsHandler &dbms_handler,
|
||||
replication::ReplicationClient &instance_client) {
|
||||
if (!allow_mt_repl && dbms_handler.All().size() > 1) {
|
||||
spdlog::warn("Multi-tenant replication is currently not supported!");
|
||||
}
|
||||
|
||||
bool all_clients_good = true;
|
||||
|
||||
// Add database specific clients (NOTE Currently all databases are connected to each replica)
|
||||
dbms_handler.ForEach([&](DatabaseAccess db_acc) {
|
||||
auto *storage = db_acc->storage();
|
||||
if (!allow_mt_repl && storage->name() != kDefaultDB) {
|
||||
return;
|
||||
}
|
||||
// TODO: ATM only IN_MEMORY_TRANSACTIONAL, fix other modes
|
||||
if (storage->storage_mode_ != storage::StorageMode::IN_MEMORY_TRANSACTIONAL) return;
|
||||
|
||||
all_clients_good &= storage->repl_storage_state_.replication_clients_.WithLock(
|
||||
[storage, &instance_client, db_acc = std::move(db_acc)](auto &storage_clients) mutable { // NOLINT
|
||||
auto client = std::make_unique<storage::ReplicationStorageClient>(instance_client);
|
||||
// All good, start replica client
|
||||
client->Start(storage, std::move(db_acc));
|
||||
// After start the storage <-> replica state should be READY or RECOVERING (if correctly started)
|
||||
// MAYBE_BEHIND isn't a statement of the current state, this is the default value
|
||||
// Failed to start due an error like branching of MAIN and REPLICA
|
||||
if (client->State() == storage::replication::ReplicaState::MAYBE_BEHIND) {
|
||||
return false; // TODO: sometimes we need to still add to storage_clients
|
||||
}
|
||||
storage_clients.push_back(std::move(client));
|
||||
return true;
|
||||
});
|
||||
});
|
||||
|
||||
return all_clients_good;
|
||||
}
|
||||
|
||||
inline std::optional<RegisterReplicaError> HandleRegisterReplicaStatus(
|
||||
utils::BasicResult<replication::RegisterReplicaError, replication::ReplicationClient *> &instance_client) {
|
||||
if (instance_client.HasError()) switch (instance_client.GetError()) {
|
||||
case replication::RegisterReplicaError::NOT_MAIN:
|
||||
MG_ASSERT(false, "Only main instance can register a replica!");
|
||||
return {};
|
||||
case replication::RegisterReplicaError::NAME_EXISTS:
|
||||
return dbms::RegisterReplicaError::NAME_EXISTS;
|
||||
case replication::RegisterReplicaError::ENDPOINT_EXISTS:
|
||||
return dbms::RegisterReplicaError::ENDPOINT_EXISTS;
|
||||
case replication::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
|
||||
return dbms::RegisterReplicaError::COULD_NOT_BE_PERSISTED;
|
||||
case replication::RegisterReplicaError::SUCCESS:
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
@@ -6,6 +6,7 @@ add_library(mg-flags STATIC audit.cpp
|
||||
memory_limit.cpp
|
||||
run_time_configurable.cpp
|
||||
storage_mode.cpp
|
||||
query.cpp)
|
||||
query.cpp
|
||||
replication.cpp)
|
||||
target_include_directories(mg-flags PUBLIC ${CMAKE_SOURCE_DIR}/include)
|
||||
target_link_libraries(mg-flags PUBLIC spdlog::spdlog mg-settings mg-utils)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -17,5 +17,6 @@
|
||||
#include "flags/log_level.hpp"
|
||||
#include "flags/memory_limit.hpp"
|
||||
#include "flags/query.hpp"
|
||||
#include "flags/replication.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "flags/storage_mode.hpp"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -156,13 +156,6 @@ DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Puls
|
||||
|
||||
// Query flags.
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
|
||||
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
|
||||
"The MAIN instance allocates a new thread for each REPLICA.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(replication_restore_state_on_startup, false, "Restore replication state on startup, e.g. recover replica");
|
||||
|
||||
DEFINE_VALIDATED_string(query_modules_directory, "",
|
||||
"Directory where modules with custom query procedures are stored. "
|
||||
"NOTE: Multiple comma-separated directories can be defined.",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -112,11 +112,6 @@ namespace memgraph::flags {
|
||||
auto ParseQueryModulesDirectory() -> std::vector<std::filesystem::path>;
|
||||
} // namespace memgraph::flags
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(replication_replica_check_frequency_sec);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(replication_restore_state_on_startup);
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_string(license_key);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
|
||||
26
src/flags/replication.cpp
Normal file
26
src/flags/replication.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "replication.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(coordinator, false, "Controls whether the instance is a replication coordinator.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint32(coordinator_server_port, 0, "Port on which coordinator servers will be started.");
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_uint64(replication_replica_check_frequency_sec, 1,
|
||||
"The time duration between two replica checks/pings. If < 1, replicas will NOT be checked at all. NOTE: "
|
||||
"The MAIN instance allocates a new thread for each REPLICA.");
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DEFINE_bool(replication_restore_state_on_startup, false, "Restore replication state on startup, e.g. recover replica");
|
||||
26
src/flags/replication.hpp
Normal file
26
src/flags/replication.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gflags/gflags.h"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(coordinator);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint32(coordinator_server_port);
|
||||
#endif
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_uint64(replication_replica_check_frequency_sec);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
DECLARE_bool(replication_restore_state_on_startup);
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -193,12 +193,13 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
for (const auto &[key, bolt_param] : params) {
|
||||
params_pv.emplace(key, ToPropertyValue(bolt_param));
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
const std::string *username{nullptr};
|
||||
if (user_) {
|
||||
username = &user_->username();
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (memgraph::license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
auto &db = interpreter_.current_db_.db_acc_;
|
||||
audit_log_->Record(endpoint_.address().to_string(), user_ ? *username : "", query,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -66,6 +66,8 @@ auth::Permission PrivilegeToPermission(query::AuthQuery::Privilege privilege) {
|
||||
return auth::Permission::MULTI_DATABASE_EDIT;
|
||||
case query::AuthQuery::Privilege::MULTI_DATABASE_USE:
|
||||
return auth::Permission::MULTI_DATABASE_USE;
|
||||
case query::AuthQuery::Privilege::COORDINATOR:
|
||||
return auth::Permission::COORDINATOR;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,4 +8,5 @@ find_package(fmt REQUIRED)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_library(mg-io STATIC ${io_src_files})
|
||||
add_library(mg::io ALIAS mg-io)
|
||||
target_link_libraries(mg-io stdc++fs Threads::Threads fmt::fmt mg-utils)
|
||||
|
||||
@@ -166,7 +166,7 @@ bool Endpoint::IsResolvableAddress(const std::string &address, uint16_t port) {
|
||||
}
|
||||
|
||||
std::optional<std::pair<std::string, uint16_t>> Endpoint::ParseSocketOrAddress(
|
||||
const std::string &address, const std::optional<uint16_t> default_port = {}) {
|
||||
const std::string &address, const std::optional<uint16_t> default_port) {
|
||||
const std::string delimiter = ":";
|
||||
std::vector<std::string> parts = utils::Split(address, delimiter);
|
||||
if (parts.size() == 1) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -92,7 +92,7 @@ void RegisterLicenseSettings(LicenseChecker &license_checker, utils::Settings &s
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
LicenseChecker global_license_checker;
|
||||
|
||||
LicenseChecker::~LicenseChecker() { scheduler_.Stop(); }
|
||||
LicenseChecker::~LicenseChecker() { Finalize(); }
|
||||
|
||||
std::pair<std::string, std::string> LicenseChecker::ExtractLicenseInfo(const utils::Settings &settings) const {
|
||||
if (license_info_override_) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2022 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -89,6 +89,8 @@ struct LicenseChecker {
|
||||
|
||||
utils::Synchronized<std::optional<LicenseInfo>, utils::SpinLock> &GetLicenseInfo();
|
||||
|
||||
void Finalize() { scheduler_.Stop(); }
|
||||
|
||||
private:
|
||||
std::pair<std::string, std::string> ExtractLicenseInfo(const utils::Settings &settings) const;
|
||||
void RevalidateLicense(const utils::Settings &settings);
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
#include "dbms/constants.hpp"
|
||||
#include "dbms/inmemory/replication_handlers.hpp"
|
||||
#include "flags/all.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "glue/MonitoringServerT.hpp"
|
||||
#include "glue/ServerT.hpp"
|
||||
#include "glue/auth_checker.hpp"
|
||||
@@ -256,6 +255,8 @@ int main(int argc, char **argv) {
|
||||
// register all runtime settings
|
||||
memgraph::license::RegisterLicenseSettings(memgraph::license::global_license_checker,
|
||||
memgraph::utils::global_settings);
|
||||
memgraph::utils::OnScopeExit global_license_finalizer([] { memgraph::license::global_license_checker.Finalize(); });
|
||||
|
||||
memgraph::flags::run_time::Initialize();
|
||||
|
||||
memgraph::license::global_license_checker.CheckEnvLicense();
|
||||
@@ -546,6 +547,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
memgraph::query::procedure::gModuleRegistry.UnloadAllModules();
|
||||
|
||||
python_gc_scheduler.Stop();
|
||||
Py_END_ALLOW_THREADS;
|
||||
// Shutdown Python
|
||||
Py_Finalize();
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
#include "utils/timer.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
using memgraph::replication::ReplicationRole;
|
||||
using memgraph::replication_coordination_glue::ReplicationRole;
|
||||
|
||||
bool ValidateControlCharacter(const char *flagname, const std::string &value) {
|
||||
if (value.empty()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -72,8 +72,9 @@ class TypedValueVectorCompare final {
|
||||
|
||||
/// Raise QueryRuntimeException if the value for symbol isn't of expected type.
|
||||
inline void ExpectType(const Symbol &symbol, const TypedValue &value, TypedValue::Type expected) {
|
||||
if (value.type() != expected)
|
||||
if (value.type() != expected) [[unlikely]] {
|
||||
throw QueryRuntimeException("Expected a {} for '{}', but got {}.", expected, symbol.name(), value.type());
|
||||
}
|
||||
}
|
||||
|
||||
inline void ProcessError(const storage::Error error) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -259,6 +259,13 @@ class ReplicationModificationInMulticommandTxException : public QueryException {
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(ReplicationModificationInMulticommandTxException)
|
||||
};
|
||||
|
||||
class CoordinatorModificationInMulticommandTxException : public QueryException {
|
||||
public:
|
||||
CoordinatorModificationInMulticommandTxException()
|
||||
: QueryException("Coordinator clause not allowed in multicommand transactions.") {}
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(CoordinatorModificationInMulticommandTxException)
|
||||
};
|
||||
|
||||
class ReplicationDisabledOnDiskStorage : public QueryException {
|
||||
public:
|
||||
ReplicationDisabledOnDiskStorage() : QueryException("Replication is not supported while in on-disk storage mode.") {}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -239,6 +239,9 @@ constexpr utils::TypeInfo query::DumpQuery::kType{utils::TypeId::AST_DUMP_QUERY,
|
||||
constexpr utils::TypeInfo query::ReplicationQuery::kType{utils::TypeId::AST_REPLICATION_QUERY, "ReplicationQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::CoordinatorQuery::kType{utils::TypeId::AST_COORDINATOR_QUERY, "CoordinatorQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
constexpr utils::TypeInfo query::LockPathQuery::kType{utils::TypeId::AST_LOCK_PATH_QUERY, "LockPathQuery",
|
||||
&query::Query::kType};
|
||||
|
||||
|
||||
@@ -1209,7 +1209,8 @@ class PropertyLookup : public memgraph::query::Expression {
|
||||
}
|
||||
|
||||
protected:
|
||||
PropertyLookup(Expression *expression, PropertyIx property) : expression_(expression), property_(property) {}
|
||||
PropertyLookup(Expression *expression, PropertyIx property)
|
||||
: expression_(expression), property_(std::move(property)) {}
|
||||
|
||||
private:
|
||||
friend class AstStorage;
|
||||
@@ -1805,9 +1806,9 @@ class EdgeAtom : public memgraph::query::PatternAtom {
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class Type { SINGLE, DEPTH_FIRST, BREADTH_FIRST, WEIGHTED_SHORTEST_PATH, ALL_SHORTEST_PATHS };
|
||||
enum class Type : uint8_t { SINGLE, DEPTH_FIRST, BREADTH_FIRST, WEIGHTED_SHORTEST_PATH, ALL_SHORTEST_PATHS };
|
||||
|
||||
enum class Direction { IN, OUT, BOTH };
|
||||
enum class Direction : uint8_t { IN, OUT, BOTH };
|
||||
|
||||
/// Lambda for use in filtering or weight calculation during variable expand.
|
||||
struct Lambda {
|
||||
@@ -2216,7 +2217,7 @@ class IndexQuery : public memgraph::query::Query {
|
||||
|
||||
protected:
|
||||
IndexQuery(Action action, LabelIx label, std::vector<PropertyIx> properties)
|
||||
: action_(action), label_(label), properties_(properties) {}
|
||||
: action_(action), label_(std::move(label)), properties_(std::move(properties)) {}
|
||||
|
||||
private:
|
||||
friend class AstStorage;
|
||||
@@ -2849,6 +2850,7 @@ class AuthQuery : public memgraph::query::Query {
|
||||
TRANSACTION_MANAGEMENT,
|
||||
MULTI_DATABASE_EDIT,
|
||||
MULTI_DATABASE_USE,
|
||||
COORDINATOR
|
||||
};
|
||||
|
||||
enum class FineGrainedPrivilege { NOTHING, READ, UPDATE, CREATE_DELETE };
|
||||
@@ -2927,7 +2929,8 @@ const std::vector<AuthQuery::Privilege> kPrivilegesAll = {AuthQuery::Privilege::
|
||||
AuthQuery::Privilege::TRANSACTION_MANAGEMENT,
|
||||
AuthQuery::Privilege::STORAGE_MODE,
|
||||
AuthQuery::Privilege::MULTI_DATABASE_EDIT,
|
||||
AuthQuery::Privilege::MULTI_DATABASE_USE};
|
||||
AuthQuery::Privilege::MULTI_DATABASE_USE,
|
||||
AuthQuery::Privilege::COORDINATOR};
|
||||
|
||||
class DatabaseInfoQuery : public memgraph::query::Query {
|
||||
public:
|
||||
@@ -3039,8 +3042,9 @@ class ReplicationQuery : public memgraph::query::Query {
|
||||
|
||||
memgraph::query::ReplicationQuery::Action action_;
|
||||
memgraph::query::ReplicationQuery::ReplicationRole role_;
|
||||
std::string replica_name_;
|
||||
std::string instance_name_;
|
||||
memgraph::query::Expression *socket_address_{nullptr};
|
||||
memgraph::query::Expression *coordinator_socket_address_{nullptr};
|
||||
memgraph::query::Expression *port_{nullptr};
|
||||
memgraph::query::ReplicationQuery::SyncMode sync_mode_;
|
||||
|
||||
@@ -3048,10 +3052,53 @@ class ReplicationQuery : public memgraph::query::Query {
|
||||
ReplicationQuery *object = storage->Create<ReplicationQuery>();
|
||||
object->action_ = action_;
|
||||
object->role_ = role_;
|
||||
object->replica_name_ = replica_name_;
|
||||
object->instance_name_ = instance_name_;
|
||||
object->socket_address_ = socket_address_ ? socket_address_->Clone(storage) : nullptr;
|
||||
object->port_ = port_ ? port_->Clone(storage) : nullptr;
|
||||
object->sync_mode_ = sync_mode_;
|
||||
object->coordinator_socket_address_ =
|
||||
coordinator_socket_address_ ? coordinator_socket_address_->Clone(storage) : nullptr;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class AstStorage;
|
||||
};
|
||||
|
||||
class CoordinatorQuery : public memgraph::query::Query {
|
||||
public:
|
||||
static const utils::TypeInfo kType;
|
||||
const utils::TypeInfo &GetTypeInfo() const override { return kType; }
|
||||
|
||||
enum class Action {
|
||||
REGISTER_INSTANCE,
|
||||
SET_INSTANCE_TO_MAIN,
|
||||
SHOW_REPLICATION_CLUSTER,
|
||||
};
|
||||
|
||||
enum class SyncMode { SYNC, ASYNC };
|
||||
|
||||
CoordinatorQuery() = default;
|
||||
|
||||
DEFVISITABLE(QueryVisitor<void>);
|
||||
|
||||
memgraph::query::CoordinatorQuery::Action action_;
|
||||
std::string instance_name_;
|
||||
memgraph::query::Expression *replication_socket_address_{nullptr};
|
||||
memgraph::query::Expression *coordinator_socket_address_{nullptr};
|
||||
memgraph::query::CoordinatorQuery::SyncMode sync_mode_;
|
||||
|
||||
CoordinatorQuery *Clone(AstStorage *storage) const override {
|
||||
auto *object = storage->Create<CoordinatorQuery>();
|
||||
object->action_ = action_;
|
||||
object->instance_name_ = instance_name_;
|
||||
object->replication_socket_address_ =
|
||||
replication_socket_address_ ? replication_socket_address_->Clone(storage) : nullptr;
|
||||
object->sync_mode_ = sync_mode_;
|
||||
object->coordinator_socket_address_ =
|
||||
coordinator_socket_address_ ? coordinator_socket_address_->Clone(storage) : nullptr;
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -108,6 +108,7 @@ class MultiDatabaseQuery;
|
||||
class ShowDatabasesQuery;
|
||||
class EdgeImportModeQuery;
|
||||
class PatternComprehension;
|
||||
class CoordinatorQuery;
|
||||
|
||||
using TreeCompositeVisitor = utils::CompositeVisitor<
|
||||
SingleQuery, CypherUnion, NamedExpression, OrOperator, XorOperator, AndOperator, NotOperator, AdditionOperator,
|
||||
@@ -146,6 +147,7 @@ class QueryVisitor
|
||||
SystemInfoQuery, ConstraintQuery, DumpQuery, ReplicationQuery, LockPathQuery,
|
||||
FreeMemoryQuery, TriggerQuery, IsolationLevelQuery, CreateSnapshotQuery, StreamQuery,
|
||||
SettingQuery, VersionQuery, ShowConfigQuery, TransactionQueueQuery, StorageModeQuery,
|
||||
AnalyzeGraphQuery, MultiDatabaseQuery, ShowDatabasesQuery, EdgeImportModeQuery> {};
|
||||
AnalyzeGraphQuery, MultiDatabaseQuery, ShowDatabasesQuery, EdgeImportModeQuery,
|
||||
CoordinatorQuery> {};
|
||||
|
||||
} // namespace memgraph::query
|
||||
|
||||
@@ -302,6 +302,13 @@ antlrcpp::Any CypherMainVisitor::visitReplicationQuery(MemgraphCypher::Replicati
|
||||
return replication_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitCoordinatorQuery(MemgraphCypher::CoordinatorQueryContext *ctx) {
|
||||
MG_ASSERT(ctx->children.size() == 1, "CoordinatorQuery should have exactly one child!");
|
||||
auto *coordinator_query = std::any_cast<CoordinatorQuery *>(ctx->children[0]->accept(this));
|
||||
query_ = coordinator_query;
|
||||
return coordinator_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitEdgeImportModeQuery(MemgraphCypher::EdgeImportModeQueryContext *ctx) {
|
||||
auto *edge_import_mode_query = storage_->Create<EdgeImportModeQuery>();
|
||||
if (ctx->ACTIVE()) {
|
||||
@@ -316,24 +323,34 @@ antlrcpp::Any CypherMainVisitor::visitEdgeImportModeQuery(MemgraphCypher::EdgeIm
|
||||
antlrcpp::Any CypherMainVisitor::visitSetReplicationRole(MemgraphCypher::SetReplicationRoleContext *ctx) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::SET_REPLICATION_ROLE;
|
||||
|
||||
auto set_replication_port = [replication_query, ctx, this]() -> void {
|
||||
if (ctx->port->numberLiteral() && ctx->port->numberLiteral()->integerLiteral()) {
|
||||
replication_query->port_ = std::any_cast<Expression *>(ctx->port->accept(this));
|
||||
} else {
|
||||
throw SyntaxException("Port must be an integer literal!");
|
||||
}
|
||||
};
|
||||
|
||||
if (ctx->MAIN()) {
|
||||
replication_query->role_ = ReplicationQuery::ReplicationRole::MAIN;
|
||||
if (ctx->WITH() || ctx->PORT()) {
|
||||
throw SemanticException("Main can't set a port!");
|
||||
}
|
||||
replication_query->role_ = ReplicationQuery::ReplicationRole::MAIN;
|
||||
|
||||
} else if (ctx->REPLICA()) {
|
||||
replication_query->role_ = ReplicationQuery::ReplicationRole::REPLICA;
|
||||
if (ctx->WITH() && ctx->PORT()) {
|
||||
if (ctx->port->numberLiteral() && ctx->port->numberLiteral()->integerLiteral()) {
|
||||
replication_query->port_ = std::any_cast<Expression *>(ctx->port->accept(this));
|
||||
} else {
|
||||
throw SyntaxException("Port must be an integer literal!");
|
||||
}
|
||||
set_replication_port();
|
||||
} else {
|
||||
throw SemanticException("Replica must set a port!");
|
||||
}
|
||||
}
|
||||
|
||||
return replication_query;
|
||||
}
|
||||
antlrcpp::Any CypherMainVisitor::visitShowReplicationRole(MemgraphCypher::ShowReplicationRoleContext *ctx) {
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitShowReplicationRole(MemgraphCypher::ShowReplicationRoleContext * /*ctx*/) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::SHOW_REPLICATION_ROLE;
|
||||
return replication_query;
|
||||
@@ -342,7 +359,7 @@ antlrcpp::Any CypherMainVisitor::visitShowReplicationRole(MemgraphCypher::ShowRe
|
||||
antlrcpp::Any CypherMainVisitor::visitRegisterReplica(MemgraphCypher::RegisterReplicaContext *ctx) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::REGISTER_REPLICA;
|
||||
replication_query->replica_name_ = std::any_cast<std::string>(ctx->replicaName()->symbolicName()->accept(this));
|
||||
replication_query->instance_name_ = std::any_cast<std::string>(ctx->instanceName()->symbolicName()->accept(this));
|
||||
if (ctx->SYNC()) {
|
||||
replication_query->sync_mode_ = memgraph::query::ReplicationQuery::SyncMode::SYNC;
|
||||
} else if (ctx->ASYNC()) {
|
||||
@@ -351,26 +368,67 @@ antlrcpp::Any CypherMainVisitor::visitRegisterReplica(MemgraphCypher::RegisterRe
|
||||
|
||||
if (!ctx->socketAddress()->literal()->StringLiteral()) {
|
||||
throw SemanticException("Socket address should be a string literal!");
|
||||
} else {
|
||||
replication_query->socket_address_ = std::any_cast<Expression *>(ctx->socketAddress()->accept(this));
|
||||
}
|
||||
replication_query->socket_address_ = std::any_cast<Expression *>(ctx->socketAddress()->accept(this));
|
||||
|
||||
return replication_query;
|
||||
}
|
||||
|
||||
// License check is done in the interpreter.
|
||||
antlrcpp::Any CypherMainVisitor::visitRegisterInstanceOnCoordinator(
|
||||
MemgraphCypher::RegisterInstanceOnCoordinatorContext *ctx) {
|
||||
auto *coordinator_query = storage_->Create<CoordinatorQuery>();
|
||||
if (!ctx->replicationSocketAddress()->literal()->StringLiteral()) {
|
||||
throw SemanticException("Replication socket address should be a string literal!");
|
||||
}
|
||||
|
||||
if (!ctx->coordinatorSocketAddress()->literal()->StringLiteral()) {
|
||||
throw SemanticException("Coordinator socket address should be a string literal!");
|
||||
}
|
||||
coordinator_query->action_ = CoordinatorQuery::Action::REGISTER_INSTANCE;
|
||||
coordinator_query->replication_socket_address_ =
|
||||
std::any_cast<Expression *>(ctx->replicationSocketAddress()->accept(this));
|
||||
coordinator_query->coordinator_socket_address_ =
|
||||
std::any_cast<Expression *>(ctx->coordinatorSocketAddress()->accept(this));
|
||||
coordinator_query->instance_name_ = std::any_cast<std::string>(ctx->instanceName()->symbolicName()->accept(this));
|
||||
if (ctx->ASYNC()) {
|
||||
coordinator_query->sync_mode_ = memgraph::query::CoordinatorQuery::SyncMode::ASYNC;
|
||||
} else {
|
||||
coordinator_query->sync_mode_ = memgraph::query::CoordinatorQuery::SyncMode::SYNC;
|
||||
}
|
||||
|
||||
return coordinator_query;
|
||||
}
|
||||
|
||||
// License check is done in the interpreter
|
||||
antlrcpp::Any CypherMainVisitor::visitShowReplicationCluster(MemgraphCypher::ShowReplicationClusterContext * /*ctx*/) {
|
||||
auto *coordinator_query = storage_->Create<CoordinatorQuery>();
|
||||
coordinator_query->action_ = CoordinatorQuery::Action::SHOW_REPLICATION_CLUSTER;
|
||||
return coordinator_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitDropReplica(MemgraphCypher::DropReplicaContext *ctx) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::DROP_REPLICA;
|
||||
replication_query->replica_name_ = std::any_cast<std::string>(ctx->replicaName()->symbolicName()->accept(this));
|
||||
replication_query->instance_name_ = std::any_cast<std::string>(ctx->instanceName()->symbolicName()->accept(this));
|
||||
return replication_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitShowReplicas(MemgraphCypher::ShowReplicasContext *ctx) {
|
||||
antlrcpp::Any CypherMainVisitor::visitShowReplicas(MemgraphCypher::ShowReplicasContext * /*ctx*/) {
|
||||
auto *replication_query = storage_->Create<ReplicationQuery>();
|
||||
replication_query->action_ = ReplicationQuery::Action::SHOW_REPLICAS;
|
||||
return replication_query;
|
||||
}
|
||||
|
||||
// License check is done in the interpreter
|
||||
antlrcpp::Any CypherMainVisitor::visitSetInstanceToMain(MemgraphCypher::SetInstanceToMainContext *ctx) {
|
||||
auto *coordinator_query = storage_->Create<CoordinatorQuery>();
|
||||
coordinator_query->action_ = CoordinatorQuery::Action::SET_INSTANCE_TO_MAIN;
|
||||
coordinator_query->instance_name_ = std::any_cast<std::string>(ctx->instanceName()->symbolicName()->accept(this));
|
||||
query_ = coordinator_query;
|
||||
return coordinator_query;
|
||||
}
|
||||
|
||||
antlrcpp::Any CypherMainVisitor::visitLockPathQuery(MemgraphCypher::LockPathQueryContext *ctx) {
|
||||
auto *lock_query = storage_->Create<LockPathQuery>();
|
||||
if (ctx->STATUS()) {
|
||||
@@ -1638,6 +1696,7 @@ antlrcpp::Any CypherMainVisitor::visitPrivilege(MemgraphCypher::PrivilegeContext
|
||||
if (ctx->STORAGE_MODE()) return AuthQuery::Privilege::STORAGE_MODE;
|
||||
if (ctx->MULTI_DATABASE_EDIT()) return AuthQuery::Privilege::MULTI_DATABASE_EDIT;
|
||||
if (ctx->MULTI_DATABASE_USE()) return AuthQuery::Privilege::MULTI_DATABASE_USE;
|
||||
if (ctx->COORDINATOR()) return AuthQuery::Privilege::COORDINATOR;
|
||||
LOG_FATAL("Should not get here - unknown privilege!");
|
||||
}
|
||||
|
||||
@@ -1752,7 +1811,11 @@ antlrcpp::Any CypherMainVisitor::visitReturnBody(MemgraphCypher::ReturnBodyConte
|
||||
body.skip = static_cast<Expression *>(std::any_cast<Expression *>(ctx->skip()->accept(this)));
|
||||
}
|
||||
if (ctx->limit()) {
|
||||
body.limit = static_cast<Expression *>(std::any_cast<Expression *>(ctx->limit()->accept(this)));
|
||||
if (ctx->limit()->expression()) {
|
||||
body.limit = std::any_cast<Expression *>(ctx->limit()->accept(this));
|
||||
} else {
|
||||
body.limit = std::any_cast<ParameterLookup *>(ctx->limit()->accept(this));
|
||||
}
|
||||
}
|
||||
std::tie(body.all_identifiers, body.named_expressions) =
|
||||
std::any_cast<std::pair<bool, std::vector<NamedExpression *>>>(ctx->returnItems()->accept(this));
|
||||
|
||||
@@ -233,6 +233,26 @@ class CypherMainVisitor : public antlropencypher::MemgraphCypherBaseVisitor {
|
||||
*/
|
||||
antlrcpp::Any visitShowReplicas(MemgraphCypher::ShowReplicasContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return CoordinatorQuery*
|
||||
*/
|
||||
antlrcpp::Any visitCoordinatorQuery(MemgraphCypher::CoordinatorQueryContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return CoordinatorQuery*
|
||||
*/
|
||||
antlrcpp::Any visitRegisterInstanceOnCoordinator(MemgraphCypher::RegisterInstanceOnCoordinatorContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return CoordinatorQuery*
|
||||
*/
|
||||
antlrcpp::Any visitSetInstanceToMain(MemgraphCypher::SetInstanceToMainContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return CoordinatorQuery*
|
||||
*/
|
||||
antlrcpp::Any visitShowReplicationCluster(MemgraphCypher::ShowReplicationClusterContext *ctx) override;
|
||||
|
||||
/**
|
||||
* @return LockPathQuery*
|
||||
*/
|
||||
|
||||
@@ -143,7 +143,7 @@ order : ORDER BY sortItem ( ',' sortItem )* ;
|
||||
|
||||
skip : L_SKIP expression ;
|
||||
|
||||
limit : LIMIT expression ;
|
||||
limit : LIMIT ( expression | parameter ) ;
|
||||
|
||||
sortItem : expression ( ASCENDING | ASC | DESCENDING | DESC )? ;
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ FILTER : F I L T E R ;
|
||||
IN : I N ;
|
||||
INDEX : I N D E X ;
|
||||
INFO : I N F O ;
|
||||
INSTANCE : I N S T A N C E ;
|
||||
IS : I S ;
|
||||
KB : K B ;
|
||||
KEY : K E Y ;
|
||||
@@ -122,6 +123,7 @@ PROCEDURE : P R O C E D U R E ;
|
||||
PROFILE : P R O F I L E ;
|
||||
QUERY : Q U E R Y ;
|
||||
REDUCE : R E D U C E ;
|
||||
REGISTER : R E G I S T E R;
|
||||
REMOVE : R E M O V E ;
|
||||
RETURN : R E T U R N ;
|
||||
SET : S E T ;
|
||||
|
||||
@@ -48,10 +48,12 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| DATABASE
|
||||
| DENY
|
||||
| DROP
|
||||
| DO
|
||||
| DUMP
|
||||
| EDGE
|
||||
| EDGE_TYPES
|
||||
| EXECUTE
|
||||
| FAILOVER
|
||||
| FOR
|
||||
| FOREACH
|
||||
| FREE
|
||||
@@ -61,6 +63,7 @@ memgraphCypherKeyword : cypherKeyword
|
||||
| GRANT
|
||||
| HEADER
|
||||
| IDENTIFIED
|
||||
| INSTANCE
|
||||
| NODE_LABELS
|
||||
| NULLIF
|
||||
| IMPORT
|
||||
@@ -151,6 +154,7 @@ query : cypherQuery
|
||||
| multiDatabaseQuery
|
||||
| showDatabases
|
||||
| edgeImportModeQuery
|
||||
| coordinatorQuery
|
||||
;
|
||||
|
||||
cypherQuery : ( indexHints )? singleQuery ( cypherUnion )* ( queryMemoryLimit )? ;
|
||||
@@ -183,6 +187,11 @@ replicationQuery : setReplicationRole
|
||||
| showReplicas
|
||||
;
|
||||
|
||||
coordinatorQuery : registerInstanceOnCoordinator
|
||||
| setInstanceToMain
|
||||
| showReplicationCluster
|
||||
;
|
||||
|
||||
triggerQuery : createTrigger
|
||||
| dropTrigger
|
||||
| showTriggers
|
||||
@@ -323,6 +332,7 @@ privilege : CREATE
|
||||
| STORAGE_MODE
|
||||
| MULTI_DATABASE_EDIT
|
||||
| MULTI_DATABASE_USE
|
||||
| COORDINATOR
|
||||
;
|
||||
|
||||
granularPrivilege : NOTHING | READ | UPDATE | CREATE_DELETE ;
|
||||
@@ -364,14 +374,23 @@ setReplicationRole : SET REPLICATION ROLE TO ( MAIN | REPLICA )
|
||||
|
||||
showReplicationRole : SHOW REPLICATION ROLE ;
|
||||
|
||||
replicaName : symbolicName ;
|
||||
showReplicationCluster : SHOW REPLICATION CLUSTER ;
|
||||
|
||||
instanceName : symbolicName ;
|
||||
|
||||
socketAddress : literal ;
|
||||
|
||||
registerReplica : REGISTER REPLICA replicaName ( SYNC | ASYNC )
|
||||
coordinatorSocketAddress : literal ;
|
||||
replicationSocketAddress : literal ;
|
||||
|
||||
registerReplica : REGISTER REPLICA instanceName ( SYNC | ASYNC )
|
||||
TO socketAddress ;
|
||||
|
||||
dropReplica : DROP REPLICA replicaName ;
|
||||
registerInstanceOnCoordinator : REGISTER INSTANCE instanceName ON coordinatorSocketAddress ( AS ASYNC ) ? WITH replicationSocketAddress ;
|
||||
|
||||
setInstanceToMain : SET INSTANCE instanceName TO MAIN ;
|
||||
|
||||
dropReplica : DROP REPLICA instanceName ;
|
||||
|
||||
showReplicas : SHOW REPLICAS ;
|
||||
|
||||
|
||||
@@ -39,15 +39,18 @@ BOOTSTRAP_SERVERS : B O O T S T R A P UNDERSCORE S E R V E R S ;
|
||||
CALL : C A L L ;
|
||||
CHECK : C H E C K ;
|
||||
CLEAR : C L E A R ;
|
||||
CLUSTER : C L U S T E R ;
|
||||
COMMIT : C O M M I T ;
|
||||
COMMITTED : C O M M I T T E D ;
|
||||
CONFIG : C O N F I G ;
|
||||
CONFIGS : C O N F I G S;
|
||||
CONSUMER_GROUP : C O N S U M E R UNDERSCORE G R O U P ;
|
||||
COORDINATOR : C O O R D I N A T O R ;
|
||||
CREATE_DELETE : C R E A T E UNDERSCORE D E L E T E ;
|
||||
CREDENTIALS : C R E D E N T I A L S ;
|
||||
CSV : C S V ;
|
||||
DATA : D A T A ;
|
||||
DO : D O ;
|
||||
DELIMITER : D E L I M I T E R ;
|
||||
DATABASE : D A T A B A S E ;
|
||||
DATABASES : D A T A B A S E S ;
|
||||
@@ -59,6 +62,7 @@ DURABILITY : D U R A B I L I T Y ;
|
||||
EDGE : E D G E ;
|
||||
EDGE_TYPES : E D G E UNDERSCORE T Y P E S ;
|
||||
EXECUTE : E X E C U T E ;
|
||||
FAILOVER : F A I L O V E R ;
|
||||
FOR : F O R ;
|
||||
FOREACH : F O R E A C H;
|
||||
FREE : F R E E ;
|
||||
@@ -75,6 +79,7 @@ IMPORT : I M P O R T ;
|
||||
INACTIVE : I N A C T I V E ;
|
||||
IN_MEMORY_ANALYTICAL : I N UNDERSCORE M E M O R Y UNDERSCORE A N A L Y T I C A L ;
|
||||
IN_MEMORY_TRANSACTIONAL : I N UNDERSCORE M E M O R Y UNDERSCORE T R A N S A C T I O N A L ;
|
||||
INSTANCE : I N S T A N C E ;
|
||||
ISOLATION : I S O L A T I O N ;
|
||||
KAFKA : K A F K A ;
|
||||
LABELS : L A B E L S ;
|
||||
@@ -107,6 +112,7 @@ REVOKE : R E V O K E ;
|
||||
ROLE : R O L E ;
|
||||
ROLES : R O L E S ;
|
||||
QUOTE : Q U O T E ;
|
||||
SERVER : S E R V E R ;
|
||||
SERVICE_URL : S E R V I C E UNDERSCORE U R L ;
|
||||
SESSION : S E S S I O N ;
|
||||
SETTING : S E T T I N G ;
|
||||
|
||||
@@ -116,6 +116,8 @@ class PrivilegeExtractor : public QueryVisitor<void>, public HierarchicalTreeVis
|
||||
AddPrivilege(AuthQuery::Privilege::MULTI_DATABASE_USE); /* OR EDIT */
|
||||
}
|
||||
|
||||
void Visit(CoordinatorQuery & /*coordinator_query*/) override { AddPrivilege(AuthQuery::Privilege::COORDINATOR); }
|
||||
|
||||
bool PreVisit(Create & /*unused*/) override {
|
||||
AddPrivilege(AuthQuery::Privilege::CREATE);
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -218,7 +218,8 @@ const trie::Trie kKeywords = {"union",
|
||||
"directory",
|
||||
"lock",
|
||||
"unlock",
|
||||
"build"};
|
||||
"build",
|
||||
"instance"};
|
||||
|
||||
// Unicode codepoints that are allowed at the start of the unescaped name.
|
||||
const std::bitset<kBitsetSize> kUnescapedNameAllowedStarts(
|
||||
|
||||
@@ -1121,11 +1121,11 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
throw QueryRuntimeException("Unexpected error when getting properties.");
|
||||
}
|
||||
}
|
||||
return *maybe_props;
|
||||
return *std::move(maybe_props);
|
||||
}
|
||||
|
||||
template <class TRecordAccessor>
|
||||
storage::PropertyValue GetProperty(const TRecordAccessor &record_accessor, PropertyIx prop) {
|
||||
storage::PropertyValue GetProperty(const TRecordAccessor &record_accessor, const PropertyIx &prop) {
|
||||
auto maybe_prop = record_accessor.GetProperty(view_, ctx_->properties[prop.ix]);
|
||||
if (maybe_prop.HasError() && maybe_prop.GetError() == storage::Error::NONEXISTENT_OBJECT) {
|
||||
// This is a very nasty and temporary hack in order to make MERGE work.
|
||||
@@ -1148,7 +1148,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
throw QueryRuntimeException("Unexpected error when getting a property.");
|
||||
}
|
||||
}
|
||||
return *maybe_prop;
|
||||
return *std::move(maybe_prop);
|
||||
}
|
||||
|
||||
template <class TRecordAccessor>
|
||||
@@ -1178,7 +1178,7 @@ class ExpressionEvaluator : public ExpressionVisitor<TypedValue> {
|
||||
return *maybe_prop;
|
||||
}
|
||||
|
||||
storage::LabelId GetLabel(LabelIx label) { return ctx_->labels[label.ix]; }
|
||||
storage::LabelId GetLabel(const LabelIx &label) { return ctx_->labels[label.ix]; }
|
||||
|
||||
Frame *frame_;
|
||||
const SymbolTable *symbol_table_;
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
#include "auth/models.hpp"
|
||||
#include "csv/parsing.hpp"
|
||||
#include "dbms/database.hpp"
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
#include "dbms/global.hpp"
|
||||
#include "dbms/inmemory/storage_helper.hpp"
|
||||
#include "flags/replication.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "glue/communication.hpp"
|
||||
#include "license/license.hpp"
|
||||
@@ -101,12 +101,17 @@
|
||||
#include "utils/typeinfo.hpp"
|
||||
#include "utils/variant_helpers.hpp"
|
||||
|
||||
#include "dbms/coordinator_handler.hpp"
|
||||
#include "dbms/dbms_handler.hpp"
|
||||
#include "dbms/replication_handler.hpp"
|
||||
#include "query/auth_query_handler.hpp"
|
||||
#include "query/interpreter_context.hpp"
|
||||
#include "replication/state.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "coordination/constants.hpp"
|
||||
#endif
|
||||
|
||||
namespace memgraph::metrics {
|
||||
extern Event ReadQuery;
|
||||
extern Event WriteQuery;
|
||||
@@ -121,6 +126,7 @@ extern const Event CommitedTransactions;
|
||||
extern const Event RollbackedTransactions;
|
||||
extern const Event ActiveTransactions;
|
||||
} // namespace memgraph::metrics
|
||||
|
||||
void memgraph::query::CurrentDB::SetupDatabaseTransaction(
|
||||
std::optional<storage::IsolationLevel> override_isolation_level, bool could_commit, bool unique) {
|
||||
auto &db_acc = *db_acc_;
|
||||
@@ -260,17 +266,32 @@ bool IsAllShortestPathsQuery(const std::vector<memgraph::query::Clause *> &claus
|
||||
return false;
|
||||
}
|
||||
|
||||
inline auto convertToReplicationMode(const ReplicationQuery::SyncMode &sync_mode) -> replication::ReplicationMode {
|
||||
inline auto convertFromCoordinatorToReplicationMode(const CoordinatorQuery::SyncMode &sync_mode)
|
||||
-> replication_coordination_glue::ReplicationMode {
|
||||
switch (sync_mode) {
|
||||
case ReplicationQuery::SyncMode::ASYNC: {
|
||||
return replication::ReplicationMode::ASYNC;
|
||||
case CoordinatorQuery::SyncMode::ASYNC: {
|
||||
return replication_coordination_glue::ReplicationMode::ASYNC;
|
||||
}
|
||||
case ReplicationQuery::SyncMode::SYNC: {
|
||||
return replication::ReplicationMode::SYNC;
|
||||
case CoordinatorQuery::SyncMode::SYNC: {
|
||||
return replication_coordination_glue::ReplicationMode::SYNC;
|
||||
}
|
||||
}
|
||||
// TODO: C++23 std::unreachable()
|
||||
return replication::ReplicationMode::ASYNC;
|
||||
return replication_coordination_glue::ReplicationMode::ASYNC;
|
||||
}
|
||||
|
||||
inline auto convertToReplicationMode(const ReplicationQuery::SyncMode &sync_mode)
|
||||
-> replication_coordination_glue::ReplicationMode {
|
||||
switch (sync_mode) {
|
||||
case ReplicationQuery::SyncMode::ASYNC: {
|
||||
return replication_coordination_glue::ReplicationMode::ASYNC;
|
||||
}
|
||||
case ReplicationQuery::SyncMode::SYNC: {
|
||||
return replication_coordination_glue::ReplicationMode::SYNC;
|
||||
}
|
||||
}
|
||||
// TODO: C++23 std::unreachable()
|
||||
return replication_coordination_glue::ReplicationMode::ASYNC;
|
||||
}
|
||||
|
||||
class ReplQueryHandler {
|
||||
@@ -289,14 +310,17 @@ class ReplQueryHandler {
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
void SetReplicationRole(ReplicationQuery::ReplicationRole replication_role, std::optional<int64_t> port) {
|
||||
if (replication_role == ReplicationQuery::ReplicationRole::MAIN) {
|
||||
if (!handler_.SetReplicationRoleMain()) {
|
||||
throw QueryRuntimeException("Couldn't set role to main!");
|
||||
}
|
||||
} else {
|
||||
if (!port || *port < 0 || *port > std::numeric_limits<uint16_t>::max()) {
|
||||
auto ValidatePort = [](std::optional<int64_t> port) -> void {
|
||||
if (*port < 0 || *port > std::numeric_limits<uint16_t>::max()) {
|
||||
throw QueryRuntimeException("Port number invalid!");
|
||||
}
|
||||
};
|
||||
if (replication_role == ReplicationQuery::ReplicationRole::MAIN) {
|
||||
if (!handler_.SetReplicationRoleMain()) {
|
||||
throw QueryRuntimeException("Couldn't set replication role to main!");
|
||||
}
|
||||
} else {
|
||||
ValidatePort(port);
|
||||
|
||||
auto const config = memgraph::replication::ReplicationServerConfig{
|
||||
.ip_address = memgraph::replication::kDefaultReplicationServerIp,
|
||||
@@ -312,9 +336,9 @@ class ReplQueryHandler {
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
ReplicationQuery::ReplicationRole ShowReplicationRole() const {
|
||||
switch (handler_.GetRole()) {
|
||||
case memgraph::replication::ReplicationRole::MAIN:
|
||||
case memgraph::replication_coordination_glue::ReplicationRole::MAIN:
|
||||
return ReplicationQuery::ReplicationRole::MAIN;
|
||||
case memgraph::replication::ReplicationRole::REPLICA:
|
||||
case memgraph::replication_coordination_glue::ReplicationRole::REPLICA:
|
||||
return ReplicationQuery::ReplicationRole::REPLICA;
|
||||
}
|
||||
throw QueryRuntimeException("Couldn't show replication role - invalid role set!");
|
||||
@@ -323,27 +347,33 @@ class ReplQueryHandler {
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
void RegisterReplica(const std::string &name, const std::string &socket_address,
|
||||
const ReplicationQuery::SyncMode sync_mode, const std::chrono::seconds replica_check_frequency) {
|
||||
// Coordinator is main by default so this check is OK although it should actually be nothing (neither main nor
|
||||
// replica)
|
||||
if (handler_.IsReplica()) {
|
||||
// replica can't register another replica
|
||||
throw QueryRuntimeException("Replica can't register another replica!");
|
||||
}
|
||||
|
||||
auto repl_mode = convertToReplicationMode(sync_mode);
|
||||
const auto repl_mode = convertToReplicationMode(sync_mode);
|
||||
|
||||
auto maybe_ip_and_port =
|
||||
const auto maybe_ip_and_port =
|
||||
io::network::Endpoint::ParseSocketOrAddress(socket_address, memgraph::replication::kDefaultReplicationPort);
|
||||
if (maybe_ip_and_port) {
|
||||
auto [ip, port] = *maybe_ip_and_port;
|
||||
auto config = replication::ReplicationClientConfig{.name = name,
|
||||
.mode = repl_mode,
|
||||
.ip_address = ip,
|
||||
.port = port,
|
||||
.replica_check_frequency = replica_check_frequency,
|
||||
.ssl = std::nullopt};
|
||||
auto ret = handler_.RegisterReplica(config);
|
||||
if (ret.HasError()) {
|
||||
const auto [ip, port] = *maybe_ip_and_port;
|
||||
const auto replication_config =
|
||||
replication::ReplicationClientConfig{.name = name,
|
||||
.mode = repl_mode,
|
||||
.ip_address = ip,
|
||||
.port = port,
|
||||
.replica_check_frequency = replica_check_frequency,
|
||||
.ssl = std::nullopt};
|
||||
|
||||
const auto error = handler_.RegisterReplica(replication_config).HasError();
|
||||
|
||||
if (error) {
|
||||
throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name));
|
||||
}
|
||||
|
||||
} else {
|
||||
throw QueryRuntimeException("Invalid socket address!");
|
||||
}
|
||||
@@ -382,10 +412,10 @@ class ReplQueryHandler {
|
||||
replica.name = repl_info.name;
|
||||
replica.socket_address = repl_info.endpoint.SocketAddress();
|
||||
switch (repl_info.mode) {
|
||||
case memgraph::replication::ReplicationMode::SYNC:
|
||||
case replication_coordination_glue::ReplicationMode::SYNC:
|
||||
replica.sync_mode = ReplicationQuery::SyncMode::SYNC;
|
||||
break;
|
||||
case memgraph::replication::ReplicationMode::ASYNC:
|
||||
case replication_coordination_glue::ReplicationMode::ASYNC:
|
||||
replica.sync_mode = ReplicationQuery::SyncMode::ASYNC;
|
||||
break;
|
||||
}
|
||||
@@ -420,6 +450,100 @@ class ReplQueryHandler {
|
||||
dbms::ReplicationHandler handler_;
|
||||
};
|
||||
|
||||
class CoordQueryHandler final : public query::CoordinatorQueryHandler {
|
||||
public:
|
||||
explicit CoordQueryHandler(dbms::DbmsHandler *dbms_handler) : handler_ { *dbms_handler }
|
||||
#ifdef MG_ENTERPRISE
|
||||
, coordinator_handler_(*dbms_handler)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
void RegisterInstance(const std::string &coordinator_socket_address, const std::string &replication_socket_address,
|
||||
const std::chrono::seconds instance_check_frequency, const std::string &instance_name,
|
||||
CoordinatorQuery::SyncMode sync_mode) override {
|
||||
const auto maybe_replication_ip_port =
|
||||
io::network::Endpoint::ParseSocketOrAddress(replication_socket_address, std::nullopt);
|
||||
if (!maybe_replication_ip_port) {
|
||||
throw QueryRuntimeException("Invalid replication socket address!");
|
||||
}
|
||||
|
||||
const auto maybe_coordinator_ip_port =
|
||||
io::network::Endpoint::ParseSocketOrAddress(coordinator_socket_address, std::nullopt);
|
||||
if (!maybe_replication_ip_port) {
|
||||
throw QueryRuntimeException("Invalid replication socket address!");
|
||||
}
|
||||
|
||||
const auto [replication_ip, replication_port] = *maybe_replication_ip_port;
|
||||
const auto [coordinator_server_ip, coordinator_server_port] = *maybe_coordinator_ip_port;
|
||||
const auto repl_config = coordination::CoordinatorClientConfig::ReplicationClientInfo{
|
||||
.instance_name = instance_name,
|
||||
.replication_mode = convertFromCoordinatorToReplicationMode(sync_mode),
|
||||
.replication_ip_address = replication_ip,
|
||||
.replication_port = replication_port};
|
||||
|
||||
auto coordinator_client_config =
|
||||
coordination::CoordinatorClientConfig{.instance_name = instance_name,
|
||||
.ip_address = coordinator_server_ip,
|
||||
.port = coordinator_server_port,
|
||||
.health_check_frequency_sec = instance_check_frequency,
|
||||
.replication_client_info = repl_config,
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto status = coordinator_handler_.RegisterInstance(coordinator_client_config);
|
||||
switch (status) {
|
||||
using enum memgraph::coordination::RegisterInstanceCoordinatorStatus;
|
||||
case NAME_EXISTS:
|
||||
throw QueryRuntimeException("Couldn't register replica instance since instance with such name already exists!");
|
||||
case END_POINT_EXISTS:
|
||||
throw QueryRuntimeException(
|
||||
"Couldn't register replica instance since instance with such endpoint already exists!");
|
||||
case NOT_COORDINATOR:
|
||||
throw QueryRuntimeException("Couldn't register replica instance since this instance is not a coordinator!");
|
||||
case RPC_FAILED:
|
||||
throw QueryRuntimeException(
|
||||
"Couldn't register instance because setting instance to replica failed! Check logs on replica to find out "
|
||||
"more "
|
||||
"info!");
|
||||
case SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void SetInstanceToMain(const std::string &instance_name) override {
|
||||
auto status = coordinator_handler_.SetInstanceToMain(instance_name);
|
||||
switch (status) {
|
||||
using enum memgraph::coordination::SetInstanceToMainCoordinatorStatus;
|
||||
case NO_INSTANCE_WITH_NAME:
|
||||
throw QueryRuntimeException("No instance with such name!");
|
||||
case NOT_COORDINATOR:
|
||||
throw QueryRuntimeException("Couldn't set replica instance to main since this instance is not a coordinator!");
|
||||
case COULD_NOT_PROMOTE_TO_MAIN:
|
||||
throw QueryRuntimeException(
|
||||
"Couldn't set replica instance to main. Check coordinator and replica for more logs");
|
||||
case SUCCESS:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
std::vector<coordination::CoordinatorInstanceStatus> ShowInstances() const override {
|
||||
return coordinator_handler_.ShowInstances();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
private:
|
||||
dbms::ReplicationHandler handler_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
dbms::CoordinatorHandler coordinator_handler_;
|
||||
#endif
|
||||
};
|
||||
|
||||
/// returns false if the replication role can't be set
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
|
||||
@@ -723,6 +847,15 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
Callback callback;
|
||||
switch (repl_query->action_) {
|
||||
case ReplicationQuery::Action::SET_REPLICATION_ROLE: {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Coordinator can't set roles!");
|
||||
}
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
throw QueryRuntimeException("Can't set role manually on instance with coordinator server port.");
|
||||
}
|
||||
#endif
|
||||
|
||||
auto port = EvaluateOptionalExpression(repl_query->port_, evaluator);
|
||||
std::optional<int64_t> maybe_port;
|
||||
if (port.IsInt()) {
|
||||
@@ -743,6 +876,12 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::SHOW_REPLICATION_ROLE: {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Coordinator doesn't have a replication role!");
|
||||
}
|
||||
#endif
|
||||
|
||||
callback.header = {"replication role"};
|
||||
callback.fn = [handler = ReplQueryHandler{dbms_handler}] {
|
||||
auto mode = handler.ShowReplicationRole();
|
||||
@@ -758,7 +897,12 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::REGISTER_REPLICA: {
|
||||
const auto &name = repl_query->replica_name_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
throw QueryRuntimeException("Can't register replica manually on instance with coordinator server port.");
|
||||
}
|
||||
#endif
|
||||
const auto &name = repl_query->instance_name_;
|
||||
const auto &sync_mode = repl_query->sync_mode_;
|
||||
auto socket_address = repl_query->socket_address_->Accept(evaluator);
|
||||
const auto replica_check_frequency = config.replication_replica_check_frequency;
|
||||
@@ -769,20 +913,32 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::REGISTER_REPLICA,
|
||||
fmt::format("Replica {} is registered.", repl_query->replica_name_));
|
||||
fmt::format("Replica {} is registered.", repl_query->instance_name_));
|
||||
return callback;
|
||||
}
|
||||
|
||||
case ReplicationQuery::Action::DROP_REPLICA: {
|
||||
const auto &name = repl_query->replica_name_;
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
throw QueryRuntimeException("Can't drop replica manually on instance with coordinator server port.");
|
||||
}
|
||||
#endif
|
||||
const auto &name = repl_query->instance_name_;
|
||||
callback.fn = [handler = ReplQueryHandler{dbms_handler}, name]() mutable {
|
||||
handler.DropReplica(name);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
notifications->emplace_back(SeverityLevel::INFO, NotificationCode::DROP_REPLICA,
|
||||
fmt::format("Replica {} is dropped.", repl_query->replica_name_));
|
||||
fmt::format("Replica {} is dropped.", repl_query->instance_name_));
|
||||
return callback;
|
||||
}
|
||||
case ReplicationQuery::Action::SHOW_REPLICAS: {
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Coordinator cannot call SHOW REPLICAS! Use SHOW REPLICATION CLUSTER instead.");
|
||||
}
|
||||
#endif
|
||||
|
||||
callback.header = {
|
||||
"name", "socket_address", "sync_mode", "current_timestamp_of_replica", "number_of_timestamp_behind_master",
|
||||
"state"};
|
||||
@@ -834,6 +990,110 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
|
||||
}
|
||||
}
|
||||
|
||||
Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Parameters ¶meters,
|
||||
dbms::DbmsHandler *dbms_handler, const query::InterpreterConfig &config,
|
||||
std::vector<Notification> *notifications) {
|
||||
Callback callback;
|
||||
switch (coordinator_query->action_) {
|
||||
case CoordinatorQuery::Action::REGISTER_INSTANCE: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Only coordinator can register coordinator server!");
|
||||
}
|
||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
||||
// the argument to Callback.
|
||||
EvaluationContext evaluation_context{.timestamp = QueryTimestamp(), .parameters = parameters};
|
||||
auto evaluator = PrimitiveLiteralExpressionEvaluator{evaluation_context};
|
||||
|
||||
auto coordinator_socket_address_tv = coordinator_query->coordinator_socket_address_->Accept(evaluator);
|
||||
auto replication_socket_address_tv = coordinator_query->replication_socket_address_->Accept(evaluator);
|
||||
callback.fn = [handler = CoordQueryHandler{dbms_handler}, coordinator_socket_address_tv,
|
||||
replication_socket_address_tv, main_check_frequency = config.replication_replica_check_frequency,
|
||||
instance_name = coordinator_query->instance_name_,
|
||||
sync_mode = coordinator_query->sync_mode_]() mutable {
|
||||
handler.RegisterInstance(std::string(coordinator_socket_address_tv.ValueString()),
|
||||
std::string(replication_socket_address_tv.ValueString()), main_check_frequency,
|
||||
instance_name, sync_mode);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
|
||||
notifications->emplace_back(
|
||||
SeverityLevel::INFO, NotificationCode::REGISTER_COORDINATOR_SERVER,
|
||||
fmt::format("Coordinator has registered coordinator server on {} for instance {}.",
|
||||
coordinator_socket_address_tv.ValueString(), coordinator_query->instance_name_));
|
||||
return callback;
|
||||
#endif
|
||||
}
|
||||
case CoordinatorQuery::Action::SET_INSTANCE_TO_MAIN: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Only coordinator can register coordinator server!");
|
||||
}
|
||||
// TODO: MemoryResource for EvaluationContext, it should probably be passed as
|
||||
// the argument to Callback.
|
||||
EvaluationContext evaluation_context{.timestamp = QueryTimestamp(), .parameters = parameters};
|
||||
auto evaluator = PrimitiveLiteralExpressionEvaluator{evaluation_context};
|
||||
|
||||
callback.fn = [handler = CoordQueryHandler{dbms_handler},
|
||||
instance_name = coordinator_query->instance_name_]() mutable {
|
||||
handler.SetInstanceToMain(instance_name);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
|
||||
return callback;
|
||||
#endif
|
||||
}
|
||||
case CoordinatorQuery::Action::SHOW_REPLICATION_CLUSTER: {
|
||||
if (!license::global_license_checker.IsEnterpriseValidFast()) {
|
||||
throw QueryException("Trying to use enterprise feature without a valid license.");
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
if constexpr (!coordination::allow_ha) {
|
||||
throw QueryRuntimeException(
|
||||
"High availability is experimental feature. Please set MG_EXPERIMENTAL_HIGH_AVAILABILITY compile flag to "
|
||||
"be able to use this functionality.");
|
||||
}
|
||||
if (!FLAGS_coordinator) {
|
||||
throw QueryRuntimeException("Only coordinator can run SHOW REPLICATION CLUSTER.");
|
||||
}
|
||||
|
||||
callback.header = {"name", "socket_address", "alive", "role"};
|
||||
callback.fn = [handler = CoordQueryHandler{dbms_handler}, replica_nfields = callback.header.size()]() mutable {
|
||||
auto const instances = handler.ShowInstances();
|
||||
std::vector<std::vector<TypedValue>> result{};
|
||||
result.reserve(result.size());
|
||||
|
||||
std::ranges::transform(instances, std::back_inserter(result),
|
||||
[](const auto &status) -> std::vector<TypedValue> {
|
||||
return {TypedValue{status.instance_name}, TypedValue{status.socket_address},
|
||||
TypedValue{status.is_alive}, TypedValue{status.replication_role}};
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
return callback;
|
||||
#endif
|
||||
}
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
|
||||
stream::CommonStreamInfo GetCommonStreamInfo(StreamQuery *stream_query, ExpressionVisitor<TypedValue> &evaluator) {
|
||||
return {
|
||||
.batch_interval = GetOptionalValue<std::chrono::milliseconds>(stream_query->batch_interval_, evaluator)
|
||||
@@ -2293,6 +2553,34 @@ PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
}
|
||||
|
||||
PreparedQuery PrepareCoordinatorQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
|
||||
std::vector<Notification> *notifications, dbms::DbmsHandler &dbms_handler,
|
||||
const InterpreterConfig &config) {
|
||||
if (in_explicit_transaction) {
|
||||
throw CoordinatorModificationInMulticommandTxException();
|
||||
}
|
||||
|
||||
auto *coordinator_query = utils::Downcast<CoordinatorQuery>(parsed_query.query);
|
||||
auto callback =
|
||||
HandleCoordinatorQuery(coordinator_query, parsed_query.parameters, &dbms_handler, config, notifications);
|
||||
|
||||
return PreparedQuery{callback.header, std::move(parsed_query.required_privileges),
|
||||
[callback_fn = std::move(callback.fn), pull_plan = std::shared_ptr<PullPlanVector>{nullptr}](
|
||||
AnyStream *stream, std::optional<int> n) mutable -> std::optional<QueryHandlerResult> {
|
||||
if (UNLIKELY(!pull_plan)) {
|
||||
pull_plan = std::make_shared<PullPlanVector>(callback_fn());
|
||||
}
|
||||
|
||||
if (pull_plan->Pull(stream, n)) [[likely]] {
|
||||
return QueryHandlerResult::COMMIT;
|
||||
}
|
||||
return std::nullopt;
|
||||
},
|
||||
RWType::NONE};
|
||||
// False positive report for the std::make_shared above
|
||||
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
|
||||
}
|
||||
|
||||
PreparedQuery PrepareLockPathQuery(ParsedQuery parsed_query, bool in_explicit_transaction, CurrentDB ¤t_db) {
|
||||
if (in_explicit_transaction) {
|
||||
throw LockPathModificationInMulticommandTxException();
|
||||
@@ -2794,7 +3082,7 @@ PreparedQuery PrepareEdgeImportModeQuery(ParsedQuery parsed_query, CurrentDB &cu
|
||||
}
|
||||
|
||||
PreparedQuery PrepareCreateSnapshotQuery(ParsedQuery parsed_query, bool in_explicit_transaction, CurrentDB ¤t_db,
|
||||
replication::ReplicationRole replication_role) {
|
||||
replication_coordination_glue::ReplicationRole replication_role) {
|
||||
if (in_explicit_transaction) {
|
||||
throw CreateSnapshotInMulticommandTxException();
|
||||
}
|
||||
@@ -3751,16 +4039,21 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
utils::Downcast<MultiDatabaseQuery>(parsed_query.query) ||
|
||||
utils::Downcast<ShowDatabasesQuery>(parsed_query.query) ||
|
||||
utils::Downcast<ReplicationQuery>(parsed_query.query);
|
||||
|
||||
// TODO Split SHOW REPLICAS (which needs the db) and other replication queries
|
||||
if (system_queries) {
|
||||
// TODO: Ordering between system and data queries
|
||||
// Start a system transaction
|
||||
auto system_unique = std::unique_lock{interpreter_context_->dbms_handler->system_lock_, std::defer_lock};
|
||||
if (!system_unique.try_lock_for(std::chrono::milliseconds(kSystemTxTryMS))) {
|
||||
throw ConcurrentSystemQueriesException("Multiple concurrent system queries are not supported.");
|
||||
auto system_transaction_guard = std::invoke([&]() -> std::optional<SystemTransactionGuard> {
|
||||
if (system_queries) {
|
||||
// TODO: Ordering between system and data queries
|
||||
// Start a system transaction
|
||||
auto system_unique = std::unique_lock{interpreter_context_->dbms_handler->system_lock_, std::defer_lock};
|
||||
if (!system_unique.try_lock_for(std::chrono::milliseconds(kSystemTxTryMS))) {
|
||||
throw ConcurrentSystemQueriesException("Multiple concurrent system queries are not supported.");
|
||||
}
|
||||
return std::optional<SystemTransactionGuard>{std::in_place, std::move(system_unique),
|
||||
*interpreter_context_->dbms_handler};
|
||||
}
|
||||
system_transaction_.emplace(std::move(system_unique), *interpreter_context_->dbms_handler);
|
||||
}
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
// Some queries do not require a database to be executed (current_db_ won't be passed on to the Prepare*; special
|
||||
// case for use database which overwrites the current database)
|
||||
@@ -3790,6 +4083,13 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
SetupDatabaseTransaction(could_commit, unique);
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
if (FLAGS_coordinator && !utils::Downcast<CoordinatorQuery>(parsed_query.query) &&
|
||||
!utils::Downcast<SettingQuery>(parsed_query.query)) {
|
||||
throw QueryRuntimeException("Coordinator can run only coordinator queries!");
|
||||
}
|
||||
#endif
|
||||
|
||||
utils::Timer planning_timer;
|
||||
PreparedQuery prepared_query;
|
||||
utils::MemoryResource *memory_resource =
|
||||
@@ -3832,6 +4132,10 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
prepared_query =
|
||||
PrepareReplicationQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
*interpreter_context_->dbms_handler, current_db_, interpreter_context_->config);
|
||||
} else if (utils::Downcast<CoordinatorQuery>(parsed_query.query)) {
|
||||
prepared_query =
|
||||
PrepareCoordinatorQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
|
||||
*interpreter_context_->dbms_handler, interpreter_context_->config);
|
||||
} else if (utils::Downcast<LockPathQuery>(parsed_query.query)) {
|
||||
prepared_query = PrepareLockPathQuery(std::move(parsed_query), in_explicit_transaction_, current_db_);
|
||||
} else if (utils::Downcast<FreeMemoryQuery>(parsed_query.query)) {
|
||||
@@ -3906,6 +4210,8 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
|
||||
}
|
||||
query_execution->summary["db"] = *query_execution->prepared_query->db;
|
||||
|
||||
// prepare is done, move system txn guard to be owned by interpreter
|
||||
system_transaction_guard_ = std::move(system_transaction_guard);
|
||||
return {query_execution->prepared_query->header, query_execution->prepared_query->privileges, qid,
|
||||
query_execution->prepared_query->db};
|
||||
} catch (const utils::BasicException &) {
|
||||
@@ -4055,13 +4361,13 @@ void Interpreter::Commit() {
|
||||
current_transaction_.reset();
|
||||
if (!current_db_.db_transactional_accessor_ || !current_db_.db_acc_) {
|
||||
// No database nor db transaction; check for system transaction
|
||||
if (!system_transaction_) return;
|
||||
if (!system_transaction_guard_) return;
|
||||
|
||||
// TODO Distinguish between data and system transaction state
|
||||
// Think about updating the status to a struct with bitfield
|
||||
// Clean transaction status on exit
|
||||
utils::OnScopeExit clean_status([this]() {
|
||||
system_transaction_.reset();
|
||||
system_transaction_guard_.reset();
|
||||
// System transactions are not terminable
|
||||
// Durability has happened at time of PULL
|
||||
// Commit is doing replication and timestamp update
|
||||
@@ -4079,7 +4385,7 @@ void Interpreter::Commit() {
|
||||
}
|
||||
});
|
||||
|
||||
system_transaction_->Commit();
|
||||
system_transaction_guard_->Commit();
|
||||
return;
|
||||
}
|
||||
auto *db = current_db_.db_acc_->get();
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
#include "utils/timer.hpp"
|
||||
#include "utils/tsc.hpp"
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
#include "coordination/coordinator_instance_status.hpp"
|
||||
#endif
|
||||
|
||||
namespace memgraph::metrics {
|
||||
extern const Event FailedQuery;
|
||||
extern const Event FailedPrepare;
|
||||
@@ -68,6 +72,55 @@ inline constexpr size_t kExecutionPoolMaxBlockSize = 1024UL; // 2 ^ 10
|
||||
|
||||
enum class QueryHandlerResult { COMMIT, ABORT, NOTHING };
|
||||
|
||||
class CoordinatorQueryHandler {
|
||||
public:
|
||||
CoordinatorQueryHandler() = default;
|
||||
virtual ~CoordinatorQueryHandler() = default;
|
||||
|
||||
CoordinatorQueryHandler(const CoordinatorQueryHandler &) = default;
|
||||
CoordinatorQueryHandler &operator=(const CoordinatorQueryHandler &) = default;
|
||||
|
||||
CoordinatorQueryHandler(CoordinatorQueryHandler &&) = default;
|
||||
CoordinatorQueryHandler &operator=(CoordinatorQueryHandler &&) = default;
|
||||
|
||||
struct Replica {
|
||||
std::string name;
|
||||
std::string socket_address;
|
||||
ReplicationQuery::SyncMode sync_mode;
|
||||
std::optional<double> timeout;
|
||||
uint64_t current_timestamp_of_replica;
|
||||
uint64_t current_number_of_timestamp_behind_master;
|
||||
ReplicationQuery::ReplicaState state;
|
||||
};
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
struct MainReplicaStatus {
|
||||
std::string_view name;
|
||||
std::string_view socket_address;
|
||||
bool alive;
|
||||
bool is_main;
|
||||
|
||||
MainReplicaStatus(std::string_view name, std::string_view socket_address, bool alive, bool is_main)
|
||||
: name{name}, socket_address{socket_address}, alive{alive}, is_main{is_main} {}
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RegisterInstance(const std::string &coordinator_socket_address,
|
||||
const std::string &replication_socket_address,
|
||||
const std::chrono::seconds instance_check_frequency, const std::string &instance_name,
|
||||
CoordinatorQuery::SyncMode sync_mode) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void SetInstanceToMain(const std::string &instance_name) = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual std::vector<coordination::CoordinatorInstanceStatus> ShowInstances() const = 0;
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
class AnalyzeGraphQueryHandler {
|
||||
public:
|
||||
AnalyzeGraphQueryHandler() = default;
|
||||
@@ -243,13 +296,17 @@ class Interpreter final {
|
||||
|
||||
void SetUser(std::string_view username);
|
||||
|
||||
struct SystemTransaction {
|
||||
explicit SystemTransaction(std::unique_lock<utils::ResourceLock> guard, dbms::DbmsHandler &dbms_handler)
|
||||
struct SystemTransactionGuard {
|
||||
explicit SystemTransactionGuard(std::unique_lock<utils::ResourceLock> guard, dbms::DbmsHandler &dbms_handler)
|
||||
: system_guard_(std::move(guard)), dbms_handler_{&dbms_handler} {
|
||||
dbms_handler_->NewSystemTransaction();
|
||||
}
|
||||
SystemTransactionGuard &operator=(SystemTransactionGuard &&) = default;
|
||||
SystemTransactionGuard(SystemTransactionGuard &&) = default;
|
||||
|
||||
~SystemTransaction() { dbms_handler_->ResetSystemTransaction(); }
|
||||
~SystemTransactionGuard() {
|
||||
if (system_guard_.owns_lock()) dbms_handler_->ResetSystemTransaction();
|
||||
}
|
||||
|
||||
dbms::AllSyncReplicaStatus Commit() { return dbms_handler_->Commit(); }
|
||||
|
||||
@@ -258,13 +315,13 @@ class Interpreter final {
|
||||
dbms::DbmsHandler *dbms_handler_;
|
||||
};
|
||||
|
||||
std::optional<SystemTransaction> system_transaction_{};
|
||||
std::optional<SystemTransactionGuard> system_transaction_guard_{};
|
||||
|
||||
private:
|
||||
void ResetInterpreter() {
|
||||
query_executions_.clear();
|
||||
system_guard.reset();
|
||||
system_transaction_.reset();
|
||||
system_transaction_guard_.reset();
|
||||
transaction_queries_->clear();
|
||||
if (current_db_.db_acc_ && current_db_.db_acc_->is_deleting()) {
|
||||
current_db_.db_acc_.reset();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -66,6 +66,10 @@ constexpr std::string_view GetCodeString(const NotificationCode code) {
|
||||
return "PlanHinting"sv;
|
||||
case NotificationCode::REGISTER_REPLICA:
|
||||
return "RegisterReplica"sv;
|
||||
#ifdef MG_ENTERPRISE
|
||||
case NotificationCode::REGISTER_COORDINATOR_SERVER:
|
||||
return "RegisterCoordinatorServer"sv;
|
||||
#endif
|
||||
case NotificationCode::REPLICA_PORT_WARNING:
|
||||
return "ReplicaPortWarning"sv;
|
||||
case NotificationCode::SET_REPLICA:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -42,6 +42,9 @@ enum class NotificationCode : uint8_t {
|
||||
PLAN_HINTING,
|
||||
REPLICA_PORT_WARNING,
|
||||
REGISTER_REPLICA,
|
||||
#ifdef MG_ENTERPRISE
|
||||
REGISTER_COORDINATOR_SERVER,
|
||||
#endif
|
||||
SET_REPLICA,
|
||||
START_STREAM,
|
||||
START_ALL_STREAMS,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -257,7 +257,7 @@ void Filters::EraseFilter(const FilterInfo &filter) {
|
||||
all_filters_.end());
|
||||
}
|
||||
|
||||
void Filters::EraseLabelFilter(const Symbol &symbol, LabelIx label, std::vector<Expression *> *removed_filters) {
|
||||
void Filters::EraseLabelFilter(const Symbol &symbol, const LabelIx &label, std::vector<Expression *> *removed_filters) {
|
||||
for (auto filter_it = all_filters_.begin(); filter_it != all_filters_.end();) {
|
||||
if (filter_it->type != FilterInfo::Type::Label) {
|
||||
++filter_it;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -366,7 +366,8 @@ class Filters final {
|
||||
/// Remove a label filter for symbol; may invalidate iterators.
|
||||
/// If removed_filters is not nullptr, fills the vector with original
|
||||
/// `Expression *` which are now completely removed.
|
||||
void EraseLabelFilter(const Symbol &, LabelIx, std::vector<Expression *> *removed_filters = nullptr);
|
||||
void EraseLabelFilter(const Symbol &symbol, const LabelIx &label,
|
||||
std::vector<Expression *> *removed_filters = nullptr);
|
||||
|
||||
/// Returns a vector of FilterInfo for properties.
|
||||
auto PropertyFilters(const Symbol &symbol) const {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -655,9 +655,9 @@ class IndexLookupRewriter final : public HierarchicalLogicalOperatorVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
storage::LabelId GetLabel(LabelIx label) { return db_->NameToLabel(label.name); }
|
||||
storage::LabelId GetLabel(const LabelIx &label) { return db_->NameToLabel(label.name); }
|
||||
|
||||
storage::PropertyId GetProperty(PropertyIx prop) { return db_->NameToProperty(prop.name); }
|
||||
storage::PropertyId GetProperty(const PropertyIx &prop) { return db_->NameToProperty(prop.name); }
|
||||
|
||||
std::optional<LabelIx> FindBestLabelIndex(const std::unordered_set<LabelIx> &labels) {
|
||||
MG_ASSERT(!labels.empty(), "Trying to find the best label without any labels.");
|
||||
|
||||
@@ -271,9 +271,9 @@ class RuleBasedPlanner {
|
||||
private:
|
||||
TPlanningContext *context_;
|
||||
|
||||
storage::LabelId GetLabel(LabelIx label) { return context_->db->NameToLabel(label.name); }
|
||||
storage::LabelId GetLabel(const LabelIx &label) { return context_->db->NameToLabel(label.name); }
|
||||
|
||||
storage::PropertyId GetProperty(PropertyIx prop) { return context_->db->NameToProperty(prop.name); }
|
||||
storage::PropertyId GetProperty(const PropertyIx &prop) { return context_->db->NameToProperty(prop.name); }
|
||||
|
||||
storage::EdgeTypeId GetEdgeType(EdgeTypeIx edge_type) { return context_->db->NameToEdgeType(edge_type.name); }
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -868,7 +868,10 @@ py::Object MgpListToPyTuple(mgp_list *list, PyObject *py_graph) {
|
||||
}
|
||||
|
||||
void PyCollectGarbage() {
|
||||
if (!Py_IsInitialized() || _Py_IsFinalizing()) {
|
||||
// NOTE: No need to call _Py_IsFinalizing(), we ensure
|
||||
// Python GC thread is stopped before Py_Finalize() is called
|
||||
// in memgraph.cpp
|
||||
if (!Py_IsInitialized()) {
|
||||
// Calling EnsureGIL will crash the program if this is true.
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -125,9 +125,7 @@ TypedValue::TypedValue(storage::PropertyValue &&other, utils::MemoryResource *me
|
||||
case storage::PropertyValue::Type::List: {
|
||||
type_ = Type::List;
|
||||
auto &vec = other.ValueList();
|
||||
new (&list_v) TVector(memory_);
|
||||
list_v.reserve(vec.size());
|
||||
for (auto &v : vec) list_v.emplace_back(std::move(v));
|
||||
new (&list_v) TVector(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end()), memory_);
|
||||
break;
|
||||
}
|
||||
case storage::PropertyValue::Type::Map: {
|
||||
@@ -324,13 +322,13 @@ TypedValue::operator storage::PropertyValue() const {
|
||||
|
||||
#define DEFINE_VALUE_AND_TYPE_GETTERS(type_param, type_enum, field) \
|
||||
type_param &TypedValue::Value##type_enum() { \
|
||||
if (type_ != Type::type_enum) \
|
||||
if (type_ != Type::type_enum) [[unlikely]] \
|
||||
throw TypedValueException("TypedValue is of type '{}', not '{}'", type_, Type::type_enum); \
|
||||
return field; \
|
||||
} \
|
||||
\
|
||||
const type_param &TypedValue::Value##type_enum() const { \
|
||||
if (type_ != Type::type_enum) \
|
||||
if (type_ != Type::type_enum) [[unlikely]] \
|
||||
throw TypedValueException("TypedValue is of type '{}', not '{}'", type_, Type::type_enum); \
|
||||
return field; \
|
||||
} \
|
||||
|
||||
@@ -5,10 +5,8 @@ target_sources(mg-replication
|
||||
include/replication/state.hpp
|
||||
include/replication/epoch.hpp
|
||||
include/replication/config.hpp
|
||||
include/replication/mode.hpp
|
||||
include/replication/messages.hpp
|
||||
include/replication/role.hpp
|
||||
include/replication/status.hpp
|
||||
include/replication/messages.hpp
|
||||
include/replication/replication_client.hpp
|
||||
include/replication/replication_server.hpp
|
||||
|
||||
@@ -25,6 +23,6 @@ target_include_directories(mg-replication PUBLIC include)
|
||||
|
||||
find_package(fmt REQUIRED)
|
||||
target_link_libraries(mg-replication
|
||||
PUBLIC mg::utils mg::kvstore lib::json mg::rpc mg::slk
|
||||
PUBLIC mg::utils mg::kvstore lib::json mg::rpc mg::slk mg::io mg::repl_coord_glue
|
||||
PRIVATE fmt::fmt
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "replication/mode.hpp"
|
||||
#include "replication_coordination_glue/mode.hpp"
|
||||
|
||||
namespace memgraph::replication {
|
||||
|
||||
@@ -24,7 +24,7 @@ inline constexpr auto *kDefaultReplicationServerIp = "0.0.0.0";
|
||||
|
||||
struct ReplicationClientConfig {
|
||||
std::string name;
|
||||
ReplicationMode mode{};
|
||||
replication_coordination_glue::ReplicationMode mode{};
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
|
||||
@@ -40,7 +40,7 @@ struct ReplicationClientConfig {
|
||||
friend bool operator==(const SSL &, const SSL &) = default;
|
||||
};
|
||||
|
||||
std::optional<SSL> ssl;
|
||||
std::optional<SSL> ssl{};
|
||||
|
||||
friend bool operator==(ReplicationClientConfig const &, ReplicationClientConfig const &) = default;
|
||||
};
|
||||
|
||||
@@ -15,27 +15,6 @@
|
||||
#include "slk/serialization.hpp"
|
||||
|
||||
namespace memgraph::replication {
|
||||
|
||||
struct FrequentHeartbeatReq {
|
||||
static const utils::TypeInfo kType; // TODO: make constexpr?
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; } // WHAT?
|
||||
|
||||
static void Load(FrequentHeartbeatReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const FrequentHeartbeatReq &self, memgraph::slk::Builder *builder);
|
||||
FrequentHeartbeatReq() = default;
|
||||
};
|
||||
|
||||
struct FrequentHeartbeatRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(FrequentHeartbeatRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const FrequentHeartbeatRes &self, memgraph::slk::Builder *builder);
|
||||
FrequentHeartbeatRes() = default;
|
||||
};
|
||||
|
||||
using FrequentHeartbeatRpc = rpc::RequestResponse<FrequentHeartbeatReq, FrequentHeartbeatRes>;
|
||||
|
||||
struct SystemHeartbeatReq {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
@@ -58,16 +37,9 @@ struct SystemHeartbeatRes {
|
||||
};
|
||||
|
||||
using SystemHeartbeatRpc = rpc::RequestResponse<SystemHeartbeatReq, SystemHeartbeatRes>;
|
||||
|
||||
void FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder);
|
||||
|
||||
} // namespace memgraph::replication
|
||||
|
||||
namespace memgraph::slk {
|
||||
void Save(const memgraph::replication::FrequentHeartbeatRes &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::replication::FrequentHeartbeatRes *self, memgraph::slk::Reader *reader);
|
||||
void Save(const memgraph::replication::FrequentHeartbeatReq & /*self*/, memgraph::slk::Builder * /*builder*/);
|
||||
void Load(memgraph::replication::FrequentHeartbeatReq * /*self*/, memgraph::slk::Reader * /*reader*/);
|
||||
void Save(const memgraph::replication::SystemHeartbeatRes &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::replication::SystemHeartbeatRes *self, memgraph::slk::Reader *reader);
|
||||
void Save(const memgraph::replication::SystemHeartbeatReq & /*self*/, memgraph::slk::Builder * /*builder*/);
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "replication/config.hpp"
|
||||
#include "replication/messages.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
#include "rpc/client.hpp"
|
||||
#include "utils/scheduler.hpp"
|
||||
#include "utils/synchronized.hpp"
|
||||
@@ -45,7 +45,7 @@ struct ReplicationClient {
|
||||
[this, cb = std::forward<F>(callback), reconnect = false]() mutable {
|
||||
try {
|
||||
{
|
||||
auto stream{rpc_client_.Stream<memgraph::replication::FrequentHeartbeatRpc>()};
|
||||
auto stream{rpc_client_.Stream<memgraph::replication_coordination_glue::FrequentHeartbeatRpc>()};
|
||||
stream.AwaitResponse();
|
||||
}
|
||||
cb(reconnect, *this);
|
||||
@@ -73,7 +73,7 @@ struct ReplicationClient {
|
||||
};
|
||||
utils::Synchronized<State> state_{State::BEHIND};
|
||||
|
||||
memgraph::replication::ReplicationMode mode_{memgraph::replication::ReplicationMode::SYNC};
|
||||
replication_coordination_glue::ReplicationMode mode_{replication_coordination_glue::ReplicationMode::SYNC};
|
||||
// This thread pool is used for background tasks so we don't
|
||||
// block the main storage thread
|
||||
// We use only 1 thread for 2 reasons:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -14,9 +14,9 @@
|
||||
#include "kvstore/kvstore.hpp"
|
||||
#include "replication/config.hpp"
|
||||
#include "replication/epoch.hpp"
|
||||
#include "replication/mode.hpp"
|
||||
#include "replication/replication_client.hpp"
|
||||
#include "replication/role.hpp"
|
||||
#include "replication_coordination_glue/mode.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
#include "replication_server.hpp"
|
||||
#include "status.hpp"
|
||||
#include "utils/result.hpp"
|
||||
@@ -32,7 +32,8 @@ namespace memgraph::replication {
|
||||
|
||||
enum class RolePersisted : uint8_t { UNKNOWN_OR_NO, YES };
|
||||
|
||||
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, END_POINT_EXISTS, COULD_NOT_BE_PERSISTED, NOT_MAIN, SUCCESS };
|
||||
// TODO: (andi) Rename Error to Status
|
||||
enum class RegisterReplicaError : uint8_t { NAME_EXISTS, ENDPOINT_EXISTS, COULD_NOT_BE_PERSISTED, NOT_MAIN, SUCCESS };
|
||||
|
||||
struct RoleMainData {
|
||||
RoleMainData() = default;
|
||||
@@ -72,12 +73,13 @@ struct ReplicationState {
|
||||
using FetchReplicationResult_t = utils::BasicResult<FetchReplicationError, ReplicationData_t>;
|
||||
auto FetchReplicationData() -> FetchReplicationResult_t;
|
||||
|
||||
auto GetRole() const -> ReplicationRole {
|
||||
return std::holds_alternative<RoleReplicaData>(replication_data_) ? ReplicationRole::REPLICA
|
||||
: ReplicationRole::MAIN;
|
||||
auto GetRole() const -> replication_coordination_glue::ReplicationRole {
|
||||
return std::holds_alternative<RoleReplicaData>(replication_data_)
|
||||
? replication_coordination_glue::ReplicationRole::REPLICA
|
||||
: replication_coordination_glue::ReplicationRole::MAIN;
|
||||
}
|
||||
bool IsMain() const { return GetRole() == ReplicationRole::MAIN; }
|
||||
bool IsReplica() const { return GetRole() == ReplicationRole::REPLICA; }
|
||||
bool IsMain() const { return GetRole() == replication_coordination_glue::ReplicationRole::MAIN; }
|
||||
bool IsReplica() const { return GetRole() == replication_coordination_glue::ReplicationRole::REPLICA; }
|
||||
|
||||
bool HasDurability() const { return nullptr != durability_; }
|
||||
|
||||
@@ -92,7 +94,6 @@ struct ReplicationState {
|
||||
utils::BasicResult<RegisterReplicaError, ReplicationClient *> RegisterReplica(const ReplicationClientConfig &config);
|
||||
|
||||
bool SetReplicationRoleMain();
|
||||
|
||||
bool SetReplicationRoleReplica(const ReplicationServerConfig &config);
|
||||
|
||||
private:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "replication/config.hpp"
|
||||
#include "replication/epoch.hpp"
|
||||
#include "replication/role.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
|
||||
namespace memgraph::replication::durability {
|
||||
|
||||
@@ -42,7 +42,7 @@ struct MainRole {
|
||||
|
||||
// fragment of key: "__replication_role"
|
||||
struct ReplicaRole {
|
||||
ReplicationServerConfig config;
|
||||
ReplicationServerConfig config{};
|
||||
friend bool operator==(ReplicaRole const &, ReplicaRole const &) = default;
|
||||
};
|
||||
|
||||
|
||||
@@ -8,25 +8,32 @@
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "replication/messages.hpp"
|
||||
#include "rpc/messages.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
#include "slk/streams.hpp"
|
||||
|
||||
namespace memgraph::replication {
|
||||
|
||||
constexpr utils::TypeInfo SystemHeartbeatReq::kType{utils::TypeId::REP_SYSTEM_HEARTBEAT_REQ, "SystemHeartbeatReq",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo SystemHeartbeatRes::kType{utils::TypeId::REP_SYSTEM_HEARTBEAT_RES, "SystemHeartbeatRes",
|
||||
nullptr};
|
||||
|
||||
void SystemHeartbeatReq::Save(const SystemHeartbeatReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void SystemHeartbeatReq::Load(SystemHeartbeatReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
void SystemHeartbeatRes::Save(const SystemHeartbeatRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void SystemHeartbeatRes::Load(SystemHeartbeatRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
} // namespace memgraph::replication
|
||||
|
||||
namespace memgraph::slk {
|
||||
// Serialize code for FrequentHeartbeatRes
|
||||
void Save(const memgraph::replication::FrequentHeartbeatRes &self, memgraph::slk::Builder *builder) {}
|
||||
void Load(memgraph::replication::FrequentHeartbeatRes *self, memgraph::slk::Reader *reader) {}
|
||||
|
||||
// Serialize code for FrequentHeartbeatReq
|
||||
void Save(const memgraph::replication::FrequentHeartbeatReq & /*self*/, memgraph::slk::Builder * /*builder*/) {
|
||||
/* Nothing to serialize */
|
||||
}
|
||||
void Load(memgraph::replication::FrequentHeartbeatReq * /*self*/, memgraph::slk::Reader * /*reader*/) {
|
||||
/* Nothing to serialize */
|
||||
}
|
||||
|
||||
// Serialize code for SystemHeartbeatRes
|
||||
void Save(const memgraph::replication::SystemHeartbeatRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self.system_timestamp, builder);
|
||||
@@ -42,55 +49,4 @@ void Save(const memgraph::replication::SystemHeartbeatReq & /*self*/, memgraph::
|
||||
void Load(memgraph::replication::SystemHeartbeatReq * /*self*/, memgraph::slk::Reader * /*reader*/) {
|
||||
/* Nothing to serialize */
|
||||
}
|
||||
|
||||
} // namespace memgraph::slk
|
||||
|
||||
namespace memgraph::replication {
|
||||
|
||||
constexpr utils::TypeInfo FrequentHeartbeatReq::kType{utils::TypeId::REP_FREQUENT_HEARTBEAT_REQ, "FrequentHeartbeatReq",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo FrequentHeartbeatRes::kType{utils::TypeId::REP_FREQUENT_HEARTBEAT_RES, "FrequentHeartbeatRes",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo SystemHeartbeatReq::kType{utils::TypeId::REP_SYSTEM_HEARTBEAT_REQ, "SystemHeartbeatReq",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo SystemHeartbeatRes::kType{utils::TypeId::REP_SYSTEM_HEARTBEAT_RES, "SystemHeartbeatRes",
|
||||
nullptr};
|
||||
|
||||
void FrequentHeartbeatReq::Save(const FrequentHeartbeatReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void FrequentHeartbeatReq::Load(FrequentHeartbeatReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
void FrequentHeartbeatRes::Save(const FrequentHeartbeatRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void FrequentHeartbeatRes::Load(FrequentHeartbeatRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void SystemHeartbeatReq::Save(const SystemHeartbeatReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void SystemHeartbeatReq::Load(SystemHeartbeatReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
void SystemHeartbeatRes::Save(const SystemHeartbeatRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void SystemHeartbeatRes::Load(SystemHeartbeatRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
|
||||
FrequentHeartbeatReq req;
|
||||
FrequentHeartbeatReq::Load(&req, req_reader);
|
||||
memgraph::slk::Load(&req, req_reader);
|
||||
FrequentHeartbeatRes res{};
|
||||
memgraph::slk::Save(res, res_builder);
|
||||
}
|
||||
|
||||
} // namespace memgraph::replication
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -28,8 +28,8 @@ ReplicationClient::ReplicationClient(const memgraph::replication::ReplicationCli
|
||||
mode_{config.mode} {}
|
||||
|
||||
ReplicationClient::~ReplicationClient() {
|
||||
auto endpoint = rpc_client_.Endpoint();
|
||||
try {
|
||||
auto const &endpoint = rpc_client_.Endpoint();
|
||||
spdlog::trace("Closing replication client on {}:{}", endpoint.address, endpoint.port);
|
||||
} catch (...) {
|
||||
// Logging can throw. Not a big deal, just ignore.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -10,7 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "replication/replication_server.hpp"
|
||||
#include "replication/messages.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
|
||||
namespace memgraph::replication {
|
||||
namespace {
|
||||
@@ -32,9 +32,9 @@ ReplicationServer::ReplicationServer(const memgraph::replication::ReplicationSer
|
||||
: rpc_server_context_{CreateServerContext(config)},
|
||||
rpc_server_{io::network::Endpoint{config.ip_address, config.port}, &rpc_server_context_,
|
||||
kReplicationServerThreads} {
|
||||
rpc_server_.Register<FrequentHeartbeatRpc>([](auto *req_reader, auto *res_builder) {
|
||||
rpc_server_.Register<replication_coordination_glue::FrequentHeartbeatRpc>([](auto *req_reader, auto *res_builder) {
|
||||
spdlog::debug("Received FrequentHeartbeatRpc");
|
||||
FrequentHeartbeatHandler(req_reader, res_builder);
|
||||
replication_coordination_glue::FrequentHeartbeatHandler(req_reader, res_builder);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -219,10 +219,12 @@ bool ReplicationState::TryPersistRegisteredReplica(const ReplicationClientConfig
|
||||
|
||||
bool ReplicationState::SetReplicationRoleMain() {
|
||||
auto new_epoch = utils::GenerateUUID();
|
||||
|
||||
if (!TryPersistRoleMain(new_epoch)) {
|
||||
return false;
|
||||
}
|
||||
replication_data_ = RoleMainData{ReplicationEpoch{new_epoch}};
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -237,6 +239,7 @@ bool ReplicationState::SetReplicationRoleReplica(const ReplicationServerConfig &
|
||||
utils::BasicResult<RegisterReplicaError, ReplicationClient *> ReplicationState::RegisterReplica(
|
||||
const ReplicationClientConfig &config) {
|
||||
auto const replica_handler = [](RoleReplicaData const &) { return RegisterReplicaError::NOT_MAIN; };
|
||||
|
||||
ReplicationClient *client{nullptr};
|
||||
auto const main_handler = [&client, &config, this](RoleMainData &mainData) -> RegisterReplicaError {
|
||||
// name check
|
||||
@@ -257,7 +260,7 @@ utils::BasicResult<RegisterReplicaError, ReplicationClient *> ReplicationState::
|
||||
return std::any_of(replicas.begin(), replicas.end(), endpoint_matches);
|
||||
};
|
||||
if (endpoint_check(mainData.registered_replicas_)) {
|
||||
return RegisterReplicaError::END_POINT_EXISTS;
|
||||
return RegisterReplicaError::ENDPOINT_EXISTS;
|
||||
}
|
||||
|
||||
// Durability
|
||||
@@ -276,4 +279,5 @@ utils::BasicResult<RegisterReplicaError, ReplicationClient *> ReplicationState::
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace memgraph::replication
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -29,12 +29,14 @@ constexpr auto *kVersion = "durability_version";
|
||||
|
||||
void to_json(nlohmann::json &j, const ReplicationRoleEntry &p) {
|
||||
auto processMAIN = [&](MainRole const &main) {
|
||||
j = nlohmann::json{{kVersion, p.version}, {kReplicationRole, ReplicationRole::MAIN}, {kEpoch, main.epoch.id()}};
|
||||
j = nlohmann::json{{kVersion, p.version},
|
||||
{kReplicationRole, replication_coordination_glue::ReplicationRole::MAIN},
|
||||
{kEpoch, main.epoch.id()}};
|
||||
};
|
||||
auto processREPLICA = [&](ReplicaRole const &replica) {
|
||||
j = nlohmann::json{
|
||||
{kVersion, p.version},
|
||||
{kReplicationRole, ReplicationRole::REPLICA},
|
||||
{kReplicationRole, replication_coordination_glue::ReplicationRole::REPLICA},
|
||||
{kIpAddress, replica.config.ip_address},
|
||||
{kPort, replica.config.port}
|
||||
// TODO: SSL
|
||||
@@ -47,17 +49,17 @@ void from_json(const nlohmann::json &j, ReplicationRoleEntry &p) {
|
||||
// This value did not exist in V1, hence default DurabilityVersion::V1
|
||||
DurabilityVersion version = j.value(kVersion, DurabilityVersion::V1);
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
ReplicationRole role;
|
||||
replication_coordination_glue::ReplicationRole role;
|
||||
j.at(kReplicationRole).get_to(role);
|
||||
switch (role) {
|
||||
case ReplicationRole::MAIN: {
|
||||
case replication_coordination_glue::ReplicationRole::MAIN: {
|
||||
auto json_epoch = j.value(kEpoch, std::string{});
|
||||
auto epoch = ReplicationEpoch{};
|
||||
if (!json_epoch.empty()) epoch.SetEpoch(json_epoch);
|
||||
p = ReplicationRoleEntry{.version = version, .role = MainRole{.epoch = std::move(epoch)}};
|
||||
break;
|
||||
}
|
||||
case ReplicationRole::REPLICA: {
|
||||
case memgraph::replication_coordination_glue::ReplicationRole::REPLICA: {
|
||||
std::string ip_address;
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-init-variables)
|
||||
uint16_t port;
|
||||
@@ -95,7 +97,7 @@ void from_json(const nlohmann::json &j, ReplicationReplicaEntry &p) {
|
||||
auto seconds = j.at(kCheckFrequency).get<std::chrono::seconds::rep>();
|
||||
auto config = ReplicationClientConfig{
|
||||
.name = j.at(kReplicaName).get<std::string>(),
|
||||
.mode = j.at(kSyncMode).get<ReplicationMode>(),
|
||||
.mode = j.at(kSyncMode).get<replication_coordination_glue::ReplicationMode>(),
|
||||
.ip_address = j.at(kIpAddress).get<std::string>(),
|
||||
.port = j.at(kPort).get<uint16_t>(),
|
||||
.replica_check_frequency = std::chrono::seconds{seconds},
|
||||
|
||||
14
src/replication_coordination_glue/CMakeLists.txt
Normal file
14
src/replication_coordination_glue/CMakeLists.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
add_library(mg-repl_coord_glue STATIC )
|
||||
add_library(mg::repl_coord_glue ALIAS mg-repl_coord_glue)
|
||||
|
||||
target_sources(mg-repl_coord_glue
|
||||
PUBLIC
|
||||
messages.hpp
|
||||
mode.hpp
|
||||
role.hpp
|
||||
|
||||
PRIVATE
|
||||
messages.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(mg-repl_coord_glue mg-rpc mg-slk)
|
||||
63
src/replication_coordination_glue/messages.cpp
Normal file
63
src/replication_coordination_glue/messages.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
#include "rpc/messages.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
#include "slk/streams.hpp"
|
||||
|
||||
namespace memgraph::slk {
|
||||
// Serialize code for FrequentHeartbeatRes
|
||||
void Save(const memgraph::replication_coordination_glue::FrequentHeartbeatRes &self, memgraph::slk::Builder *builder) {}
|
||||
void Load(memgraph::replication_coordination_glue::FrequentHeartbeatRes *self, memgraph::slk::Reader *reader) {}
|
||||
|
||||
// Serialize code for FrequentHeartbeatReq
|
||||
void Save(const memgraph::replication_coordination_glue::FrequentHeartbeatReq & /*self*/,
|
||||
memgraph::slk::Builder * /*builder*/) {
|
||||
/* Nothing to serialize */
|
||||
}
|
||||
void Load(memgraph::replication_coordination_glue::FrequentHeartbeatReq * /*self*/,
|
||||
memgraph::slk::Reader * /*reader*/) {
|
||||
/* Nothing to serialize */
|
||||
}
|
||||
|
||||
} // namespace memgraph::slk
|
||||
|
||||
namespace memgraph::replication_coordination_glue {
|
||||
|
||||
constexpr utils::TypeInfo FrequentHeartbeatReq::kType{utils::TypeId::REP_FREQUENT_HEARTBEAT_REQ, "FrequentHeartbeatReq",
|
||||
nullptr};
|
||||
|
||||
constexpr utils::TypeInfo FrequentHeartbeatRes::kType{utils::TypeId::REP_FREQUENT_HEARTBEAT_RES, "FrequentHeartbeatRes",
|
||||
nullptr};
|
||||
|
||||
void FrequentHeartbeatReq::Save(const FrequentHeartbeatReq &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void FrequentHeartbeatReq::Load(FrequentHeartbeatReq *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
void FrequentHeartbeatRes::Save(const FrequentHeartbeatRes &self, memgraph::slk::Builder *builder) {
|
||||
memgraph::slk::Save(self, builder);
|
||||
}
|
||||
void FrequentHeartbeatRes::Load(FrequentHeartbeatRes *self, memgraph::slk::Reader *reader) {
|
||||
memgraph::slk::Load(self, reader);
|
||||
}
|
||||
|
||||
void FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
|
||||
FrequentHeartbeatReq req;
|
||||
FrequentHeartbeatReq::Load(&req, req_reader);
|
||||
memgraph::slk::Load(&req, req_reader);
|
||||
FrequentHeartbeatRes res{};
|
||||
memgraph::slk::Save(res, res_builder);
|
||||
}
|
||||
|
||||
} // namespace memgraph::replication_coordination_glue
|
||||
49
src/replication_coordination_glue/messages.hpp
Normal file
49
src/replication_coordination_glue/messages.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2024 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
|
||||
// License, and you may not use this file except in compliance with the Business Source License.
|
||||
//
|
||||
// As of the Change Date specified in that file, in accordance with
|
||||
// the Business Source License, use of this software will be governed
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "rpc/messages.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
|
||||
namespace memgraph::replication_coordination_glue {
|
||||
|
||||
struct FrequentHeartbeatReq {
|
||||
static const utils::TypeInfo kType; // TODO: make constexpr?
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(FrequentHeartbeatReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const FrequentHeartbeatReq &self, memgraph::slk::Builder *builder);
|
||||
FrequentHeartbeatReq() = default;
|
||||
};
|
||||
|
||||
struct FrequentHeartbeatRes {
|
||||
static const utils::TypeInfo kType;
|
||||
static const utils::TypeInfo &GetTypeInfo() { return kType; }
|
||||
|
||||
static void Load(FrequentHeartbeatRes *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const FrequentHeartbeatRes &self, memgraph::slk::Builder *builder);
|
||||
FrequentHeartbeatRes() = default;
|
||||
};
|
||||
|
||||
using FrequentHeartbeatRpc = rpc::RequestResponse<FrequentHeartbeatReq, FrequentHeartbeatRes>;
|
||||
|
||||
void FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder);
|
||||
|
||||
} // namespace memgraph::replication_coordination_glue
|
||||
|
||||
namespace memgraph::slk {
|
||||
void Save(const memgraph::replication_coordination_glue::FrequentHeartbeatRes &self, memgraph::slk::Builder *builder);
|
||||
void Load(memgraph::replication_coordination_glue::FrequentHeartbeatRes *self, memgraph::slk::Reader *reader);
|
||||
void Save(const memgraph::replication_coordination_glue::FrequentHeartbeatReq & /*self*/,
|
||||
memgraph::slk::Builder * /*builder*/);
|
||||
void Load(memgraph::replication_coordination_glue::FrequentHeartbeatReq * /*self*/, memgraph::slk::Reader * /*reader*/);
|
||||
} // namespace memgraph::slk
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2023 Memgraph Ltd.
|
||||
// Copyright 2024 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
|
||||
@@ -10,7 +10,9 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
namespace memgraph::replication {
|
||||
|
||||
namespace memgraph::replication_coordination_glue {
|
||||
enum class ReplicationMode : std::uint8_t { SYNC, ASYNC };
|
||||
}
|
||||
} // namespace memgraph::replication_coordination_glue
|
||||
@@ -12,8 +12,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
namespace memgraph::replication {
|
||||
namespace memgraph::replication_coordination_glue {
|
||||
|
||||
// TODO: figure out a way of ensuring that usage of this type is never uninitialed/defaulted incorrectly to MAIN
|
||||
enum class ReplicationRole : uint8_t { MAIN, REPLICA };
|
||||
} // namespace memgraph::replication
|
||||
} // namespace memgraph::replication_coordination_glue
|
||||
@@ -48,7 +48,7 @@ class GenericRpcFailedException : public RpcFailedException {
|
||||
class SlkRpcFailedException : public RpcFailedException {
|
||||
public:
|
||||
SlkRpcFailedException()
|
||||
: RpcFailedException("Received malformed message from cluster. Please raise an issue with Memgraph developer.") {}
|
||||
: RpcFailedException("Received malformed message from cluster. Please raise an issue on Memgraph GitHub issues.") {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(SlkRpcFailedException);
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user