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;
+};
+
+}
+}