sstables_manager: trigger reclaim/reload on components_memory_reclaim_threshold update

The config variable `components_memory_reclaim_threshold` limits the
memory available to the sstable bloom filters. Any change to its value
is not immediately propagated to the sstable manager, despite it being
a LiveUpdate variable. The updated value takes effect only when a new
sstable is created or deleted.

This patch updates the sstable manager to subscribe to any changes in
the above mentioned config value and immediately trigger the
reclaim/reload fiber when a change occurs. Also, adds a testcase to
verify the fix.

Fixes #21947

Signed-off-by: Lakshmi Narayanan Sreethar <lakshmi.sreethar@scylladb.com>
This commit is contained in:
Lakshmi Narayanan Sreethar
2025-02-04 21:39:03 +05:30
parent 10fffcd646
commit 064bf2fd85
2 changed files with 64 additions and 0 deletions

View File

@@ -186,9 +186,15 @@ size_t sstables_manager::get_memory_available_for_reclaimable_components() const
}
future<> sstables_manager::components_reclaim_reload_fiber() {
auto components_memory_reclaim_threshold_observer = _db_config.components_memory_reclaim_threshold.observe([&] (double) {
// any change to the components_memory_reclaim_threshold config should trigger reload/reclaim
_components_memory_change_event.signal();
});
co_await coroutine::switch_to(_maintenance_sg);
sstlog.trace("components_reloader_fiber start");
while (true) {
co_await _components_memory_change_event.when();

View File

@@ -13,6 +13,7 @@
#include "test/lib/sstable_test_env.hh"
#include "test/lib/sstable_utils.hh"
#include "db/config.hh"
#include "readers/from_mutations_v2.hh"
#include "utils/bloom_filter.hh"
#include "utils/error_injection.hh"
@@ -347,3 +348,60 @@ SEASTAR_TEST_CASE(test_bloom_filter_reclaim_after_unlink) {
.available_memory = 100
});
};
SEASTAR_TEST_CASE(test_components_memory_reclaim_threshold_liveupdateness) {
return test_env::do_with_async([] (test_env& env) {
simple_schema ss;
auto schema_ptr = ss.schema();
auto& sst_mgr = env.manager();
BOOST_REQUIRE_EQUAL(env.db_config().components_memory_reclaim_threshold(), 0.2);
// create a few sstables and verify their bloom filters are still in memory
auto [sst1, sst1_bf_memory] = create_sstable_with_bloom_filter(env, sst_mgr, schema_ptr, 70);
auto [sst2, sst2_bf_memory] = create_sstable_with_bloom_filter(env, sst_mgr, schema_ptr, 50);
auto [sst3, sst3_bf_memory] = create_sstable_with_bloom_filter(env, sst_mgr, schema_ptr, 20);
BOOST_REQUIRE_EQUAL(sst1->filter_memory_size(), sst1_bf_memory);
BOOST_REQUIRE_EQUAL(sst2->filter_memory_size(), sst2_bf_memory);
BOOST_REQUIRE_EQUAL(sst3->filter_memory_size(), sst3_bf_memory);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_memory_reclaimed(), 0);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_reclaimable_memory(), sst1_bf_memory + sst2_bf_memory + sst3_bf_memory);
// reduce the threshold to 0.1 and verify that sst1's bloom filter, which occupies most memory, gets evicted
env.db_config().components_memory_reclaim_threshold.set(0.1);
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst1->filter_memory_size(); }, 0);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_memory_reclaimed(), sst1_bf_memory);
// the other two ssts are untouched
BOOST_REQUIRE_EQUAL(sst2->filter_memory_size(), sst2_bf_memory);
BOOST_REQUIRE_EQUAL(sst3->filter_memory_size(), sst3_bf_memory);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_reclaimable_memory(), sst2_bf_memory + sst3_bf_memory);
// reduce the threshold to 0 and verify that no bloom filter is in memory
env.db_config().components_memory_reclaim_threshold.set(0);
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst_mgr.get_total_memory_reclaimed(); }, sst1_bf_memory + sst2_bf_memory + sst3_bf_memory);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_reclaimable_memory(), 0);
BOOST_REQUIRE_EQUAL(sst1->filter_memory_size(), 0);
BOOST_REQUIRE_EQUAL(sst2->filter_memory_size(), 0);
BOOST_REQUIRE_EQUAL(sst3->filter_memory_size(), 0);
// increase threshold back 0.1 and expect sst2 and sst3's bloom filter to be reloaded
env.db_config().components_memory_reclaim_threshold.set(0.1);
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst3->filter_memory_size(); }, sst3_bf_memory);
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst2->filter_memory_size(); }, sst2_bf_memory);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_reclaimable_memory(), sst2_bf_memory + sst3_bf_memory);
// sst1's bloom filter is not reloaded yet due to lack of available memory
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst_mgr.get_total_memory_reclaimed(); }, sst1_bf_memory);
BOOST_REQUIRE_EQUAL(sst1->filter_memory_size(), 0);
// increase threshold back to 0.2 and expect sst1 to be reloaded
env.db_config().components_memory_reclaim_threshold.set(0.2);
REQUIRE_EVENTUALLY_EQUAL<size_t>([&] { return sst_mgr.get_total_memory_reclaimed(); }, 0);
BOOST_REQUIRE_EQUAL(sst1->filter_memory_size(), sst1_bf_memory);
BOOST_REQUIRE_EQUAL(sst2->filter_memory_size(), sst2_bf_memory);
BOOST_REQUIRE_EQUAL(sst3->filter_memory_size(), sst3_bf_memory);
BOOST_REQUIRE_EQUAL(sst_mgr.get_total_reclaimable_memory(), sst1_bf_memory + sst2_bf_memory + sst3_bf_memory);
}, {
// limit available memory to the sstables_manager to test reclaiming.
// this will set the reclaim threshold to 200 bytes.
.available_memory = 1000
});
}