When the monitor is started, the first disk utilization value is obtained from the actual host filesystem and not from the fake space source function. Thus, register a fake space source function before the monitor is started. Fixes: https://github.com/scylladb/scylladb/issues/26036 Backport is not required. The test has been added recently. Closes scylladb/scylladb#26054
185 lines
7.2 KiB
C++
185 lines
7.2 KiB
C++
/*
|
|
* Copyright (C) 2025-present ScyllaDB
|
|
*
|
|
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
*/
|
|
#undef SEASTAR_TESTING_MAIN
|
|
#include <seastar/testing/test_case.hh>
|
|
#include <seastar/testing/thread_test_case.hh>
|
|
#include <seastar/util/defer.hh>
|
|
|
|
#include "test/lib/log.hh"
|
|
#include "test/lib/tmpdir.hh"
|
|
|
|
#include "utils/disk_space_monitor.hh"
|
|
#include "utils/updateable_value.hh"
|
|
|
|
BOOST_AUTO_TEST_SUITE(disk_space_monitor_test)
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_capacity_override) {
|
|
utils::updateable_value_source<uint64_t> data_file_capacity(0);
|
|
|
|
utils::disk_space_monitor::config dsm_cfg = {
|
|
.sched_group = create_scheduling_group("streaming", 200).get(),
|
|
// The test controls polling by triggering it manually so make it big
|
|
.normal_polling_interval = utils::updateable_value<int>(std::numeric_limits<int>::max()),
|
|
.high_polling_interval = utils::updateable_value<int>(std::numeric_limits<int>::max()),
|
|
.polling_interval_threshold = utils::updateable_value<float>(1.0),
|
|
.capacity_override = utils::updateable_value<uint64_t>(data_file_capacity),
|
|
};
|
|
|
|
seastar::abort_source as;
|
|
tmpdir data_dir;
|
|
auto data_dir_path = data_dir.path().string();
|
|
|
|
utils::disk_space_monitor dsm(as, data_dir_path, dsm_cfg);
|
|
auto stop_dsm = defer([&dsm] { dsm.stop().get(); });
|
|
|
|
std::filesystem::space_info orig_space = {
|
|
.capacity = 100,
|
|
.free = 12,
|
|
.available = 11,
|
|
};
|
|
auto reg = dsm.set_space_source([orig_space] { return make_ready_future<std::filesystem::space_info>(orig_space); });
|
|
|
|
dsm.start().get();
|
|
|
|
utils::phased_barrier poll_barrier("poll_barrier"); // new operation started whenever monitor calls listeners.
|
|
auto op = poll_barrier.start();
|
|
auto listener_registration = dsm.listen([&] (auto& mon) mutable {
|
|
op = poll_barrier.start();
|
|
return make_ready_future<>();
|
|
});
|
|
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
BOOST_REQUIRE(dsm.space() == orig_space);
|
|
|
|
data_file_capacity.set(90);
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
|
|
BOOST_REQUIRE_EQUAL(dsm.space().capacity, 90);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().available, 1);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().free, 2);
|
|
|
|
data_file_capacity.set(10);
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
BOOST_REQUIRE_EQUAL(dsm.space().capacity, 10);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().available, 0);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().free, 0);
|
|
|
|
data_file_capacity.set(1000);
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
BOOST_REQUIRE_EQUAL(dsm.space().capacity, 1000);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().available, 911);
|
|
BOOST_REQUIRE_EQUAL(dsm.space().free, 912);
|
|
|
|
data_file_capacity.set(0);
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
BOOST_REQUIRE(dsm.space() == orig_space);
|
|
}
|
|
|
|
SEASTAR_THREAD_TEST_CASE(test_subscription_options) {
|
|
utils::disk_space_monitor::config dsm_cfg = {
|
|
.sched_group = create_scheduling_group("streaming", 200).get(),
|
|
// The test controls polling by triggering it manually so make it big
|
|
.normal_polling_interval = utils::updateable_value<int>(std::numeric_limits<int>::max()),
|
|
.high_polling_interval = utils::updateable_value<int>(std::numeric_limits<int>::max()),
|
|
.polling_interval_threshold = utils::updateable_value<float>(1.0),
|
|
.capacity_override = utils::updateable_value<uint64_t>(0),
|
|
};
|
|
|
|
seastar::abort_source as;
|
|
tmpdir data_dir;
|
|
auto data_dir_path = data_dir.path().string();
|
|
|
|
utils::disk_space_monitor dsm(as, data_dir_path, dsm_cfg);
|
|
auto stop_dsm = defer([&dsm] { dsm.stop().get(); });
|
|
|
|
float disk_utilization = 0.1;
|
|
auto registration = dsm.set_space_source([&] {
|
|
std::filesystem::space_info space = {
|
|
.capacity = 100,
|
|
.free = 100*(1.0-disk_utilization),
|
|
.available = 100*(1-disk_utilization),
|
|
};
|
|
return make_ready_future<std::filesystem::space_info>(space);
|
|
});
|
|
|
|
dsm.start().get();
|
|
|
|
struct stats {
|
|
size_t constant_updates = 0;
|
|
size_t constant_updates_only_above = 0;
|
|
size_t constant_updates_only_below = 0;
|
|
|
|
size_t crossing_threshold_updates = 0;
|
|
size_t crossing_threshold_updates_only_above = 0;
|
|
size_t crossing_threshold_updates_only_below = 0;
|
|
} stats;
|
|
|
|
utils::phased_barrier poll_barrier("poll_barrier");
|
|
auto op = poll_barrier.start();
|
|
auto listener_registration = dsm.listen([&] (auto&) mutable {
|
|
op = poll_barrier.start();
|
|
return make_ready_future<>();
|
|
});
|
|
|
|
auto s1 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.constant_updates;
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = false, .when_above_threshold = true, .when_below_threshold = true});
|
|
|
|
auto s2 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.constant_updates_only_above;
|
|
BOOST_CHECK(above_threshold == utils::disk_space_monitor::above_threshold::yes);
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = false, .when_above_threshold = true, .when_below_threshold = false});
|
|
|
|
auto s3 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.constant_updates_only_below;
|
|
BOOST_CHECK(above_threshold == utils::disk_space_monitor::above_threshold::no);
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = false, .when_above_threshold = false, .when_below_threshold = true});
|
|
|
|
auto s4 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.crossing_threshold_updates;
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = true, .when_above_threshold = true, .when_below_threshold = true});
|
|
|
|
auto s5 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.crossing_threshold_updates_only_above;
|
|
BOOST_CHECK(above_threshold == utils::disk_space_monitor::above_threshold::yes);
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = true, .when_above_threshold = true, .when_below_threshold = false});
|
|
|
|
auto s6 = dsm.subscribe(0.8, [&stats] (auto above_threshold) -> future<> {
|
|
++stats.crossing_threshold_updates_only_below;
|
|
BOOST_CHECK(above_threshold == utils::disk_space_monitor::above_threshold::no);
|
|
return make_ready_future<>();
|
|
}, {.only_crossing_threshold = true, .when_above_threshold = false, .when_below_threshold = true});
|
|
|
|
for (float du : {0.1, 0.5, 0.85, 0.9, 0.7, 0.6, 0.93, 0.9, 0.82, 0.81}) {
|
|
disk_utilization = du;
|
|
dsm.trigger_poll();
|
|
poll_barrier.advance_and_await().get();
|
|
}
|
|
|
|
dsm.stop().get();
|
|
stop_dsm.cancel();
|
|
|
|
BOOST_REQUIRE_EQUAL(stats.constant_updates, 10);
|
|
BOOST_REQUIRE_EQUAL(stats.constant_updates_only_above, 6);
|
|
BOOST_REQUIRE_EQUAL(stats.constant_updates_only_below, 4);
|
|
|
|
BOOST_REQUIRE_EQUAL(stats.crossing_threshold_updates, 3);
|
|
BOOST_REQUIRE_EQUAL(stats.crossing_threshold_updates_only_above, 2);
|
|
BOOST_REQUIRE_EQUAL(stats.crossing_threshold_updates_only_below, 1);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|