Add more runtime configurable settings (#1183)

server name, query timeout settings, log.level, log.to_stderr
This commit is contained in:
andrejtonev 2023-09-11 17:30:54 +02:00 committed by GitHub
parent 060b9d1c16
commit 5e5f4ffc5d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 949 additions and 97 deletions

View File

@ -18,6 +18,7 @@ add_subdirectory(rpc)
add_subdirectory(license)
add_subdirectory(auth)
add_subdirectory(audit)
add_subdirectory(flags)
string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type)
@ -31,19 +32,13 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR})
# ----------------------------------------------------------------------------
set(mg_single_node_v2_sources
memgraph.cpp
flags/isolation_level.cpp
flags/memory_limit.cpp
flags/log_level.cpp
flags/general.cpp
flags/audit.cpp
flags/bolt.cpp
)
# memgraph main executable
add_executable(memgraph ${mg_single_node_v2_sources})
target_include_directories(memgraph PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(memgraph stdc++fs Threads::Threads
mg-telemetry mg-communication mg-memory mg-utils mg-license mg-settings mg-glue)
mg-telemetry mg-communication mg-memory mg-utils mg-license mg-settings mg-glue mg-flags)
# NOTE: `include/mg_procedure.syms` describes a pattern match for symbols which
# should be dynamically exported, so that `dlopen` can correctly link the

View File

