diff --git a/locator/topology.hh b/locator/topology.hh index d8ff9eb653..3ac3bf1cd6 100644 --- a/locator/topology.hh +++ b/locator/topology.hh @@ -100,6 +100,11 @@ private: /** * compares two endpoints in relation to the target endpoint, returning as * Comparator.compare would + * + * The closest nodes to a given node are: + * 1. The node itself + * 2. Nodes in the same RACK as the reference node + * 3. Nodes in the same DC as the reference node */ int compare_endpoints(const inet_address& address, const inet_address& a1, const inet_address& a2) const; @@ -123,6 +128,9 @@ private: std::unordered_set _datacenters; void calculate_datacenters(); + +public: + void test_compare_endpoints(const inet_address& address, const inet_address& a1, const inet_address& a2) const; }; } // namespace locator diff --git a/test/boost/network_topology_strategy_test.cc b/test/boost/network_topology_strategy_test.cc index 5379114165..c921a8ae5d 100644 --- a/test/boost/network_topology_strategy_test.cc +++ b/test/boost/network_topology_strategy_test.cc @@ -631,3 +631,91 @@ SEASTAR_TEST_CASE(test_invalid_dcs) { }); } +namespace locator { + +void topology::test_compare_endpoints(const inet_address& address, const inet_address& a1, const inet_address& a2) const { + std::optional expected; + const auto& loc = get_location(address); + const auto& loc1 = get_location(a1); + const auto& loc2 = get_location(a2); + if (a1 == a2) { + expected = 0; + } else { + if (a1 == address) { + expected = -1; + } else if (a2 == address) { + expected = 1; + } else { + if (loc1.dc == loc.dc) { + if (loc2.dc != loc.dc) { + expected = -1; + } else { + if (loc1.rack == loc.rack) { + if (loc2.rack != loc.rack) { + expected = -1; + } + } else if (loc2.rack == loc.rack) { + expected = 1; + } + } + } else if (loc2.dc == loc.dc) { + expected = 1; + } + } + } + auto res = compare_endpoints(address, a1, a2); + testlog.debug("compare_endpoint: address={} [{}/{}] a1={} [{}/{}] a2={} [{}/{}]: res={} expected={} expected_value={}", + address, loc.dc, loc.rack, + a1, loc1.dc, loc1.rack, + a2, loc2.dc, loc2.rack, + res, bool(expected), expected.value_or(0)); + if (expected) { + BOOST_REQUIRE_EQUAL(res, *expected); + } +} + +} // namespace locator + +SEASTAR_THREAD_TEST_CASE(test_topology_compare_endpoints) { + utils::fb_utilities::set_broadcast_address(gms::inet_address("localhost")); + utils::fb_utilities::set_broadcast_rpc_address(gms::inet_address("localhost")); + + constexpr size_t NODES = 10; + + std::unordered_map datacenters = { + { "rf1", 1 }, + { "rf2", 2 }, + { "rf3", 3 }, + }; + std::vector nodes; + nodes.reserve(NODES); + + auto make_address = [] (unsigned i) { + return inet_address((127u << 24) | i); + }; + + std::generate_n(std::back_inserter(nodes), NODES, [&, i = 0u]() mutable { + return make_address(++i); + }); + auto bogus_address = make_address(NODES + 1); + + semaphore sem(1); + shared_token_metadata stm([&sem] () noexcept { return get_units(sem, 1); }, locator::token_metadata::config{}); + auto topo = generate_topology(datacenters, nodes); + + const auto& address = nodes[tests::random::get_int(0, NODES-1)]; + const auto& a1 = nodes[tests::random::get_int(0, NODES-1)]; + const auto& a2 = nodes[tests::random::get_int(0, NODES-1)]; + + topo->test_compare_endpoints(address, address, address); + topo->test_compare_endpoints(address, address, a1); + topo->test_compare_endpoints(address, a1, address); + topo->test_compare_endpoints(address, a1, a1); + topo->test_compare_endpoints(address, a1, a2); + topo->test_compare_endpoints(address, a2, a1); + + topo->test_compare_endpoints(bogus_address, bogus_address, bogus_address); + topo->test_compare_endpoints(address, bogus_address, bogus_address); + topo->test_compare_endpoints(address, a1, bogus_address); + topo->test_compare_endpoints(address, bogus_address, a2); +}