diff --git a/dht/token.cc b/dht/token.cc index 0a9df83c23..45910dd339 100644 --- a/dht/token.cc +++ b/dht/token.cc @@ -257,4 +257,17 @@ token last_token_of_compaction_group(unsigned most_significant_bits, size_t grou return bias(n); } +utils::chunked_vector get_uniform_tokens(size_t count) { + utils::chunked_vector tokens; + tokens.reserve(count); + + for (size_t i = 1; i <= count; ++i) { + uint64_t n = (uint128_t(i) * std::numeric_limits::max()) / count; + tokens.push_back(raw_token{bias(n)}); + assert(tokens.back().value != std::numeric_limits::min()); // See token::normalize() + } + + return tokens; +} + } // namespace dht diff --git a/dht/token.hh b/dht/token.hh index 687416d096..36b9d60a21 100644 --- a/dht/token.hh +++ b/dht/token.hh @@ -358,6 +358,10 @@ inline constexpr token bias(uint64_t n) { size_t compaction_group_of(unsigned most_significant_bits, const token& t); token last_token_of_compaction_group(unsigned most_significant_bits, size_t group); +// Generates 'count' tokens uniformly distributed in the token ring. Sorted. +// All values are in the range [first_token(), last_token()] +utils::chunked_vector get_uniform_tokens(size_t count); + struct token_comparator { // Return values are those of a trichotomic comparison. constexpr std::strong_ordering operator()(const token& t1, const token& t2) const noexcept { diff --git a/test/boost/tablets_test.cc b/test/boost/tablets_test.cc index 91abed0670..1b1a33a088 100644 --- a/test/boost/tablets_test.cc +++ b/test/boost/tablets_test.cc @@ -4885,6 +4885,21 @@ SEASTAR_TEST_CASE(test_load_stats_migrate_tablet_size) { return make_ready_future<>(); } +// We want to generate the same uniform boundaries if tablet count is a power-of-two as +// we did before implementing support for arbitrary token boundaries. +// So that when advertising a "power of two" layout, e.g in the snapshot descriptor, +// it means the same thing for all scylla versions. +SEASTAR_THREAD_TEST_CASE(test_get_uniform_tokens_is_compatible_with_dht_last_token_of_compaction_group) { + for (auto log2count : {0ul, 1ul, 2ul, 3ul, 10ul}) { + auto tokens = dht::get_uniform_tokens(1ul << log2count); + for (size_t i = 0; i < tokens.size(); i++) { + testlog.debug("i {}, token {}", i, tokens[i]); + BOOST_REQUIRE_EQUAL(tokens[i], dht::last_token_of_compaction_group(log2count, i)); + thread::maybe_yield(); + } + } +} + SEASTAR_TEST_CASE(test_tablet_id_and_range_side) { static constexpr size_t tablet_count = 128; locator::tablet_map tmap(tablet_count);