@ -159,6 +159,7 @@ class Session : public dbms::SessionInterface {
break;
case State::Idle:
case State::Result:
at_least_one_run_ = true;
state_ = StateExecutingRun(*this, state_);
break;
case State::Error:
@ -180,6 +181,12 @@ class Session : public dbms::SessionInterface {
}
}
void HandleError() {
if (!at_least_one_run_) {
spdlog::info("Sudden connection loss. Make sure the client supports Memgraph.");
}
}
// TODO: Rethink if there is a way to hide some members. At the momement all of them are public.
TInputStream &input_stream_;
TOutputStream &output_stream_;
@ -192,6 +199,7 @@ class Session : public dbms::SessionInterface {
bool handshake_done_{false};
State state_{State::Handshake};
bool at_least_one_run_{false};
struct Version {
uint8_t major;

View File

@ -174,7 +174,7 @@ State SendSuccessMessage(TSession &session) {
// we send a hardcoded value for now.
std::map<std::string, Value> metadata{{"connection_id", "bolt-1"}};
if (auto server_name = session.GetServerNameForInit(); server_name) {
metadata.insert({"server", *server_name});
metadata.insert({"server", std::move(*server_name)});
}
bool success_sent = session.encoder_.MessageSuccess(metadata);
if (!success_sent) {

View File

@ -413,6 +413,8 @@ class Session final : public std::enable_shared_from_this<Session<TSession, TSes
void OnRead(const boost::system::error_code &ec, const size_t bytes_transferred) {
if (ec) {
// TODO Check if client disconnected
session_.HandleError();
return OnError(ec);
}
input_buffer_.write_end()->Written(bytes_transferred);

9
src/flags/CMakeLists.txt Normal file
View File

@ -0,0 +1,9 @@
add_library(mg-flags STATIC audit.cpp
bolt.cpp
general.cpp
isolation_level.cpp
log_level.cpp
memory_limit.cpp
run_time_configurable.cpp)
target_include_directories(mg-flags PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(mg-flags PUBLIC spdlog::spdlog mg-settings mg-utils)

View File

@ -16,3 +16,4 @@
#include "flags/isolation_level.hpp"
#include "flags/log_level.hpp"
#include "flags/memory_limit.hpp"
#include "flags/run_time_configurable.hpp"

View File

@ -36,3 +36,7 @@ DEFINE_VALIDATED_int32(bolt_session_inactivity_timeout, 1800,
DEFINE_string(bolt_cert_file, "", "Certificate file which should be used for the Bolt server.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(bolt_key_file, "", "Key file which should be used for the Bolt server.");
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(bolt_server_name_for_init, "",
"Server name which the database should send to the client in the "
"Bolt INIT message.");

View File

@ -25,3 +25,5 @@ DECLARE_int32(bolt_session_inactivity_timeout);
DECLARE_string(bolt_cert_file);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_string(bolt_key_file);
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_string(bolt_server_name_for_init);

View File

@ -11,9 +11,13 @@
#include "general.hpp"
#include "glue/auth_global.hpp"
#include "storage/v2/config.hpp"
#include "utils/file.hpp"
#include "utils/flag_validation.hpp"
#include "utils/string.hpp"
#include "glue/auth_handler.hpp"
#include <thread>
// Short help flag.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
@ -142,7 +146,7 @@ DEFINE_string(pulsar_service_url, "", "Default URL used while connecting to Puls
// Query flags.
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_double(query_execution_timeout_sec, 600,
DEFINE_double(query_execution_timeout_sec, -1,
"Maximum allowed query execution time. Queries exceeding this "
"limit will be aborted. Value of 0 means no limit.");

View File

@ -37,7 +37,10 @@ inline constexpr std::array log_level_mappings{
const std::string log_level_help_string = fmt::format("Minimum log level. Allowed values: {}",
memgraph::utils::GetAllowedEnumValuesString(log_level_mappings));
DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(), {
DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(),
{ return memgraph::flags::ValidLogLevel(value); });
bool memgraph::flags::ValidLogLevel(std::string_view value) {
if (const auto result = memgraph::utils::IsValidEnumValueString(value, log_level_mappings); result.HasError()) {
const auto error = result.GetError();
switch (error) {
@ -55,10 +58,14 @@ DEFINE_VALIDATED_string(log_level, "WARNING", log_level_help_string.c_str(), {
}
return true;
});
}
std::optional<spdlog::level::level_enum> memgraph::flags::LogLevelToEnum(std::string_view value) {
return memgraph::utils::StringToEnum<spdlog::level::level_enum>(value, log_level_mappings);
}
spdlog::level::level_enum ParseLogLevel() {
const auto log_level = memgraph::utils::StringToEnum<spdlog::level::level_enum>(FLAGS_log_level, log_level_mappings);
const auto log_level = memgraph::flags::LogLevelToEnum(FLAGS_log_level);
MG_ASSERT(log_level, "Invalid log level");
return *log_level;
}
@ -70,14 +77,19 @@ void CreateLoggerFromSink(const auto &sinks, const auto log_level) {
logger->set_level(log_level);
logger->flush_on(spdlog::level::trace);
spdlog::set_default_logger(std::move(logger));
// Enable stderr sink
if (FLAGS_also_log_to_stderr) {
memgraph::flags::LogToStderr(log_level);
}
}
void memgraph::flags::InitializeLogger() {
std::vector<spdlog::sink_ptr> sinks;
if (FLAGS_also_log_to_stderr) {
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
}
// Force the stderr logger to be at the front of the sinks vector
// Will be used to disable/enable it at run-time by settings its log level
sinks.emplace_back(std::make_shared<spdlog::sinks::stderr_color_sink_mt>());
sinks.back()->set_level(spdlog::level::off);
if (!FLAGS_log_file.empty()) {
// get local time
@ -93,9 +105,18 @@ void memgraph::flags::InitializeLogger() {
CreateLoggerFromSink(sinks, ParseLogLevel());
}
// TODO: Make sure this is used in a safe way
void memgraph::flags::AddLoggerSink(spdlog::sink_ptr new_sink) {
auto default_logger = spdlog::default_logger();
auto sinks = default_logger->sinks();
sinks.push_back(new_sink);
CreateLoggerFromSink(sinks, default_logger->level());
}
// Thread-safe because the level enum is an atomic
// NOTE: default_logger is not thread-safe and shouldn't be changed during application lifetime
void memgraph::flags::LogToStderr(spdlog::level::level_enum log_level) {
auto default_logger = spdlog::default_logger();
auto sink = default_logger->sinks().front();
sink->set_level(log_level);
}

View File

@ -11,8 +11,19 @@
#pragma once
#include <spdlog/sinks/sink.h>
#include <optional>
#include "gflags/gflags.h"
DECLARE_string(log_level);
DECLARE_bool(also_log_to_stderr);
namespace memgraph::flags {
bool ValidLogLevel(std::string_view value);
std::optional<spdlog::level::level_enum> LogLevelToEnum(std::string_view value);
void InitializeLogger();
void AddLoggerSink(spdlog::sink_ptr new_sink);
void LogToStderr(spdlog::level::level_enum log_level);
} // namespace memgraph::flags

View File

@ -0,0 +1,111 @@
// Copyright 2023 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 "flags/run_time_configurable.hpp"
#include <string>
#include "flags/bolt.hpp"
#include "flags/general.hpp"
#include "flags/log_level.hpp"
#include "spdlog/cfg/helpers-inl.h"
#include "spdlog/spdlog.h"
#include "utils/exceptions.hpp"
#include "utils/settings.hpp"
#include "utils/string.hpp"
namespace {
// Bolt server name
constexpr auto kServerNameSettingKey = "server.name";
constexpr auto kDefaultServerName = "Neo4j/v5.11.0 compatible graph database server - Memgraph";
// Query timeout
constexpr auto kQueryTxSettingKey = "query.timeout";
constexpr auto kDefaultQueryTx = "600"; // seconds
// Log level
// No default value because it is not persistent
constexpr auto kLogLevelSettingKey = "log.level";
// Log to stderr
// No default value because it is not persistent
constexpr auto kLogToStderrSettingKey = "log.to_stderr";
} // namespace
namespace memgraph::flags::run_time {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
memgraph::utils::Synchronized<std::string, memgraph::utils::SpinLock> bolt_server_name_;
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
std::atomic<double> execution_timeout_sec_;
void Initialize() {
// Register bolt server name settings
memgraph::utils::global_settings.RegisterSetting(kServerNameSettingKey, kDefaultServerName, [&] {
const auto server_name = memgraph::utils::global_settings.GetValue(kServerNameSettingKey);
MG_ASSERT(server_name, "Bolt server name is missing from the settings");
*(bolt_server_name_.Lock()) = *server_name;
});
// Update value from read settings
const auto &name = memgraph::utils::global_settings.GetValue(kServerNameSettingKey);
MG_ASSERT(name, "Failed to read server name from settings.");
*(bolt_server_name_.Lock()) = *name;
// Override server name if passed via command line argument
if (!FLAGS_bolt_server_name_for_init.empty()) {
memgraph::utils::global_settings.SetValue(kServerNameSettingKey, FLAGS_bolt_server_name_for_init);
}
// Register query timeout
memgraph::utils::global_settings.RegisterSetting(kQueryTxSettingKey, kDefaultQueryTx, [&] {
const auto query_tx = memgraph::utils::global_settings.GetValue(kQueryTxSettingKey);
MG_ASSERT(query_tx, "Query timeout is missing from the settings");
execution_timeout_sec_ = std::stod(*query_tx);
});
// Update value from read settings
const auto &tx = memgraph::utils::global_settings.GetValue(kQueryTxSettingKey);
MG_ASSERT(tx, "Failed to read query timeout from settings.");
execution_timeout_sec_ = std::stod(*tx);
// Override query timeout if passed via command line argument
if (FLAGS_query_execution_timeout_sec != -1) {
memgraph::utils::global_settings.SetValue(kQueryTxSettingKey, std::to_string(FLAGS_query_execution_timeout_sec));
}
// Register log level
auto get_global_log_level = []() {
const auto log_level = memgraph::utils::global_settings.GetValue(kLogLevelSettingKey);
MG_ASSERT(log_level, "Log level is missing from the settings");
const auto ll_enum = memgraph::flags::LogLevelToEnum(*log_level);
if (!ll_enum) {
throw utils::BasicException("Unsupported log level {}", *log_level);
}
return *ll_enum;
};
memgraph::utils::global_settings.RegisterSetting(
kLogLevelSettingKey, FLAGS_log_level, [&] { spdlog::set_level(get_global_log_level()); },
memgraph::flags::ValidLogLevel);
// Always override log level with command line argument
memgraph::utils::global_settings.SetValue(kLogLevelSettingKey, FLAGS_log_level);
// Register logging to stderr
auto bool_to_str = [](bool in) { return in ? "true" : "false"; };
const std::string log_to_stderr_s = bool_to_str(FLAGS_also_log_to_stderr);
memgraph::utils::global_settings.RegisterSetting(
kLogToStderrSettingKey, log_to_stderr_s,
[&] {
const auto enable = memgraph::utils::global_settings.GetValue(kLogToStderrSettingKey);
if (enable == "true") {
LogToStderr(get_global_log_level());
} else {
LogToStderr(spdlog::level::off);
}
},
[](std::string_view in) {
const auto lc = memgraph::utils::ToLowerCase(in);
return lc == "false" || lc == "true";
});
// Always override log to stderr with command line argument
memgraph::utils::global_settings.SetValue(kLogToStderrSettingKey, log_to_stderr_s);
}
} // namespace memgraph::flags::run_time

View File

@ -0,0 +1,26 @@
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
#include "utils/spin_lock.hpp"
#include "utils/synchronized.hpp"
namespace memgraph::flags::run_time {
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern utils::Synchronized<std::string, utils::SpinLock> bolt_server_name_;
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
extern std::atomic<double> execution_timeout_sec_;
void Initialize();
} // namespace memgraph::flags::run_time

View File

@ -10,12 +10,15 @@
// licenses/APL.txt.
#include "glue/SessionHL.hpp"
#include <optional>
#include "audit/log.hpp"
#include "flags/run_time_configurable.hpp"
#include "glue/auth_checker.hpp"
#include "glue/communication.hpp"
#include "license/license.hpp"
#include "query/discard_value_stream.hpp"
#include "utils/spin_lock.hpp"
#include "gflags/gflags.h"
@ -23,11 +26,6 @@ namespace memgraph::metrics {
extern const Event ActiveBoltSessions;
} // namespace memgraph::metrics
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DEFINE_string(bolt_server_name_for_init, "",
"Server name which the database should send to the client in the "
"Bolt INIT message.");
auto ToQueryExtras(const memgraph::communication::bolt::Value &extra) -> memgraph::query::QueryExtras {
auto const &as_map = extra.ValueMap();
@ -151,9 +149,10 @@ memgraph::dbms::SetForResult SessionHL::OnChange(const std::string &db_name) {
std::string SessionHL::GetDatabaseName() const { return interpreter_context_->db->id(); }
std::optional<std::string> SessionHL::GetServerNameForInit() {
if (FLAGS_bolt_server_name_for_init.empty()) return std::nullopt;
return FLAGS_bolt_server_name_for_init;
auto locked_name = flags::run_time::bolt_server_name_.Lock();
return locked_name->empty() ? std::nullopt : std::make_optional(*locked_name);
}
bool SessionHL::Authenticate(const std::string &username, const std::string &password) {
auto locked_auth = auth_->Lock();
if (!locked_auth->HasUsers()) {
@ -304,6 +303,7 @@ SessionHL::SessionHL(
#endif
endpoint_(endpoint),
run_id_(current_.run_id()) {
// Metrics update
memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveBoltSessions);
}

16
src/glue/auth_global.hpp Normal file
View File

@ -0,0 +1,16 @@
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
// License, and you may not use this file except in compliance with the Business Source License.
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#pragma once
namespace memgraph::glue {
inline constexpr std::string_view kDefaultUserRoleRegex = "[a-zA-Z0-9_.+-@]+";
} // namespace memgraph::glue

View File

@ -14,6 +14,7 @@
#include <regex>
#include "auth/auth.hpp"
#include "auth_global.hpp"
#include "glue/auth.hpp"
#include "license/license.hpp"
#include "query/interpreter.hpp"
@ -21,8 +22,6 @@
namespace memgraph::glue {
inline constexpr std::string_view kDefaultUserRoleRegex = "[a-zA-Z0-9_.+-@]+";
class AuthQueryHandler final : public memgraph::query::AuthQueryHandler {
memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
std::string name_regex_string_;

View File

@ -9,6 +9,7 @@
// by the Apache License, Version 2.0, included in the file
// licenses/APL.txt.
#include "flags/run_time_configurable.hpp"
#ifndef MG_ENTERPRISE
#include "dbms/session_context_handler.hpp"
#endif
@ -209,6 +210,7 @@ int main(int argc, char **argv) {
// register all runtime settings
memgraph::license::RegisterLicenseSettings(memgraph::license::global_license_checker,
memgraph::utils::global_settings);
memgraph::flags::run_time::Initialize();
memgraph::license::global_license_checker.CheckEnvLicense();
if (!FLAGS_organization_name.empty() && !FLAGS_license_key.empty()) {
@ -291,7 +293,6 @@ int main(int argc, char **argv) {
// Default interpreter configuration
memgraph::query::InterpreterConfig interp_config{
.query = {.allow_load_csv = FLAGS_allow_load_csv},
.execution_timeout_sec = FLAGS_query_execution_timeout_sec,
.replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
.default_kafka_bootstrap_servers = FLAGS_kafka_bootstrap_servers,
.default_pulsar_service_url = FLAGS_pulsar_service_url,

View File

@ -40,7 +40,18 @@ set(mg_query_sources
add_library(mg-query STATIC ${mg_query_sources})
target_include_directories(mg-query PUBLIC ${CMAKE_SOURCE_DIR}/include)
target_link_libraries(mg-query PUBLIC dl cppitertools Python3::Python mg-integrations-pulsar mg-integrations-kafka mg-storage-v2 mg-license mg-utils mg-kvstore mg-memory mg::csv)
target_link_libraries(mg-query PUBLIC dl
cppitertools
Python3::Python
mg-integrations-pulsar
mg-integrations-kafka
mg-storage-v2
mg-license
mg-utils
mg-kvstore
mg-memory
mg::csv
mg-flags)
if(NOT "${MG_PYTHON_PATH}" STREQUAL "")
set(Python3_ROOT_DIR "${MG_PYTHON_PATH}")
endif()

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 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
@ -19,8 +19,6 @@ struct InterpreterConfig {
bool allow_load_csv{true};
} query;
// The default execution timeout is 10 minutes.
double execution_timeout_sec{600.0};
// The same as \ref memgraph::storage::replication::ReplicationClientConfig
std::chrono::seconds replication_replica_check_frequency{1};

View File

@ -35,6 +35,7 @@
#include "csv/parsing.hpp"
#include "dbms/global.hpp"
#include "dbms/session_context_handler.hpp"
#include "flags/run_time_configurable.hpp"
#include "glue/communication.hpp"
#include "license/license.hpp"
#include "memory/memory_control.hpp"
@ -1377,7 +1378,7 @@ Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_
auto DetermineTxTimeout(std::optional<int64_t> tx_timeout_ms, InterpreterConfig const &config) -> TxTimeout {
using double_seconds = std::chrono::duration<double>;
auto const global_tx_timeout = double_seconds{config.execution_timeout_sec};
auto const global_tx_timeout = double_seconds{flags::run_time::execution_timeout_sec_};
auto const valid_global_tx_timeout = global_tx_timeout > double_seconds{0};
if (tx_timeout_ms) {
@ -3798,7 +3799,7 @@ void RunTriggersIndividually(const utils::SkipList<Trigger> &triggers, Interpret
auto trigger_context = original_trigger_context;
trigger_context.AdaptForAccessor(&db_accessor);
try {
trigger.Execute(&db_accessor, &execution_memory, interpreter_context->config.execution_timeout_sec,
trigger.Execute(&db_accessor, &execution_memory, flags::run_time::execution_timeout_sec_,
&interpreter_context->is_shutting_down, transaction_status, trigger_context,
interpreter_context->auth_checker);
} catch (const utils::BasicException &exception) {
@ -3905,7 +3906,7 @@ void Interpreter::Commit() {
utils::MonotonicBufferResource execution_memory{kExecutionMemoryBlockSize};
AdvanceCommand();
try {
trigger.Execute(&*execution_db_accessor_, &execution_memory, interpreter_context_->config.execution_timeout_sec,
trigger.Execute(&*execution_db_accessor_, &execution_memory, flags::run_time::execution_timeout_sec_,
&interpreter_context_->is_shutting_down, &transaction_status_, *trigger_context,
interpreter_context_->auth_checker);
} catch (const utils::BasicException &e) {

View File

@ -32,7 +32,6 @@ add_library(mg-storage-v2 STATIC
disk/label_property_index.cpp
disk/unique_constraints.cpp
storage_mode.cpp
isolation_level.cpp
replication/replication_client.cpp
replication/replication_server.cpp
replication/serialization.cpp

View File

@ -15,7 +15,6 @@
#include <cstdint>
#include <filesystem>
#include "storage/v2/isolation_level.hpp"
#include "storage/v2/transaction.hpp"
#include "utils/exceptions.hpp"
namespace memgraph::storage {

View File

@ -1,34 +0,0 @@
// Copyright 2023 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 "isolation_level.hpp"
namespace memgraph::storage {
std::string_view IsolationLevelToString(IsolationLevel isolation_level) {
switch (isolation_level) {
case IsolationLevel::READ_COMMITTED:
return "READ_COMMITTED";
case IsolationLevel::READ_UNCOMMITTED:
return "READ_UNCOMMITTED";
case IsolationLevel::SNAPSHOT_ISOLATION:
return "SNAPSHOT_ISOLATION";
}
}
std::string_view IsolationLevelToString(std::optional<IsolationLevel> isolation_level) {
if (isolation_level) {
return IsolationLevelToString(*isolation_level);
}
return "";
}
} // namespace memgraph::storage

View File

@ -19,7 +19,22 @@ namespace memgraph::storage {
enum class IsolationLevel : std::uint8_t { SNAPSHOT_ISOLATION, READ_COMMITTED, READ_UNCOMMITTED };
std::string_view IsolationLevelToString(IsolationLevel isolation_level);
std::string_view IsolationLevelToString(std::optional<IsolationLevel> isolation_level);
static inline std::string_view IsolationLevelToString(IsolationLevel isolation_level) {
switch (isolation_level) {
case IsolationLevel::READ_COMMITTED:
return "READ_COMMITTED";
case IsolationLevel::READ_UNCOMMITTED:
return "READ_UNCOMMITTED";
case IsolationLevel::SNAPSHOT_ISOLATION:
return "SNAPSHOT_ISOLATION";
}
}
static inline std::string_view IsolationLevelToString(std::optional<IsolationLevel> isolation_level) {
if (isolation_level) {
return IsolationLevelToString(*isolation_level);
}
return "";
}
} // namespace memgraph::storage

View File

@ -12,7 +12,9 @@
#pragma once
#include "kvstore/kvstore.hpp"
#include "storage/v2/delta.hpp"
#include "storage/v2/durability/storage_global_operation.hpp"
#include "storage/v2/transaction.hpp"
#include "utils/result.hpp"
/// REPLICATION ///

View File

@ -31,6 +31,7 @@
#include "storage/v2/replication/replication_server.hpp"
#include "storage/v2/storage_error.hpp"
#include "storage/v2/storage_mode.hpp"
#include "storage/v2/transaction.hpp"
#include "storage/v2/vertices_iterable.hpp"
#include "utils/event_counter.hpp"
#include "utils/event_histogram.hpp"

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -29,18 +29,25 @@ void Settings::Finalize() {
on_change_callbacks_.clear();
}
void Settings::RegisterSetting(std::string name, const std::string &default_value, OnChangeCallback callback) {
void Settings::RegisterSetting(std::string name, const std::string &default_value, OnChangeCallback callback,
Validation validation) {
std::lock_guard settings_guard{settings_lock_};
MG_ASSERT(storage_);
MG_ASSERT(validation(default_value), "\"{}\"'s default value does not satisfy the validation condition.", name);
if (const auto maybe_value = storage_->Get(name); maybe_value) {
SPDLOG_INFO("The setting with name {} already exists!", name);
} else {
MG_ASSERT(storage_->Put(name, default_value), "Failed to register a setting");
}
const auto [it, inserted] = on_change_callbacks_.emplace(std::move(name), callback);
MG_ASSERT(inserted, "Settings storage is out of sync");
{
const auto [_, inserted] = on_change_callbacks_.emplace(name, callback);
MG_ASSERT(inserted, "Settings storage is out of sync");
}
{
const auto [_, inserted] = validations_.emplace(std::move(name), validation);
MG_ASSERT(inserted, "Settings storage is out of sync");
}
}
std::optional<std::string> Settings::GetValue(const std::string &setting_name) const {
@ -59,6 +66,12 @@ bool Settings::SetValue(const std::string &setting_name, const std::string &new_
return std::nullopt;
}
const auto val = validations_.find(setting_name);
MG_ASSERT(val != validations_.end(), "Settings storage is out of sync");
if (!val->second(new_value)) {
throw utils::BasicException("'{}' not valid for '{}'", new_value, setting_name);
}
MG_ASSERT(storage_->Put(setting_name, new_value), "Failed to modify the setting");
const auto it = on_change_callbacks_.find(setting_name);

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 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
@ -22,12 +22,15 @@
namespace memgraph::utils {
struct Settings {
using OnChangeCallback = std::function<void()>;
using Validation = std::function<bool(std::string_view)>;
void Initialize(std::filesystem::path storage_path);
// RocksDB depends on statically allocated objects so we need to delete it before the static destruction kicks in
void Finalize();
void RegisterSetting(std::string name, const std::string &default_value, OnChangeCallback callback);
void RegisterSetting(
std::string name, const std::string &default_value, OnChangeCallback callback,
Validation validation = [](auto) { return true; });
std::optional<std::string> GetValue(const std::string &setting_name) const;
bool SetValue(const std::string &setting_name, const std::string &new_value);
std::vector<std::pair<std::string, std::string>> AllSettings() const;
@ -35,6 +38,7 @@ struct Settings {
private:
mutable utils::RWLock settings_lock_{RWLock::Priority::WRITE};
std::unordered_map<std::string, OnChangeCallback> on_change_callbacks_;
std::unordered_map<std::string, Validation> validations_;
std::optional<kvstore::KVStore> storage_;
};

View File

@ -25,3 +25,4 @@ python3 docs_how_to_query.py || exit 1
python3 max_query_length.py || exit 1
python3 transactions.py || exit 1
python3 path.py || exit 1
python3 server_name.py || exit 1

View File

@ -0,0 +1,52 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Copyright 2021 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.
from neo4j import GraphDatabase, basic_auth
from neo4j.exceptions import ClientError, TransientError
def get_server_name(tx):
res = tx.run("SHOW DATABASE SETTINGS").values()
for setting in res:
if setting[0] == "server.name":
return setting[1]
assert False, "No setting named server.name"
def set_server_name(tx, name):
tx.run("SET DATABASE SETTING 'server.name' TO '{}'".format(name)).consume()
# Connect, check name, set a new name and recheck
with GraphDatabase.driver("bolt://localhost:7687", auth=None, encrypted=False) as driver:
with driver.session() as session:
default_name = get_server_name(session)
assert driver.get_server_info().agent == default_name, "Wrong server name! Expected {} and got {}".format(
default_name, driver.get_server_info().agent
)
with driver.session() as session:
set_server_name(session, "Neo4j/1.1 compatible database")
with GraphDatabase.driver("bolt://localhost:7687", auth=None, encrypted=False) as driver:
assert (
driver.get_server_info().agent == "Neo4j/1.1 compatible database"
), 'Wrong server name! Expected "Neo4j/1.1 compatible database" and got {}'.format(driver.get_server_info().agent)
with driver.session() as session:
set_server_name(session, default_name)
print("All ok!")

View File

@ -12,6 +12,8 @@
# by the Apache License, Version 2.0, included in the file
# licenses/APL.txt.
import time
from neo4j import GraphDatabase, basic_auth
from neo4j.exceptions import ClientError, TransientError
@ -35,6 +37,44 @@ def tx_too_long(tx):
tx.run("MATCH (a), (b), (c), (d), (e), (f) RETURN COUNT(*) AS cnt")
def assert_timeout(set_timeout, measure_timeout):
print(measure_timeout)
print(set_timeout)
assert (
measure_timeout >= set_timeout and measure_timeout < set_timeout * 1.2
), "Wrong timeout; expected {}s and measured {}s".format(set_timeout, measure_timeout)
def get_timeout(tx):
res = tx.run("SHOW DATABASE SETTINGS").values()
for setting in res:
if setting[0] == "query.timeout":
return float(setting[1])
assert False, "No setting named query.timeout"
def set_timeout(tx, timeout):
tx.run("SET DATABASE SETTING 'query.timeout' TO '{}'".format(timeout)).consume()
def test_timeout(driver, set_timeout):
# Query that will run for a very long time, transient error expected.
timed_out = False
try:
with driver.session() as session:
start_time = time.time()
session.run("MATCH (a), (b), (c), (d), (e), (f) RETURN COUNT(*) AS cnt").consume()
except TransientError:
end_time = time.time()
assert_timeout(set_timeout, end_time - start_time)
timed_out = True
if timed_out:
print("The query timed out as was expected.")
else:
raise Exception("The query should have timed out, but it didn't!")
with GraphDatabase.driver("bolt://localhost:7687", auth=None, encrypted=False) as driver:
def add_person(f, name, name2):
@ -53,17 +93,16 @@ with GraphDatabase.driver("bolt://localhost:7687", auth=None, encrypted=False) a
with driver.session() as session:
session.run("UNWIND range(1, 100000) AS x CREATE ()").consume()
# Query that will run for a very long time, transient error expected.
timed_out = False
try:
with driver.session() as session:
session.run("MATCH (a), (b), (c), (d), (e), (f) RETURN COUNT(*) AS cnt").consume()
except TransientError:
timed_out = True
# Test changing the timeout at run-time
with driver.session() as session:
default_timeout = get_timeout(session)
test_timeout(driver, default_timeout)
if timed_out:
print("The query timed out as was expected.")
else:
raise Exception("The query should have timed out, but it didn't!")
with driver.session() as session:
set_timeout(session, 1)
test_timeout(driver, 1)
with driver.session() as session:
set_timeout(session, default_timeout)
print("All ok!")

View File

@ -1,5 +1,9 @@
#!/bin/bash
# Old v1 tests
run_v1.sh
# New tests
pushd () { command pushd "$@" > /dev/null; }
popd () { command popd "$@" > /dev/null; }
@ -30,7 +34,6 @@ $binary_dir/memgraph \
--query-execution-timeout-sec=5 \
--bolt-session-inactivity-timeout=10 \
--bolt-cert-file="" \
--bolt-server-name-for-init="Neo4j/1.1" \
--log-file=$tmpdir/logs/memgarph.log \
--also-log-to-stderr \
--log-level ERROR &
@ -45,7 +48,8 @@ for i in *; do
echo "Running: $i"
# run all versions
for v in *; do
if [ ! -d $v ]; then continue; fi
#skip v1 (needs different server name)
if [[ "$v" == "v1" || ! -d "$v" ]]; then continue; fi
pushd $v
echo "Running version: $v"
./run.sh

84
tests/drivers/run_v1.sh Executable file
View File

@ -0,0 +1,84 @@
#!/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 memgraph.
$binary_dir/memgraph \
--data-directory=$tmpdir \
--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/memgarph.log \
--also-log-to-stderr \
--log-level ERROR &
pid=$!
wait_for_server 7687
# Run all available tests
code_test=0
for i in *; do
if [ ! -d $i ]; then continue; fi
pushd $i
echo "Running: $i"
# run all versions
for v in *; do
# Run only v1
if [[ "$v" != "v1" || ! -d "$v" ]]; then continue; fi
pushd $v
echo "Running version: $v"
./run.sh
code_test=$?
if [ $code_test -ne 0 ]; then
echo "FAILED: $i"
break
fi
popd
done;
echo
popd
done
# Stop memgraph.
kill $pid
wait $pid
code_mg=$?
# Temporary directory cleanup.
if [ -d $tmpdir ]; then
rm -rf $tmpdir
fi
# Check memgraph exit code.
if [ $code_mg -ne 0 ]; then
echo "The memgraph process didn't terminate properly!"
exit $code_mg
fi
# Check test exit code.
if [ $code_test -ne 0 ]; then
echo "One of the tests failed!"
exit $code_test
fi

View File

@ -117,8 +117,8 @@ startup_config_dict = {
"password_encryption_algorithm": ("bcrypt", "bcrypt", "The password encryption algorithm used for authentication."),
"pulsar_service_url": ("", "", "Default URL used while connecting to Pulsar brokers."),
"query_execution_timeout_sec": (
"600",
"600",
"-1",
"-1",
"Maximum allowed query execution time. Queries exceeding this limit will be aborted. Value of 0 means no limit.",
),
"query_modules_directory": (

View File

@ -31,5 +31,8 @@ add_subdirectory(env_variable_check)
#flag check binaries
add_subdirectory(flag_check)
#flag check binaries
#storage mode binaries
add_subdirectory(storage_mode)
#run time settings binaries
add_subdirectory(run_time_settings)

View File

@ -0,0 +1,16 @@
set(target_name memgraph__integration__executor)
set(tester_target_name ${target_name}__tester)
set(flag_tester_target_name ${target_name}__flag_tester)
set(executor_target_name ${target_name}__executor)
add_executable(${tester_target_name} tester.cpp)
set_target_properties(${tester_target_name} PROPERTIES OUTPUT_NAME tester)
target_link_libraries(${tester_target_name} mg-communication)
add_executable(${flag_tester_target_name} flag_tester.cpp)
set_target_properties(${flag_tester_target_name} PROPERTIES OUTPUT_NAME flag_tester)
target_link_libraries(${flag_tester_target_name} mg-communication)
add_executable(${executor_target_name} executor.cpp)
set_target_properties(${executor_target_name} PROPERTIES OUTPUT_NAME executor)
target_link_libraries(${executor_target_name} mg-communication)

View File

@ -0,0 +1,53 @@
// Copyright 2023 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 <gflags/gflags.h>
#include <cstdlib>
#include "communication/bolt/client.hpp"
#include "io/network/endpoint.hpp"
#include "io/network/utils.hpp"
#include "utils/logging.hpp"
DEFINE_string(address, "127.0.0.1", "Server address");
DEFINE_int32(port, 7687, "Server port");
DEFINE_string(username, "admin", "Username for the database");
DEFINE_string(password, "admin", "Password for the database");
DEFINE_bool(use_ssl, false, "Set to true to connect with SSL to the server.");
/**
* Verifies that user 'user' has privileges that are given as positional
* arguments.
*/
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
memgraph::communication::SSLInit sslInit;
memgraph::io::network::Endpoint endpoint(memgraph::io::network::ResolveHostname(FLAGS_address), FLAGS_port);
memgraph::communication::ClientContext context(FLAGS_use_ssl);
memgraph::communication::bolt::Client client(context);
client.Connect(endpoint, FLAGS_username, FLAGS_password);
try {
std::string query(argv[1]);
auto ret = client.Execute(query, {});
} catch (const memgraph::communication::bolt::ClientQueryException &e) {
LOG_FATAL(
"The query shouldn't have failed but it failed with an "
"error message '{}', {}",
e.what(), argv[0]);
}
return 0;
}

View File

@ -0,0 +1,69 @@
// Copyright 2023 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 <gflags/gflags.h>
#include "communication/bolt/client.hpp"
#include "communication/bolt/v1/value.hpp"
#include "io/network/endpoint.hpp"
#include "io/network/utils.hpp"
#include "utils/logging.hpp"
DEFINE_string(address, "127.0.0.1", "Server address");
DEFINE_int32(port, 7687, "Server port");
DEFINE_string(field, "", "Expected settings field to check");
DEFINE_string(value, "", "Expected string result from field");
/**
* Executes queries passed as positional arguments and verifies whether they
* succeeded, failed, failed with a specific error message or executed without a
* specific error occurring.
*/
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
memgraph::communication::SSLInit sslInit;
memgraph::io::network::Endpoint endpoint(memgraph::io::network::ResolveHostname(FLAGS_address), FLAGS_port);
memgraph::communication::ClientContext context(false);
memgraph::communication::bolt::Client client(context);
try {
client.Connect(endpoint, "", "");
} catch (const memgraph::utils::BasicException &e) {
LOG_FATAL("");
}
const auto &res = client.Execute("SHOW DATABASE SETTINGS", {});
MG_ASSERT(res.fields[0] == "setting_name", "Expected \"setting_name\" field in the query result.");
MG_ASSERT(res.fields[1] == "setting_value", "Expected \"setting_value\" field in the query result.");
unsigned i = 0;
for (const auto &record : res.records) {
const auto &settings_name = record[0].ValueString();
if (settings_name == FLAGS_field) {
const auto &settings_value = record[1].ValueString();
// First try to encode the flags as float; if that fails just compare the raw strings
try {
MG_ASSERT(std::stof(settings_value) == std::stof(FLAGS_value),
"Failed when checking \"{}\"; expected \"{}\", found \"{}\"!", FLAGS_field, FLAGS_value,
settings_value);
} catch (const std::invalid_argument &) {
MG_ASSERT(settings_value == FLAGS_value, "Failed when checking \"{}\"; expected \"{}\", found \"{}\"!",
FLAGS_field, FLAGS_value, settings_value);
}
return 0;
}
}
LOG_FATAL("No setting named \"{}\" found!", FLAGS_field);
}

View File

@ -0,0 +1,202 @@
#!/usr/bin/python3 -u
# Copyright 2022 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.
import argparse
import atexit
import fcntl
import os
import subprocess
import sys
import tempfile
import time
from typing import List
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
PROJECT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", ".."))
def wait_for_server(port: int, delay: float = 0.1) -> float:
cmd = ["nc", "-z", "-w", "1", "127.0.0.1", str(port)]
while subprocess.call(cmd) != 0:
time.sleep(0.01)
time.sleep(delay)
def execute_tester(
binary,
queries,
should_fail=False,
failure_message="",
username="",
password="",
check_failure=True,
connection_should_fail=False,
):
args = [binary, "--username", username, "--password", password]
if should_fail:
args.append("--should-fail")
if failure_message:
args.extend(["--failure-message", failure_message])
if check_failure:
args.append("--check-failure")
if connection_should_fail:
args.append("--connection-should-fail")
args.extend(queries)
subprocess.run(args).check_returncode()
def execute_query(binary: str, queries: List[str], username: str = "", password: str = "") -> None:
args = [binary, "--username", username, "--password", password]
args.extend(queries)
subprocess.run(args).check_returncode()
def make_non_blocking(fd):
flags = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, flags | os.O_NONBLOCK)
def start_memgraph(memgraph_args: List[any]) -> subprocess:
memgraph = subprocess.Popen(
list(map(str, memgraph_args)), stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
)
time.sleep(0.1)
assert memgraph.poll() is None, "Memgraph process died prematurely!"
wait_for_server(7687)
# Make the stdout and stderr pipes non-blocking
make_non_blocking(memgraph.stdout.fileno())
make_non_blocking(memgraph.stderr.fileno())
return memgraph
def check_flag(tester_binary: str, flag: str, value: str) -> None:
args = [tester_binary, "--field", flag, "--value", value]
subprocess.run(args).check_returncode()
def cleanup(memgraph: subprocess):
if memgraph.poll() is None:
memgraph.terminate()
assert memgraph.wait() == 0, "Memgraph process didn't exit cleanly!"
def run_test(tester_binary: str, memgraph_args: List[str], server_name: str, query_tx: str):
memgraph = start_memgraph(memgraph_args)
atexit.register(cleanup, memgraph)
check_flag(tester_binary, "server.name", server_name)
check_flag(tester_binary, "query.timeout", query_tx)
cleanup(memgraph)
atexit.unregister(cleanup)
def run_test_w_query(tester_binary: str, memgraph_args: List[str], executor_binary: str):
memgraph = start_memgraph(memgraph_args)
atexit.register(cleanup, memgraph)
execute_query(executor_binary, ["SET DATABASE SETTING 'server.name' TO 'New Name';"])
execute_query(executor_binary, ["SET DATABASE SETTING 'query.timeout' TO '123';"])
check_flag(tester_binary, "server.name", "New Name")
check_flag(tester_binary, "query.timeout", "123")
cleanup(memgraph)
atexit.unregister(cleanup)
def consume(stream):
res = []
while True:
line = stream.readline()
if not line:
break
res.append(line.strip())
return res
def run_log_test(tester_binary: str, memgraph_args: List[str], executor_binary: str):
# Test if command line parameters work
memgraph = start_memgraph(memgraph_args + ["--log-level", "TRACE", "--also-log-to-stderr"])
atexit.register(cleanup, memgraph)
std_err = consume(memgraph.stderr)
assert len(std_err) > 5, "Failed to log to stderr"
# Test if run-time setting log.to_stderr works
execute_query(executor_binary, ["SET DATABASE SETTING 'log.to_stderr' TO 'false';"])
consume(memgraph.stderr)
execute_query(executor_binary, ["SET DATABASE SETTING 'query.timeout' TO '123';"])
std_err = consume(memgraph.stderr)
assert len(std_err) == 0, "Still writing to stderr even after disabling it"
# Test if run-time setting log.level works
execute_query(executor_binary, ["SET DATABASE SETTING 'log.to_stderr' TO 'true';"])
execute_query(executor_binary, ["SET DATABASE SETTING 'log.level' TO 'CRITICAL';"])
consume(memgraph.stderr)
execute_query(executor_binary, ["SET DATABASE SETTING 'query.timeout' TO '123';"])
std_err = consume(memgraph.stderr)
assert len(std_err) == 0, "Log level not updated"
# Tets that unsupported values cause an exception
execute_tester(
tester_binary,
["SET DATABASE SETTING 'log.to_stderr' TO 'something'"],
should_fail=True,
failure_message="'something' not valid for 'log.to_stderr'",
)
execute_tester(
tester_binary,
["SET DATABASE SETTING 'log.level' TO 'something'"],
should_fail=True,
failure_message="'something' not valid for 'log.level'",
)
cleanup(memgraph)
atexit.unregister(cleanup)
def execute_test(memgraph_binary: str, tester_binary: str, flag_tester_binary: str, executor_binary: str) -> None:
storage_directory = tempfile.TemporaryDirectory()
memgraph_args = [memgraph_binary, "--data-directory", storage_directory.name]
print("\033[1;36m~~ Starting run-time settings check test ~~\033[0m")
print("\033[1;34m~~ server.name and query.timeout ~~\033[0m")
# Check default flags
run_test(flag_tester_binary, memgraph_args, "Neo4j/v5.11.0 compatible graph database server - Memgraph", "600")
# Check changing flags via command-line arguments
run_test(
flag_tester_binary,
memgraph_args + ["--bolt-server-name-for-init", "Memgraph", "--query-execution-timeout-sec", "1000"],
"Memgraph",
"1000",
)
# Check changing flags via query
run_test_w_query(flag_tester_binary, memgraph_args, executor_binary)
print("\033[1;34m~~ log.level and log.to_stderr ~~\033[0m")
# Check log settings
run_log_test(tester_binary, memgraph_args, executor_binary)
print("\033[1;36m~~ Finished run-time settings check test ~~\033[0m")
if __name__ == "__main__":
memgraph_binary = os.path.join(PROJECT_DIR, "build", "memgraph")
tester_binary = os.path.join(PROJECT_DIR, "build", "tests", "integration", "run_time_settings", "tester")
flag_tester_binary = os.path.join(PROJECT_DIR, "build", "tests", "integration", "run_time_settings", "flag_tester")
executor_binary = os.path.join(PROJECT_DIR, "build", "tests", "integration", "run_time_settings", "executor")
parser = argparse.ArgumentParser()
parser.add_argument("--memgraph", default=memgraph_binary)
parser.add_argument("--tester", default=tester_binary)
parser.add_argument("--flag_tester", default=flag_tester_binary)
parser.add_argument("--executor", default=executor_binary)
args = parser.parse_args()
execute_test(args.memgraph, args.tester, args.flag_tester, args.executor)
sys.exit(0)

View File

@ -0,0 +1,106 @@
// Copyright 2023 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 <regex>
#include <gflags/gflags.h>
#include "communication/bolt/client.hpp"
#include "io/network/endpoint.hpp"
#include "io/network/utils.hpp"
DEFINE_string(address, "127.0.0.1", "Server address");
DEFINE_int32(port, 7687, "Server port");
DEFINE_string(username, "", "Username for the database");
DEFINE_string(password, "", "Password for the database");
DEFINE_bool(use_ssl, false, "Set to true to connect with SSL to the server.");
DEFINE_bool(check_failure, false, "Set to true to enable failure checking.");
DEFINE_bool(should_fail, false, "Set to true to expect a failure.");
DEFINE_bool(connection_should_fail, false, "Set to true to expect a connection failure.");
DEFINE_string(failure_message, "", "Set to the expected failure message.");
/**
* Executes queries passed as positional arguments and verifies whether they
* succeeded, failed, failed with a specific error message or executed without a
* specific error occurring.
*/
int main(int argc, char **argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
memgraph::communication::SSLInit sslInit;
memgraph::io::network::Endpoint endpoint(memgraph::io::network::ResolveHostname(FLAGS_address), FLAGS_port);
memgraph::communication::ClientContext context(FLAGS_use_ssl);
memgraph::communication::bolt::Client client(context);
std::regex re(FLAGS_failure_message);
try {
client.Connect(endpoint, FLAGS_username, FLAGS_password);
} catch (const memgraph::communication::bolt::ClientFatalException &e) {
if (FLAGS_connection_should_fail) {
if (!FLAGS_failure_message.empty() && !std::regex_match(e.what(), re)) {
LOG_FATAL(
"The connection should have failed with an error message of '{}'' but "
"instead it failed with '{}'",
FLAGS_failure_message, e.what());
}
return 0;
} else {
LOG_FATAL(
"The connection shoudn't have failed but it failed with an "
"error message '{}'",
e.what());
}
}
for (int i = 1; i < argc; ++i) {
std::string query(argv[i]);
try {
client.Execute(query, {});
} catch (const memgraph::communication::bolt::ClientQueryException &e) {
if (!FLAGS_check_failure) {
if (!FLAGS_failure_message.empty() && std::regex_match(e.what(), re)) {
LOG_FATAL(
"The query should have succeeded or failed with an error "
"message that isn't equal to '{}' but it failed with that error "
"message",
FLAGS_failure_message);
}
continue;
}
if (FLAGS_should_fail) {
if (!FLAGS_failure_message.empty() && !std::regex_match(e.what(), re)) {
LOG_FATAL(
"The query should have failed with an error message of '{}'' but "
"instead it failed with '{}'",
FLAGS_failure_message, e.what());
}
return 0;
} else {
LOG_FATAL(
"The query shoudn't have failed but it failed with an "
"error message '{}'",
e.what());
}
}
if (!FLAGS_check_failure) continue;
if (FLAGS_should_fail) {
LOG_FATAL(
"The query should have failed but instead it executed "
"successfully!");
}
}
return 0;
}

View File

@ -1,4 +1,4 @@
// Copyright 2022 Memgraph Ltd.
// Copyright 2023 Memgraph Ltd.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@ -15,6 +15,7 @@
#include "auth/auth.hpp"
#include "auth/models.hpp"
#include "glue/auth_global.hpp"
#include "glue/auth_handler.hpp"
#include "query/typed_value.hpp"
#include "utils/file.hpp"

View File

@ -17,6 +17,7 @@
#include "communication/result_stream_faker.hpp"
#include "csv/parsing.hpp"
#include "disk_test_utils.hpp"
#include "flags/run_time_configurable.hpp"
#include "glue/communication.hpp"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
@ -56,8 +57,10 @@ class InterpreterTest : public ::testing::Test {
InterpreterTest()
: data_directory(std::filesystem::temp_directory_path() / "MG_tests_unit_interpreter"),
interpreter_context(std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)),
{.execution_timeout_sec = 600}, data_directory) {}
interpreter_context(std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)), {},
data_directory) {
memgraph::flags::run_time::execution_timeout_sec_ = 600.0;
}
std::filesystem::path data_directory;
memgraph::query::InterpreterContext interpreter_context;