Files
scylladb/utils/disk_space_monitor.cc
Benny Halevy 8d2ff8a915 utils: add disk_space_monitor
Instantiated only on shard 0.
Currently, only subscribe from unit test

Manual unit test using loop mount was added.
Note that the test requires sudo access
and root access to /dev/loop, so it cannot
run in rootless podman instance, and it'd
fail with Permission denied.

Signed-off-by: Benny Halevy <bhalevy@scylladb.com>

Closes scylladb/scylladb#21523
2025-01-12 14:51:15 +02:00

88 lines
2.6 KiB
C++

/*
* Copyright (C) 2024-present ScyllaDB
*/
/*
* SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
*/
#include <filesystem>
#include <seastar/core/reactor.hh>
#include <seastar/core/sleep.hh>
#include <seastar/core/thread.hh>
#include "utils/disk_space_monitor.hh"
#include "utils/assert.hh"
#include "utils/log.hh"
using namespace std::chrono_literals;
namespace utils {
seastar::logger dsmlog("disk_space_monitor");
disk_space_monitor::disk_space_monitor(abort_source& as, std::filesystem::path data_dir, config cfg)
: _as_sub(as.subscribe([this] () noexcept { _as.request_abort(); }))
, _data_dir(std::move(data_dir))
, _cfg(std::move(cfg))
{}
disk_space_monitor::~disk_space_monitor() {
SCYLLA_ASSERT(_poller_fut.available());
}
future<> disk_space_monitor::start() {
_space_info = co_await get_filesystem_space();
_poller_fut = poll();
}
future<> disk_space_monitor::stop() noexcept {
_as.request_abort();
co_await _signal_barrier.advance_and_await();
co_await std::exchange(_poller_fut, make_ready_future());
}
disk_space_monitor::signal_connection_type disk_space_monitor::listen(signal_callback_type callback) {
return _signal_source.connect([this, callback = std::move(callback)] () mutable -> future<> {
auto op = _signal_barrier.start();
co_await callback(*this);
});
}
future<> disk_space_monitor::poll() {
try {
while (!_as.abort_requested()) {
auto now = clock_type::now();
_space_info = co_await get_filesystem_space();
if (_as.abort_requested()) {
co_return;
}
co_await _signal_barrier.advance_and_await();
_signal_source();
auto passed = clock_type::now() - now;
auto interval = get_polling_interval();
if (interval > passed) {
co_await sleep_abortable<clock_type>(interval - passed, _as);
}
}
} catch (const sleep_aborted&) {
} catch (const abort_requested_exception&) {
} catch (...) {
dsmlog.error("poll loop exited with error: {}", std::current_exception());
}
}
future<std::filesystem::space_info> disk_space_monitor::get_filesystem_space() {
return engine().file_system_space(_data_dir.native());
}
disk_space_monitor::clock_type::duration disk_space_monitor::get_polling_interval() const noexcept {
auto du = disk_utilization();
return std::chrono::seconds(du < _cfg.polling_interval_threshold.get() ? _cfg.normal_polling_interval.get() : _cfg.high_polling_interval.get());
}
} // namespace utils