mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-26 03:20:37 +00:00
when building scylla with the standard library from GCC-14.2, shipped by
fedora 41, we have following build failure:
```
/home/kefu/.local/bin/clang++ -DDEBUG -DDEBUG_LSA_SANITIZER -DFMT_SHARED -DSANITIZE -DSCYLLA_BUILD_MODE=debug -DSCYLLA_ENABLE_ERROR_INJECTION -DSEASTAR_API_LEVEL=7 -DSEASTAR_DEBUG -DSEASTAR_DEBUG_PROMISE -DSEASTAR_DEBUG_SHARED_PTR -DSEASTAR_DEFAULT_ALLOCATOR -DSEASTAR_LOGGER_COMPILE_TIME_FMT -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_SHUFFLE_TASK_QUEUE -DSEASTAR_SSTRING -DSEASTAR_TYPE_ERASE_MORE -DXXH_PRIVATE_API -DCMAKE_INTDIR=\"Debug\" -I/home/kefu/dev/scylladb -I/home/kefu/dev/scylladb/build/gen -I/home/kefu/dev/scylladb/seastar/include -I/home/kefu/dev/scylladb/build/seastar/gen/include -I/home/kefu/dev/scylladb/build/seastar/gen/src -isystem /home/kefu/dev/scylladb/abseil -g -Og -g -gz -std=gnu++23 -fvisibility=hidden -Wall -Werror -Wextra -Wno-error=deprecated-declarations -Wimplicit-fallthrough -Wno-c++11-narrowing -Wno-deprecated-copy -Wno-mismatched-tags -Wno-missing-field-initializers -Wno-overloaded-virtual -Wno-unsupported-friend -Wno-unused-parameter -ffile-prefix-map=/home/kefu/dev/scylladb/build=. -march=x86-64-v3 -mpclmul -Xclang -fexperimental-assignment-tracking=disabled -Werror=unused-result -fstack-clash-protection -fsanitize=address -fsanitize=undefined -MD -MT CMakeFiles/scylla-main.dir/Debug/init.cc.o -MF CMakeFiles/scylla-main.dir/Debug/init.cc.o.d -o CMakeFiles/scylla-main.dir/Debug/init.cc.o -c /home/kefu/dev/scylladb/init.cc
In file included from /home/kefu/dev/scylladb/init.cc:12:
In file included from /home/kefu/dev/scylladb/db/config.hh:20:
In file included from /home/kefu/dev/scylladb/locator/abstract_replication_strategy.hh:26:
/home/kefu/dev/scylladb/locator/tablets.hh:410:30: error: unexpected type name 'size_t': expected expression
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
/home/kefu/dev/scylladb/locator/tablets.hh:410:23: error: no member named 'irange' in namespace 'boost'
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ~~~~~~~^
/home/kefu/dev/scylladb/locator/tablets.hh:410:38: error: left operand of comma operator has no effect [-Werror,-Wunused-value]
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
3 errors generated.
[16/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/keys.cc.o
[17/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/counters.cc.o
[18/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/partition_slice_builder.cc.o
[19/782] Building CXX object CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o
FAILED: CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o
/home/kefu/.local/bin/clang++ -DDEBUG -DDEBUG_LSA_SANITIZER -DFMT_SHARED -DSANITIZE -DSCYLLA_BUILD_MODE=debug -DSCYLLA_ENABLE_ERROR_INJECTION -DSEASTAR_API_LEVEL=7 -DSEASTAR_DEBUG -DSEASTAR_DEBUG_PROMISE -DSEASTAR_DEBUG_SHARED_PTR -DSEASTAR_DEFAULT_ALLOCATOR -DSEASTAR_LOGGER_COMPILE_TIME_FMT -DSEASTAR_LOGGER_TYPE_STDOUT -DSEASTAR_SCHEDULING_GROUPS_COUNT=16 -DSEASTAR_SHUFFLE_TASK_QUEUE -DSEASTAR_SSTRING -DSEASTAR_TYPE_ERASE_MORE -DXXH_PRIVATE_API -DCMAKE_INTDIR=\"Debug\" -I/home/kefu/dev/scylladb -I/home/kefu/dev/scylladb/build/gen -I/home/kefu/dev/scylladb/seastar/include -I/home/kefu/dev/scylladb/build/seastar/gen/include -I/home/kefu/dev/scylladb/build/seastar/gen/src -isystem /home/kefu/dev/scylladb/abseil -g -Og -g -gz -std=gnu++23 -fvisibility=hidden -Wall -Werror -Wextra -Wno-error=deprecated-declarations -Wimplicit-fallthrough -Wno-c++11-narrowing -Wno-deprecated-copy -Wno-mismatched-tags -Wno-missing-field-initializers -Wno-overloaded-virtual -Wno-unsupported-friend -Wno-unused-parameter -ffile-prefix-map=/home/kefu/dev/scylladb/build=. -march=x86-64-v3 -mpclmul -Xclang -fexperimental-assignment-tracking=disabled -Werror=unused-result -fstack-clash-protection -fsanitize=address -fsanitize=undefined -MD -MT CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o -MF CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o.d -o CMakeFiles/scylla-main.dir/Debug/mutation_query.cc.o -c /home/kefu/dev/scylladb/mutation_query.cc
In file included from /home/kefu/dev/scylladb/mutation_query.cc:12:
In file included from /home/kefu/dev/scylladb/schema/schema_registry.hh:17:
In file included from /home/kefu/dev/scylladb/replica/database.hh:11:
In file included from /home/kefu/dev/scylladb/locator/abstract_replication_strategy.hh:26:
/home/kefu/dev/scylladb/locator/tablets.hh:410:30: error: unexpected type name 'size_t': expected expression
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
/home/kefu/dev/scylladb/locator/tablets.hh:410:23: error: no member named 'irange' in namespace 'boost'
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ~~~~~~~^
/home/kefu/dev/scylladb/locator/tablets.hh:410:38: error: left operand of comma operator has no effect [-Werror,-Wunused-value]
410 | return boost::irange<size_t>(0, tablet_count()) | boost::adaptors::transformed([] (size_t i) {
| ^
In file included from /home/kefu/dev/scylladb/mutation_query.cc:12:
In file included from /home/kefu/dev/scylladb/schema/schema_registry.hh:17:
In file included from /home/kefu/dev/scylladb/replica/database.hh:37:
In file included from /home/kefu/dev/scylladb/db/snapshot-ctl.hh:20:
/home/kefu/dev/scylladb/tasks/task_manager.hh:403:54: error: no member named 'irange' in namespace 'boost'
403 | co_await coroutine::parallel_for_each(boost::irange(0u, smp::count), [&tm, id, &res, &func] (unsigned shard) -> future<> {
| ~~~~~~~^
4 errors generated.
```
so let's take the opportunity to switch from `boost::irange` to
`std::views::iota`.
in this change, we:
- switch from boost::irange to std::views::iota for better standard library compatibility
- retain boost::irange where step parameter is used, as std::views::iota doesn't support it
- this change partially modernizes our range usage while maintaining
- existing functionality
Signed-off-by: Kefu Chai <kefu.chai@scylladb.com>
Closes scylladb/scylladb#20924
274 lines
8.1 KiB
C++
274 lines
8.1 KiB
C++
/*
|
|
* Copyright (C) 2022-present ScyllaDB
|
|
*/
|
|
|
|
/*
|
|
* SPDX-License-Identifier: AGPL-3.0-or-later
|
|
*/
|
|
|
|
#include <ranges>
|
|
#include <seastar/core/future-util.hh>
|
|
#include <seastar/core/sharded.hh>
|
|
#include <seastar/core/sleep.hh>
|
|
#include <seastar/testing/test_case.hh>
|
|
|
|
#include "direct_failure_detector/failure_detector.hh"
|
|
|
|
#include "test/raft/helpers.hh"
|
|
|
|
future<> ping_shards() {
|
|
if (smp::count == 1) {
|
|
return seastar::yield();
|
|
}
|
|
|
|
return parallel_for_each(std::views::iota(0u, smp::count), [] (shard_id s) {
|
|
return smp::submit_to(s, [](){});
|
|
});
|
|
}
|
|
|
|
struct test_pinger: public direct_failure_detector::pinger {
|
|
std::unordered_set<endpoint_id> _responding;
|
|
std::unordered_map<endpoint_id, size_t> _pings;
|
|
bool _block = false;
|
|
|
|
virtual future<bool> ping(endpoint_id ep, abort_source& as) override {
|
|
bool ret = false;
|
|
co_await invoke_abortable_on(0, [this, ep, &ret] (abort_source& as) -> future<> {
|
|
++_pings[ep];
|
|
if (!_block) {
|
|
ret = _responding.contains(ep);
|
|
co_return;
|
|
}
|
|
|
|
promise<> p;
|
|
auto f = p.get_future();
|
|
auto sub = as.subscribe([&, p = std::move(p)] () mutable noexcept {
|
|
p.set_value();
|
|
});
|
|
if (!sub) {
|
|
throw abort_requested_exception{};
|
|
}
|
|
co_await std::move(f);
|
|
throw abort_requested_exception{};
|
|
}, as);
|
|
co_return ret;
|
|
}
|
|
};
|
|
|
|
struct test_clock : public direct_failure_detector::clock {
|
|
std::atomic<int64_t> _ticks{0};
|
|
condition_variable _cond;
|
|
|
|
future<> tick() {
|
|
++_ticks;
|
|
_cond.broadcast();
|
|
return ping_shards();
|
|
}
|
|
|
|
virtual timepoint_t now() noexcept override {
|
|
return _ticks;
|
|
}
|
|
|
|
virtual future<> sleep_until(timepoint_t tp, abort_source& as) override {
|
|
try {
|
|
co_await invoke_abortable_on(0, [this, tp] (abort_source& as) -> future<> {
|
|
bool aborted = false;
|
|
auto sub = as.subscribe([&] () noexcept {
|
|
aborted = true;
|
|
_cond.broadcast();
|
|
});
|
|
if (!sub) {
|
|
throw sleep_aborted{};
|
|
}
|
|
|
|
co_await _cond.wait([&] { return aborted || _ticks >= tp; });
|
|
|
|
if (_ticks < tp) {
|
|
throw sleep_aborted{};
|
|
}
|
|
}, as);
|
|
} catch (abort_requested_exception&) {
|
|
throw sleep_aborted{};
|
|
}
|
|
}
|
|
};
|
|
|
|
struct test_listener : public direct_failure_detector::listener {
|
|
using endpoint_id = direct_failure_detector::pinger::endpoint_id;
|
|
|
|
std::unordered_set<endpoint_id> _live;
|
|
condition_variable _cond;
|
|
|
|
std::unordered_map<endpoint_id, size_t> _notifications;
|
|
|
|
virtual future<> mark_alive(endpoint_id ep) override {
|
|
_notifications[ep]++;
|
|
_live.insert(ep);
|
|
_cond.signal();
|
|
co_return;
|
|
}
|
|
|
|
virtual future<> mark_dead(endpoint_id ep) override {
|
|
_notifications[ep]++;
|
|
_live.erase(ep);
|
|
_cond.signal();
|
|
co_return;
|
|
}
|
|
|
|
bool is_alive(endpoint_id ep) {
|
|
return _live.contains(ep);
|
|
}
|
|
|
|
future<> wait_for(endpoint_id ep, bool alive) {
|
|
return _cond.wait([this, alive, ep] { return alive == is_alive(ep); });
|
|
}
|
|
};
|
|
|
|
|
|
SEASTAR_TEST_CASE(failure_detector_test) {
|
|
test_pinger pinger;
|
|
test_clock clock;
|
|
sharded<direct_failure_detector::failure_detector> fd;
|
|
co_await fd.start(std::ref(pinger), std::ref(clock), 10, 30);
|
|
|
|
test_listener l1, l2;
|
|
auto sub1 = co_await fd.local().register_listener(l1, 95);
|
|
auto sub2 = co_await fd.local().register_listener(l2, 45);
|
|
|
|
direct_failure_detector::pinger::endpoint_id ep1{0, 1};
|
|
direct_failure_detector::pinger::endpoint_id ep2{0, 2};
|
|
|
|
BOOST_REQUIRE(!l1.is_alive(ep1));
|
|
BOOST_REQUIRE(!l2.is_alive(ep1));
|
|
|
|
fd.local().add_endpoint(ep1);
|
|
fd.local().add_endpoint(ep2);
|
|
|
|
auto tick = [&clock] (size_t n) -> future<> {
|
|
for (size_t i = 0; i < n; ++i) {
|
|
co_await clock.tick();
|
|
}
|
|
};
|
|
|
|
auto tick_until_ping = [&] (direct_failure_detector::pinger::endpoint_id ep) -> future<> {
|
|
auto p = pinger._pings[ep];
|
|
while (pinger._pings[ep] == p) {
|
|
co_await tick(1);
|
|
}
|
|
};
|
|
|
|
co_await tick(200);
|
|
|
|
BOOST_REQUIRE(!l1.is_alive(ep1));
|
|
BOOST_REQUIRE(!l2.is_alive(ep1));
|
|
|
|
pinger._responding.insert(ep1);
|
|
// 10 ticks (ping_period) must be enough for the fd to mark ep1 alive.
|
|
co_await tick(10);
|
|
co_await l1.wait_for(ep1, true);
|
|
co_await l2.wait_for(ep1, true);
|
|
|
|
BOOST_REQUIRE(l1.is_alive(ep1));
|
|
BOOST_REQUIRE(l2.is_alive(ep1));
|
|
BOOST_REQUIRE(!l1.is_alive(ep2));
|
|
BOOST_REQUIRE(!l2.is_alive(ep2));
|
|
|
|
pinger._responding.insert(ep2);
|
|
co_await tick(10);
|
|
co_await l1.wait_for(ep2, true);
|
|
co_await l2.wait_for(ep2, true);
|
|
|
|
BOOST_REQUIRE(l1.is_alive(ep1));
|
|
BOOST_REQUIRE(l2.is_alive(ep1));
|
|
BOOST_REQUIRE(l1.is_alive(ep2));
|
|
BOOST_REQUIRE(l2.is_alive(ep2));
|
|
|
|
pinger._responding.erase(ep1);
|
|
pinger._responding.erase(ep2);
|
|
|
|
// Wait until the time from last successful ping to ep1 is >= 45 (l2's threshold)
|
|
co_await tick_until_ping(ep1);
|
|
co_await tick(45);
|
|
|
|
co_await l2.wait_for(ep1, false);
|
|
co_await l2.wait_for(ep2, false);
|
|
|
|
BOOST_REQUIRE(l1.is_alive(ep1));
|
|
BOOST_REQUIRE(l1.is_alive(ep2));
|
|
BOOST_REQUIRE(!l2.is_alive(ep1));
|
|
BOOST_REQUIRE(!l2.is_alive(ep2));
|
|
|
|
pinger._responding.insert(ep2);
|
|
co_await tick(10);
|
|
co_await l1.wait_for(ep2, true);
|
|
co_await l2.wait_for(ep2, true);
|
|
BOOST_REQUIRE(l1.is_alive(ep1));
|
|
BOOST_REQUIRE(!l2.is_alive(ep1));
|
|
|
|
// >= 55 ticks passed since ep1 stopped responding, wait another 40 for l1's threshold to pass
|
|
co_await tick(40);
|
|
co_await l1.wait_for(ep1, false);
|
|
BOOST_REQUIRE(!l2.is_alive(ep1));
|
|
|
|
// ep2 is currently marked alive by l2.
|
|
// Let's periodically make ep2 unresponsive and then responsive, so that ~1 ping is successful
|
|
// during each period of 45 ticks (= l2's threshold).
|
|
// If the time between two successful pings does not exceed the threshold,
|
|
// the fd should not mark the endpoint as dead.
|
|
{
|
|
BOOST_REQUIRE(l2.is_alive(ep2));
|
|
|
|
co_await tick_until_ping(ep2);
|
|
auto last_successful_ping = clock.now();
|
|
auto n = l2._notifications[ep2];
|
|
for (int i = 0; i < 100; ++i) {
|
|
pinger._responding.erase(ep2);
|
|
co_await tick(30);
|
|
pinger._responding.insert(ep2);
|
|
co_await tick_until_ping(ep2);
|
|
auto diff = clock.now() - last_successful_ping;
|
|
if (diff < 45) {
|
|
// The time between two successful pings did not exceed threshold.
|
|
// There should have been no notifications in between.
|
|
BOOST_REQUIRE_EQUAL(l2._notifications[ep2], n);
|
|
}
|
|
|
|
last_successful_ping = clock.now();
|
|
n = l2._notifications[ep2];
|
|
}
|
|
}
|
|
|
|
// Destroy the l2 subscription.
|
|
std::optional<direct_failure_detector::subscription> sub_opt{std::move(sub2)};
|
|
sub_opt.reset();
|
|
|
|
// Remove a live and responsive endpoint.
|
|
// l1 should receive a final mark_dead notification. l2 should not, since it's no longer subscribed.
|
|
{
|
|
auto n = l2._notifications[ep2];
|
|
BOOST_REQUIRE(l1.is_alive(ep2));
|
|
BOOST_REQUIRE(l2.is_alive(ep2));
|
|
fd.local().remove_endpoint(ep2);
|
|
co_await l1.wait_for(ep2, false);
|
|
BOOST_REQUIRE(l2.is_alive(ep2));
|
|
BOOST_REQUIRE_EQUAL(l2._notifications[ep2], n);
|
|
}
|
|
|
|
// Readd a responsive endpoint.
|
|
// l1 should eventually receive a mark_live notification.
|
|
BOOST_REQUIRE(!l1.is_alive(ep2));
|
|
fd.local().add_endpoint(ep2);
|
|
co_await l1.wait_for(ep2, true);
|
|
|
|
// Verify that the fd continues working even if a ping blocks until it is aborted.
|
|
pinger._block = true;
|
|
co_await tick(95);
|
|
co_await l1.wait_for(ep2, false);
|
|
|
|
// Destroy the l1 subscription
|
|
sub_opt.emplace(std::move(sub1));
|
|
sub_opt.reset();
|
|
|
|
co_await fd.stop();
|
|
}
|