/* * 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 "utils/chunked_vector.hh" #include namespace locator { static future> construct_range_to_endpoint_map( locator::effective_replication_map_ptr erm, const dht::token_range_vector& ranges) { std::unordered_map res; res.reserve(ranges.size()); for (auto r : ranges) { res[r] = erm->get_natural_replicas(r.end()->value(), true); co_await coroutine::maybe_yield(); } co_return res; } // Caller is responsible to hold token_metadata valid until the returned future is resolved static future get_all_ranges(const utils::chunked_vector& 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); for (int i = 1; i < size; ++i) { dht::token_range r(wrapping_interval::bound(sorted_tokens[i - 1], false), wrapping_interval::bound(sorted_tokens[i], true)); ranges.push_back(r); co_await coroutine::maybe_yield(); } // Add the wrapping range ranges.emplace_back(wrapping_interval::bound(sorted_tokens[size - 1], false), wrapping_interval::bound(sorted_tokens[0], true)); co_await coroutine::maybe_yield(); co_return ranges; } // Caller is responsible to hold token_metadata valid until the returned future is resolved future> get_range_to_address_map(locator::effective_replication_map_ptr erm, const utils::chunked_vector& 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> get_tokens_in_local_dc(const locator::token_metadata& tm) { utils::chunked_vector 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> 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 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> // get_range_to_address_map(const replica::database& db, const sstring& keyspace) { // return get_range_to_address_map(db.find_keyspace(keyspace).get_static_effective_replication_map()); // } future> 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> describe_ring(const replica::database& db, const gms::gossiper& gossiper, const sstring& keyspace, bool include_only_local_dc) { utils::chunked_vector ranges; std::unordered_map host_infos; auto erm = db.find_keyspace(keyspace).get_static_effective_replication_map(); std::unordered_map 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) ); ranges.reserve(range_to_address_map.size()); auto tmptr = erm->get_token_metadata_ptr(); const auto& topology = tmptr->get_topology(); for (const auto& [range, addresses] : range_to_address_map) { 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(); } tr._endpoints.reserve(addresses.size()); tr._rpc_endpoints.reserve(addresses.size()); tr._endpoint_details.reserve(addresses.size()); for (auto endpoint : addresses) { auto it = host_infos.find(endpoint); if (it == host_infos.end()) { it = host_infos.emplace(endpoint, get_describe_ring_endpoint_info(endpoint, topology, gossiper)).first; } tr._rpc_endpoints.emplace_back(it->second.rpc_addr); tr._endpoints.emplace_back(fmt::to_string(it->second.details._host)); tr._endpoint_details.emplace_back(it->second.details); } ranges.push_back(std::move(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; if (left_inf != right_inf && left_inf != ranges.end() && right_inf != ranges.end() && ((left_inf->_endpoints | std::ranges::to()) == (right_inf->_endpoints | std::ranges::to()))) { left_inf->_start_token = std::move(right_inf->_start_token); ranges.erase(right_inf); } co_return ranges; } describe_ring_endpoint_info get_describe_ring_endpoint_info(host_id endpoint, const topology& topology, const gms::gossiper& gossiper) { auto& loc = topology.get_node(endpoint).dc_rack(); return describe_ring_endpoint_info{ .details = dht::endpoint_details{ ._host = gossiper.get_address_map().get(endpoint), ._datacenter = loc.dc, ._rack = loc.rack, }, .rpc_addr = gossiper.get_rpc_address(endpoint) }; } }