diff --git a/configure.py b/configure.py index ba35c14420..fc801b0f19 100755 --- a/configure.py +++ b/configure.py @@ -718,6 +718,7 @@ scylla_core = (['database.cc', 'db/data_listeners.cc', 'db/hints/manager.cc', 'db/hints/resource_manager.cc', + 'db/hints/host_filter.cc', 'db/config.cc', 'db/extensions.cc', 'db/heat_load_balance.cc', diff --git a/db/hints/host_filter.cc b/db/hints/host_filter.cc new file mode 100644 index 0000000000..7754bfe5a3 --- /dev/null +++ b/db/hints/host_filter.cc @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2020 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla 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 Scylla. If not, see . + */ + +#include +#include +#include "to_string.hh" +#include "host_filter.hh" + +namespace db { +namespace hints { + +host_filter::host_filter(host_filter::enabled_for_all_tag) + : _enabled_kind(host_filter::enabled_kind::enabled_for_all) { +} + +host_filter::host_filter(host_filter::disabled_for_all_tag) + : _enabled_kind(host_filter::enabled_kind::disabled_for_all) { +} + +host_filter::host_filter(std::unordered_set allowed_dcs) + : _enabled_kind(allowed_dcs.empty() ? enabled_kind::disabled_for_all : enabled_kind::enabled_selectively) + , _dcs(std::move(allowed_dcs)) { +} + +bool host_filter::can_hint_for(locator::snitch_ptr& snitch, gms::inet_address ep) const { + switch (_enabled_kind) { + case enabled_kind::enabled_for_all: + return true; + case enabled_kind::enabled_selectively: + return _dcs.count(snitch->get_datacenter(ep)); + case enabled_kind::disabled_for_all: + return false; + } + throw std::logic_error("Uncovered variant of enabled_kind"); +} + +host_filter host_filter::parse_from_config_string(sstring opt) { + if (boost::iequals(opt, "false") || opt == "0") { + return host_filter(disabled_for_all_tag()); + } else if (boost::iequals(opt, "true") || opt == "1") { + return host_filter(enabled_for_all_tag()); + } + + return parse_from_dc_list(std::move(opt)); +} + +host_filter host_filter::parse_from_dc_list(sstring opt) { + using namespace boost::algorithm; + + std::vector dcs; + split(dcs, opt, is_any_of(",")); + + std::for_each(dcs.begin(), dcs.end(), [] (sstring& dc) { + trim(dc); + if (dc.empty()) { + throw hints_configuration_parse_error("hinted_handoff_enabled: DC name may not be an empty string"); + } + }); + + return host_filter(std::unordered_set(dcs.begin(), dcs.end())); +} + +std::istream& operator>>(std::istream& is, host_filter& f) { + sstring tmp; + is >> tmp; + f = host_filter::parse_from_config_string(std::move(tmp)); + return is; +} + +sstring host_filter::to_configuration_string() const { + switch (_enabled_kind) { + case enabled_kind::enabled_for_all: + return "true"; + case enabled_kind::enabled_selectively: + return ::join(",", _dcs); + case enabled_kind::disabled_for_all: + return "false"; + } + throw std::logic_error("Uncovered variant of enabled_kind"); +} + + +std::string_view host_filter::enabled_kind_to_string(host_filter::enabled_kind ek) { + switch (ek) { + case host_filter::enabled_kind::enabled_for_all: + return "enabled_for_all"; + case host_filter::enabled_kind::enabled_selectively: + return "enabled_selectively"; + case host_filter::enabled_kind::disabled_for_all: + return "disabled_for_all"; + } + throw std::logic_error("Uncovered variant of enabled_kind"); +} + +std::ostream& operator<<(std::ostream& os, const host_filter& f) { + os << "host_filter{enabled_kind=" + << host_filter::enabled_kind_to_string(f._enabled_kind); + if (f._enabled_kind == host_filter::enabled_kind::enabled_selectively) { + os << ", dcs={" << ::join(",", f._dcs); + } + os << "}"; + return os; +} + +} +} + diff --git a/db/hints/host_filter.hh b/db/hints/host_filter.hh new file mode 100644 index 0000000000..2618d1d5a0 --- /dev/null +++ b/db/hints/host_filter.hh @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 ScyllaDB + */ + +/* + * This file is part of Scylla. + * + * Scylla is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Scylla 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 Scylla. If not, see . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include "gms/inet_address.hh" +#include "locator/snitch_base.hh" +#include "seastarx.hh" + +namespace db { +namespace hints { + +// host_filter tells hints_manager towards which endpoints it is allowed to generate hints. +class host_filter final { +private: + enum class enabled_kind { + enabled_for_all, + enabled_selectively, + disabled_for_all, + }; + + enabled_kind _enabled_kind; + std::unordered_set _dcs; + + static std::string_view enabled_kind_to_string(host_filter::enabled_kind ek); + +public: + struct enabled_for_all_tag {}; + struct disabled_for_all_tag {}; + + // Creates a filter that allows hints to all endpoints (default) + host_filter(enabled_for_all_tag tag = {}); + + // Creates a filter that does not allow any hints. + host_filter(disabled_for_all_tag); + + // Creates a filter that allows sending hints to specified DCs. + explicit host_filter(std::unordered_set allowed_dcs); + + // Parses hint filtering configuration from the hinted_handoff_enabled option. + static host_filter parse_from_config_string(sstring opt); + + // Parses hint filtering configuration from a list of DCs. + static host_filter parse_from_dc_list(sstring opt); + + bool can_hint_for(locator::snitch_ptr& snitch, gms::inet_address ep) const; + + inline const std::unordered_set& get_dcs() const { + return _dcs; + } + + bool operator==(const host_filter& other) const noexcept { + return _enabled_kind == other._enabled_kind + && _dcs == other._dcs; + } + + inline bool is_enabled_for_all() const noexcept { + return _enabled_kind == enabled_kind::enabled_for_all; + } + + inline bool is_disabled_for_all() const noexcept { + return _enabled_kind == enabled_kind::disabled_for_all; + } + + sstring to_configuration_string() const; + + friend std::ostream& operator<<(std::ostream& os, const host_filter& f); +}; + +std::istream& operator>>(std::istream& is, host_filter& f); + +class hints_configuration_parse_error : public std::runtime_error { +public: + using std::runtime_error::runtime_error; +}; + +} +}