Support bolt+routing (#1796)
This commit is contained in:
parent
9629f10166
commit
f699c0b37f
13
.github/workflows/diff.yaml
vendored
13
.github/workflows/diff.yaml
vendored
@ -257,6 +257,17 @@ jobs:
|
||||
--organization-name $MEMGRAPH_ORGANIZATION_NAME \
|
||||
test-memgraph drivers
|
||||
|
||||
- name: Run HA driver tests
|
||||
run: |
|
||||
./release/package/mgbuild.sh \
|
||||
--toolchain $TOOLCHAIN \
|
||||
--os $OS \
|
||||
--arch $ARCH \
|
||||
--threads $THREADS \
|
||||
--enterprise-license $MEMGRAPH_ENTERPRISE_LICENSE \
|
||||
--organization-name $MEMGRAPH_ORGANIZATION_NAME \
|
||||
test-memgraph drivers-high-availability
|
||||
|
||||
- name: Run integration tests
|
||||
run: |
|
||||
./release/package/mgbuild.sh \
|
||||
@ -278,7 +289,7 @@ jobs:
|
||||
--enterprise-license $MEMGRAPH_ENTERPRISE_LICENSE \
|
||||
--organization-name $MEMGRAPH_ORGANIZATION_NAME \
|
||||
test-memgraph cppcheck-and-clang-format
|
||||
|
||||
|
||||
- name: Save cppcheck and clang-format errors
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
|
@ -48,9 +48,9 @@ SUPPORTED_ARCHS=(
|
||||
)
|
||||
SUPPORTED_TESTS=(
|
||||
clang-tidy cppcheck-and-clang-format code-analysis
|
||||
code-coverage drivers durability e2e gql-behave
|
||||
code-coverage drivers drivers-high-availability durability e2e gql-behave
|
||||
integration leftover-CTest macro-benchmark
|
||||
mgbench stress-plain stress-ssl
|
||||
mgbench stress-plain stress-ssl
|
||||
unit unit-coverage upload-to-bench-graph
|
||||
|
||||
)
|
||||
@ -116,7 +116,7 @@ print_help () {
|
||||
|
||||
echo -e "\nToolchain v5 supported OSs:"
|
||||
echo -e " \"${SUPPORTED_OS_V5[*]}\""
|
||||
|
||||
|
||||
echo -e "\nExample usage:"
|
||||
echo -e " $SCRIPT_NAME --os debian-12 --toolchain v5 --arch amd run"
|
||||
echo -e " $SCRIPT_NAME --os debian-12 --toolchain v5 --arch amd --build-type RelWithDebInfo build-memgraph --community"
|
||||
@ -296,7 +296,7 @@ build_memgraph () {
|
||||
docker cp "$PROJECT_ROOT/." "$build_container:$MGBUILD_ROOT_DIR/"
|
||||
fi
|
||||
# Change ownership of copied files so the mg user inside container can access them
|
||||
docker exec -u root $build_container bash -c "chown -R mg:mg $MGBUILD_ROOT_DIR"
|
||||
docker exec -u root $build_container bash -c "chown -R mg:mg $MGBUILD_ROOT_DIR"
|
||||
|
||||
echo "Installing dependencies using '/memgraph/environment/os/$os.sh' script..."
|
||||
docker exec -u root "$build_container" bash -c "$MGBUILD_ROOT_DIR/environment/os/$os.sh check TOOLCHAIN_RUN_DEPS || /environment/os/$os.sh install TOOLCHAIN_RUN_DEPS"
|
||||
@ -318,10 +318,9 @@ build_memgraph () {
|
||||
# Define cmake command
|
||||
local cmake_cmd="cmake $build_type_flag $arm_flag $community_flag $telemetry_id_override_flag $coverage_flag $asan_flag $ubsan_flag .."
|
||||
docker exec -u mg "$build_container" bash -c "cd $container_build_dir && $ACTIVATE_TOOLCHAIN && $ACTIVATE_CARGO && $cmake_cmd"
|
||||
|
||||
# ' is used instead of " because we need to run make within the allowed
|
||||
# container resources.
|
||||
# Default value for $threads is 0 instead of $(nproc) because macos
|
||||
# Default value for $threads is 0 instead of $(nproc) because macos
|
||||
# doesn't support the nproc command.
|
||||
# 0 is set for default value and checked here because mgbuild containers
|
||||
# support nproc
|
||||
@ -363,7 +362,7 @@ copy_memgraph() {
|
||||
local container_output_path="$MGBUILD_ROOT_DIR/build/memgraph"
|
||||
local host_output_path="$PROJECT_ROOT/build/memgraph"
|
||||
mkdir -p "$PROJECT_ROOT/build"
|
||||
docker cp -L $build_container:$container_output_path $host_output_path
|
||||
docker cp -L $build_container:$container_output_path $host_output_path
|
||||
echo "Binary saved to $host_output_path"
|
||||
;;
|
||||
--build-logs)
|
||||
@ -371,7 +370,7 @@ copy_memgraph() {
|
||||
local container_output_path="$MGBUILD_ROOT_DIR/build/logs"
|
||||
local host_output_path="$PROJECT_ROOT/build/logs"
|
||||
mkdir -p "$PROJECT_ROOT/build"
|
||||
docker cp -L $build_container:$container_output_path $host_output_path
|
||||
docker cp -L $build_container:$container_output_path $host_output_path
|
||||
echo "Build logs saved to $host_output_path"
|
||||
;;
|
||||
--package)
|
||||
@ -418,6 +417,9 @@ test_memgraph() {
|
||||
drivers)
|
||||
docker exec -u mg $build_container bash -c "$EXPORT_LICENSE && $EXPORT_ORG_NAME && cd $MGBUILD_ROOT_DIR "'&& ./tests/drivers/run.sh'
|
||||
;;
|
||||
drivers-high-availability)
|
||||
docker exec -u mg $build_container bash -c "$EXPORT_LICENSE && $EXPORT_ORG_NAME && cd $MGBUILD_ROOT_DIR "'&& ./tests/drivers/run_cluster.sh'
|
||||
;;
|
||||
integration)
|
||||
docker exec -u mg $build_container bash -c "$EXPORT_LICENSE && $EXPORT_ORG_NAME && cd $MGBUILD_ROOT_DIR "'&& tests/integration/run.sh'
|
||||
;;
|
||||
@ -664,4 +666,4 @@ case $command in
|
||||
echo "Error: Unknown command '$command'"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
@ -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
|
||||
@ -88,6 +88,12 @@ class Session {
|
||||
|
||||
virtual void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &run_time_info) = 0;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
virtual auto Route(std::map<std::string, Value> const &routing,
|
||||
std::vector<memgraph::communication::bolt::Value> const &bookmarks,
|
||||
std::map<std::string, Value> const &extra) -> std::map<std::string, Value> = 0;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Put results of the processed query in the `encoder`.
|
||||
*
|
||||
|
@ -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
|
||||
@ -79,9 +79,9 @@ State RunHandlerV4(Signature signature, TSession &session, State state, Marker m
|
||||
}
|
||||
case Signature::Route: {
|
||||
if constexpr (bolt_minor >= 3) {
|
||||
if (signature == Signature::Route) return HandleRoute<TSession>(session, marker);
|
||||
return HandleRoute<TSession>(session, marker);
|
||||
} else {
|
||||
spdlog::trace("Supported only in bolt v4.3");
|
||||
spdlog::trace("Supported only in bolt versions >= 4.3");
|
||||
return State::Close;
|
||||
}
|
||||
}
|
||||
|
@ -478,9 +478,6 @@ State HandleGoodbye() {
|
||||
|
||||
template <typename TSession>
|
||||
State HandleRoute(TSession &session, const Marker marker) {
|
||||
// Route message is not implemented since it is Neo4j specific, therefore we will receive it and inform user that
|
||||
// there is no implementation. Before that, we have to read out the fields from the buffer to leave it in a clean
|
||||
// state.
|
||||
if (marker != Marker::TinyStruct3) {
|
||||
spdlog::trace("Expected TinyStruct3 marker, but received 0x{:02x}!", utils::UnderlyingCast(marker));
|
||||
return State::Close;
|
||||
@ -496,11 +493,27 @@ State HandleRoute(TSession &session, const Marker marker) {
|
||||
spdlog::trace("Couldn't read bookmarks field!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
// TODO: (andi) Fix Bolt versions
|
||||
Value db;
|
||||
if (!session.decoder_.ReadValue(&db)) {
|
||||
spdlog::trace("Couldn't read db field!");
|
||||
return State::Close;
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
try {
|
||||
auto res = session.Route(routing.ValueMap(), bookmarks.ValueList(), {});
|
||||
if (!session.encoder_.MessageSuccess(std::move(res))) {
|
||||
spdlog::trace("Couldn't send result of routing!");
|
||||
return State::Close;
|
||||
}
|
||||
return State::Idle;
|
||||
} catch (const std::exception &e) {
|
||||
return HandleFailure(session, e);
|
||||
}
|
||||
|
||||
#else
|
||||
session.encoder_buffer_.Clear();
|
||||
bool fail_sent =
|
||||
session.encoder_.MessageFailure({{"code", "66"}, {"message", "Route message is not supported in Memgraph!"}});
|
||||
@ -509,6 +522,7 @@ State HandleRoute(TSession &session, const Marker marker) {
|
||||
return State::Close;
|
||||
}
|
||||
return State::Error;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename TSession>
|
||||
|
@ -6,7 +6,7 @@ target_sources(mg-coordination
|
||||
include/coordination/coordinator_state.hpp
|
||||
include/coordination/coordinator_rpc.hpp
|
||||
include/coordination/coordinator_server.hpp
|
||||
include/coordination/coordinator_config.hpp
|
||||
include/coordination/coordinator_communication_config.hpp
|
||||
include/coordination/coordinator_exceptions.hpp
|
||||
include/coordination/coordinator_slk.hpp
|
||||
include/coordination/coordinator_instance.hpp
|
||||
@ -23,7 +23,7 @@ target_sources(mg-coordination
|
||||
include/nuraft/coordinator_state_manager.hpp
|
||||
|
||||
PRIVATE
|
||||
coordinator_config.cpp
|
||||
coordinator_communication_config.cpp
|
||||
coordinator_client.cpp
|
||||
coordinator_state.cpp
|
||||
coordinator_rpc.cpp
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
#include "coordination/coordinator_client.hpp"
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "coordination/coordinator_rpc.hpp"
|
||||
#include "replication_coordination_glue/common.hpp"
|
||||
#include "replication_coordination_glue/messages.hpp"
|
||||
@ -23,18 +23,17 @@
|
||||
namespace memgraph::coordination {
|
||||
|
||||
namespace {
|
||||
auto CreateClientContext(memgraph::coordination::CoordinatorClientConfig const &config)
|
||||
auto CreateClientContext(memgraph::coordination::CoordinatorToReplicaConfig const &config)
|
||||
-> communication::ClientContext {
|
||||
return (config.ssl) ? communication::ClientContext{config.ssl->key_file, config.ssl->cert_file}
|
||||
: communication::ClientContext{};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CoordinatorClient::CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
||||
CoordinatorClient::CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorToReplicaConfig config,
|
||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb)
|
||||
: rpc_context_{CreateClientContext(config)},
|
||||
rpc_client_{io::network::Endpoint(io::network::Endpoint::needs_resolving, config.ip_address, config.port),
|
||||
&rpc_context_},
|
||||
rpc_client_{config.mgt_server, &rpc_context_},
|
||||
config_{std::move(config)},
|
||||
coord_instance_{coord_instance},
|
||||
succ_cb_{std::move(succ_cb)},
|
||||
@ -86,7 +85,9 @@ void CoordinatorClient::StopFrequentCheck() { instance_checker_.Stop(); }
|
||||
void CoordinatorClient::PauseFrequentCheck() { instance_checker_.Pause(); }
|
||||
void CoordinatorClient::ResumeFrequentCheck() { instance_checker_.Resume(); }
|
||||
|
||||
auto CoordinatorClient::ReplicationClientInfo() const -> ReplClientInfo { return config_.replication_client_info; }
|
||||
auto CoordinatorClient::ReplicationClientInfo() const -> coordination::ReplicationClientInfo {
|
||||
return config_.replication_client_info;
|
||||
}
|
||||
|
||||
auto CoordinatorClient::SendPromoteReplicaToMainRpc(const utils::UUID &uuid,
|
||||
ReplicationClientsInfo replication_clients_info) const -> bool {
|
||||
|
@ -18,86 +18,88 @@
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
void to_json(nlohmann::json &j, InstanceState const &instance_state) {
|
||||
void to_json(nlohmann::json &j, ReplicationInstanceState const &instance_state) {
|
||||
j = nlohmann::json{{"config", instance_state.config}, {"status", instance_state.status}};
|
||||
}
|
||||
|
||||
void from_json(nlohmann::json const &j, InstanceState &instance_state) {
|
||||
void from_json(nlohmann::json const &j, ReplicationInstanceState &instance_state) {
|
||||
j.at("config").get_to(instance_state.config);
|
||||
j.at("status").get_to(instance_state.status);
|
||||
}
|
||||
|
||||
CoordinatorClusterState::CoordinatorClusterState(std::map<std::string, InstanceState, std::less<>> instances)
|
||||
: instances_{std::move(instances)} {}
|
||||
CoordinatorClusterState::CoordinatorClusterState(std::map<std::string, ReplicationInstanceState, std::less<>> instances)
|
||||
: repl_instances_{std::move(instances)} {}
|
||||
|
||||
CoordinatorClusterState::CoordinatorClusterState(CoordinatorClusterState const &other) : instances_{other.instances_} {}
|
||||
CoordinatorClusterState::CoordinatorClusterState(CoordinatorClusterState const &other)
|
||||
: repl_instances_{other.repl_instances_} {}
|
||||
|
||||
CoordinatorClusterState &CoordinatorClusterState::operator=(CoordinatorClusterState const &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
instances_ = other.instances_;
|
||||
repl_instances_ = other.repl_instances_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
CoordinatorClusterState::CoordinatorClusterState(CoordinatorClusterState &&other) noexcept
|
||||
: instances_{std::move(other.instances_)} {}
|
||||
: repl_instances_{std::move(other.repl_instances_)} {}
|
||||
|
||||
CoordinatorClusterState &CoordinatorClusterState::operator=(CoordinatorClusterState &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
instances_ = std::move(other.instances_);
|
||||
repl_instances_ = std::move(other.repl_instances_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::MainExists() const -> bool {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
return std::ranges::any_of(instances_,
|
||||
return std::ranges::any_of(repl_instances_,
|
||||
[](auto const &entry) { return entry.second.status == ReplicationRole::MAIN; });
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::IsMain(std::string_view instance_name) const -> bool {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
auto const it = instances_.find(instance_name);
|
||||
return it != instances_.end() && it->second.status == ReplicationRole::MAIN;
|
||||
auto const it = repl_instances_.find(instance_name);
|
||||
return it != repl_instances_.end() && it->second.status == ReplicationRole::MAIN;
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::IsReplica(std::string_view instance_name) const -> bool {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
auto const it = instances_.find(instance_name);
|
||||
return it != instances_.end() && it->second.status == ReplicationRole::REPLICA;
|
||||
auto const it = repl_instances_.find(instance_name);
|
||||
return it != repl_instances_.end() && it->second.status == ReplicationRole::REPLICA;
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::InsertInstance(std::string instance_name, InstanceState instance_state) -> void {
|
||||
auto CoordinatorClusterState::InsertInstance(std::string instance_name, ReplicationInstanceState instance_state)
|
||||
-> void {
|
||||
auto lock = std::lock_guard{log_lock_};
|
||||
instances_.insert_or_assign(std::move(instance_name), std::move(instance_state));
|
||||
repl_instances_.insert_or_assign(std::move(instance_name), std::move(instance_state));
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::DoAction(TRaftLog log_entry, RaftLogAction log_action) -> void {
|
||||
auto lock = std::lock_guard{log_lock_};
|
||||
switch (log_action) {
|
||||
case RaftLogAction::REGISTER_REPLICATION_INSTANCE: {
|
||||
auto const &config = std::get<CoordinatorClientConfig>(log_entry);
|
||||
instances_[config.instance_name] = InstanceState{config, ReplicationRole::REPLICA};
|
||||
auto const &config = std::get<CoordinatorToReplicaConfig>(log_entry);
|
||||
repl_instances_[config.instance_name] = ReplicationInstanceState{config, ReplicationRole::REPLICA};
|
||||
break;
|
||||
}
|
||||
case RaftLogAction::UNREGISTER_REPLICATION_INSTANCE: {
|
||||
auto const instance_name = std::get<std::string>(log_entry);
|
||||
instances_.erase(instance_name);
|
||||
repl_instances_.erase(instance_name);
|
||||
break;
|
||||
}
|
||||
case RaftLogAction::SET_INSTANCE_AS_MAIN: {
|
||||
auto const instance_name = std::get<std::string>(log_entry);
|
||||
auto it = instances_.find(instance_name);
|
||||
MG_ASSERT(it != instances_.end(), "Instance does not exist as part of raft state!");
|
||||
auto it = repl_instances_.find(instance_name);
|
||||
MG_ASSERT(it != repl_instances_.end(), "Instance does not exist as part of raft state!");
|
||||
it->second.status = ReplicationRole::MAIN;
|
||||
break;
|
||||
}
|
||||
case RaftLogAction::SET_INSTANCE_AS_REPLICA: {
|
||||
auto const instance_name = std::get<std::string>(log_entry);
|
||||
auto it = instances_.find(instance_name);
|
||||
MG_ASSERT(it != instances_.end(), "Instance does not exist as part of raft state!");
|
||||
auto it = repl_instances_.find(instance_name);
|
||||
MG_ASSERT(it != repl_instances_.end(), "Instance does not exist as part of raft state!");
|
||||
it->second.status = ReplicationRole::REPLICA;
|
||||
break;
|
||||
}
|
||||
@ -105,13 +107,18 @@ auto CoordinatorClusterState::DoAction(TRaftLog log_entry, RaftLogAction log_act
|
||||
uuid_ = std::get<utils::UUID>(log_entry);
|
||||
break;
|
||||
}
|
||||
case RaftLogAction::ADD_COORDINATOR_INSTANCE: {
|
||||
auto const &config = std::get<CoordinatorToCoordinatorConfig>(log_entry);
|
||||
coordinators_.emplace_back(CoordinatorInstanceState{config});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::Serialize(ptr<buffer> &data) -> void {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
|
||||
auto const log = nlohmann::json(instances_).dump();
|
||||
auto const log = nlohmann::json(repl_instances_).dump();
|
||||
|
||||
data = buffer::alloc(sizeof(uint32_t) + log.size());
|
||||
buffer_serializer bs(data);
|
||||
@ -121,27 +128,22 @@ auto CoordinatorClusterState::Serialize(ptr<buffer> &data) -> void {
|
||||
auto CoordinatorClusterState::Deserialize(buffer &data) -> CoordinatorClusterState {
|
||||
buffer_serializer bs(data);
|
||||
auto const j = nlohmann::json::parse(bs.get_str());
|
||||
auto instances = j.get<std::map<std::string, InstanceState, std::less<>>>();
|
||||
auto instances = j.get<std::map<std::string, ReplicationInstanceState, std::less<>>>();
|
||||
|
||||
return CoordinatorClusterState{std::move(instances)};
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::GetInstances() const -> std::vector<InstanceState> {
|
||||
auto CoordinatorClusterState::GetReplicationInstances() const -> std::vector<ReplicationInstanceState> {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
return instances_ | ranges::views::values | ranges::to<std::vector<InstanceState>>;
|
||||
return repl_instances_ | ranges::views::values | ranges::to<std::vector<ReplicationInstanceState>>;
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState> {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
return coordinators_;
|
||||
}
|
||||
|
||||
auto CoordinatorClusterState::GetUUID() const -> utils::UUID { return uuid_; }
|
||||
|
||||
auto CoordinatorClusterState::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
||||
auto lock = std::shared_lock{log_lock_};
|
||||
auto const it =
|
||||
std::ranges::find_if(instances_, [](auto const &entry) { return entry.second.status == ReplicationRole::MAIN; });
|
||||
if (it == instances_.end()) {
|
||||
return {};
|
||||
}
|
||||
return it->first;
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
@ -11,43 +11,53 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
void to_json(nlohmann::json &j, ReplClientInfo const &config) {
|
||||
void to_json(nlohmann::json &j, CoordinatorToCoordinatorConfig const &config) {
|
||||
j = nlohmann::json{{"coordinator_server_id", config.coordinator_server_id},
|
||||
{"coordinator_server", config.coordinator_server},
|
||||
{"bolt_server", config.bolt_server}};
|
||||
}
|
||||
|
||||
void from_json(nlohmann::json const &j, CoordinatorToCoordinatorConfig &config) {
|
||||
config.coordinator_server_id = j.at("coordinator_server_id").get<uint32_t>();
|
||||
config.coordinator_server = j.at("coordinator_server").get<io::network::Endpoint>();
|
||||
config.bolt_server = j.at("bolt_server").get<io::network::Endpoint>();
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, ReplicationClientInfo const &config) {
|
||||
j = nlohmann::json{{"instance_name", config.instance_name},
|
||||
{"replication_mode", config.replication_mode},
|
||||
{"replication_ip_address", config.replication_ip_address},
|
||||
{"replication_port", config.replication_port}};
|
||||
{"replication_server", config.replication_server}};
|
||||
}
|
||||
|
||||
void from_json(nlohmann::json const &j, ReplClientInfo &config) {
|
||||
void from_json(nlohmann::json const &j, ReplicationClientInfo &config) {
|
||||
config.instance_name = j.at("instance_name").get<std::string>();
|
||||
config.replication_mode = j.at("replication_mode").get<replication_coordination_glue::ReplicationMode>();
|
||||
config.replication_ip_address = j.at("replication_ip_address").get<std::string>();
|
||||
config.replication_port = j.at("replication_port").get<uint16_t>();
|
||||
config.replication_server = j.at("replication_server").get<io::network::Endpoint>();
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, CoordinatorClientConfig const &config) {
|
||||
void to_json(nlohmann::json &j, CoordinatorToReplicaConfig const &config) {
|
||||
j = nlohmann::json{{"instance_name", config.instance_name},
|
||||
{"ip_address", config.ip_address},
|
||||
{"port", config.port},
|
||||
{"mgt_server", config.mgt_server},
|
||||
{"bolt_server", config.bolt_server},
|
||||
{"instance_health_check_frequency_sec", config.instance_health_check_frequency_sec.count()},
|
||||
{"instance_down_timeout_sec", config.instance_down_timeout_sec.count()},
|
||||
{"instance_get_uuid_frequency_sec", config.instance_get_uuid_frequency_sec.count()},
|
||||
{"replication_client_info", config.replication_client_info}};
|
||||
}
|
||||
|
||||
void from_json(nlohmann::json const &j, CoordinatorClientConfig &config) {
|
||||
void from_json(nlohmann::json const &j, CoordinatorToReplicaConfig &config) {
|
||||
config.instance_name = j.at("instance_name").get<std::string>();
|
||||
config.ip_address = j.at("ip_address").get<std::string>();
|
||||
config.port = j.at("port").get<uint16_t>();
|
||||
config.mgt_server = j.at("mgt_server").get<io::network::Endpoint>();
|
||||
config.bolt_server = j.at("bolt_server").get<io::network::Endpoint>();
|
||||
config.instance_health_check_frequency_sec =
|
||||
std::chrono::seconds{j.at("instance_health_check_frequency_sec").get<int>()};
|
||||
config.instance_down_timeout_sec = std::chrono::seconds{j.at("instance_down_timeout_sec").get<int>()};
|
||||
config.instance_get_uuid_frequency_sec = std::chrono::seconds{j.at("instance_get_uuid_frequency_sec").get<int>()};
|
||||
config.replication_client_info = j.at("replication_client_info").get<ReplClientInfo>();
|
||||
config.replication_client_info = j.at("replication_client_info").get<ReplicationClientInfo>();
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
@ -95,8 +95,8 @@ void CoordinatorHandlers::DemoteMainToReplicaHandler(replication::ReplicationHan
|
||||
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};
|
||||
.ip_address = req.replication_client_info.replication_server.address,
|
||||
.port = req.replication_client_info.replication_server.port};
|
||||
|
||||
if (!replication_handler.SetReplicationRoleReplica(clients_config, std::nullopt)) {
|
||||
spdlog::error("Demoting main to replica failed!");
|
||||
@ -136,8 +136,8 @@ void CoordinatorHandlers::PromoteReplicaToMainHandler(replication::ReplicationHa
|
||||
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,
|
||||
.ip_address = repl_info_config.replication_server.address,
|
||||
.port = repl_info_config.replication_server.port,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "coordination/fmt.hpp"
|
||||
#include "dbms/constants.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "nuraft/coordinator_state_manager.hpp"
|
||||
@ -34,7 +33,7 @@ CoordinatorInstance::CoordinatorInstance()
|
||||
: raft_state_(RaftState::MakeRaftState(
|
||||
[this]() {
|
||||
spdlog::info("Leader changed, starting all replication instances!");
|
||||
auto const instances = raft_state_.GetInstances();
|
||||
auto const instances = raft_state_.GetReplicationInstances();
|
||||
auto replicas = instances | ranges::views::filter([](auto const &instance) {
|
||||
return instance.status == ReplicationRole::REPLICA;
|
||||
});
|
||||
@ -133,7 +132,7 @@ auto CoordinatorInstance::ShowInstances() const -> std::vector<InstanceStatus> {
|
||||
.health = "unknown"};
|
||||
};
|
||||
|
||||
std::ranges::transform(raft_state_.GetInstances(), std::back_inserter(instances_status),
|
||||
std::ranges::transform(raft_state_.GetReplicationInstances(), std::back_inserter(instances_status),
|
||||
process_repl_instance_as_follower);
|
||||
}
|
||||
|
||||
@ -288,7 +287,7 @@ auto CoordinatorInstance::SetReplicationInstanceToMain(std::string_view instance
|
||||
return SetInstanceToMainCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
||||
auto CoordinatorInstance::RegisterReplicationInstance(CoordinatorToReplicaConfig const &config)
|
||||
-> RegisterInstanceCoordinatorStatus {
|
||||
auto lock = std::lock_guard{coord_instance_lock_};
|
||||
|
||||
@ -382,9 +381,12 @@ auto CoordinatorInstance::UnregisterReplicationInstance(std::string_view instanc
|
||||
return UnregisterInstanceCoordinatorStatus::SUCCESS;
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
||||
std::string_view raft_address) -> void {
|
||||
raft_state_.AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
||||
auto CoordinatorInstance::AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void {
|
||||
raft_state_.AddCoordinatorInstance(config);
|
||||
// NOTE: We ignore error we added coordinator instance to networkign stuff but not in raft log.
|
||||
if (!raft_state_.AppendAddCoordinatorInstanceLog(config)) {
|
||||
spdlog::error("Failed to append add coordinator instance log");
|
||||
}
|
||||
}
|
||||
|
||||
void CoordinatorInstance::MainFailCallback(std::string_view repl_instance_name) {
|
||||
@ -557,5 +559,56 @@ auto CoordinatorInstance::IsReplica(std::string_view instance_name) const -> boo
|
||||
return raft_state_.IsReplica(instance_name);
|
||||
}
|
||||
|
||||
auto CoordinatorInstance::GetRoutingTable(std::map<std::string, std::string> const &routing) -> RoutingTable {
|
||||
auto res = RoutingTable{};
|
||||
|
||||
auto const repl_instance_to_bolt = [](ReplicationInstanceState const &instance) {
|
||||
return instance.config.BoltSocketAddress();
|
||||
};
|
||||
|
||||
// TODO: (andi) This is wrong check, Fico will correct in #1819.
|
||||
auto const is_instance_main = [&](ReplicationInstanceState const &instance) {
|
||||
return instance.status == ReplicationRole::MAIN;
|
||||
};
|
||||
|
||||
auto const is_instance_replica = [&](ReplicationInstanceState const &instance) {
|
||||
return instance.status == ReplicationRole::REPLICA;
|
||||
};
|
||||
|
||||
auto const &raft_log_repl_instances = raft_state_.GetReplicationInstances();
|
||||
|
||||
auto bolt_mains = raft_log_repl_instances | ranges::views::filter(is_instance_main) |
|
||||
ranges::views::transform(repl_instance_to_bolt) | ranges::to<std::vector>();
|
||||
MG_ASSERT(bolt_mains.size() <= 1, "There can be at most one main instance active!");
|
||||
|
||||
if (!std::ranges::empty(bolt_mains)) {
|
||||
res.emplace_back(std::move(bolt_mains), "WRITE");
|
||||
}
|
||||
|
||||
auto bolt_replicas = raft_log_repl_instances | ranges::views::filter(is_instance_replica) |
|
||||
ranges::views::transform(repl_instance_to_bolt) | ranges::to<std::vector>();
|
||||
if (!std::ranges::empty(bolt_replicas)) {
|
||||
res.emplace_back(std::move(bolt_replicas), "READ");
|
||||
}
|
||||
|
||||
auto const coord_instance_to_bolt = [](CoordinatorInstanceState const &instance) {
|
||||
return instance.config.bolt_server.SocketAddress();
|
||||
};
|
||||
|
||||
auto const &raft_log_coord_instances = raft_state_.GetCoordinatorInstances();
|
||||
auto bolt_coords =
|
||||
raft_log_coord_instances | ranges::views::transform(coord_instance_to_bolt) | ranges::to<std::vector>();
|
||||
|
||||
auto const &local_bolt_coord = routing.find("address");
|
||||
if (local_bolt_coord == routing.end()) {
|
||||
throw InvalidRoutingTableException("No bolt address found in routing table for the current coordinator!");
|
||||
}
|
||||
|
||||
bolt_coords.push_back(local_bolt_coord->second);
|
||||
res.emplace_back(std::move(bolt_coords), "ROUTE");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
@ -18,8 +18,7 @@ namespace memgraph::coordination {
|
||||
|
||||
namespace {
|
||||
|
||||
auto CreateServerContext(const memgraph::coordination::CoordinatorServerConfig &config)
|
||||
-> communication::ServerContext {
|
||||
auto CreateServerContext(const memgraph::coordination::ManagementServerConfig &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{};
|
||||
@ -32,7 +31,7 @@ constexpr auto kCoordinatorServerThreads = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
CoordinatorServer::CoordinatorServer(const CoordinatorServerConfig &config)
|
||||
CoordinatorServer::CoordinatorServer(const ManagementServerConfig &config)
|
||||
: rpc_server_context_{CreateServerContext(config)},
|
||||
rpc_server_{io::network::Endpoint{config.ip_address, config.port}, &rpc_server_context_,
|
||||
kCoordinatorServerThreads} {
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include "coordination/coordinator_state.hpp"
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
#include "flags/replication.hpp"
|
||||
#include "spdlog/spdlog.h"
|
||||
@ -31,7 +31,7 @@ CoordinatorState::CoordinatorState() {
|
||||
spdlog::info("Executing coordinator constructor");
|
||||
if (FLAGS_coordinator_server_port) {
|
||||
spdlog::info("Coordinator server port set");
|
||||
auto const config = CoordinatorServerConfig{
|
||||
auto const config = ManagementServerConfig{
|
||||
.ip_address = kDefaultReplicationServerIp,
|
||||
.port = static_cast<uint16_t>(FLAGS_coordinator_server_port),
|
||||
};
|
||||
@ -41,7 +41,7 @@ CoordinatorState::CoordinatorState() {
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorState::RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
||||
auto CoordinatorState::RegisterReplicationInstance(CoordinatorToReplicaConfig const &config)
|
||||
-> RegisterInstanceCoordinatorStatus {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
@ -98,11 +98,16 @@ auto CoordinatorState::GetCoordinatorServer() const -> CoordinatorServer & {
|
||||
return *std::get<CoordinatorMainReplicaData>(data_).coordinator_server_;
|
||||
}
|
||||
|
||||
auto CoordinatorState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
||||
std::string_view raft_address) -> void {
|
||||
auto CoordinatorState::AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
"Coordinator cannot register replica since variant holds wrong alternative");
|
||||
return std::get<CoordinatorInstance>(data_).AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
||||
return std::get<CoordinatorInstance>(data_).AddCoordinatorInstance(config);
|
||||
}
|
||||
|
||||
auto CoordinatorState::GetRoutingTable(std::map<std::string, std::string> const &routing) -> RoutingTable {
|
||||
MG_ASSERT(std::holds_alternative<CoordinatorInstance>(data_),
|
||||
"Coordinator cannot get routing table since variant holds wrong alternative");
|
||||
return std::get<CoordinatorInstance>(data_).GetRoutingTable(routing);
|
||||
}
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
|
@ -20,10 +20,6 @@ constexpr int MAX_SNAPSHOTS = 3;
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
auto CoordinatorStateMachine::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
||||
return cluster_state_.FindCurrentMainInstanceName();
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::MainExists() const -> bool { return cluster_state_.MainExists(); }
|
||||
|
||||
auto CoordinatorStateMachine::IsMain(std::string_view instance_name) const -> bool {
|
||||
@ -42,7 +38,7 @@ auto CoordinatorStateMachine::CreateLog(nlohmann::json &&log) -> ptr<buffer> {
|
||||
return log_buf;
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::SerializeRegisterInstance(CoordinatorClientConfig const &config) -> ptr<buffer> {
|
||||
auto CoordinatorStateMachine::SerializeRegisterInstance(CoordinatorToReplicaConfig const &config) -> ptr<buffer> {
|
||||
return CreateLog({{"action", RaftLogAction::REGISTER_REPLICATION_INSTANCE}, {"info", config}});
|
||||
}
|
||||
|
||||
@ -62,6 +58,11 @@ auto CoordinatorStateMachine::SerializeUpdateUUID(utils::UUID const &uuid) -> pt
|
||||
return CreateLog({{"action", RaftLogAction::UPDATE_UUID}, {"info", uuid}});
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::SerializeAddCoordinatorInstance(CoordinatorToCoordinatorConfig const &config)
|
||||
-> ptr<buffer> {
|
||||
return CreateLog({{"action", RaftLogAction::ADD_COORDINATOR_INSTANCE}, {"info", config}});
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::DecodeLog(buffer &data) -> std::pair<TRaftLog, RaftLogAction> {
|
||||
buffer_serializer bs(data);
|
||||
auto const json = nlohmann::json::parse(bs.get_str());
|
||||
@ -71,7 +72,7 @@ auto CoordinatorStateMachine::DecodeLog(buffer &data) -> std::pair<TRaftLog, Raf
|
||||
|
||||
switch (action) {
|
||||
case RaftLogAction::REGISTER_REPLICATION_INSTANCE:
|
||||
return {info.get<CoordinatorClientConfig>(), action};
|
||||
return {info.get<CoordinatorToReplicaConfig>(), action};
|
||||
case RaftLogAction::UPDATE_UUID:
|
||||
return {info.get<utils::UUID>(), action};
|
||||
case RaftLogAction::UNREGISTER_REPLICATION_INSTANCE:
|
||||
@ -79,6 +80,8 @@ auto CoordinatorStateMachine::DecodeLog(buffer &data) -> std::pair<TRaftLog, Raf
|
||||
[[fallthrough]];
|
||||
case RaftLogAction::SET_INSTANCE_AS_REPLICA:
|
||||
return {info.get<std::string>(), action};
|
||||
case RaftLogAction::ADD_COORDINATOR_INSTANCE:
|
||||
return {info.get<CoordinatorToCoordinatorConfig>(), action};
|
||||
}
|
||||
throw std::runtime_error("Unknown action");
|
||||
}
|
||||
@ -133,6 +136,7 @@ auto CoordinatorStateMachine::read_logical_snp_obj(snapshot &snapshot, void *& /
|
||||
} else {
|
||||
// Object ID > 0: second object, put actual value.
|
||||
ctx->cluster_state_.Serialize(data_out);
|
||||
is_last_obj = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -155,6 +159,7 @@ auto CoordinatorStateMachine::save_logical_snp_obj(snapshot &snapshot, ulong &ob
|
||||
DMG_ASSERT(entry != snapshots_.end());
|
||||
entry->second->cluster_state_ = cluster_state;
|
||||
}
|
||||
obj_id++;
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::apply_snapshot(snapshot &s) -> bool {
|
||||
@ -205,8 +210,12 @@ auto CoordinatorStateMachine::create_snapshot_internal(ptr<snapshot> snapshot) -
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::GetInstances() const -> std::vector<InstanceState> {
|
||||
return cluster_state_.GetInstances();
|
||||
auto CoordinatorStateMachine::GetReplicationInstances() const -> std::vector<ReplicationInstanceState> {
|
||||
return cluster_state_.GetReplicationInstances();
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState> {
|
||||
return cluster_state_.GetCoordinatorInstances();
|
||||
}
|
||||
|
||||
auto CoordinatorStateMachine::GetUUID() const -> utils::UUID { return cluster_state_.GetUUID(); }
|
||||
|
@ -33,6 +33,7 @@ CoordinatorStateManager::CoordinatorStateManager(int srv_id, std::string const &
|
||||
auto CoordinatorStateManager::load_config() -> ptr<cluster_config> {
|
||||
// Just return in-memory data in this example.
|
||||
// May require reading from disk here, if it has been written to disk.
|
||||
spdlog::trace("Loading cluster config");
|
||||
return cluster_config_;
|
||||
}
|
||||
|
||||
@ -41,6 +42,11 @@ auto CoordinatorStateManager::save_config(cluster_config const &config) -> void
|
||||
// Need to write to disk here, if want to make it durable.
|
||||
ptr<buffer> buf = config.serialize();
|
||||
cluster_config_ = cluster_config::deserialize(*buf);
|
||||
spdlog::info("Saving cluster config.");
|
||||
auto servers = cluster_config_->get_servers();
|
||||
for (auto const &server : servers) {
|
||||
spdlog::trace("Server id: {}, endpoint: {}", server->get_id(), server->get_endpoint());
|
||||
}
|
||||
}
|
||||
|
||||
auto CoordinatorStateManager::save_state(srv_state const &state) -> void {
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "replication_coordination_glue/common.hpp"
|
||||
#include "rpc/client.hpp"
|
||||
#include "rpc_errors.hpp"
|
||||
@ -25,11 +25,11 @@ namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorInstance;
|
||||
using HealthCheckClientCallback = std::function<void(CoordinatorInstance *, std::string_view)>;
|
||||
using ReplicationClientsInfo = std::vector<ReplClientInfo>;
|
||||
using ReplicationClientsInfo = std::vector<ReplicationClientInfo>;
|
||||
|
||||
class CoordinatorClient {
|
||||
public:
|
||||
explicit CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorClientConfig config,
|
||||
explicit CoordinatorClient(CoordinatorInstance *coord_instance, CoordinatorToReplicaConfig config,
|
||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb);
|
||||
|
||||
~CoordinatorClient() = default;
|
||||
@ -62,7 +62,7 @@ class CoordinatorClient {
|
||||
|
||||
auto SendGetInstanceUUIDRpc() const -> memgraph::utils::BasicResult<GetInstanceUUIDError, std::optional<utils::UUID>>;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
auto ReplicationClientInfo() const -> ReplicationClientInfo;
|
||||
|
||||
auto SendGetInstanceTimestampsRpc() const
|
||||
-> utils::BasicResult<GetInstanceUUIDError, replication_coordination_glue::DatabaseHistories>;
|
||||
@ -83,7 +83,7 @@ class CoordinatorClient {
|
||||
communication::ClientContext rpc_context_;
|
||||
mutable rpc::Client rpc_client_;
|
||||
|
||||
CoordinatorClientConfig config_;
|
||||
CoordinatorToReplicaConfig config_;
|
||||
CoordinatorInstance *coord_instance_;
|
||||
HealthCheckClientCallback succ_cb_;
|
||||
HealthCheckClientCallback fail_cb_;
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "replication_coordination_glue/mode.hpp"
|
||||
#include "utils/string.hpp"
|
||||
|
||||
@ -28,46 +29,50 @@ namespace memgraph::coordination {
|
||||
|
||||
inline constexpr auto *kDefaultReplicationServerIp = "0.0.0.0";
|
||||
|
||||
struct CoordinatorClientConfig {
|
||||
std::string instance_name;
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
struct ReplicationClientInfo {
|
||||
std::string instance_name{};
|
||||
replication_coordination_glue::ReplicationMode replication_mode{};
|
||||
io::network::Endpoint replication_server;
|
||||
|
||||
friend bool operator==(ReplicationClientInfo const &, ReplicationClientInfo const &) = default;
|
||||
};
|
||||
|
||||
struct CoordinatorToReplicaConfig {
|
||||
auto BoltSocketAddress() const -> std::string { return bolt_server.SocketAddress(); }
|
||||
auto CoordinatorSocketAddress() const -> std::string { return mgt_server.SocketAddress(); }
|
||||
auto ReplicationSocketAddress() const -> std::string {
|
||||
return replication_client_info.replication_server.SocketAddress();
|
||||
}
|
||||
|
||||
std::string instance_name{};
|
||||
io::network::Endpoint mgt_server;
|
||||
io::network::Endpoint bolt_server;
|
||||
ReplicationClientInfo replication_client_info;
|
||||
|
||||
std::chrono::seconds instance_health_check_frequency_sec{1};
|
||||
std::chrono::seconds instance_down_timeout_sec{5};
|
||||
std::chrono::seconds instance_get_uuid_frequency_sec{10};
|
||||
|
||||
auto CoordinatorSocketAddress() const -> std::string { return fmt::format("{}:{}", ip_address, port); }
|
||||
auto ReplicationSocketAddress() const -> std::string {
|
||||
return fmt::format("{}:{}", replication_client_info.replication_ip_address,
|
||||
replication_client_info.replication_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;
|
||||
friend bool operator==(CoordinatorToReplicaConfig const &, CoordinatorToReplicaConfig const &) = default;
|
||||
};
|
||||
|
||||
using ReplClientInfo = CoordinatorClientConfig::ReplicationClientInfo;
|
||||
struct CoordinatorToCoordinatorConfig {
|
||||
uint32_t coordinator_server_id{0};
|
||||
io::network::Endpoint bolt_server;
|
||||
io::network::Endpoint coordinator_server;
|
||||
|
||||
struct CoordinatorServerConfig {
|
||||
friend bool operator==(CoordinatorToCoordinatorConfig const &, CoordinatorToCoordinatorConfig const &) = default;
|
||||
};
|
||||
|
||||
struct ManagementServerConfig {
|
||||
std::string ip_address;
|
||||
uint16_t port{};
|
||||
struct SSL {
|
||||
@ -80,14 +85,17 @@ struct CoordinatorServerConfig {
|
||||
|
||||
std::optional<SSL> ssl;
|
||||
|
||||
friend bool operator==(CoordinatorServerConfig const &, CoordinatorServerConfig const &) = default;
|
||||
friend bool operator==(ManagementServerConfig const &, ManagementServerConfig const &) = default;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, CoordinatorClientConfig const &config);
|
||||
void from_json(nlohmann::json const &j, CoordinatorClientConfig &config);
|
||||
void to_json(nlohmann::json &j, CoordinatorToReplicaConfig const &config);
|
||||
void from_json(nlohmann::json const &j, CoordinatorToReplicaConfig &config);
|
||||
|
||||
void to_json(nlohmann::json &j, ReplClientInfo const &config);
|
||||
void from_json(nlohmann::json const &j, ReplClientInfo &config);
|
||||
void to_json(nlohmann::json &j, CoordinatorToCoordinatorConfig const &config);
|
||||
void from_json(nlohmann::json const &j, CoordinatorToCoordinatorConfig &config);
|
||||
|
||||
void to_json(nlohmann::json &j, ReplicationClientInfo const &config);
|
||||
void from_json(nlohmann::json const &j, ReplicationClientInfo &config);
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
@ -94,5 +94,16 @@ class InvalidRaftLogActionException final : public utils::BasicException {
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(InvalidRaftLogActionException)
|
||||
};
|
||||
|
||||
class InvalidRoutingTableException final : public utils::BasicException {
|
||||
public:
|
||||
explicit InvalidRoutingTableException(std::string_view what) noexcept : BasicException(what) {}
|
||||
|
||||
template <class... Args>
|
||||
explicit InvalidRoutingTableException(fmt::format_string<Args...> fmt, Args &&...args) noexcept
|
||||
: InvalidRoutingTableException(fmt::format(fmt, std::forward<Args>(args)...)) {}
|
||||
|
||||
SPECIALIZE_GET_EXCEPTION_NAME(InvalidRoutingTableException)
|
||||
};
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
@ -26,6 +26,8 @@
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
using RoutingTable = std::vector<std::pair<std::vector<std::string>, std::string>>;
|
||||
|
||||
struct NewMainRes {
|
||||
std::string most_up_to_date_instance;
|
||||
std::string latest_epoch;
|
||||
@ -36,8 +38,14 @@ using InstanceNameDbHistories = std::pair<std::string, replication_coordination_
|
||||
class CoordinatorInstance {
|
||||
public:
|
||||
CoordinatorInstance();
|
||||
CoordinatorInstance(CoordinatorInstance const &) = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance const &) = delete;
|
||||
CoordinatorInstance(CoordinatorInstance &&) noexcept = delete;
|
||||
CoordinatorInstance &operator=(CoordinatorInstance &&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
||||
~CoordinatorInstance() = default;
|
||||
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorToReplicaConfig const &config)
|
||||
-> RegisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string_view instance_name)
|
||||
-> UnregisterInstanceCoordinatorStatus;
|
||||
@ -48,15 +56,15 @@ class CoordinatorInstance {
|
||||
|
||||
auto TryFailover() -> void;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
||||
auto AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void;
|
||||
|
||||
auto GetRoutingTable(std::map<std::string, std::string> const &routing) -> RoutingTable;
|
||||
|
||||
static auto ChooseMostUpToDateInstance(std::span<InstanceNameDbHistories> histories) -> NewMainRes;
|
||||
|
||||
private:
|
||||
HealthCheckClientCallback client_succ_cb_, client_fail_cb_;
|
||||
|
||||
auto OnRaftCommitCallback(TRaftLog const &log_entry, RaftLogAction log_action) -> void;
|
||||
|
||||
auto FindReplicationInstance(std::string_view replication_instance_name) -> ReplicationInstance &;
|
||||
|
||||
void MainFailCallback(std::string_view);
|
||||
@ -71,7 +79,6 @@ class CoordinatorInstance {
|
||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
||||
|
||||
// NOTE: Must be std::list because we rely on pointer stability.
|
||||
// Leader and followers should both have same view on repl_instances_
|
||||
std::list<ReplicationInstance> repl_instances_;
|
||||
mutable utils::ResourceLock coord_instance_lock_{};
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "utils/uuid.hpp"
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "replication_coordination_glue/common.hpp"
|
||||
#include "rpc/messages.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
@ -28,14 +28,13 @@ struct PromoteReplicaToMainReq {
|
||||
static void Load(PromoteReplicaToMainReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const PromoteReplicaToMainReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit PromoteReplicaToMainReq(const utils::UUID &uuid,
|
||||
std::vector<CoordinatorClientConfig::ReplicationClientInfo> replication_clients_info)
|
||||
explicit PromoteReplicaToMainReq(const utils::UUID &uuid, std::vector<ReplicationClientInfo> replication_clients_info)
|
||||
: main_uuid_(uuid), replication_clients_info(std::move(replication_clients_info)) {}
|
||||
PromoteReplicaToMainReq() = default;
|
||||
|
||||
// get uuid here
|
||||
utils::UUID main_uuid_;
|
||||
std::vector<CoordinatorClientConfig::ReplicationClientInfo> replication_clients_info;
|
||||
std::vector<ReplicationClientInfo> replication_clients_info;
|
||||
};
|
||||
|
||||
struct PromoteReplicaToMainRes {
|
||||
@ -60,12 +59,12 @@ struct DemoteMainToReplicaReq {
|
||||
static void Load(DemoteMainToReplicaReq *self, memgraph::slk::Reader *reader);
|
||||
static void Save(const DemoteMainToReplicaReq &self, memgraph::slk::Builder *builder);
|
||||
|
||||
explicit DemoteMainToReplicaReq(CoordinatorClientConfig::ReplicationClientInfo replication_client_info)
|
||||
explicit DemoteMainToReplicaReq(ReplicationClientInfo replication_client_info)
|
||||
: replication_client_info(std::move(replication_client_info)) {}
|
||||
|
||||
DemoteMainToReplicaReq() = default;
|
||||
|
||||
CoordinatorClientConfig::ReplicationClientInfo replication_client_info;
|
||||
ReplicationClientInfo replication_client_info;
|
||||
};
|
||||
|
||||
struct DemoteMainToReplicaRes {
|
||||
|
@ -13,14 +13,14 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "rpc/server.hpp"
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorServer {
|
||||
public:
|
||||
explicit CoordinatorServer(const CoordinatorServerConfig &config);
|
||||
explicit CoordinatorServer(const ManagementServerConfig &config);
|
||||
CoordinatorServer(const CoordinatorServer &) = delete;
|
||||
CoordinatorServer(CoordinatorServer &&) = delete;
|
||||
CoordinatorServer &operator=(const CoordinatorServer &) = delete;
|
||||
|
@ -13,27 +13,37 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "replication_coordination_glue/common.hpp"
|
||||
#include "slk/serialization.hpp"
|
||||
#include "slk/streams.hpp"
|
||||
|
||||
namespace memgraph::slk {
|
||||
|
||||
using ReplicationClientInfo = coordination::CoordinatorClientConfig::ReplicationClientInfo;
|
||||
using ReplicationClientInfo = coordination::ReplicationClientInfo;
|
||||
|
||||
inline void Save(const ReplicationClientInfo &obj, Builder *builder) {
|
||||
inline void Save(io::network::Endpoint const &obj, Builder *builder) {
|
||||
Save(obj.address, builder);
|
||||
Save(obj.port, builder);
|
||||
Save(obj.family, builder);
|
||||
}
|
||||
|
||||
inline void Load(io::network::Endpoint *obj, Reader *reader) {
|
||||
Load(&obj->address, reader);
|
||||
Load(&obj->port, reader);
|
||||
Load(&obj->family, reader);
|
||||
}
|
||||
|
||||
inline void Save(ReplicationClientInfo const &obj, Builder *builder) {
|
||||
Save(obj.instance_name, builder);
|
||||
Save(obj.replication_mode, builder);
|
||||
Save(obj.replication_ip_address, builder);
|
||||
Save(obj.replication_port, builder);
|
||||
Save(obj.replication_server, 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);
|
||||
Load(&obj->replication_server, reader);
|
||||
}
|
||||
|
||||
inline void Save(const replication_coordination_glue::DatabaseHistory &obj, Builder *builder) {
|
||||
|
@ -33,7 +33,7 @@ class CoordinatorState {
|
||||
CoordinatorState(CoordinatorState &&) noexcept = delete;
|
||||
CoordinatorState &operator=(CoordinatorState &&) noexcept = delete;
|
||||
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorClientConfig const &config)
|
||||
[[nodiscard]] auto RegisterReplicationInstance(CoordinatorToReplicaConfig const &config)
|
||||
-> RegisterInstanceCoordinatorStatus;
|
||||
[[nodiscard]] auto UnregisterReplicationInstance(std::string_view instance_name)
|
||||
-> UnregisterInstanceCoordinatorStatus;
|
||||
@ -42,11 +42,13 @@ class CoordinatorState {
|
||||
|
||||
auto ShowInstances() const -> std::vector<InstanceStatus>;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
||||
auto AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void;
|
||||
|
||||
// NOTE: The client code must check that the server exists before calling this method.
|
||||
auto GetCoordinatorServer() const -> CoordinatorServer &;
|
||||
|
||||
auto GetRoutingTable(std::map<std::string, std::string> const &routing) -> RoutingTable;
|
||||
|
||||
private:
|
||||
struct CoordinatorMainReplicaData {
|
||||
std::unique_ptr<CoordinatorServer> coordinator_server_;
|
||||
|
@ -23,7 +23,7 @@
|
||||
namespace memgraph::coordination {
|
||||
|
||||
class CoordinatorInstance;
|
||||
struct CoordinatorClientConfig;
|
||||
struct CoordinatorToReplicaConfig;
|
||||
|
||||
using BecomeLeaderCb = std::function<void()>;
|
||||
using BecomeFollowerCb = std::function<void()>;
|
||||
@ -58,24 +58,27 @@ class RaftState {
|
||||
auto InstanceName() const -> std::string;
|
||||
auto RaftSocketAddress() const -> std::string;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
||||
auto AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void;
|
||||
auto GetAllCoordinators() const -> std::vector<ptr<srv_config>>;
|
||||
|
||||
auto RequestLeadership() -> bool;
|
||||
auto IsLeader() const -> bool;
|
||||
|
||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
||||
auto MainExists() const -> bool;
|
||||
auto IsMain(std::string_view instance_name) const -> bool;
|
||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
||||
|
||||
auto AppendRegisterReplicationInstanceLog(CoordinatorClientConfig const &config) -> bool;
|
||||
auto AppendRegisterReplicationInstanceLog(CoordinatorToReplicaConfig const &config) -> bool;
|
||||
auto AppendUnregisterReplicationInstanceLog(std::string_view instance_name) -> bool;
|
||||
auto AppendSetInstanceAsMainLog(std::string_view instance_name) -> bool;
|
||||
auto AppendSetInstanceAsReplicaLog(std::string_view instance_name) -> bool;
|
||||
auto AppendUpdateUUIDLog(utils::UUID const &uuid) -> bool;
|
||||
auto AppendAddCoordinatorInstanceLog(CoordinatorToCoordinatorConfig const &config) -> bool;
|
||||
|
||||
auto GetReplicationInstances() const -> std::vector<ReplicationInstanceState>;
|
||||
// TODO: (andi) Do we need then GetAllCoordinators?
|
||||
auto GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState>;
|
||||
|
||||
auto GetInstances() const -> std::vector<InstanceState>;
|
||||
auto GetUUID() const -> utils::UUID;
|
||||
|
||||
private:
|
||||
|
@ -32,7 +32,7 @@ using HealthCheckInstanceCallback = void (CoordinatorInstance::*)(std::string_vi
|
||||
|
||||
class ReplicationInstance {
|
||||
public:
|
||||
ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config, HealthCheckClientCallback succ_cb,
|
||||
ReplicationInstance(CoordinatorInstance *peer, CoordinatorToReplicaConfig config, HealthCheckClientCallback succ_cb,
|
||||
HealthCheckClientCallback fail_cb, HealthCheckInstanceCallback succ_instance_cb,
|
||||
HealthCheckInstanceCallback fail_instance_cb);
|
||||
|
||||
@ -67,7 +67,7 @@ class ReplicationInstance {
|
||||
auto PauseFrequentCheck() -> void;
|
||||
auto ResumeFrequentCheck() -> void;
|
||||
|
||||
auto ReplicationClientInfo() const -> ReplClientInfo;
|
||||
auto ReplicationClientInfo() const -> ReplicationClientInfo;
|
||||
|
||||
auto EnsureReplicaHasCorrectMainUUID(utils::UUID const &curr_main_uuid) -> bool;
|
||||
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "nuraft/raft_log_action.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
#include "utils/resource_lock.hpp"
|
||||
@ -32,19 +32,29 @@ namespace memgraph::coordination {
|
||||
|
||||
using replication_coordination_glue::ReplicationRole;
|
||||
|
||||
struct InstanceState {
|
||||
CoordinatorClientConfig config;
|
||||
struct ReplicationInstanceState {
|
||||
CoordinatorToReplicaConfig config;
|
||||
ReplicationRole status;
|
||||
|
||||
friend auto operator==(InstanceState const &lhs, InstanceState const &rhs) -> bool {
|
||||
friend auto operator==(ReplicationInstanceState const &lhs, ReplicationInstanceState const &rhs) -> bool {
|
||||
return lhs.config == rhs.config && lhs.status == rhs.status;
|
||||
}
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, InstanceState const &instance_state);
|
||||
void from_json(nlohmann::json const &j, InstanceState &instance_state);
|
||||
// NOTE: Currently instance of coordinator doesn't change from the registration. Hence, just wrap
|
||||
// CoordinatorToCoordinatorConfig.
|
||||
struct CoordinatorInstanceState {
|
||||
CoordinatorToCoordinatorConfig config;
|
||||
|
||||
using TRaftLog = std::variant<CoordinatorClientConfig, std::string, utils::UUID>;
|
||||
friend auto operator==(CoordinatorInstanceState const &lhs, CoordinatorInstanceState const &rhs) -> bool {
|
||||
return lhs.config == rhs.config;
|
||||
}
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, ReplicationInstanceState const &instance_state);
|
||||
void from_json(nlohmann::json const &j, ReplicationInstanceState &instance_state);
|
||||
|
||||
using TRaftLog = std::variant<CoordinatorToReplicaConfig, CoordinatorToCoordinatorConfig, std::string, utils::UUID>;
|
||||
|
||||
using nuraft::buffer;
|
||||
using nuraft::buffer_serializer;
|
||||
@ -53,7 +63,7 @@ using nuraft::ptr;
|
||||
class CoordinatorClusterState {
|
||||
public:
|
||||
CoordinatorClusterState() = default;
|
||||
explicit CoordinatorClusterState(std::map<std::string, InstanceState, std::less<>> instances);
|
||||
explicit CoordinatorClusterState(std::map<std::string, ReplicationInstanceState, std::less<>> instances);
|
||||
|
||||
CoordinatorClusterState(CoordinatorClusterState const &);
|
||||
CoordinatorClusterState &operator=(CoordinatorClusterState const &);
|
||||
@ -62,15 +72,13 @@ class CoordinatorClusterState {
|
||||
CoordinatorClusterState &operator=(CoordinatorClusterState &&other) noexcept;
|
||||
~CoordinatorClusterState() = default;
|
||||
|
||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
||||
|
||||
auto MainExists() const -> bool;
|
||||
|
||||
auto IsMain(std::string_view instance_name) const -> bool;
|
||||
|
||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
||||
|
||||
auto InsertInstance(std::string instance_name, InstanceState instance_state) -> void;
|
||||
auto InsertInstance(std::string instance_name, ReplicationInstanceState instance_state) -> void;
|
||||
|
||||
auto DoAction(TRaftLog log_entry, RaftLogAction log_action) -> void;
|
||||
|
||||
@ -78,12 +86,15 @@ class CoordinatorClusterState {
|
||||
|
||||
static auto Deserialize(buffer &data) -> CoordinatorClusterState;
|
||||
|
||||
auto GetInstances() const -> std::vector<InstanceState>;
|
||||
auto GetReplicationInstances() const -> std::vector<ReplicationInstanceState>;
|
||||
|
||||
auto GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState>;
|
||||
|
||||
auto GetUUID() const -> utils::UUID;
|
||||
|
||||
private:
|
||||
std::map<std::string, InstanceState, std::less<>> instances_{};
|
||||
std::vector<CoordinatorInstanceState> coordinators_{};
|
||||
std::map<std::string, ReplicationInstanceState, std::less<>> repl_instances_{};
|
||||
utils::UUID uuid_{};
|
||||
mutable utils::ResourceLock log_lock_{};
|
||||
};
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "nuraft/coordinator_cluster_state.hpp"
|
||||
#include "nuraft/raft_log_action.hpp"
|
||||
|
||||
@ -42,17 +42,18 @@ class CoordinatorStateMachine : public state_machine {
|
||||
CoordinatorStateMachine &operator=(CoordinatorStateMachine &&) = delete;
|
||||
~CoordinatorStateMachine() override {}
|
||||
|
||||
auto FindCurrentMainInstanceName() const -> std::optional<std::string>;
|
||||
// TODO: (andi) Check API of this class.
|
||||
auto MainExists() const -> bool;
|
||||
auto IsMain(std::string_view instance_name) const -> bool;
|
||||
auto IsReplica(std::string_view instance_name) const -> bool;
|
||||
|
||||
static auto CreateLog(nlohmann::json &&log) -> ptr<buffer>;
|
||||
static auto SerializeRegisterInstance(CoordinatorClientConfig const &config) -> ptr<buffer>;
|
||||
static auto SerializeRegisterInstance(CoordinatorToReplicaConfig const &config) -> ptr<buffer>;
|
||||
static auto SerializeUnregisterInstance(std::string_view instance_name) -> ptr<buffer>;
|
||||
static auto SerializeSetInstanceAsMain(std::string_view instance_name) -> ptr<buffer>;
|
||||
static auto SerializeSetInstanceAsReplica(std::string_view instance_name) -> ptr<buffer>;
|
||||
static auto SerializeUpdateUUID(utils::UUID const &uuid) -> ptr<buffer>;
|
||||
static auto SerializeAddCoordinatorInstance(CoordinatorToCoordinatorConfig const &config) -> ptr<buffer>;
|
||||
|
||||
static auto DecodeLog(buffer &data) -> std::pair<TRaftLog, RaftLogAction>;
|
||||
|
||||
@ -80,7 +81,10 @@ class CoordinatorStateMachine : public state_machine {
|
||||
|
||||
auto create_snapshot(snapshot &s, async_result<bool>::handler_type &when_done) -> void override;
|
||||
|
||||
auto GetInstances() const -> std::vector<InstanceState>;
|
||||
auto GetReplicationInstances() const -> std::vector<ReplicationInstanceState>;
|
||||
|
||||
auto GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState>;
|
||||
|
||||
auto GetUUID() const -> utils::UUID;
|
||||
|
||||
private:
|
||||
|
@ -27,16 +27,16 @@ enum class RaftLogAction : uint8_t {
|
||||
UNREGISTER_REPLICATION_INSTANCE,
|
||||
SET_INSTANCE_AS_MAIN,
|
||||
SET_INSTANCE_AS_REPLICA,
|
||||
UPDATE_UUID
|
||||
UPDATE_UUID,
|
||||
ADD_COORDINATOR_INSTANCE
|
||||
};
|
||||
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(RaftLogAction, {
|
||||
{RaftLogAction::REGISTER_REPLICATION_INSTANCE, "register"},
|
||||
{RaftLogAction::UNREGISTER_REPLICATION_INSTANCE, "unregister"},
|
||||
{RaftLogAction::SET_INSTANCE_AS_MAIN, "promote"},
|
||||
{RaftLogAction::SET_INSTANCE_AS_REPLICA, "demote"},
|
||||
{RaftLogAction::UPDATE_UUID, "update_uuid"},
|
||||
})
|
||||
NLOHMANN_JSON_SERIALIZE_ENUM(RaftLogAction, {{RaftLogAction::REGISTER_REPLICATION_INSTANCE, "register"},
|
||||
{RaftLogAction::UNREGISTER_REPLICATION_INSTANCE, "unregister"},
|
||||
{RaftLogAction::SET_INSTANCE_AS_MAIN, "promote"},
|
||||
{RaftLogAction::SET_INSTANCE_AS_REPLICA, "demote"},
|
||||
{RaftLogAction::UPDATE_UUID, "update_uuid"},
|
||||
{RaftLogAction::ADD_COORDINATOR_INSTANCE, "add_coordinator_instance"}})
|
||||
|
||||
} // namespace memgraph::coordination
|
||||
#endif
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include <chrono>
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "coordination/coordinator_exceptions.hpp"
|
||||
#include "coordination/raft_state.hpp"
|
||||
#include "utils/counter.hpp"
|
||||
@ -113,10 +113,9 @@ auto RaftState::InstanceName() const -> std::string {
|
||||
|
||||
auto RaftState::RaftSocketAddress() const -> std::string { return raft_endpoint_.SocketAddress(); }
|
||||
|
||||
auto RaftState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address)
|
||||
-> void {
|
||||
auto const endpoint = fmt::format("{}:{}", raft_address, raft_port);
|
||||
srv_config const srv_config_to_add(static_cast<int>(raft_server_id), endpoint);
|
||||
auto RaftState::AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void {
|
||||
auto const endpoint = config.coordinator_server.SocketAddress();
|
||||
srv_config const srv_config_to_add(static_cast<int>(config.coordinator_server_id), endpoint);
|
||||
|
||||
auto cmd_result = raft_server_->add_srv(srv_config_to_add);
|
||||
|
||||
@ -134,9 +133,9 @@ auto RaftState::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_po
|
||||
bool added{false};
|
||||
while (!maybe_stop()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(waiting_period));
|
||||
const auto server_config = raft_server_->get_srv_config(static_cast<nuraft::int32>(raft_server_id));
|
||||
const auto server_config = raft_server_->get_srv_config(static_cast<nuraft::int32>(config.coordinator_server_id));
|
||||
if (server_config) {
|
||||
spdlog::trace("Server with id {} added to cluster", raft_server_id);
|
||||
spdlog::trace("Server with id {} added to cluster", config.coordinator_server_id);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
@ -158,7 +157,7 @@ auto RaftState::IsLeader() const -> bool { return raft_server_->is_leader(); }
|
||||
|
||||
auto RaftState::RequestLeadership() -> bool { return raft_server_->is_leader() || raft_server_->request_leadership(); }
|
||||
|
||||
auto RaftState::AppendRegisterReplicationInstanceLog(CoordinatorClientConfig const &config) -> bool {
|
||||
auto RaftState::AppendRegisterReplicationInstanceLog(CoordinatorToReplicaConfig const &config) -> bool {
|
||||
auto new_log = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
auto const res = raft_server_->append_entries({new_log});
|
||||
|
||||
@ -261,8 +260,26 @@ auto RaftState::AppendUpdateUUIDLog(utils::UUID const &uuid) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto RaftState::FindCurrentMainInstanceName() const -> std::optional<std::string> {
|
||||
return state_machine_->FindCurrentMainInstanceName();
|
||||
auto RaftState::AppendAddCoordinatorInstanceLog(CoordinatorToCoordinatorConfig const &config) -> bool {
|
||||
auto new_log = CoordinatorStateMachine::SerializeAddCoordinatorInstance(config);
|
||||
auto const res = raft_server_->append_entries({new_log});
|
||||
if (!res->get_accepted()) {
|
||||
spdlog::error(
|
||||
"Failed to accept request for adding coordinator instance {}. Most likely the reason is that the instance is "
|
||||
"not the leader.",
|
||||
config.coordinator_server_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
spdlog::info("Request for adding coordinator instance {} accepted", config.coordinator_server_id);
|
||||
|
||||
if (res->get_result_code() != nuraft::cmd_result_code::OK) {
|
||||
spdlog::error("Failed to add coordinator instance {} with error code {}", config.coordinator_server_id,
|
||||
static_cast<int>(res->get_result_code()));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto RaftState::MainExists() const -> bool { return state_machine_->MainExists(); }
|
||||
@ -273,7 +290,13 @@ auto RaftState::IsReplica(std::string_view instance_name) const -> bool {
|
||||
return state_machine_->IsReplica(instance_name);
|
||||
}
|
||||
|
||||
auto RaftState::GetInstances() const -> std::vector<InstanceState> { return state_machine_->GetInstances(); }
|
||||
auto RaftState::GetReplicationInstances() const -> std::vector<ReplicationInstanceState> {
|
||||
return state_machine_->GetReplicationInstances();
|
||||
}
|
||||
|
||||
auto RaftState::GetCoordinatorInstances() const -> std::vector<CoordinatorInstanceState> {
|
||||
return state_machine_->GetCoordinatorInstances();
|
||||
}
|
||||
|
||||
auto RaftState::GetUUID() const -> utils::UUID { return state_machine_->GetUUID(); }
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
namespace memgraph::coordination {
|
||||
|
||||
ReplicationInstance::ReplicationInstance(CoordinatorInstance *peer, CoordinatorClientConfig config,
|
||||
ReplicationInstance::ReplicationInstance(CoordinatorInstance *peer, CoordinatorToReplicaConfig config,
|
||||
HealthCheckClientCallback succ_cb, HealthCheckClientCallback fail_cb,
|
||||
HealthCheckInstanceCallback succ_instance_cb,
|
||||
HealthCheckInstanceCallback fail_instance_cb)
|
||||
@ -82,7 +82,7 @@ auto ReplicationInstance::StopFrequentCheck() -> void { client_.StopFrequentChec
|
||||
auto ReplicationInstance::PauseFrequentCheck() -> void { client_.PauseFrequentCheck(); }
|
||||
auto ReplicationInstance::ResumeFrequentCheck() -> void { client_.ResumeFrequentCheck(); }
|
||||
|
||||
auto ReplicationInstance::ReplicationClientInfo() const -> CoordinatorClientConfig::ReplicationClientInfo {
|
||||
auto ReplicationInstance::ReplicationClientInfo() const -> coordination::ReplicationClientInfo {
|
||||
return client_.ReplicationClientInfo();
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@ namespace memgraph::dbms {
|
||||
CoordinatorHandler::CoordinatorHandler(coordination::CoordinatorState &coordinator_state)
|
||||
: coordinator_state_(coordinator_state) {}
|
||||
|
||||
auto CoordinatorHandler::RegisterReplicationInstance(coordination::CoordinatorClientConfig const &config)
|
||||
auto CoordinatorHandler::RegisterReplicationInstance(coordination::CoordinatorToReplicaConfig const &config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus {
|
||||
return coordinator_state_.RegisterReplicationInstance(config);
|
||||
}
|
||||
@ -39,9 +39,8 @@ auto CoordinatorHandler::ShowInstances() const -> std::vector<coordination::Inst
|
||||
return coordinator_state_.ShowInstances();
|
||||
}
|
||||
|
||||
auto CoordinatorHandler::AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port,
|
||||
std::string_view raft_address) -> void {
|
||||
coordinator_state_.AddCoordinatorInstance(raft_server_id, raft_port, raft_address);
|
||||
auto CoordinatorHandler::AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void {
|
||||
coordinator_state_.AddCoordinatorInstance(config);
|
||||
}
|
||||
|
||||
} // namespace memgraph::dbms
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "coordination/coordinator_state.hpp"
|
||||
#include "coordination/instance_status.hpp"
|
||||
#include "coordination/register_main_replica_coordinator_status.hpp"
|
||||
@ -30,7 +30,7 @@ class CoordinatorHandler {
|
||||
|
||||
// TODO: (andi) When moving coordinator state on same instances, rename from RegisterReplicationInstance to
|
||||
// RegisterInstance
|
||||
auto RegisterReplicationInstance(coordination::CoordinatorClientConfig const &config)
|
||||
auto RegisterReplicationInstance(coordination::CoordinatorToReplicaConfig const &config)
|
||||
-> coordination::RegisterInstanceCoordinatorStatus;
|
||||
|
||||
auto UnregisterReplicationInstance(std::string_view instance_name)
|
||||
@ -40,7 +40,7 @@ class CoordinatorHandler {
|
||||
|
||||
auto ShowInstances() const -> std::vector<coordination::InstanceStatus>;
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, uint32_t raft_port, std::string_view raft_address) -> void;
|
||||
auto AddCoordinatorInstance(coordination::CoordinatorToCoordinatorConfig const &config) -> void;
|
||||
|
||||
private:
|
||||
coordination::CoordinatorState &coordinator_state_;
|
||||
|
@ -249,6 +249,40 @@ std::pair<std::vector<std::string>, std::optional<int>> SessionHL::Interpret(
|
||||
}
|
||||
}
|
||||
|
||||
using memgraph::communication::bolt::Value;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto SessionHL::Route(std::map<std::string, Value> const &routing,
|
||||
std::vector<memgraph::communication::bolt::Value> const & /*bookmarks*/,
|
||||
std::map<std::string, Value> const & /*extra*/) -> std::map<std::string, Value> {
|
||||
auto routing_map = ranges::views::transform(
|
||||
routing, [](auto const &pair) { return std::pair(pair.first, pair.second.ValueString()); }) |
|
||||
ranges::to<std::map<std::string, std::string>>();
|
||||
|
||||
auto routing_table_res = interpreter_.Route(routing_map);
|
||||
|
||||
auto create_server = [](auto const &server_info) -> Value {
|
||||
auto const &[addresses, role] = server_info;
|
||||
std::map<std::string, Value> server_map;
|
||||
auto bolt_addresses = ranges::views::transform(addresses, [](auto const &addr) { return Value{addr}; }) |
|
||||
ranges::to<std::vector<Value>>();
|
||||
|
||||
server_map["addresses"] = std::move(bolt_addresses);
|
||||
server_map["role"] = memgraph::communication::bolt::Value{role};
|
||||
return Value{std::move(server_map)};
|
||||
};
|
||||
|
||||
std::map<std::string, Value> communication_res;
|
||||
communication_res["ttl"] = Value{routing_table_res.ttl};
|
||||
communication_res["db"] = Value{};
|
||||
|
||||
auto servers = ranges::views::transform(routing_table_res.servers, create_server) | ranges::to<std::vector<Value>>();
|
||||
communication_res["servers"] = memgraph::communication::bolt::Value{std::move(servers)};
|
||||
|
||||
return {{"rt", memgraph::communication::bolt::Value{std::move(communication_res)}}};
|
||||
}
|
||||
#endif
|
||||
|
||||
void SessionHL::RollbackTransaction() {
|
||||
try {
|
||||
interpreter_.RollbackTransaction();
|
||||
|
@ -55,6 +55,13 @@ class SessionHL final : public memgraph::communication::bolt::Session<memgraph::
|
||||
const std::string &query, const std::map<std::string, memgraph::communication::bolt::Value> ¶ms,
|
||||
const std::map<std::string, memgraph::communication::bolt::Value> &extra) override;
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto Route(std::map<std::string, memgraph::communication::bolt::Value> const &routing,
|
||||
std::vector<memgraph::communication::bolt::Value> const &bookmarks,
|
||||
std::map<std::string, memgraph::communication::bolt::Value> const &extra)
|
||||
-> std::map<std::string, memgraph::communication::bolt::Value> override;
|
||||
#endif
|
||||
|
||||
std::map<std::string, memgraph::communication::bolt::Value> Pull(TEncoder *encoder, std::optional<int> n,
|
||||
std::optional<int> qid) override;
|
||||
|
||||
|
@ -82,8 +82,7 @@ bool Endpoint::IsResolvableAddress(std::string_view address, uint16_t port) {
|
||||
return status == 0;
|
||||
}
|
||||
|
||||
std::optional<ParsedAddress> Endpoint::ParseSocketOrAddress(std::string_view address,
|
||||
std::optional<uint16_t> default_port) {
|
||||
std::optional<Endpoint> Endpoint::ParseSocketOrAddress(std::string_view address, std::optional<uint16_t> default_port) {
|
||||
auto const parts = utils::SplitView(address, delimiter);
|
||||
|
||||
if (parts.size() > 2) {
|
||||
@ -109,13 +108,13 @@ std::optional<ParsedAddress> Endpoint::ParseSocketOrAddress(std::string_view add
|
||||
}();
|
||||
|
||||
if (GetIpFamily(addr) == IpFamily::NONE) {
|
||||
if (IsResolvableAddress(addr, *port)) { // NOLINT
|
||||
return std::pair{addr, *port}; // NOLINT
|
||||
if (IsResolvableAddress(addr, *port)) { // NOLINT
|
||||
return Endpoint{std::string(addr), *port}; // NOLINT
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
return std::pair{addr, *port}; // NOLINT
|
||||
return Endpoint{std::string(addr), *port}; // NOLINT
|
||||
}
|
||||
|
||||
auto Endpoint::ValidatePort(std::optional<uint16_t> port) -> bool {
|
||||
@ -138,4 +137,14 @@ auto Endpoint::ValidatePort(std::optional<uint16_t> port) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
void to_json(nlohmann::json &j, Endpoint const &config) {
|
||||
j = nlohmann::json{{"address", config.address}, {"port", config.port}, {"family", config.family}};
|
||||
}
|
||||
|
||||
void from_json(nlohmann::json const &j, Endpoint &config) {
|
||||
config.address = j.at("address").get<std::string>();
|
||||
config.port = j.at("port").get<uint16_t>();
|
||||
config.family = j.at("family").get<Endpoint::IpFamily>();
|
||||
}
|
||||
|
||||
} // namespace memgraph::io::network
|
||||
|
@ -17,9 +17,9 @@
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace memgraph::io::network {
|
||||
#include "json/json.hpp"
|
||||
|
||||
using ParsedAddress = std::pair<std::string_view, uint16_t>;
|
||||
namespace memgraph::io::network {
|
||||
|
||||
struct Endpoint {
|
||||
static const struct needs_resolving_t {
|
||||
@ -39,8 +39,8 @@ struct Endpoint {
|
||||
|
||||
enum class IpFamily : std::uint8_t { NONE, IP4, IP6 };
|
||||
|
||||
static std::optional<ParsedAddress> ParseSocketOrAddress(std::string_view address,
|
||||
std::optional<uint16_t> default_port = {});
|
||||
static std::optional<Endpoint> ParseSocketOrAddress(std::string_view address,
|
||||
std::optional<uint16_t> default_port = {});
|
||||
|
||||
std::string SocketAddress() const;
|
||||
|
||||
@ -59,4 +59,7 @@ struct Endpoint {
|
||||
static auto ValidatePort(std::optional<uint16_t> port) -> bool;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json &j, Endpoint const &config);
|
||||
void from_json(nlohmann::json const &j, Endpoint &config);
|
||||
|
||||
} // namespace memgraph::io::network
|
||||
|
@ -328,15 +328,14 @@ class ReplQueryHandler {
|
||||
|
||||
const auto repl_mode = convertToReplicationMode(sync_mode);
|
||||
|
||||
const auto maybe_ip_and_port =
|
||||
auto maybe_endpoint =
|
||||
io::network::Endpoint::ParseSocketOrAddress(socket_address, memgraph::replication::kDefaultReplicationPort);
|
||||
if (maybe_ip_and_port) {
|
||||
const auto [ip, port] = *maybe_ip_and_port;
|
||||
if (maybe_endpoint) {
|
||||
const auto replication_config =
|
||||
replication::ReplicationClientConfig{.name = name,
|
||||
.mode = repl_mode,
|
||||
.ip_address = std::string(ip),
|
||||
.port = port,
|
||||
.ip_address = std::move(maybe_endpoint->address),
|
||||
.port = maybe_endpoint->port,
|
||||
.replica_check_frequency = replica_check_frequency,
|
||||
.ssl = std::nullopt};
|
||||
|
||||
@ -413,39 +412,41 @@ class CoordQueryHandler final : public query::CoordinatorQueryHandler {
|
||||
}
|
||||
}
|
||||
|
||||
void RegisterReplicationInstance(std::string_view coordinator_socket_address,
|
||||
std::string_view replication_socket_address,
|
||||
void RegisterReplicationInstance(std::string_view bolt_server, std::string_view management_server,
|
||||
std::string_view replication_server,
|
||||
std::chrono::seconds const &instance_check_frequency,
|
||||
std::chrono::seconds const &instance_down_timeout,
|
||||
std::chrono::seconds const &instance_get_uuid_frequency,
|
||||
std::string_view instance_name, CoordinatorQuery::SyncMode sync_mode) override {
|
||||
const auto maybe_replication_ip_port = io::network::Endpoint::ParseSocketOrAddress(replication_socket_address);
|
||||
if (!maybe_replication_ip_port) {
|
||||
auto const maybe_bolt_server = io::network::Endpoint::ParseSocketOrAddress(bolt_server);
|
||||
if (!maybe_bolt_server) {
|
||||
throw QueryRuntimeException("Invalid bolt socket address!");
|
||||
}
|
||||
|
||||
auto const maybe_management_server = io::network::Endpoint::ParseSocketOrAddress(management_server);
|
||||
if (!maybe_management_server) {
|
||||
throw QueryRuntimeException("Invalid management socket address!");
|
||||
}
|
||||
|
||||
auto const maybe_replication_server = io::network::Endpoint::ParseSocketOrAddress(replication_server);
|
||||
if (!maybe_replication_server) {
|
||||
throw QueryRuntimeException("Invalid replication socket address!");
|
||||
}
|
||||
|
||||
const auto maybe_coordinator_ip_port = io::network::Endpoint::ParseSocketOrAddress(coordinator_socket_address);
|
||||
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 = std::string(instance_name),
|
||||
.replication_mode = convertFromCoordinatorToReplicationMode(sync_mode),
|
||||
.replication_ip_address = std::string(replication_ip),
|
||||
.replication_port = replication_port};
|
||||
auto const repl_config =
|
||||
coordination::ReplicationClientInfo{.instance_name = std::string(instance_name),
|
||||
.replication_mode = convertFromCoordinatorToReplicationMode(sync_mode),
|
||||
.replication_server = *maybe_replication_server};
|
||||
|
||||
auto coordinator_client_config =
|
||||
coordination::CoordinatorClientConfig{.instance_name = std::string(instance_name),
|
||||
.ip_address = std::string(coordinator_server_ip),
|
||||
.port = coordinator_server_port,
|
||||
.instance_health_check_frequency_sec = instance_check_frequency,
|
||||
.instance_down_timeout_sec = instance_down_timeout,
|
||||
.instance_get_uuid_frequency_sec = instance_get_uuid_frequency,
|
||||
.replication_client_info = repl_config,
|
||||
.ssl = std::nullopt};
|
||||
coordination::CoordinatorToReplicaConfig{.instance_name = std::string(instance_name),
|
||||
.mgt_server = *maybe_management_server,
|
||||
.bolt_server = *maybe_bolt_server,
|
||||
.replication_client_info = repl_config,
|
||||
.instance_health_check_frequency_sec = instance_check_frequency,
|
||||
.instance_down_timeout_sec = instance_down_timeout,
|
||||
.instance_get_uuid_frequency_sec = instance_get_uuid_frequency,
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto status = coordinator_handler_.RegisterReplicationInstance(coordinator_client_config);
|
||||
switch (status) {
|
||||
@ -473,15 +474,25 @@ class CoordQueryHandler final : public query::CoordinatorQueryHandler {
|
||||
}
|
||||
}
|
||||
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, std::string_view raft_socket_address) -> void override {
|
||||
auto const maybe_ip_and_port = io::network::Endpoint::ParseSocketOrAddress(raft_socket_address);
|
||||
if (maybe_ip_and_port) {
|
||||
auto const [ip, port] = *maybe_ip_and_port;
|
||||
spdlog::info("Adding instance {} with raft socket address {}:{}.", raft_server_id, ip, port);
|
||||
coordinator_handler_.AddCoordinatorInstance(raft_server_id, port, ip);
|
||||
} else {
|
||||
spdlog::error("Invalid raft socket address {}.", raft_socket_address);
|
||||
auto AddCoordinatorInstance(uint32_t raft_server_id, std::string_view bolt_server,
|
||||
std::string_view coordinator_server) -> void override {
|
||||
auto const maybe_coordinator_server = io::network::Endpoint::ParseSocketOrAddress(coordinator_server);
|
||||
if (!maybe_coordinator_server) {
|
||||
throw QueryRuntimeException("Invalid coordinator socket address!");
|
||||
}
|
||||
|
||||
auto const maybe_bolt_server = io::network::Endpoint::ParseSocketOrAddress(bolt_server);
|
||||
if (!maybe_bolt_server) {
|
||||
throw QueryRuntimeException("Invalid bolt socket address!");
|
||||
}
|
||||
|
||||
auto const coord_coord_config =
|
||||
coordination::CoordinatorToCoordinatorConfig{.coordinator_server_id = raft_server_id,
|
||||
.bolt_server = *maybe_bolt_server,
|
||||
.coordinator_server = *maybe_coordinator_server};
|
||||
|
||||
coordinator_handler_.AddCoordinatorInstance(coord_coord_config);
|
||||
spdlog::info("Added instance on coordinator server {}", maybe_coordinator_server->SocketAddress());
|
||||
}
|
||||
|
||||
void SetReplicationInstanceToMain(std::string_view instance_name) override {
|
||||
@ -1197,8 +1208,9 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
auto coord_server_id = coordinator_query->coordinator_server_id_->Accept(evaluator).ValueInt();
|
||||
|
||||
callback.fn = [handler = CoordQueryHandler{*coordinator_state}, coord_server_id,
|
||||
bolt_server = bolt_server_it->second,
|
||||
coordinator_server = coordinator_server_it->second]() mutable {
|
||||
handler.AddCoordinatorInstance(coord_server_id, coordinator_server);
|
||||
handler.AddCoordinatorInstance(coord_server_id, bolt_server, coordinator_server);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
|
||||
@ -1243,15 +1255,15 @@ Callback HandleCoordinatorQuery(CoordinatorQuery *coordinator_query, const Param
|
||||
|
||||
callback.fn = [handler = CoordQueryHandler{*coordinator_state},
|
||||
instance_health_check_frequency_sec = config.instance_health_check_frequency_sec,
|
||||
management_server = management_server_it->second,
|
||||
replication_server = replication_server_it->second, bolt_server = bolt_server_it->second,
|
||||
bolt_server = bolt_server_it->second, management_server = management_server_it->second,
|
||||
replication_server = replication_server_it->second,
|
||||
instance_name = coordinator_query->instance_name_,
|
||||
instance_down_timeout_sec = config.instance_down_timeout_sec,
|
||||
instance_get_uuid_frequency_sec = config.instance_get_uuid_frequency_sec,
|
||||
sync_mode = coordinator_query->sync_mode_]() mutable {
|
||||
handler.RegisterReplicationInstance(management_server, replication_server, instance_health_check_frequency_sec,
|
||||
instance_down_timeout_sec, instance_get_uuid_frequency_sec, instance_name,
|
||||
sync_mode);
|
||||
handler.RegisterReplicationInstance(bolt_server, management_server, replication_server,
|
||||
instance_health_check_frequency_sec, instance_down_timeout_sec,
|
||||
instance_get_uuid_frequency_sec, instance_name, sync_mode);
|
||||
return std::vector<std::vector<TypedValue>>();
|
||||
};
|
||||
|
||||
@ -4266,6 +4278,28 @@ void Interpreter::RollbackTransaction() {
|
||||
ResetInterpreter();
|
||||
}
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto Interpreter::Route(std::map<std::string, std::string> const &routing) -> RouteResult {
|
||||
// TODO: (andi) Test
|
||||
if (!FLAGS_raft_server_id) {
|
||||
auto const &address = routing.find("address");
|
||||
if (address == routing.end()) {
|
||||
throw QueryException("Routing table must contain address field.");
|
||||
}
|
||||
|
||||
auto result = RouteResult{};
|
||||
if (interpreter_context_->repl_state->IsMain()) {
|
||||
result.servers.emplace_back(std::vector<std::string>{address->second}, "WRITE");
|
||||
} else {
|
||||
result.servers.emplace_back(std::vector<std::string>{address->second}, "READ");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return RouteResult{.servers = interpreter_context_->coordinator_state_->GetRoutingTable(routing)};
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MG_ENTERPRISE
|
||||
// Before Prepare or during Prepare, but single-threaded.
|
||||
// TODO: Is there any cleanup?
|
||||
|
@ -143,8 +143,8 @@ class CoordinatorQueryHandler {
|
||||
};
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual void RegisterReplicationInstance(std::string_view coordinator_socket_address,
|
||||
std::string_view replication_socket_address,
|
||||
virtual void RegisterReplicationInstance(std::string_view bolt_server, std::string_view management_server,
|
||||
std::string_view replication_server,
|
||||
std::chrono::seconds const &instance_health_check_frequency,
|
||||
std::chrono::seconds const &instance_down_timeout,
|
||||
std::chrono::seconds const &instance_get_uuid_frequency,
|
||||
@ -160,7 +160,8 @@ class CoordinatorQueryHandler {
|
||||
virtual std::vector<coordination::InstanceStatus> ShowInstances() const = 0;
|
||||
|
||||
/// @throw QueryRuntimeException if an error ocurred.
|
||||
virtual auto AddCoordinatorInstance(uint32_t raft_server_id, std::string_view coordinator_socket_address) -> void = 0;
|
||||
virtual auto AddCoordinatorInstance(uint32_t raft_server_id, std::string_view bolt_server,
|
||||
std::string_view coordinator_server) -> void = 0;
|
||||
};
|
||||
#endif
|
||||
|
||||
@ -247,6 +248,14 @@ class Interpreter final {
|
||||
std::optional<std::string> db;
|
||||
};
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
struct RouteResult {
|
||||
int ttl{300};
|
||||
std::string db{}; // Currently not used since we don't have any specific replication groups etc.
|
||||
coordination::RoutingTable servers{};
|
||||
};
|
||||
#endif
|
||||
|
||||
std::shared_ptr<QueryUserOrRole> user_or_role_{};
|
||||
bool in_explicit_transaction_{false};
|
||||
CurrentDB current_db_;
|
||||
@ -272,6 +281,10 @@ class Interpreter final {
|
||||
const std::map<std::string, storage::PropertyValue> ¶ms,
|
||||
QueryExtras const &extras);
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto Route(std::map<std::string, std::string> const &routing) -> RouteResult;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Execute the last prepared query and stream *all* of the results into the
|
||||
* given stream.
|
||||
|
@ -13,12 +13,13 @@ func handle_if_error(err error) {
|
||||
}
|
||||
|
||||
func main() {
|
||||
dbUri := "bolt://localhost:7687"
|
||||
driver, err := neo4j.NewDriver(dbUri, neo4j.BasicAuth("", "", ""))
|
||||
if err != nil {
|
||||
log.Fatal("An error occurred opening conn: %s", err)
|
||||
}
|
||||
defer driver.Close()
|
||||
fmt.Println("Started running docs_quick_start.go test")
|
||||
dbUri := "bolt://localhost:7687"
|
||||
driver, err := neo4j.NewDriver(dbUri, neo4j.BasicAuth("", "", ""))
|
||||
if err != nil {
|
||||
log.Fatal("An error occurred opening conn: %s", err)
|
||||
}
|
||||
defer driver.Close()
|
||||
|
||||
session := driver.NewSession(neo4j.SessionConfig{})
|
||||
defer session.Close()
|
||||
@ -33,7 +34,7 @@ func main() {
|
||||
|
||||
_,err = session.WriteTransaction(testAll)
|
||||
handle_if_error(err)
|
||||
fmt.Println("All ok!")
|
||||
fmt.Println("doc_quick_start.go test finished successfully.")
|
||||
}
|
||||
|
||||
func clearDatabase(tx neo4j.Transaction) (interface{}, error) {
|
||||
@ -75,15 +76,14 @@ func testAll(tx neo4j.Transaction) (interface{}, error) {
|
||||
handle_if_error(err)
|
||||
age, err := neo4j.GetProperty[int64](node_value, "age")
|
||||
handle_if_error(err)
|
||||
|
||||
|
||||
if label != "Person" && name != "Alice" && age != 22 {
|
||||
return nil, fmt.Errorf("Data doesn't match.")
|
||||
}
|
||||
|
||||
|
||||
fmt.Println("Label", label)
|
||||
fmt.Println("name", name)
|
||||
fmt.Println("age", age)
|
||||
|
||||
return result.Consume()
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,6 @@ module bolt-test
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.13.0 // indirect
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.18.0 // indirect
|
||||
golang.org/dl v0.0.0-20230502172222-5216546bad51 // indirect
|
||||
)
|
||||
|
@ -8,5 +8,7 @@ github.com/neo4j/neo4j-go-driver/v5 v5.9.0 h1:TYxT0RSiwnvVFia90V7TLnRXv8HkdQQ6rT
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.9.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.13.0 h1:NmyUxh4LYTdcJdI6EnazHyUKu1f0/BPiHCYUZUZIGQw=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.13.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.18.0 h1:3dmYsCYt/Fc/bPeSyGRGGfn/T6h06/OmHm72OFQKa3c=
|
||||
github.com/neo4j/neo4j-go-driver/v5 v5.18.0/go.mod h1:Vff8OwT7QpLm7L2yYr85XNWe9Rbqlbeb9asNXJTHO4k=
|
||||
golang.org/dl v0.0.0-20230502172222-5216546bad51 h1:Bmo/kmR2hzyhGt3jjtl1ghkCqa5LINbB9D3QTkiLJIY=
|
||||
golang.org/dl v0.0.0-20230502172222-5216546bad51/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
|
||||
|
51
tests/drivers/go/v5/read_routing.go
Normal file
51
tests/drivers/go/v5/read_routing.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func read_messages(uri string) {
|
||||
username := ""
|
||||
password := ""
|
||||
|
||||
// Connect to Memgraph
|
||||
driver, err := neo4j.NewDriver(uri, neo4j.BasicAuth(username, password, ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer driver.Close()
|
||||
|
||||
// Use AccessModeRead for read transactions
|
||||
session := driver.NewSession(neo4j.SessionConfig{AccessMode: neo4j.AccessModeRead})
|
||||
defer session.Close()
|
||||
|
||||
greeting, err := session.ReadTransaction(func(transaction neo4j.Transaction) (interface{}, error) {
|
||||
result, err := transaction.Run("MATCH (n:Greeting) RETURN n.message AS message LIMIT 1", nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.Next() {
|
||||
return result.Record().Values[0], nil
|
||||
}
|
||||
|
||||
return nil, result.Err()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(greeting)
|
||||
|
||||
}
|
||||
|
||||
// Test checks that you can use bolt+routing for connecting to main and coordinators for reading.
|
||||
func main() {
|
||||
fmt.Println("Started running read_route.go test")
|
||||
read_messages("neo4j://localhost:7690") // coordinator_1
|
||||
read_messages("neo4j://localhost:7691") // coordinator_2
|
||||
read_messages("neo4j://localhost:7692") // coordinator_3
|
||||
fmt.Println("Successfully finished running coordinator_route.go test")
|
||||
}
|
@ -18,4 +18,3 @@ done
|
||||
|
||||
go get github.com/neo4j/neo4j-go-driver/v5
|
||||
go run docs_quick_start.go
|
||||
# go run parallel_edge_import.go
|
||||
|
21
tests/drivers/go/v5/run_cluster_tests.sh
Executable file
21
tests/drivers/go/v5/run_cluster_tests.sh
Executable file
@ -0,0 +1,21 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
GO_VERSION="1.18.9"
|
||||
GO_VERSION_DIR="/opt/go$GO_VERSION"
|
||||
if [ -f "$GO_VERSION_DIR/go/bin/go" ]; then
|
||||
export GOROOT="$GO_VERSION_DIR/go"
|
||||
export GOPATH="$HOME/go$GO_VERSION"
|
||||
export PATH="$GO_VERSION_DIR/go/bin:$PATH"
|
||||
fi
|
||||
|
||||
# check if go is installed
|
||||
for i in go; do
|
||||
if ! which $i >/dev/null; then
|
||||
echo "Please install $i!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
go get github.com/neo4j/neo4j-go-driver/v5
|
||||
go run write_routing.go
|
||||
go run read_routing.go
|
51
tests/drivers/go/v5/write_routing.go
Normal file
51
tests/drivers/go/v5/write_routing.go
Normal file
@ -0,0 +1,51 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
||||
)
|
||||
|
||||
func create_message(uri string) {
|
||||
username := ""
|
||||
password := ""
|
||||
|
||||
// Connect to Memgraph
|
||||
driver, err := neo4j.NewDriver(uri, neo4j.BasicAuth(username, password, ""))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer driver.Close()
|
||||
|
||||
session := driver.NewSession(neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite})
|
||||
defer session.Close()
|
||||
|
||||
greeting, err := session.WriteTransaction(func(transaction neo4j.Transaction) (interface{}, error) {
|
||||
result, err := transaction.Run("CREATE (n:Greeting) SET n.message = $message RETURN n.message", map[string]interface{}{
|
||||
"message": "Hello, World!",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if result.Next() {
|
||||
return result.Record().Values[0], nil
|
||||
}
|
||||
|
||||
return nil, result.Err()
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println(greeting)
|
||||
}
|
||||
|
||||
// Test checks that you can use bolt+routing for connecting to main and coordinators for writing.
|
||||
func main() {
|
||||
fmt.Println("Started running main_route.go test")
|
||||
create_message("neo4j://localhost:7690") // coordinator_1
|
||||
create_message("neo4j://localhost:7691") // coordinator_2
|
||||
create_message("neo4j://localhost:7692") // coordinator_3
|
||||
fmt.Println("Successfully finished running main_route.go test")
|
||||
}
|
@ -104,6 +104,45 @@
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>build-e</id>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>memgraph.WriteRouting</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<finalName>WriteRouting</finalName>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>build-f</id>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>memgraph.ReadRouting</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<finalName>ReadRouting</finalName>
|
||||
</configuration>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
@ -36,4 +36,3 @@ mvn clean package
|
||||
java -jar target/DocsHowToQuery.jar
|
||||
java -jar target/MaxQueryLength.jar
|
||||
java -jar target/Transactions.jar
|
||||
# java -jar target/ParallelEdgeImport.jar
|
||||
|
37
tests/drivers/java/v5_8/run_cluster_tests.sh
Executable file
37
tests/drivers/java/v5_8/run_cluster_tests.sh
Executable file
@ -0,0 +1,37 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$DIR"
|
||||
|
||||
if [ -d "/usr/lib/jvm/java-17-oracle" ]; then
|
||||
export JAVA_HOME="/usr/lib/jvm/java-17-oracle"
|
||||
fi
|
||||
if [ -d "/usr/lib/jvm/java-17-openjdk-amd64" ]; then
|
||||
export JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"
|
||||
fi
|
||||
if [ -d "/opt/apache-maven-3.9.3" ]; then
|
||||
export M2_HOME="/opt/apache-maven-3.9.3"
|
||||
fi
|
||||
export PATH="$JAVA_HOME/bin:$M2_HOME/bin:$PATH"
|
||||
|
||||
for i in java mvn; do
|
||||
if ! which $i >/dev/null; then
|
||||
echo "Please install $i!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
JAVA_VER=$(java -version 2>&1 >/dev/null | grep 'version' | cut -d "\"" -f2 | cut -d "." -f1)
|
||||
if [ $JAVA_VER -ne 17 ]
|
||||
then
|
||||
echo "neo4j-java-driver v5.8 requires Java 17. Please install it!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# CentOS 7 doesn't have Java version that supports var keyword
|
||||
source ../../../../environment/util.sh
|
||||
|
||||
mvn clean package
|
||||
|
||||
java -jar target/WriteRouting.jar
|
||||
java -jar target/ReadRouting.jar
|
@ -0,0 +1,35 @@
|
||||
package memgraph;
|
||||
|
||||
import static org.neo4j.driver.Values.parameters;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.GraphDatabase;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.driver.Transaction;
|
||||
|
||||
public class ReadRouting {
|
||||
private Driver driver;
|
||||
|
||||
private void readMessage(String uri) {
|
||||
driver = GraphDatabase.driver(uri, AuthTokens.basic("", ""));
|
||||
try (Session session = driver.session()) {
|
||||
String greeting = session.readTransaction(tx -> {
|
||||
var result = tx.run("MATCH (n:Greeting) RETURN n.message AS message");
|
||||
System.out.println("Read txn passed!");
|
||||
return "OK";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
System.out.println("Started running ReadRoutingTest...");
|
||||
ReadRouting greeter = new ReadRouting();
|
||||
greeter.readMessage("neo4j://localhost:7690"); // coordinator_1
|
||||
greeter.readMessage("neo4j://localhost:7691"); // coordinator_2
|
||||
greeter.readMessage("neo4j://localhost:7692"); // coordinator_3
|
||||
System.out.println("All good!");
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package memgraph;
|
||||
|
||||
import static org.neo4j.driver.Values.parameters;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.neo4j.driver.AuthTokens;
|
||||
import org.neo4j.driver.Config;
|
||||
import org.neo4j.driver.Driver;
|
||||
import org.neo4j.driver.GraphDatabase;
|
||||
import org.neo4j.driver.Result;
|
||||
import org.neo4j.driver.Session;
|
||||
import org.neo4j.driver.Transaction;
|
||||
import org.neo4j.driver.TransactionWork;
|
||||
import org.neo4j.driver.exceptions.ClientException;
|
||||
import org.neo4j.driver.exceptions.TransientException;
|
||||
|
||||
public class WriteRouting {
|
||||
private Driver driver;
|
||||
|
||||
private void createMessage(String uri) {
|
||||
driver = GraphDatabase.driver(uri, AuthTokens.basic("", ""));
|
||||
try (Session session = driver.session()) {
|
||||
String greeting = session.writeTransaction(tx -> {
|
||||
var result = tx.run("CREATE (n:Greeting) SET n.message = $message RETURN n.message",
|
||||
parameters("message", "Hello, World!"));
|
||||
if (result.hasNext()) {
|
||||
return result.single().get(0).asString();
|
||||
}
|
||||
throw new RuntimeException("No result found.");
|
||||
});
|
||||
System.out.println(greeting);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
System.out.println("Started running WriteRoutingTest...");
|
||||
WriteRouting greeter = new WriteRouting();
|
||||
greeter.createMessage("neo4j://localhost:7690"); // coordinator_1
|
||||
greeter.createMessage("neo4j://localhost:7691"); // coordinator_2
|
||||
greeter.createMessage("neo4j://localhost:7692"); // coordinator_3
|
||||
System.out.println("All good!");
|
||||
}
|
||||
}
|
59
tests/drivers/node/v5_8/read_routing.js
Normal file
59
tests/drivers/node/v5_8/read_routing.js
Normal file
@ -0,0 +1,59 @@
|
||||
const neo4j = require('neo4j-driver');
|
||||
|
||||
function die() {
|
||||
session.close();
|
||||
driver.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function Neo4jService(uri) {
|
||||
const driver = neo4j.driver(uri, neo4j.auth.basic("", ""));
|
||||
|
||||
async function readGreeting() {
|
||||
const session = driver.session({ defaultAccessMode: neo4j.session.READ });
|
||||
try {
|
||||
const result = await session.readTransaction(tx =>
|
||||
tx.run('MATCH (n:Greeting) RETURN n.message AS message')
|
||||
);
|
||||
console.log("Read txn finished");
|
||||
} finally {
|
||||
await session.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function close() {
|
||||
await driver.close();
|
||||
}
|
||||
|
||||
return {
|
||||
readGreeting,
|
||||
close
|
||||
};
|
||||
}
|
||||
|
||||
async function readGreetingsFromUri(uri) {
|
||||
const service = Neo4jService(uri);
|
||||
await service.readGreeting();
|
||||
await service.close();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Started reading route");
|
||||
const uris = [
|
||||
'neo4j://localhost:7690',
|
||||
'neo4j://localhost:7691',
|
||||
'neo4j://localhost:7692'
|
||||
];
|
||||
|
||||
try {
|
||||
for (const uri of uris) {
|
||||
await readGreetingsFromUri(uri);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('An error occurred:', error);
|
||||
die();
|
||||
}
|
||||
console.log("Finished reading route");
|
||||
}
|
||||
|
||||
main().catch(error => console.error(error));
|
@ -15,4 +15,3 @@ fi
|
||||
|
||||
node docs_how_to_query.js
|
||||
node max_query_length.js
|
||||
# node parallel_edge_import.js
|
||||
|
17
tests/drivers/node/v5_8/run_cluster_tests.sh
Executable file
17
tests/drivers/node/v5_8/run_cluster_tests.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$DIR"
|
||||
|
||||
if ! which node >/dev/null; then
|
||||
echo "Please install nodejs!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d node_modules ]; then
|
||||
# Driver generated with: `npm install neo4j-driver`
|
||||
npm install --no-package-lock --no-save neo4j-driver@5.8.0
|
||||
fi
|
||||
|
||||
node write_routing.js
|
||||
node read_routing.js
|
59
tests/drivers/node/v5_8/write_routing.js
Normal file
59
tests/drivers/node/v5_8/write_routing.js
Normal file
@ -0,0 +1,59 @@
|
||||
const neo4j = require('neo4j-driver');
|
||||
|
||||
function die() {
|
||||
session.close();
|
||||
driver.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function Neo4jService(uri) {
|
||||
const driver = neo4j.driver(uri, neo4j.auth.basic("", ""));
|
||||
|
||||
async function createGreeting() {
|
||||
const session = driver.session({ defaultAccessMode: neo4j.session.WRITE });
|
||||
try {
|
||||
const result = await session.writeTransaction(tx =>
|
||||
tx.run('CREATE (n:Greeting {message: "Hello NodeJs"}) RETURN n.message AS message')
|
||||
);
|
||||
console.log("Write txn finished");
|
||||
} finally {
|
||||
await session.close();
|
||||
}
|
||||
}
|
||||
|
||||
async function close() {
|
||||
await driver.close();
|
||||
}
|
||||
|
||||
return {
|
||||
createGreeting,
|
||||
close
|
||||
};
|
||||
}
|
||||
|
||||
async function createGreetingsFromUri(uri) {
|
||||
const service = Neo4jService(uri);
|
||||
await service.createGreeting();
|
||||
await service.close();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log("Started writing route");
|
||||
const uris = [
|
||||
'neo4j://localhost:7690',
|
||||
'neo4j://localhost:7691',
|
||||
'neo4j://localhost:7692'
|
||||
];
|
||||
|
||||
try {
|
||||
for (const uri of uris) {
|
||||
await createGreetingsFromUri(uri);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('An error occurred:', error);
|
||||
die();
|
||||
}
|
||||
console.log("Finished writing route");
|
||||
}
|
||||
|
||||
main().catch(error => console.error(error));
|
41
tests/drivers/python/v5_8/read_routing.py
Normal file
41
tests/drivers/python/v5_8/read_routing.py
Normal file
@ -0,0 +1,41 @@
|
||||
from neo4j import GraphDatabase
|
||||
|
||||
|
||||
class Neo4jService:
|
||||
def __init__(self, uri, user="", password=""):
|
||||
self.driver = GraphDatabase.driver(uri, auth=(user, password))
|
||||
|
||||
def close(self):
|
||||
self.driver.close()
|
||||
|
||||
def read_greeting(self):
|
||||
with self.driver.session() as session:
|
||||
session.execute_read(self._create_and_return_greeting)
|
||||
print("Read txn passed!")
|
||||
|
||||
@staticmethod
|
||||
def _create_and_return_greeting(tx):
|
||||
tx.run("MATCH (n:Greeting) RETURN n.message AS message")
|
||||
|
||||
|
||||
def read_greetings_from_uri(uri):
|
||||
service = Neo4jService(uri)
|
||||
service.read_greeting()
|
||||
service.close()
|
||||
|
||||
|
||||
def main():
|
||||
print("Started reading route")
|
||||
uris = ["neo4j://localhost:7690", "neo4j://localhost:7691", "neo4j://localhost:7692"]
|
||||
|
||||
try:
|
||||
for uri in uris:
|
||||
read_greetings_from_uri(uri)
|
||||
except Exception as error:
|
||||
print(f"An error occurred: {error}")
|
||||
exit(-1)
|
||||
print("Finished reading route")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
25
tests/drivers/python/v5_8/run_cluster_tests.sh
Executable file
25
tests/drivers/python/v5_8/run_cluster_tests.sh
Executable file
@ -0,0 +1,25 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$DIR"
|
||||
|
||||
# system check
|
||||
if ! which virtualenv >/dev/null; then
|
||||
echo "Please install virtualenv!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# setup virtual environment
|
||||
if [ ! -d "ve3" ]; then
|
||||
virtualenv -p python3 ve3 || exit 1
|
||||
source ve3/bin/activate
|
||||
python3 -m pip install neo4j==5.8.0 || exit 1
|
||||
deactivate
|
||||
fi
|
||||
|
||||
# activate virtualenv
|
||||
source ve3/bin/activate
|
||||
|
||||
# execute test
|
||||
python3 write_routing.py || exit 1
|
||||
python3 read_routing.py || exit 1
|
41
tests/drivers/python/v5_8/write_routing.py
Normal file
41
tests/drivers/python/v5_8/write_routing.py
Normal file
@ -0,0 +1,41 @@
|
||||
from neo4j import GraphDatabase
|
||||
|
||||
|
||||
class Neo4jService:
|
||||
def __init__(self, uri, user="", password=""):
|
||||
self.driver = GraphDatabase.driver(uri, auth=(user, password))
|
||||
|
||||
def close(self):
|
||||
self.driver.close()
|
||||
|
||||
def create_greeting(self):
|
||||
with self.driver.session() as session:
|
||||
session.execute_write(self._create_and_return_greeting)
|
||||
print("Write txn passed!")
|
||||
|
||||
@staticmethod
|
||||
def _create_and_return_greeting(tx):
|
||||
tx.run("CREATE (n:Greeting {message: 'Hello from Python'}) RETURN n.message AS message")
|
||||
|
||||
|
||||
def create_greetings_from_uri(uri):
|
||||
service = Neo4jService(uri)
|
||||
service.create_greeting()
|
||||
service.close()
|
||||
|
||||
|
||||
def main():
|
||||
print("Started writing route")
|
||||
uris = ["neo4j://localhost:7690", "neo4j://localhost:7691", "neo4j://localhost:7692"]
|
||||
|
||||
try:
|
||||
for uri in uris:
|
||||
create_greetings_from_uri(uri)
|
||||
except Exception as error:
|
||||
print(f"An error occurred: {error}")
|
||||
exit(-1)
|
||||
print("Finished writing route")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
203
tests/drivers/run_cluster.sh
Executable file
203
tests/drivers/run_cluster.sh
Executable file
@ -0,0 +1,203 @@
|
||||
#!/bin/bash
|
||||
|
||||
pushd () { command pushd "$@" > /dev/null; }
|
||||
popd () { command popd "$@" > /dev/null; }
|
||||
|
||||
function wait_for_server {
|
||||
port=$1
|
||||
while ! nc -z -w 1 127.0.0.1 $port; do
|
||||
sleep 0.1
|
||||
done
|
||||
sleep 1
|
||||
}
|
||||
|
||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
cd "$DIR"
|
||||
|
||||
# create a temporary directory.
|
||||
tmpdir=/tmp/memgraph_drivers
|
||||
if [ -d $tmpdir ]; then
|
||||
rm -rf $tmpdir
|
||||
fi
|
||||
|
||||
mkdir -p $tmpdir
|
||||
|
||||
# find memgraph binaries.
|
||||
binary_dir="$DIR/../../build"
|
||||
|
||||
# Start instance_1
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7687 \
|
||||
--data-directory=$tmpdir/instance_1/ \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/instance1.log \
|
||||
--also-log-to-stderr \
|
||||
--coordinator-server-port=10011 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_instance_1=$!
|
||||
wait_for_server 7687
|
||||
|
||||
# Start instance_2
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7688 \
|
||||
--data-directory=$tmpdir/instance_2 \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/instance2.log \
|
||||
--also-log-to-stderr \
|
||||
--coordinator-server-port=10012 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_instance_2=$!
|
||||
wait_for_server 7688
|
||||
|
||||
# Start instance_3
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7689 \
|
||||
--data-directory=$tmpdir/instance_3 \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/instance3.log \
|
||||
--also-log-to-stderr \
|
||||
--coordinator-server-port=10013 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_instance_3=$!
|
||||
wait_for_server 7689
|
||||
|
||||
|
||||
# Start coordinator_1
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7690 \
|
||||
--data-directory=$tmpdir/coordinator_1 \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/coordinator1.log \
|
||||
--also-log-to-stderr \
|
||||
--raft-server-id=1 \
|
||||
--raft-server-port=10111 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_coordinator_1=$!
|
||||
wait_for_server 7690
|
||||
|
||||
# Start coordinator_2
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7691 \
|
||||
--data-directory=$tmpdir/coordinator_2 \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/coordinator2.log \
|
||||
--also-log-to-stderr \
|
||||
--raft-server-id=2 \
|
||||
--raft-server-port=10112 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_coordinator_2=$!
|
||||
wait_for_server 7691
|
||||
|
||||
# Start coordinator_3
|
||||
$binary_dir/memgraph \
|
||||
--bolt-port=7692 \
|
||||
--data-directory=$tmpdir/coordinator_3 \
|
||||
--query-execution-timeout-sec=5 \
|
||||
--bolt-session-inactivity-timeout=10 \
|
||||
--bolt-server-name-for-init="Neo4j/1.1" \
|
||||
--bolt-cert-file="" \
|
||||
--log-file=$tmpdir/logs/coordinator3.log \
|
||||
--also-log-to-stderr \
|
||||
--raft-server-id=3 \
|
||||
--raft-server-port=10113 \
|
||||
--experimental-enabled=high-availability \
|
||||
--log-level ERROR &
|
||||
pid_coordinator_3=$!
|
||||
wait_for_server 7692
|
||||
|
||||
sleep 5
|
||||
|
||||
echo 'ADD COORDINATOR 2 WITH CONFIG {"bolt_server": "127.0.0.1:7691", "coordinator_server": "127.0.0.1:10112"};' | $binary_dir/bin/mgconsole --port 7690
|
||||
echo 'ADD COORDINATOR 3 WITH CONFIG {"bolt_server": "127.0.0.1:7692", "coordinator_server": "127.0.0.1:10113"};' | $binary_dir/bin/mgconsole --port 7690
|
||||
echo 'REGISTER INSTANCE instance_1 WITH CONFIG {"bolt_server": "127.0.0.1:7687", "management_server": "127.0.0.1:10011", "replication_server": "127.0.0.1:10001"};' | $binary_dir/bin/mgconsole --port 7690
|
||||
echo 'REGISTER INSTANCE instance_2 WITH CONFIG {"bolt_server": "127.0.0.1:7688", "management_server": "127.0.0.1:10012", "replication_server": "127.0.0.1:10002"};' | $binary_dir/bin/mgconsole --port 7690
|
||||
echo 'REGISTER INSTANCE instance_3 WITH CONFIG {"bolt_server": "127.0.0.1:7689", "management_server": "127.0.0.1:10013", "replication_server": "127.0.0.1:10003"};' | $binary_dir/bin/mgconsole --port 7690
|
||||
echo 'SET INSTANCE instance_1 TO MAIN;' | $binary_dir/bin/mgconsole --port 7690
|
||||
|
||||
|
||||
code_test=0
|
||||
for lang in *; do
|
||||
if [ ! -d $lang ]; then continue; fi
|
||||
pushd $lang
|
||||
echo "Running tests for language: $lang"
|
||||
for version in *; do
|
||||
if [ ! -d $version ]; then continue; fi
|
||||
pushd $version
|
||||
if [ -f "run_cluster_tests.sh" ]; then
|
||||
echo "Running version: $version"
|
||||
./run_cluster_tests.sh
|
||||
code_test=$?
|
||||
if [ $code_test -ne 0 ]; then
|
||||
echo "FAILED: $lang-$version"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
popd
|
||||
done;
|
||||
popd
|
||||
done
|
||||
|
||||
|
||||
# Function to stop a process by PID and check its exit code
|
||||
stop_process() {
|
||||
local pid=$1 # Capture the PID from the first argument
|
||||
|
||||
# Stop the process
|
||||
kill $pid
|
||||
wait $pid
|
||||
local exit_code=$? # Capture the exit code
|
||||
|
||||
# Check the process's exit code
|
||||
if [ $exit_code -ne 0 ]; then
|
||||
echo "The process with PID $pid didn't terminate properly!"
|
||||
exit $exit_code
|
||||
else
|
||||
echo "Process with PID $pid terminated successfully."
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Stopping coordinator1"
|
||||
stop_process $pid_coordinator_1
|
||||
echo "Stopping coordinator2"
|
||||
stop_process $pid_coordinator_2
|
||||
echo "Stopping coordinator3"
|
||||
stop_process $pid_coordinator_3
|
||||
|
||||
echo "Stopping instance1"
|
||||
stop_process $pid_instance_1
|
||||
echo "Stopping instance2"
|
||||
stop_process $pid_instance_2
|
||||
echo "Stopping instance3"
|
||||
stop_process $pid_instance_3
|
||||
|
||||
|
||||
# Check test exit code.
|
||||
if [ $code_test -ne 0 ]; then
|
||||
echo "One of the tests failed!"
|
||||
exit $code_test
|
||||
fi
|
||||
|
||||
# Temporary directory cleanup.
|
||||
if [ -d $tmpdir ]; then
|
||||
rm -rf $tmpdir
|
||||
fi
|
@ -30,14 +30,3 @@ def safe_execute(function, *args):
|
||||
function(*args)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
# NOTE: Repeated execution because it can fail if Raft server is not up
|
||||
def add_coordinator(cursor, query):
|
||||
for _ in range(10):
|
||||
try:
|
||||
execute_and_fetch_all(cursor, query)
|
||||
return True
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
@ -16,7 +16,7 @@ import tempfile
|
||||
|
||||
import interactive_mg_runner
|
||||
import pytest
|
||||
from common import add_coordinator, connect, execute_and_fetch_all, safe_execute
|
||||
from common import connect, execute_and_fetch_all, safe_execute
|
||||
from mg_utils import mg_sleep_and_assert
|
||||
|
||||
interactive_mg_runner.SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
@ -110,134 +110,134 @@ MEMGRAPH_INSTANCES_DESCRIPTION = {
|
||||
}
|
||||
|
||||
|
||||
def test_register_repl_instances_then_coordinators():
|
||||
safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
|
||||
coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
)
|
||||
execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
|
||||
def check_coordinator3():
|
||||
return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
|
||||
expected_cluster_coord3 = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
mg_sleep_and_assert(expected_cluster_coord3, check_coordinator3)
|
||||
|
||||
coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
|
||||
def check_coordinator1():
|
||||
return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "replica"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
|
||||
coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
|
||||
def check_coordinator2():
|
||||
return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
|
||||
|
||||
def test_register_coordinator_then_repl_instances():
|
||||
safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
|
||||
coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
)
|
||||
execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
|
||||
def check_coordinator3():
|
||||
return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
|
||||
expected_cluster_coord3 = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
mg_sleep_and_assert(expected_cluster_coord3, check_coordinator3)
|
||||
|
||||
coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
|
||||
def check_coordinator1():
|
||||
return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "replica"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
|
||||
coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
|
||||
def check_coordinator2():
|
||||
return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# def test_register_repl_instances_then_coordinators():
|
||||
# safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
# interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
#
|
||||
# coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
#
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
# )
|
||||
# execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
# )
|
||||
#
|
||||
# def check_coordinator3():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# expected_cluster_coord3 = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
# mg_sleep_and_assert(expected_cluster_coord3, check_coordinator3)
|
||||
#
|
||||
# coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
#
|
||||
# def check_coordinator1():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "replica"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
#
|
||||
# coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
#
|
||||
# def check_coordinator2():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
#
|
||||
#
|
||||
# def test_register_coordinator_then_repl_instances():
|
||||
# safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
# interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
#
|
||||
# coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
#
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
# )
|
||||
# execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
#
|
||||
# def check_coordinator3():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# expected_cluster_coord3 = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
# mg_sleep_and_assert(expected_cluster_coord3, check_coordinator3)
|
||||
#
|
||||
# coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
#
|
||||
# def check_coordinator1():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "replica"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
#
|
||||
# coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
#
|
||||
# def check_coordinator2():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
|
||||
|
||||
def test_coordinators_communication_with_restarts():
|
||||
@ -246,11 +246,11 @@ def test_coordinators_communication_with_restarts():
|
||||
|
||||
coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
|
||||
assert add_coordinator(
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
@ -310,284 +310,284 @@ def test_coordinators_communication_with_restarts():
|
||||
|
||||
|
||||
# # TODO: (andi) Test when dealing with distributed coordinators that you can register on one coordinator and unregister from any other coordinator
|
||||
@pytest.mark.parametrize(
|
||||
"kill_instance",
|
||||
[True, False],
|
||||
)
|
||||
def test_unregister_replicas(kill_instance):
|
||||
safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
|
||||
coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
)
|
||||
execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
|
||||
def check_coordinator1():
|
||||
return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
|
||||
def check_coordinator2():
|
||||
return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
|
||||
def check_coordinator3():
|
||||
return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
|
||||
main_cursor = connect(host="localhost", port=7689).cursor()
|
||||
|
||||
def check_main():
|
||||
return sorted(list(execute_and_fetch_all(main_cursor, "SHOW REPLICAS")))
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "replica"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
expected_replicas = [
|
||||
(
|
||||
"instance_1",
|
||||
"127.0.0.1:10001",
|
||||
"sync",
|
||||
{"ts": 0, "behind": None, "status": "ready"},
|
||||
{"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
),
|
||||
(
|
||||
"instance_2",
|
||||
"127.0.0.1:10002",
|
||||
"sync",
|
||||
{"ts": 0, "behind": None, "status": "ready"},
|
||||
{"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
mg_sleep_and_assert(expected_replicas, check_main)
|
||||
|
||||
if kill_instance:
|
||||
interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_1")
|
||||
execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_1")
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
expected_replicas = [
|
||||
(
|
||||
"instance_2",
|
||||
"127.0.0.1:10002",
|
||||
"sync",
|
||||
{"ts": 0, "behind": None, "status": "ready"},
|
||||
{"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
mg_sleep_and_assert(expected_replicas, check_main)
|
||||
|
||||
if kill_instance:
|
||||
interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_2")
|
||||
execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_2")
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
expected_replicas = []
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
mg_sleep_and_assert(expected_replicas, check_main)
|
||||
|
||||
|
||||
def test_unregister_main():
|
||||
safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
|
||||
coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
)
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
)
|
||||
execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
|
||||
def check_coordinator1():
|
||||
return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
|
||||
def check_coordinator2():
|
||||
return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
|
||||
def check_coordinator3():
|
||||
return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "replica"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
|
||||
try:
|
||||
execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_3")
|
||||
except Exception as e:
|
||||
assert (
|
||||
str(e)
|
||||
== "Alive main instance can't be unregistered! Shut it down to trigger failover and then unregister it!"
|
||||
)
|
||||
|
||||
interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_3")
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "main"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
("instance_3", "", "127.0.0.1:10013", "down", "unknown"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "main"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
("instance_3", "", "", "unknown", "main"),
|
||||
]
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
|
||||
execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_3")
|
||||
|
||||
expected_cluster = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "127.0.0.1:10011", "up", "main"),
|
||||
("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
]
|
||||
|
||||
expected_cluster_shared = [
|
||||
("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
("instance_1", "", "", "unknown", "main"),
|
||||
("instance_2", "", "", "unknown", "replica"),
|
||||
]
|
||||
|
||||
expected_replicas = [
|
||||
(
|
||||
"instance_2",
|
||||
"127.0.0.1:10002",
|
||||
"sync",
|
||||
{"ts": 0, "behind": None, "status": "ready"},
|
||||
{"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
),
|
||||
]
|
||||
|
||||
main_cursor = connect(host="localhost", port=7687).cursor()
|
||||
|
||||
def check_main():
|
||||
return sorted(list(execute_and_fetch_all(main_cursor, "SHOW REPLICAS")))
|
||||
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
mg_sleep_and_assert(expected_replicas, check_main)
|
||||
# @pytest.mark.parametrize(
|
||||
# "kill_instance",
|
||||
# [True, False],
|
||||
# )
|
||||
# def test_unregister_replicas(kill_instance):
|
||||
# safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
# interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
#
|
||||
# coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
# coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
# coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
#
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
# )
|
||||
# execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
#
|
||||
# def check_coordinator1():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# def check_coordinator2():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# def check_coordinator3():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# main_cursor = connect(host="localhost", port=7689).cursor()
|
||||
#
|
||||
# def check_main():
|
||||
# return sorted(list(execute_and_fetch_all(main_cursor, "SHOW REPLICAS")))
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "replica"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_replicas = [
|
||||
# (
|
||||
# "instance_1",
|
||||
# "127.0.0.1:10001",
|
||||
# "sync",
|
||||
# {"ts": 0, "behind": None, "status": "ready"},
|
||||
# {"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
# ),
|
||||
# (
|
||||
# "instance_2",
|
||||
# "127.0.0.1:10002",
|
||||
# "sync",
|
||||
# {"ts": 0, "behind": None, "status": "ready"},
|
||||
# {"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
# ),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
# mg_sleep_and_assert(expected_replicas, check_main)
|
||||
#
|
||||
# if kill_instance:
|
||||
# interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_1")
|
||||
# execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_1")
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_replicas = [
|
||||
# (
|
||||
# "instance_2",
|
||||
# "127.0.0.1:10002",
|
||||
# "sync",
|
||||
# {"ts": 0, "behind": None, "status": "ready"},
|
||||
# {"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
# ),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
# mg_sleep_and_assert(expected_replicas, check_main)
|
||||
#
|
||||
# if kill_instance:
|
||||
# interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_2")
|
||||
# execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_2")
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
# expected_replicas = []
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
# mg_sleep_and_assert(expected_replicas, check_main)
|
||||
#
|
||||
#
|
||||
# def test_unregister_main():
|
||||
# safe_execute(shutil.rmtree, TEMP_DIR)
|
||||
# interactive_mg_runner.start_all(MEMGRAPH_INSTANCES_DESCRIPTION)
|
||||
#
|
||||
# coordinator1_cursor = connect(host="localhost", port=7690).cursor()
|
||||
# coordinator2_cursor = connect(host="localhost", port=7691).cursor()
|
||||
# coordinator3_cursor = connect(host="localhost", port=7692).cursor()
|
||||
#
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_1 WITH CONFIG {'bolt_server': '127.0.0.1:7687', 'management_server': '127.0.0.1:10011', 'replication_server': '127.0.0.1:10001'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_2 WITH CONFIG {'bolt_server': '127.0.0.1:7688', 'management_server': '127.0.0.1:10012', 'replication_server': '127.0.0.1:10002'};",
|
||||
# )
|
||||
# execute_and_fetch_all(
|
||||
# coordinator3_cursor,
|
||||
# "REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
# )
|
||||
# execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
#
|
||||
# def check_coordinator1():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator1_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# def check_coordinator2():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator2_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# def check_coordinator3():
|
||||
# return sorted(list(execute_and_fetch_all(coordinator3_cursor, "SHOW INSTANCES")))
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "replica"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "up", "main"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "replica"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
#
|
||||
# try:
|
||||
# execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_3")
|
||||
# except Exception as e:
|
||||
# assert (
|
||||
# str(e)
|
||||
# == "Alive main instance can't be unregistered! Shut it down to trigger failover and then unregister it!"
|
||||
# )
|
||||
#
|
||||
# interactive_mg_runner.kill(MEMGRAPH_INSTANCES_DESCRIPTION, "instance_3")
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "main"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ("instance_3", "", "127.0.0.1:10013", "down", "unknown"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "main"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ("instance_3", "", "", "unknown", "main"),
|
||||
# ]
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
#
|
||||
# execute_and_fetch_all(coordinator3_cursor, "UNREGISTER INSTANCE instance_3")
|
||||
#
|
||||
# expected_cluster = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "127.0.0.1:10011", "up", "main"),
|
||||
# ("instance_2", "", "127.0.0.1:10012", "up", "replica"),
|
||||
# ]
|
||||
#
|
||||
# expected_cluster_shared = [
|
||||
# ("coordinator_1", "127.0.0.1:10111", "", "unknown", "coordinator"),
|
||||
# ("coordinator_2", "127.0.0.1:10112", "", "unknown", "coordinator"),
|
||||
# ("coordinator_3", "127.0.0.1:10113", "", "unknown", "coordinator"),
|
||||
# ("instance_1", "", "", "unknown", "main"),
|
||||
# ("instance_2", "", "", "unknown", "replica"),
|
||||
# ]
|
||||
#
|
||||
# expected_replicas = [
|
||||
# (
|
||||
# "instance_2",
|
||||
# "127.0.0.1:10002",
|
||||
# "sync",
|
||||
# {"ts": 0, "behind": None, "status": "ready"},
|
||||
# {"memgraph": {"ts": 0, "behind": 0, "status": "ready"}},
|
||||
# ),
|
||||
# ]
|
||||
#
|
||||
# main_cursor = connect(host="localhost", port=7687).cursor()
|
||||
#
|
||||
# def check_main():
|
||||
# return sorted(list(execute_and_fetch_all(main_cursor, "SHOW REPLICAS")))
|
||||
#
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator1)
|
||||
# mg_sleep_and_assert(expected_cluster_shared, check_coordinator2)
|
||||
# mg_sleep_and_assert(expected_cluster, check_coordinator3)
|
||||
# mg_sleep_and_assert(expected_replicas, check_main)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
@ -16,7 +16,7 @@ import tempfile
|
||||
|
||||
import interactive_mg_runner
|
||||
import pytest
|
||||
from common import add_coordinator, connect, execute_and_fetch_all, safe_execute
|
||||
from common import connect, execute_and_fetch_all, safe_execute
|
||||
from mg_utils import mg_sleep_and_assert
|
||||
|
||||
interactive_mg_runner.SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
@ -137,11 +137,11 @@ def test_writing_disabled_on_main_restart():
|
||||
"REGISTER INSTANCE instance_3 WITH CONFIG {'bolt_server': '127.0.0.1:7689', 'management_server': '127.0.0.1:10013', 'replication_server': '127.0.0.1:10003'};",
|
||||
)
|
||||
execute_and_fetch_all(coordinator3_cursor, "SET INSTANCE instance_3 TO MAIN")
|
||||
assert add_coordinator(
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 1 WITH CONFIG {'bolt_server': '127.0.0.1:7690', 'coordinator_server': '127.0.0.1:10111'}",
|
||||
)
|
||||
assert add_coordinator(
|
||||
execute_and_fetch_all(
|
||||
coordinator3_cursor,
|
||||
"ADD COORDINATOR 2 WITH CONFIG {'bolt_server': '127.0.0.1:7691', 'coordinator_server': '127.0.0.1:10112'}",
|
||||
)
|
||||
|
@ -37,10 +37,9 @@ auto ParseDatabaseEndpoints(const std::string &database_endpoints_str) {
|
||||
const auto db_endpoints_strs = memgraph::utils::SplitView(database_endpoints_str, ",");
|
||||
std::vector<memgraph::io::network::Endpoint> database_endpoints;
|
||||
for (const auto &db_endpoint_str : db_endpoints_strs) {
|
||||
const auto maybe_host_port = memgraph::io::network::Endpoint::ParseSocketOrAddress(db_endpoint_str, 7687);
|
||||
MG_ASSERT(maybe_host_port);
|
||||
auto const [ip, port] = *maybe_host_port;
|
||||
database_endpoints.emplace_back(std::string(ip), port);
|
||||
auto maybe_endpoint = memgraph::io::network::Endpoint::ParseSocketOrAddress(db_endpoint_str, 7687);
|
||||
MG_ASSERT(maybe_endpoint);
|
||||
database_endpoints.emplace_back(std::move(*maybe_endpoint));
|
||||
}
|
||||
return database_endpoints;
|
||||
}
|
||||
|
@ -446,9 +446,16 @@ target_link_libraries(${test_prefix}raft_log_serialization gflags mg-coordinatio
|
||||
target_include_directories(${test_prefix}raft_log_serialization PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
endif()
|
||||
|
||||
# Test Raft log serialization
|
||||
# Test CoordinatorClusterState
|
||||
if(MG_ENTERPRISE)
|
||||
add_unit_test(coordinator_cluster_state.cpp)
|
||||
target_link_libraries(${test_prefix}coordinator_cluster_state gflags mg-coordination mg-repl_coord_glue)
|
||||
target_include_directories(${test_prefix}coordinator_cluster_state PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
endif()
|
||||
|
||||
# Test Raft log serialization
|
||||
if(MG_ENTERPRISE)
|
||||
add_unit_test(routing_table.cpp)
|
||||
target_link_libraries(${test_prefix}routing_table gflags mg-coordination mg-repl_coord_glue)
|
||||
target_include_directories(${test_prefix}routing_table PRIVATE ${CMAKE_SOURCE_DIR}/include)
|
||||
endif()
|
||||
|
@ -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
|
||||
@ -114,6 +114,14 @@ class TestSession final : public Session<TestInputStream, TestOutputStream> {
|
||||
|
||||
bool Authenticate(const std::string & /*username*/, const std::string & /*password*/) override { return true; }
|
||||
|
||||
#ifdef MG_ENTERPRISE
|
||||
auto Route(std::map<std::string, Value> const & /*routing*/,
|
||||
std::vector<memgraph::communication::bolt::Value> const & /*bookmarks*/,
|
||||
std::map<std::string, Value> const & /*extra*/) -> std::map<std::string, Value> override {
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
std::optional<std::string> GetServerNameForInit() override { return std::nullopt; }
|
||||
|
||||
void Configure(const std::map<std::string, memgraph::communication::bolt::Value> &) override {}
|
||||
@ -1027,104 +1035,115 @@ TEST(BoltSession, Noop) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(BoltSession, Route) {
|
||||
// Memgraph does not support route message, but it handles it
|
||||
{
|
||||
SCOPED_TRACE("v1");
|
||||
INIT_VARS;
|
||||
TEST(BoltSession, Route){{SCOPED_TRACE("v1");
|
||||
INIT_VARS;
|
||||
|
||||
ExecuteHandshake(input_stream, session, output);
|
||||
ExecuteInit(input_stream, session, output);
|
||||
ASSERT_THROW(ExecuteCommand(input_stream, session, v4_3::route, sizeof(v4_3::route)), SessionException);
|
||||
EXPECT_EQ(session.state_, State::Close);
|
||||
}
|
||||
{
|
||||
SCOPED_TRACE("v4");
|
||||
INIT_VARS;
|
||||
ExecuteHandshake(input_stream, session, output);
|
||||
ExecuteInit(input_stream, session, output);
|
||||
ASSERT_THROW(ExecuteCommand(input_stream, session, v4_3::route, sizeof(v4_3::route)), SessionException);
|
||||
EXPECT_EQ(session.state_, State::Close);
|
||||
}
|
||||
#ifdef MG_ENTERPRISE
|
||||
{
|
||||
SCOPED_TRACE("v4");
|
||||
INIT_VARS;
|
||||
|
||||
ExecuteHandshake(input_stream, session, output, v4_3::handshake_req, v4_3::handshake_resp);
|
||||
ExecuteInit(input_stream, session, output, true);
|
||||
ASSERT_NO_THROW(ExecuteCommand(input_stream, session, v4_3::route, sizeof(v4_3::route)));
|
||||
static constexpr uint8_t expected_resp[] = {
|
||||
0x00 /*two bytes of chunk header, chunk contains 64 bytes of data*/,
|
||||
0x40,
|
||||
0xb1 /*TinyStruct1*/,
|
||||
0x7f /*Failure*/,
|
||||
0xa2 /*TinyMap with 2 items*/,
|
||||
0x84 /*TinyString with 4 chars*/,
|
||||
'c',
|
||||
'o',
|
||||
'd',
|
||||
'e',
|
||||
0x82 /*TinyString with 2 chars*/,
|
||||
'6',
|
||||
'6',
|
||||
0x87 /*TinyString with 7 chars*/,
|
||||
'm',
|
||||
'e',
|
||||
's',
|
||||
's',
|
||||
'a',
|
||||
'g',
|
||||
'e',
|
||||
0xd0 /*String*/,
|
||||
0x2b /*With 43 chars*/,
|
||||
'R',
|
||||
'o',
|
||||
'u',
|
||||
't',
|
||||
'e',
|
||||
' ',
|
||||
'm',
|
||||
'e',
|
||||
's',
|
||||
's',
|
||||
'a',
|
||||
'g',
|
||||
'e',
|
||||
' ',
|
||||
'i',
|
||||
's',
|
||||
' ',
|
||||
'n',
|
||||
'o',
|
||||
't',
|
||||
' ',
|
||||
's',
|
||||
'u',
|
||||
'p',
|
||||
'p',
|
||||
'o',
|
||||
'r',
|
||||
't',
|
||||
'e',
|
||||
'd',
|
||||
' ',
|
||||
'i',
|
||||
'n',
|
||||
' ',
|
||||
'M',
|
||||
'e',
|
||||
'm',
|
||||
'g',
|
||||
'r',
|
||||
'a',
|
||||
'p',
|
||||
'h',
|
||||
'!',
|
||||
0x00 /*Terminating zeros*/,
|
||||
0x00,
|
||||
};
|
||||
EXPECT_EQ(input_stream.size(), 0U);
|
||||
CheckOutput(output, expected_resp, sizeof(expected_resp));
|
||||
EXPECT_EQ(session.state_, State::Error);
|
||||
ExecuteHandshake(input_stream, session, output, v4_3::handshake_req, v4_3::handshake_resp);
|
||||
ExecuteInit(input_stream, session, output, true);
|
||||
ASSERT_NO_THROW(ExecuteCommand(input_stream, session, v4_3::route, sizeof(v4_3::route)));
|
||||
|
||||
SCOPED_TRACE("Try to reset connection after ROUTE failed");
|
||||
ASSERT_NO_THROW(ExecuteCommand(input_stream, session, v4::reset_req, sizeof(v4::reset_req)));
|
||||
EXPECT_EQ(input_stream.size(), 0U);
|
||||
CheckOutput(output, success_resp, sizeof(success_resp));
|
||||
EXPECT_EQ(session.state_, State::Idle);
|
||||
}
|
||||
EXPECT_EQ(session.state_, State::Idle);
|
||||
CheckSuccessMessage(output);
|
||||
}
|
||||
#else
|
||||
{
|
||||
SCOPED_TRACE("v4");
|
||||
INIT_VARS;
|
||||
|
||||
ExecuteHandshake(input_stream, session, output, v4_3::handshake_req, v4_3::handshake_resp);
|
||||
ExecuteInit(input_stream, session, output, true);
|
||||
ASSERT_NO_THROW(ExecuteCommand(input_stream, session, v4_3::route, sizeof(v4_3::route)));
|
||||
static constexpr uint8_t expected_resp[] = {
|
||||
0x00 /*two bytes of chunk header, chunk contains 64 bytes of data*/,
|
||||
0x40,
|
||||
0xb1 /*TinyStruct1*/,
|
||||
0x7f /*Failure*/,
|
||||
0xa2 /*TinyMap with 2 items*/,
|
||||
0x84 /*TinyString with 4 chars*/,
|
||||
'c',
|
||||
'o',
|
||||
'd',
|
||||
'e',
|
||||
0x82 /*TinyString with 2 chars*/,
|
||||
'6',
|
||||
'6',
|
||||
0x87 /*TinyString with 7 chars*/,
|
||||
'm',
|
||||
'e',
|
||||
's',
|
||||
's',
|
||||
'a',
|
||||
'g',
|
||||
'e',
|
||||
0xd0 /*String*/,
|
||||
0x2b /*With 43 chars*/,
|
||||
'R',
|
||||
'o',
|
||||
'u',
|
||||
't',
|
||||
'e',
|
||||
' ',
|
||||
'm',
|
||||
'e',
|
||||
's',
|
||||
's',
|
||||
'a',
|
||||
'g',
|
||||
'e',
|
||||
' ',
|
||||
'i',
|
||||
's',
|
||||
' ',
|
||||
'n',
|
||||
'o',
|
||||
't',
|
||||
' ',
|
||||
's',
|
||||
'u',
|
||||
'p',
|
||||
'p',
|
||||
'o',
|
||||
'r',
|
||||
't',
|
||||
'e',
|
||||
'd',
|
||||
' ',
|
||||
'i',
|
||||
'n',
|
||||
' ',
|
||||
'M',
|
||||
'e',
|
||||
'm',
|
||||
'g',
|
||||
'r',
|
||||
'a',
|
||||
'p',
|
||||
'h',
|
||||
'!',
|
||||
0x00 /*Terminating zeros*/,
|
||||
0x00,
|
||||
};
|
||||
EXPECT_EQ(input_stream.size(), 0U);
|
||||
CheckOutput(output, expected_resp, sizeof(expected_resp));
|
||||
EXPECT_EQ(session.state_, State::Error);
|
||||
|
||||
SCOPED_TRACE("Try to reset connection after ROUTE failed");
|
||||
ASSERT_NO_THROW(ExecuteCommand(input_stream, session, v4::reset_req, sizeof(v4::reset_req)));
|
||||
EXPECT_EQ(input_stream.size(), 0U);
|
||||
CheckOutput(output, success_resp, sizeof(success_resp));
|
||||
EXPECT_EQ(session.state_, State::Idle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(BoltSession, Rollback) {
|
||||
|
@ -10,6 +10,7 @@
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "nuraft/coordinator_cluster_state.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "replication_coordination_glue/role.hpp"
|
||||
|
||||
@ -21,11 +22,12 @@
|
||||
|
||||
#include "libnuraft/nuraft.hxx"
|
||||
|
||||
using memgraph::coordination::CoordinatorClientConfig;
|
||||
using memgraph::coordination::CoordinatorClusterState;
|
||||
using memgraph::coordination::CoordinatorStateMachine;
|
||||
using memgraph::coordination::InstanceState;
|
||||
using memgraph::coordination::CoordinatorToReplicaConfig;
|
||||
using memgraph::coordination::RaftLogAction;
|
||||
using memgraph::coordination::ReplicationInstanceState;
|
||||
using memgraph::io::network::Endpoint;
|
||||
using memgraph::replication_coordination_glue::ReplicationMode;
|
||||
using memgraph::replication_coordination_glue::ReplicationRole;
|
||||
using nuraft::buffer;
|
||||
@ -42,20 +44,22 @@ class CoordinatorClusterStateTest : public ::testing::Test {
|
||||
"MG_tests_unit_coordinator_cluster_state"};
|
||||
};
|
||||
|
||||
TEST_F(CoordinatorClusterStateTest, InstanceStateSerialization) {
|
||||
InstanceState instance_state{
|
||||
CoordinatorClientConfig{"instance3",
|
||||
"127.0.0.1",
|
||||
10112,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10001},
|
||||
.ssl = std::nullopt},
|
||||
TEST_F(CoordinatorClusterStateTest, ReplicationInstanceStateSerialization) {
|
||||
ReplicationInstanceState instance_state{
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance3",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10112},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7687},
|
||||
.replication_client_info = {.instance_name = "instance_name",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10001}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt},
|
||||
ReplicationRole::MAIN};
|
||||
|
||||
nlohmann::json j = instance_state;
|
||||
InstanceState deserialized_instance_state = j.get<InstanceState>();
|
||||
ReplicationInstanceState deserialized_instance_state = j.get<ReplicationInstanceState>();
|
||||
|
||||
EXPECT_EQ(instance_state.config, deserialized_instance_state.config);
|
||||
EXPECT_EQ(instance_state.status, deserialized_instance_state.status);
|
||||
@ -65,13 +69,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
auto coordinator_cluster_state = memgraph::coordination::CoordinatorClusterState{};
|
||||
|
||||
{
|
||||
CoordinatorClientConfig config{"instance1",
|
||||
"127.0.0.1",
|
||||
10111,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10001},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance1",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10111},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7687},
|
||||
.replication_client_info = {.instance_name = "instance1",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10001}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -80,13 +87,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.DoAction(payload, action);
|
||||
}
|
||||
{
|
||||
CoordinatorClientConfig config{"instance2",
|
||||
"127.0.0.1",
|
||||
10112,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10002},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance2",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10112},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7688},
|
||||
.replication_client_info = {.instance_name = "instance2",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10002}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -95,13 +105,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.DoAction(payload, action);
|
||||
}
|
||||
{
|
||||
CoordinatorClientConfig config{"instance3",
|
||||
"127.0.0.1",
|
||||
10113,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10003},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance3",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10113},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7689},
|
||||
.replication_client_info = {.instance_name = "instance3",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10003}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -110,13 +123,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.DoAction(payload, action);
|
||||
}
|
||||
{
|
||||
CoordinatorClientConfig config{"instance4",
|
||||
"127.0.0.1",
|
||||
10114,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10004},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance4",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10114},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7690},
|
||||
.replication_client_info = {.instance_name = "instance4",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10004}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -125,13 +141,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.DoAction(payload, action);
|
||||
}
|
||||
{
|
||||
CoordinatorClientConfig config{"instance5",
|
||||
"127.0.0.1",
|
||||
10115,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10005},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance5",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10115},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7691},
|
||||
.replication_client_info = {.instance_name = "instance5",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10005}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -140,13 +159,16 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.DoAction(payload, action);
|
||||
}
|
||||
{
|
||||
CoordinatorClientConfig config{"instance6",
|
||||
"127.0.0.1",
|
||||
10116,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10006},
|
||||
auto config =
|
||||
CoordinatorToReplicaConfig{.instance_name = "instance6",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10116},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7692},
|
||||
.replication_client_info = {.instance_name = "instance6",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10006}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
@ -159,5 +181,6 @@ TEST_F(CoordinatorClusterStateTest, DoActionRegisterInstances) {
|
||||
coordinator_cluster_state.Serialize(data);
|
||||
|
||||
auto deserialized_coordinator_cluster_state = CoordinatorClusterState::Deserialize(*data);
|
||||
ASSERT_EQ(coordinator_cluster_state.GetInstances(), deserialized_coordinator_cluster_state.GetInstances());
|
||||
ASSERT_EQ(coordinator_cluster_state.GetReplicationInstances(),
|
||||
deserialized_coordinator_cluster_state.GetReplicationInstances());
|
||||
}
|
||||
|
@ -9,7 +9,8 @@
|
||||
// by the Apache License, Version 2.0, included in the file
|
||||
// licenses/APL.txt.
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "nuraft/coordinator_state_machine.hpp"
|
||||
#include "nuraft/raft_log_action.hpp"
|
||||
#include "utils/file.hpp"
|
||||
@ -19,10 +20,11 @@
|
||||
#include <gtest/gtest.h>
|
||||
#include "json/json.hpp"
|
||||
|
||||
using memgraph::coordination::CoordinatorClientConfig;
|
||||
using memgraph::coordination::CoordinatorStateMachine;
|
||||
using memgraph::coordination::CoordinatorToReplicaConfig;
|
||||
using memgraph::coordination::RaftLogAction;
|
||||
using memgraph::coordination::ReplClientInfo;
|
||||
using memgraph::coordination::ReplicationClientInfo;
|
||||
using memgraph::io::network::Endpoint;
|
||||
using memgraph::replication_coordination_glue::ReplicationMode;
|
||||
using memgraph::utils::UUID;
|
||||
|
||||
@ -36,26 +38,29 @@ class RaftLogSerialization : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(RaftLogSerialization, ReplClientInfo) {
|
||||
ReplClientInfo info{"instance_name", ReplicationMode::SYNC, "127.0.0.1", 10111};
|
||||
ReplicationClientInfo info{.instance_name = "instance_name",
|
||||
.replication_mode = ReplicationMode::SYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10111}};
|
||||
|
||||
nlohmann::json j = info;
|
||||
ReplClientInfo info2 = j.get<memgraph::coordination::ReplClientInfo>();
|
||||
ReplicationClientInfo info2 = j.get<memgraph::coordination::ReplicationClientInfo>();
|
||||
|
||||
ASSERT_EQ(info, info2);
|
||||
}
|
||||
|
||||
TEST_F(RaftLogSerialization, CoordinatorClientConfig) {
|
||||
CoordinatorClientConfig config{"instance3",
|
||||
"127.0.0.1",
|
||||
10112,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10001},
|
||||
.ssl = std::nullopt};
|
||||
TEST_F(RaftLogSerialization, CoordinatorToReplicaConfig) {
|
||||
CoordinatorToReplicaConfig config{.instance_name = "instance3",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10112},
|
||||
.replication_client_info = {.instance_name = "instance_name",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10001}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
nlohmann::json j = config;
|
||||
CoordinatorClientConfig config2 = j.get<memgraph::coordination::CoordinatorClientConfig>();
|
||||
CoordinatorToReplicaConfig config2 = j.get<memgraph::coordination::CoordinatorToReplicaConfig>();
|
||||
|
||||
ASSERT_EQ(config, config2);
|
||||
}
|
||||
@ -106,19 +111,20 @@ TEST_F(RaftLogSerialization, RaftLogActionUpdateUUID) {
|
||||
}
|
||||
|
||||
TEST_F(RaftLogSerialization, RegisterInstance) {
|
||||
CoordinatorClientConfig config{"instance3",
|
||||
"127.0.0.1",
|
||||
10112,
|
||||
std::chrono::seconds{1},
|
||||
std::chrono::seconds{5},
|
||||
std::chrono::seconds{10},
|
||||
{"instance_name", ReplicationMode::ASYNC, "replication_ip_address", 10001},
|
||||
.ssl = std::nullopt};
|
||||
CoordinatorToReplicaConfig config{.instance_name = "instance3",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10112},
|
||||
.replication_client_info = {.instance_name = "instance_name",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10001}},
|
||||
.instance_health_check_frequency_sec = std::chrono::seconds{1},
|
||||
.instance_down_timeout_sec = std::chrono::seconds{5},
|
||||
.instance_get_uuid_frequency_sec = std::chrono::seconds{10},
|
||||
.ssl = std::nullopt};
|
||||
|
||||
auto buffer = CoordinatorStateMachine::SerializeRegisterInstance(config);
|
||||
auto [payload, action] = CoordinatorStateMachine::DecodeLog(*buffer);
|
||||
ASSERT_EQ(action, RaftLogAction::REGISTER_REPLICATION_INSTANCE);
|
||||
ASSERT_EQ(config, std::get<CoordinatorClientConfig>(payload));
|
||||
ASSERT_EQ(config, std::get<CoordinatorToReplicaConfig>(payload));
|
||||
}
|
||||
|
||||
TEST_F(RaftLogSerialization, UnregisterInstance) {
|
||||
|
176
tests/unit/routing_table.cpp
Normal file
176
tests/unit/routing_table.cpp
Normal file
@ -0,0 +1,176 @@
|
||||
// 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 "auth/auth.hpp"
|
||||
#include "coordination/coordinator_instance.hpp"
|
||||
#include "flags/run_time_configurable.hpp"
|
||||
#include "interpreter_faker.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "license/license.hpp"
|
||||
#include "replication_handler/replication_handler.hpp"
|
||||
#include "storage/v2/config.hpp"
|
||||
|
||||
#include "utils/file.hpp"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include "json/json.hpp"
|
||||
|
||||
using memgraph::coordination::CoordinatorInstance;
|
||||
using memgraph::coordination::CoordinatorToCoordinatorConfig;
|
||||
using memgraph::coordination::CoordinatorToReplicaConfig;
|
||||
using memgraph::coordination::RaftState;
|
||||
using memgraph::coordination::ReplicationClientInfo;
|
||||
using memgraph::io::network::Endpoint;
|
||||
using memgraph::replication::ReplicationHandler;
|
||||
using memgraph::replication_coordination_glue::ReplicationMode;
|
||||
using memgraph::storage::Config;
|
||||
|
||||
// class MockCoordinatorInstance : CoordinatorInstance {
|
||||
// auto AddCoordinatorInstance(CoordinatorToCoordinatorConfig const &config) -> void override {}
|
||||
// };
|
||||
|
||||
class RoutingTableTest : public ::testing::Test {
|
||||
protected:
|
||||
std::filesystem::path main_data_directory{std::filesystem::temp_directory_path() /
|
||||
"MG_tests_unit_coordinator_cluster_state"};
|
||||
std::filesystem::path repl1_data_directory{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_replication_repl"};
|
||||
std::filesystem::path repl2_data_directory{std::filesystem::temp_directory_path() /
|
||||
"MG_test_unit_storage_v2_replication_repl2"};
|
||||
void SetUp() override { Clear(); }
|
||||
|
||||
void TearDown() override { Clear(); }
|
||||
|
||||
Config main_conf = [&] {
|
||||
Config config{
|
||||
.durability =
|
||||
{
|
||||
.snapshot_wal_mode = Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
},
|
||||
.salient.items = {.properties_on_edges = true},
|
||||
};
|
||||
UpdatePaths(config, main_data_directory);
|
||||
return config;
|
||||
}();
|
||||
Config repl1_conf = [&] {
|
||||
Config config{
|
||||
.durability =
|
||||
{
|
||||
.snapshot_wal_mode = Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
},
|
||||
.salient.items = {.properties_on_edges = true},
|
||||
};
|
||||
UpdatePaths(config, repl1_data_directory);
|
||||
return config;
|
||||
}();
|
||||
Config repl2_conf = [&] {
|
||||
Config config{
|
||||
.durability =
|
||||
{
|
||||
.snapshot_wal_mode = Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
|
||||
},
|
||||
.salient.items = {.properties_on_edges = true},
|
||||
};
|
||||
UpdatePaths(config, repl2_data_directory);
|
||||
return config;
|
||||
}();
|
||||
|
||||
const std::string local_host = ("127.0.0.1");
|
||||
const std::array<uint16_t, 2> ports{10000, 20000};
|
||||
const std::array<std::string, 2> replicas = {"REPLICA1", "REPLICA2"};
|
||||
|
||||
private:
|
||||
void Clear() {
|
||||
if (std::filesystem::exists(main_data_directory)) std::filesystem::remove_all(main_data_directory);
|
||||
if (std::filesystem::exists(repl1_data_directory)) std::filesystem::remove_all(repl1_data_directory);
|
||||
if (std::filesystem::exists(repl2_data_directory)) std::filesystem::remove_all(repl2_data_directory);
|
||||
}
|
||||
};
|
||||
|
||||
struct MinMemgraph {
|
||||
MinMemgraph(const memgraph::storage::Config &conf)
|
||||
: auth{conf.durability.storage_directory / "auth", memgraph::auth::Auth::Config{/* default */}},
|
||||
repl_state{ReplicationStateRootPath(conf)},
|
||||
dbms{conf, repl_state
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
auth, true
|
||||
#endif
|
||||
},
|
||||
db_acc{dbms.Get()},
|
||||
db{*db_acc.get()},
|
||||
repl_handler(repl_state, dbms
|
||||
#ifdef MG_ENTERPRISE
|
||||
,
|
||||
system_, auth
|
||||
#endif
|
||||
) {
|
||||
}
|
||||
memgraph::auth::SynchedAuth auth;
|
||||
memgraph::system::System system_;
|
||||
memgraph::replication::ReplicationState repl_state;
|
||||
memgraph::dbms::DbmsHandler dbms;
|
||||
memgraph::dbms::DatabaseAccess db_acc;
|
||||
memgraph::dbms::Database &db;
|
||||
ReplicationHandler repl_handler;
|
||||
};
|
||||
;
|
||||
|
||||
TEST_F(RoutingTableTest, GetSingleRouterRoutingTable) {
|
||||
CoordinatorInstance instance1;
|
||||
auto routing = std::map<std::string, std::string>{{"address", "localhost:7688"}};
|
||||
auto routing_table = instance1.GetRoutingTable(routing);
|
||||
|
||||
ASSERT_EQ(routing_table.size(), 1);
|
||||
|
||||
auto const routers = routing_table[0];
|
||||
ASSERT_EQ(routers.first, std::vector<std::string>{"localhost:7688"});
|
||||
ASSERT_EQ(routers.second, "ROUTE");
|
||||
}
|
||||
|
||||
TEST_F(RoutingTableTest, GetMixedRoutingTable) {
|
||||
auto instance1 = RaftState::MakeRaftState([]() {}, []() {});
|
||||
auto routing = std::map<std::string, std::string>{{"address", "localhost:7690"}};
|
||||
instance1.AppendRegisterReplicationInstanceLog(CoordinatorToReplicaConfig{
|
||||
.instance_name = "instance2",
|
||||
.mgt_server = Endpoint{"127.0.0.1", 10011},
|
||||
.bolt_server = Endpoint{"127.0.0.1", 7687},
|
||||
.replication_client_info = ReplicationClientInfo{.instance_name = "instance2",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10001}}});
|
||||
instance1.GetAllCoordinators();
|
||||
// auto routing_table = instance1.GetRoutingTable(routing);
|
||||
|
||||
// ASSERT_EQ(routing_table.size(), 1);
|
||||
// auto const routers = routing_table[0];
|
||||
// ASSERT_EQ(routers.second, "ROUTE");
|
||||
}
|
||||
|
||||
// TEST_F(RoutingTableTest, GetMultipleRoutersRoutingTable) {
|
||||
//
|
||||
// CoordinatorInstance instance1;
|
||||
// instance1.AddCoordinatorInstance(CoordinatorToCoordinatorConfig{.coordinator_server_id = 1,
|
||||
// .bolt_server = Endpoint{"127.0.0.1", 7689},
|
||||
// .coordinator_server = Endpoint{"127.0.0.1",
|
||||
// 10111}});
|
||||
//
|
||||
// auto routing = std::map<std::string, std::string>{{"address", "localhost:7688"}};
|
||||
// auto routing_table = instance1.GetRoutingTable(routing);
|
||||
//
|
||||
// ASSERT_EQ(routing_table.size(), 1);
|
||||
//
|
||||
// auto const routers = routing_table[0];
|
||||
// ASSERT_EQ(routers.second, "ROUTE");
|
||||
// ASSERT_EQ(routers.first.size(), 2);
|
||||
// auto const expected_routers = std::vector<std::string>{"localhost:7689", "localhost:7688"};
|
||||
// ASSERT_EQ(routers.first, expected_routers);
|
||||
// }
|
@ -11,8 +11,9 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "coordination/coordinator_config.hpp"
|
||||
#include "coordination/coordinator_communication_config.hpp"
|
||||
#include "coordination/coordinator_slk.hpp"
|
||||
#include "io/network/endpoint.hpp"
|
||||
#include "replication/config.hpp"
|
||||
#include "replication_coordination_glue/mode.hpp"
|
||||
#include "slk_common.hpp"
|
||||
@ -20,6 +21,8 @@
|
||||
#include "storage/v2/replication/slk.hpp"
|
||||
#include "storage/v2/temporal.hpp"
|
||||
|
||||
using memgraph::io::network::Endpoint;
|
||||
|
||||
TEST(SlkAdvanced, PropertyValueList) {
|
||||
std::vector<memgraph::storage::PropertyValue> original{
|
||||
memgraph::storage::PropertyValue("hello world!"),
|
||||
@ -119,24 +122,19 @@ TEST(SlkAdvanced, PropertyValueComplex) {
|
||||
}
|
||||
|
||||
TEST(SlkAdvanced, ReplicationClientConfigs) {
|
||||
using ReplicationClientInfo = memgraph::coordination::CoordinatorClientConfig::ReplicationClientInfo;
|
||||
using ReplicationClientInfo = memgraph::coordination::ReplicationClientInfo;
|
||||
using ReplicationClientInfoVec = std::vector<ReplicationClientInfo>;
|
||||
using ReplicationMode = memgraph::replication_coordination_glue::ReplicationMode;
|
||||
|
||||
ReplicationClientInfoVec original{ReplicationClientInfo{.instance_name = "replica1",
|
||||
.replication_mode = ReplicationMode::SYNC,
|
||||
.replication_ip_address = "127.0.0.1",
|
||||
.replication_port = 10000},
|
||||
.replication_server = Endpoint{"127.0.0.1", 10000}},
|
||||
ReplicationClientInfo{.instance_name = "replica2",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_ip_address = "127.0.1.1",
|
||||
.replication_port = 10010},
|
||||
ReplicationClientInfo{
|
||||
.instance_name = "replica3",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_ip_address = "127.1.1.1",
|
||||
.replication_port = 1110,
|
||||
}};
|
||||
.replication_server = Endpoint{"127.0.0.1", 10010}},
|
||||
ReplicationClientInfo{.instance_name = "replica3",
|
||||
.replication_mode = ReplicationMode::ASYNC,
|
||||
.replication_server = Endpoint{"127.0.0.1", 10011}}};
|
||||
|
||||
memgraph::slk::Loopback loopback;
|
||||
auto builder = loopback.GetBuilder();
|
||||
|
Loading…
Reference in New Issue
Block a user