diff --git a/include/wsrep/provider_options.hpp b/include/wsrep/provider_options.hpp new file mode 100644 index 0000000..31877f5 --- /dev/null +++ b/include/wsrep/provider_options.hpp @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2021 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +/** @file provider_options.hpp + * + * A proxy interface to control provider configuration options. + */ + +#ifndef WSREP_PROVIDER_OPTIONS_HPP +#define WSREP_PROVIDER_OPTIONS_HPP + +#include "provider.hpp" + +#include +#include +#include +#include + +namespace wsrep +{ + /** + * Proxy class for managing provider options. + * Options are strings by default, or flagged with corresponding + * provider_options::flag as either bool, integer or double. + */ + class provider_options + { + public: + struct flag + { + static const int deprecated = (1 << 0); + static const int readonly = (1 << 1); + static const int type_bool = (1 << 2); + static const int type_integer = (1 << 3); + static const int type_double = (1 << 4); + }; + + static const int flag_type_mask + = flag::type_bool | flag::type_integer | flag::type_double; + + class option_value + { + public: + virtual ~option_value(){}; + virtual const char* as_string() const = 0; + virtual const void* get_ptr() const = 0; + }; + + class option_value_string : public option_value + { + public: + option_value_string(const std::string& value) + : value_(value) + { + } + ~option_value_string() {} + const char* as_string() const WSREP_OVERRIDE + { + return value_.c_str(); + } + const void* get_ptr() const WSREP_OVERRIDE + { + return value_.c_str(); + } + + private: + std::string value_; + }; + + class option_value_bool : public option_value + { + public: + option_value_bool(bool value) + : value_(value) + { + } + ~option_value_bool() {} + const char* as_string() const WSREP_OVERRIDE + { + if (value_) + { + return "yes"; + } + else + { + return "no"; + } + } + const void* get_ptr() const WSREP_OVERRIDE { return &value_; } + + private: + bool value_; + }; + + class option_value_int : public option_value + { + public: + option_value_int(int64_t value) + : value_(value) + , value_str_(std::to_string(value)) + { + } + ~option_value_int() {} + const char* as_string() const WSREP_OVERRIDE + { + return value_str_.c_str(); + } + const void* get_ptr() const WSREP_OVERRIDE { return &value_; } + + private: + int64_t value_; + std::string value_str_; + }; + + class option_value_double : public option_value + { + public: + option_value_double(double value) + : value_(value) + , value_str_(std::to_string(value)) + { + } + ~option_value_double() {} + const char* as_string() const WSREP_OVERRIDE + { + return value_str_.c_str(); + } + const void* get_ptr() const WSREP_OVERRIDE { return &value_; } + + private: + double value_; + std::string value_str_; + }; + + class option + { + public: + option(); + /** Construct option with given values. Allocates + * memory. */ + option(const std::string& name, std::unique_ptr value, + std::unique_ptr default_value, int flags); + /** Non copy-constructible. */ + option(const option&) = delete; + /** Non copy-assignable. */ + option& operator=(const option&) = delete; + option(option&&) = delete; + option& operator=(option&&) = delete; + ~option(); + + /** + * Get name of the option. + * + * @return Name of the option. + */ + const char* name() const { return name_.c_str(); } + + /** + * Get the real name of the option. This displays the + * option name as it is visible in provider options. + */ + const char* real_name() const { return real_name_.c_str(); } + + /** + * Get value of the option. + * + * @return Value of the option. + */ + option_value* value() const { return value_.get(); } + + /** + * Get default value of the option. + * + * @return Default value of the option. + */ + option_value* default_value() const { return default_value_.get(); } + + /** + * Get flags of the option + * + * @return Flag of the option + */ + int flags() const { return flags_; } + + /** + * Update the value of the option with new_value. The old + * value is freed. + */ + void update_value(std::unique_ptr new_value); + + private: + /** Sanitized name with dots replaced with underscores */ + std::string name_; + /** Real name in provider */ + std::string real_name_; + std::unique_ptr value_; + std::unique_ptr default_value_; + int flags_; + }; + + provider_options(wsrep::provider&); + provider_options(const provider_options&) = delete; + provider_options& operator=(const provider_options&) = delete; + + /** + * Set initial options. This should be used to initialize + * provider options after the provider has been loaded. + * Individual options should be accessed through set()/get(). + * + * @return Provider status code. + */ + enum wsrep::provider::status initial_options(); + + /** + * Get the option with the given name + * + * @param name Name of the option to retrieve + */ + const option* get_option(const std::string& name) const; + + /** + * Set a value for the option. + * + * @return Wsrep provider status code. + * @return wsrep::provider::error_size_exceeded if memory could + * not be allocated for the new value. + */ + enum wsrep::provider::status set(const std::string& name, + std::unique_ptr value); + + /** + * Create a new option with default value. + */ + enum wsrep::provider::status + set_default(const std::string& name, + std::unique_ptr value, + std::unique_ptr default_value, int flags); + + void for_each(const std::function& fn); + + private: + provider& provider_; + using options_map = std::map>; + options_map options_; + }; + + /** + * Equality operator for provider option. Returns true if + * the name part is equal. + */ + bool operator==(const wsrep::provider_options::option&, + const wsrep::provider_options::option&); +} // namespace wsrep + +#endif // WSREP_PROVIDER_OPTIONS_HPP diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d5049e1..7a3ee2b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -11,6 +11,7 @@ add_library(wsrep-lib key.cpp logger.cpp provider.cpp + provider_options.cpp seqno.cpp view.cpp server_state.cpp @@ -22,5 +23,6 @@ add_library(wsrep-lib uuid.cpp reporter.cpp allowlist_service_v1.cpp - wsrep_provider_v26.cpp) + wsrep_provider_v26.cpp + config_service_v1.cpp) target_link_libraries(wsrep-lib wsrep_api_v26 pthread ${WSREP_LIB_LIBDL}) diff --git a/src/config_service_v1.cpp b/src/config_service_v1.cpp new file mode 100644 index 0000000..6c4484d --- /dev/null +++ b/src/config_service_v1.cpp @@ -0,0 +1,174 @@ +/* + * Copyright (C) 2022 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +#include "config_service_v1.hpp" +#include "service_helpers.hpp" +#include "v26/wsrep_config_service.h" +#include "wsrep/logger.hpp" +#include "wsrep/provider_options.hpp" + +namespace wsrep_config_service_v1 +{ + wsrep_config_service_v1_t service{ 0 }; + + static int map_flags(int flags) + { + int option_flags = 0; + if (flags & WSREP_PARAM_DEPRECATED) + option_flags |= wsrep::provider_options::flag::deprecated; + if (flags & WSREP_PARAM_READONLY) + option_flags |= wsrep::provider_options::flag::readonly; + if (flags & WSREP_PARAM_TYPE_BOOL) + option_flags |= wsrep::provider_options::flag::type_bool; + if (flags & WSREP_PARAM_TYPE_INTEGER) + option_flags |= wsrep::provider_options::flag::type_integer; + if (flags & WSREP_PARAM_TYPE_DOUBLE) + option_flags |= wsrep::provider_options::flag::type_double; + return option_flags; + } + + static enum wsrep::provider::status + make_option(wsrep::provider_options* opt, const char* name, const char* val, + int flags) + { + std::unique_ptr value( + new wsrep::provider_options::option_value_string(val)); + std::unique_ptr default_value( + new wsrep::provider_options::option_value_string(val)); + return opt->set_default(name, std::move(value), + std::move(default_value), flags); + } + + static enum wsrep::provider::status + make_option(wsrep::provider_options* opt, const char* name, int64_t val, + int flags) + { + std::unique_ptr value( + new wsrep::provider_options::option_value_int(val)); + std::unique_ptr default_value( + new wsrep::provider_options::option_value_int(val)); + return opt->set_default(name, std::move(value), + std::move(default_value), flags); + } + + static enum wsrep::provider::status + make_option(wsrep::provider_options* opt, const char* name, bool val, + int flags) + { + std::unique_ptr value( + new wsrep::provider_options::option_value_bool(val)); + std::unique_ptr default_value( + new wsrep::provider_options::option_value_bool(val)); + return opt->set_default(name, std::move(value), + std::move(default_value), flags); + } + + static enum wsrep::provider::status + make_option(wsrep::provider_options* opt, const char* name, double val, + int flags) + { + std::unique_ptr value( + new wsrep::provider_options::option_value_double(val)); + std::unique_ptr default_value( + new wsrep::provider_options::option_value_double(val)); + return opt->set_default(name, std::move(value), + std::move(default_value), flags); + } + + wsrep_status_t service_callback(const wsrep_parameter* p, void* context) + { + const int flags = map_flags(p->flags); + enum wsrep::provider::status ret(wsrep::provider::error_unknown); + wsrep::provider_options* options = (wsrep::provider_options*)context; + switch (p->flags & WSREP_PARAM_TYPE_MASK) + { + case WSREP_PARAM_TYPE_BOOL: + ret = make_option(options, p->name, p->value.as_bool, flags); + break; + case WSREP_PARAM_TYPE_INTEGER: + ret = make_option(options, p->name, p->value.as_integer, flags); + break; + case WSREP_PARAM_TYPE_DOUBLE: + ret = make_option(options, p->name, p->value.as_double, flags); + break; + default: + assert((p->flags & WSREP_PARAM_TYPE_MASK) == 0); + ret = make_option(options, p->name, p->value.as_string, flags); + break; + } + + if (ret == wsrep::provider::success) + return WSREP_OK; + else + return WSREP_FATAL; + } +} // namespace wsrep_config_service_v1 + +static int config_service_v1_probe(void* dlh) +{ + typedef int (*init_fn)(wsrep_config_service_v1_t*); + typedef void (*deinit_fn)(); + return wsrep_impl::service_probe( + dlh, WSREP_CONFIG_SERVICE_INIT_FUNC_V1, "config service v1") + || wsrep_impl::service_probe( + dlh, WSREP_CONFIG_SERVICE_DEINIT_FUNC_V1, "config service v1"); +} + +static int config_service_v1_init(void* dlh) +{ + typedef int (*init_fn)(wsrep_config_service_v1_t*); + return wsrep_impl::service_init( + dlh, WSREP_CONFIG_SERVICE_INIT_FUNC_V1, + &wsrep_config_service_v1::service, "config service v1"); +} + +static void config_service_v1_deinit(void* dlh) +{ + typedef int (*deinit_fn)(); + wsrep_impl::service_deinit( + dlh, WSREP_CONFIG_SERVICE_DEINIT_FUNC_V1, "config service v1"); +} + +int wsrep::config_service_v1_fetch(wsrep::provider& provider, + wsrep::provider_options* options) +{ + struct wsrep_st* wsrep = (struct wsrep_st*)provider.native(); + if (config_service_v1_probe(wsrep->dlh)) + { + wsrep::log_warning() << "Provider does not support config service v1"; + return 1; + } + if (config_service_v1_init(wsrep->dlh)) + { + wsrep::log_warning() << "Failed to initialize config service v1"; + return 1; + } + + wsrep_status_t ret = wsrep_config_service_v1::service.get_parameters( + wsrep, &wsrep_config_service_v1::service_callback, options); + + config_service_v1_deinit(wsrep->dlh); + + if (ret != WSREP_OK) + { + return 1; + } + + return 0; +} diff --git a/src/config_service_v1.hpp b/src/config_service_v1.hpp new file mode 100644 index 0000000..49532cd --- /dev/null +++ b/src/config_service_v1.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2022 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +#ifndef WSREP_CONFIG_SERVICE_V1_HPP +#define WSREP_CONFIG_SERVICE_V1_HPP + +namespace wsrep +{ + class provider; + class provider_options; + int config_service_v1_fetch(provider& provider, provider_options* opts); +} // namespace wsrep + +#endif // WSREP_CONFIG_SERVICE_V1_HPP diff --git a/src/provider_options.cpp b/src/provider_options.cpp new file mode 100644 index 0000000..fcd7a95 --- /dev/null +++ b/src/provider_options.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2021 Codership Oy + * + * This file is part of wsrep-lib. + * + * Wsrep-lib is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * Wsrep-lib is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wsrep-lib. If not, see . + */ + +#include "wsrep/provider_options.hpp" +#include "config_service_v1.hpp" +#include "wsrep/logger.hpp" + +#include +#include +#include +#include + +/** + * Provider options string separators. + */ +struct provider_options_sep +{ + /** Parameter separator. */ + char param{ ';' }; + /** Key value separator. */ + char key_value{ '=' }; +}; + +// Replace dots in option name with underscores +static void sanitize_name(std::string& name) +{ + std::transform(name.begin(), name.end(), name.begin(), + [](std::string::value_type c) { + if (c == '.') + return '_'; + return c; + }); +} + +bool wsrep::operator==(const wsrep::provider_options::option& left, + const wsrep::provider_options::option& right) +{ + return (std::strcmp(left.name(), right.name()) == 0); +} + +// wsrep-API lacks better error code for not found, and this is +// what Galera returns when parameter is not recogized, so we +// got with it. +static enum wsrep::provider::status not_found_error{ + wsrep::provider::error_warning +}; + +wsrep::provider_options::option::option() + : name_{} + , real_name_{} + , value_{} + , default_value_{} + , flags_{ 0 } +{ +} + +wsrep::provider_options::option::option( + const std::string& name, + std::unique_ptr value, + std::unique_ptr default_value, + int flags) + : name_{ name } + , real_name_{ name } + , value_{ std::move(value) } + , default_value_{ std::move(default_value) } + , flags_{ flags } +{ + sanitize_name(name_); +} + +void wsrep::provider_options::option::update_value( + std::unique_ptr value) +{ + value_ = std::move(value); +} + +wsrep::provider_options::option::~option() {} + +wsrep::provider_options::provider_options(wsrep::provider& provider) + : provider_(provider) + , options_() +{ +} + +enum wsrep::provider::status wsrep::provider_options::initial_options() +{ + options_.clear(); + if (config_service_v1_fetch(provider_, this)) + { + return wsrep::provider::error_not_implemented; + } + else + { + return wsrep::provider::success; + } +} + +const wsrep::provider_options::option* +wsrep::provider_options::get_option(const std::string& name) const +{ + auto ret(options_.find(name)); + if (ret == options_.end()) + { + return nullptr; + } + return ret->second.get(); +} + +enum wsrep::provider::status wsrep::provider_options::set( + const std::string& name, + std::unique_ptr value) +{ + auto option(options_.find(name)); + if (option == options_.end()) + { + return not_found_error; + } + provider_options_sep sep; + auto ret(provider_.options(std::string(option->second->real_name()) + + sep.key_value + value->as_string() + + sep.param)); + if (ret == provider::success) + { + option->second->update_value(std::move(value)); + } + return ret; +} + +enum wsrep::provider::status wsrep::provider_options::set_default( + const std::string& name, + std::unique_ptr value, + std::unique_ptr default_value, + int flags) +{ + auto found(options_.find(name)); + auto opt(std::unique_ptr( + new option{ name, std::move(value), std::move(default_value), flags })); + if (found != options_.end()) + { + assert(0); + return wsrep::provider::error_not_allowed; + } + options_.emplace(std::string(opt->name()), std::move(opt)); + return wsrep::provider::success; +} + +void wsrep::provider_options::for_each(const std::function& fn) +{ + std::for_each( + options_.begin(), options_.end(), + [&fn](const options_map::value_type& opt) { fn(opt.second.get()); }); +} diff --git a/src/transaction.cpp b/src/transaction.cpp index 764b183..854263f 100644 --- a/src/transaction.cpp +++ b/src/transaction.cpp @@ -1628,7 +1628,9 @@ int wsrep::transaction::certify_fragment( // and the rollback process. storage_service.rollback(wsrep::ws_handle(), wsrep::ws_meta()); ret = 1; - error = wsrep::e_deadlock_error; + error = (cert_ret == wsrep::provider::error_size_exceeded ? + wsrep::e_size_exceeded_error : + wsrep::e_deadlock_error); break; } } diff --git a/wsrep-API/v26 b/wsrep-API/v26 index 02ed172..694d6ca 160000 --- a/wsrep-API/v26 +++ b/wsrep-API/v26 @@ -1 +1 @@ -Subproject commit 02ed17238cee97b5a3ea2495ca87b63570403f01 +Subproject commit 694d6ca47f5eec7873be99b7d6babccf633d1231