The functions are called from RESful API so has to return ips for backwards compatibility, but internally we can use host ids as long as possible and convert to ips just before returning. This also drops usage of ip based erm function which we want to get rid of.
152 lines
6.2 KiB
C++
152 lines
6.2 KiB
C++
/*
|
|
* Copyright (C) 2022-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
#include "locator/util.hh"
|
|
#include "replica/database.hh"
|
|
#include "gms/gossiper.hh"
|
|
#include <seastar/coroutine/maybe_yield.hh>
|
|
|
|
namespace locator {
|
|
|
|
static future<std::unordered_map<dht::token_range, host_id_vector_replica_set>>
|
|
construct_range_to_endpoint_map(
|
|
locator::effective_replication_map_ptr erm,
|
|
const dht::token_range_vector& ranges) {
|
|
std::unordered_map<dht::token_range, host_id_vector_replica_set> res;
|
|
res.reserve(ranges.size());
|
|
for (auto r : ranges) {
|
|
res[r] = erm->get_natural_replicas(
|
|
r.end() ? r.end()->value() : dht::maximum_token());
|
|
co_await coroutine::maybe_yield();
|
|
}
|
|
co_return res;
|
|
}
|
|
|
|
// Caller is responsible to hold token_metadata valid until the returned future is resolved
|
|
static future<dht::token_range_vector>
|
|
get_all_ranges(const std::vector<token>& sorted_tokens) {
|
|
if (sorted_tokens.empty())
|
|
co_return dht::token_range_vector();
|
|
int size = sorted_tokens.size();
|
|
dht::token_range_vector ranges;
|
|
ranges.reserve(size);
|
|
ranges.push_back(dht::token_range::make_ending_with(interval_bound<token>(sorted_tokens[0], true)));
|
|
co_await coroutine::maybe_yield();
|
|
for (int i = 1; i < size; ++i) {
|
|
dht::token_range r(wrapping_interval<token>::bound(sorted_tokens[i - 1], false), wrapping_interval<token>::bound(sorted_tokens[i], true));
|
|
ranges.push_back(r);
|
|
co_await coroutine::maybe_yield();
|
|
}
|
|
ranges.push_back(dht::token_range::make_starting_with(interval_bound<token>(sorted_tokens[size-1], false)));
|
|
|
|
co_return ranges;
|
|
}
|
|
|
|
// Caller is responsible to hold token_metadata valid until the returned future is resolved
|
|
future<std::unordered_map<dht::token_range, host_id_vector_replica_set>>
|
|
get_range_to_address_map(locator::effective_replication_map_ptr erm,
|
|
const std::vector<token>& sorted_tokens) {
|
|
co_return co_await construct_range_to_endpoint_map(erm, co_await get_all_ranges(sorted_tokens));
|
|
}
|
|
|
|
// Caller is responsible to hold token_metadata valid until the returned future is resolved
|
|
static future<std::vector<token>>
|
|
get_tokens_in_local_dc(const locator::token_metadata& tm) {
|
|
std::vector<token> filtered_tokens;
|
|
auto local_dc_filter = tm.get_topology().get_local_dc_filter();
|
|
for (auto token : tm.sorted_tokens()) {
|
|
auto endpoint = tm.get_endpoint(token);
|
|
if (local_dc_filter(*endpoint))
|
|
filtered_tokens.push_back(token);
|
|
co_await coroutine::maybe_yield();
|
|
}
|
|
co_return filtered_tokens;
|
|
}
|
|
|
|
static future<std::unordered_map<dht::token_range, host_id_vector_replica_set>>
|
|
get_range_to_address_map_in_local_dc(
|
|
locator::effective_replication_map_ptr erm) {
|
|
auto tmptr = erm->get_token_metadata_ptr();
|
|
auto orig_map = co_await get_range_to_address_map(erm, co_await get_tokens_in_local_dc(*tmptr));
|
|
std::unordered_map<dht::token_range, host_id_vector_replica_set> filtered_map;
|
|
filtered_map.reserve(orig_map.size());
|
|
auto local_dc_filter = tmptr->get_topology().get_local_dc_filter();
|
|
for (auto entry : orig_map) {
|
|
auto& addresses = filtered_map[entry.first];
|
|
addresses.reserve(entry.second.size());
|
|
std::copy_if(entry.second.begin(), entry.second.end(), std::back_inserter(addresses), std::cref(local_dc_filter));
|
|
co_await coroutine::maybe_yield();
|
|
}
|
|
|
|
co_return filtered_map;
|
|
}
|
|
|
|
// static future<std::unordered_map<dht::token_range, inet_address_vector_replica_set>>
|
|
// get_range_to_address_map(const replica::database& db, const sstring& keyspace) {
|
|
// return get_range_to_address_map(db.find_keyspace(keyspace).get_vnode_effective_replication_map());
|
|
// }
|
|
|
|
future<std::unordered_map<dht::token_range, host_id_vector_replica_set>>
|
|
get_range_to_address_map(locator::effective_replication_map_ptr erm) {
|
|
return get_range_to_address_map(erm, erm->get_token_metadata_ptr()->sorted_tokens());
|
|
}
|
|
|
|
future<std::vector<dht::token_range_endpoints>>
|
|
describe_ring(const replica::database& db, const gms::gossiper& gossiper, const sstring& keyspace, bool include_only_local_dc) {
|
|
std::vector<dht::token_range_endpoints> ranges;
|
|
|
|
auto erm = db.find_keyspace(keyspace).get_vnode_effective_replication_map();
|
|
std::unordered_map<dht::token_range, host_id_vector_replica_set> range_to_address_map = co_await (
|
|
include_only_local_dc
|
|
? get_range_to_address_map_in_local_dc(erm)
|
|
: get_range_to_address_map(erm)
|
|
);
|
|
auto tmptr = erm->get_token_metadata_ptr();
|
|
for (auto entry : range_to_address_map) {
|
|
const auto& topology = tmptr->get_topology();
|
|
auto range = entry.first;
|
|
auto addresses = entry.second;
|
|
dht::token_range_endpoints tr;
|
|
if (range.start()) {
|
|
tr._start_token = range.start()->value().to_sstring();
|
|
}
|
|
if (range.end()) {
|
|
tr._end_token = range.end()->value().to_sstring();
|
|
}
|
|
for (auto endpoint : addresses) {
|
|
dht::endpoint_details details;
|
|
details._host = gossiper.get_address_map().get(endpoint);
|
|
details._datacenter = topology.get_datacenter(endpoint);
|
|
details._rack = topology.get_rack(endpoint);
|
|
tr._rpc_endpoints.push_back(gossiper.get_rpc_address(details._host));
|
|
tr._endpoints.push_back(fmt::to_string(details._host));
|
|
tr._endpoint_details.push_back(details);
|
|
}
|
|
ranges.push_back(tr);
|
|
co_await coroutine::maybe_yield();
|
|
}
|
|
// Convert to wrapping ranges
|
|
auto left_inf = std::ranges::find_if(ranges, [] (const dht::token_range_endpoints& tr) {
|
|
return tr._start_token.empty();
|
|
});
|
|
auto right_inf = std::ranges::find_if(ranges, [] (const dht::token_range_endpoints& tr) {
|
|
return tr._end_token.empty();
|
|
});
|
|
using set = std::unordered_set<sstring>;
|
|
if (left_inf != right_inf
|
|
&& left_inf != ranges.end()
|
|
&& right_inf != ranges.end()
|
|
&& ((left_inf->_endpoints | std::ranges::to<set>()) ==
|
|
(right_inf->_endpoints | std::ranges::to<set>()))) {
|
|
left_inf->_start_token = std::move(right_inf->_start_token);
|
|
ranges.erase(right_inf);
|
|
}
|
|
co_return ranges;
|
|
}
|
|
|
|
}
|