Skip to content

Commit c53446d

Browse files
committed
test/crimson/seasatore: add evction test
Signed-off-by: Zhang Song <zhangsong325@gmail.com>
1 parent 5bc650a commit c53446d

File tree

4 files changed

+260
-25
lines changed

4 files changed

+260
-25
lines changed

src/crimson/os/seastore/extent_placement_manager.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
#include "crimson/os/seastore/random_block_manager/block_rb_manager.h"
1414
#include "crimson/os/seastore/randomblock_manager_group.h"
1515

16+
class transaction_manager_test_t;
17+
1618
namespace crimson::os::seastore {
1719

1820
/**
@@ -870,6 +872,8 @@ class ExtentPlacementManager {
870872
bool is_running_until_halt = false;
871873
state_t state = state_t::STOP;
872874
eviction_state_t eviction_state;
875+
876+
friend class ::transaction_manager_test_t;
873877
};
874878

875879
std::vector<ExtentOolWriterRef> writer_refs;
@@ -881,10 +885,12 @@ class ExtentPlacementManager {
881885
Device* primary_device = nullptr;
882886
std::size_t num_devices = 0;
883887

884-
rewrite_gen_t dynamic_max_rewrite_generation;
888+
rewrite_gen_t dynamic_max_rewrite_generation = REWRITE_GENERATIONS;
885889
BackgroundProcess background_process;
886890
// TODO: drop once paddr->journal_seq_t is introduced
887891
SegmentSeqAllocatorRef ool_segment_seq_allocator;
892+
893+
friend class ::transaction_manager_test_t;
888894
};
889895

890896
using ExtentPlacementManagerRef = std::unique_ptr<ExtentPlacementManager>;

src/crimson/os/seastore/seastore_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1190,7 +1190,8 @@ enum class data_category_t : uint8_t {
11901190
std::ostream &operator<<(std::ostream &out, data_category_t c);
11911191

11921192
constexpr data_category_t get_extent_category(extent_types_t type) {
1193-
if (type == extent_types_t::OBJECT_DATA_BLOCK) {
1193+
if (type == extent_types_t::OBJECT_DATA_BLOCK ||
1194+
type == extent_types_t::TEST_BLOCK) {
11941195
return data_category_t::DATA;
11951196
} else {
11961197
return data_category_t::METADATA;

src/test/crimson/seastore/test_transaction_manager.cc

Lines changed: 223 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ struct transaction_manager_test_t :
6262
std::random_device rd;
6363
std::mt19937 gen;
6464

65-
transaction_manager_test_t(std::size_t num_devices)
66-
: TMTestState(num_devices), gen(rd()) {
65+
transaction_manager_test_t(std::size_t num_main_devices, std::size_t num_cold_devices)
66+
: TMTestState(num_main_devices, num_cold_devices), gen(rd()) {
6767
}
6868

6969
laddr_t get_random_laddr(size_t block_size, laddr_t limit) {
@@ -600,21 +600,21 @@ struct transaction_manager_test_t :
600600
EXPECT_FALSE(success);
601601
}
602602

603-
auto allocate_sequentially(const size_t& size, int &num) {
604-
return repeat_eagain([&, this] {
603+
auto allocate_sequentially(const size_t size, const int num, bool run_clean = true) {
604+
return repeat_eagain([this, size, num] {
605605
return seastar::do_with(
606606
create_transaction(),
607-
[&, this](auto &t) {
607+
[this, size, num](auto &t) {
608608
return with_trans_intr(
609609
*t.t,
610-
[&, this](auto &) {
610+
[&t, this, size, num](auto &) {
611611
return trans_intr::do_for_each(
612612
boost::make_counting_iterator(0),
613613
boost::make_counting_iterator(num),
614-
[&, this](auto) {
614+
[&t, this, size](auto) {
615615
return tm->alloc_extent<TestBlock>(
616616
*(t.t), L_ADDR_MIN, size
617-
).si_then([&, this](auto extent) {
617+
).si_then([&t, this, size](auto extent) {
618618
extent->set_contents(get_random_contents());
619619
EXPECT_FALSE(
620620
test_mappings.contains(extent->get_laddr(), t.mapping_delta));
@@ -629,8 +629,12 @@ struct transaction_manager_test_t :
629629
test_mappings.consume(t.mapping_delta);
630630
});
631631
});
632-
}).safe_then([this]() {
633-
return epm->run_background_work_until_halt();
632+
}).safe_then([this, run_clean]() {
633+
if (run_clean) {
634+
return epm->run_background_work_until_halt();
635+
} else {
636+
return epm->background_process.trimmer->trim();
637+
}
634638
}).handle_error(
635639
crimson::ct_error::assert_all{
636640
"Invalid error in SeaStore::list_collections"
@@ -731,6 +735,194 @@ struct transaction_manager_test_t :
731735
});
732736
}
733737

738+
void test_evict() {
739+
// only support segmented backend currently
740+
ASSERT_EQ(epm->get_main_backend_type(), backend_type_t::SEGMENTED);
741+
ASSERT_TRUE(epm->background_process.has_cold_tier());
742+
constexpr size_t device_size =
743+
segment_manager::DEFAULT_TEST_EPHEMERAL.size;
744+
constexpr size_t block_size =
745+
segment_manager::DEFAULT_TEST_EPHEMERAL.block_size;
746+
constexpr size_t segment_size =
747+
segment_manager::DEFAULT_TEST_EPHEMERAL.segment_size;
748+
ASSERT_GE(segment_size, block_size * 20);
749+
750+
run_async([this] {
751+
// indicates there is no available segments to reclaim
752+
double stop_ratio = (double)segment_size / (double)device_size / 2;
753+
// 1 segment
754+
double default_ratio = stop_ratio * 2;
755+
// 1.25 segment
756+
double fast_ratio = stop_ratio * 2.5;
757+
758+
epm->background_process
759+
.eviction_state
760+
.init(stop_ratio, default_ratio, fast_ratio);
761+
762+
// these variables are described in
763+
// EPM::BackgroundProcess::eviction_state_t::maybe_update_eviction_mode
764+
size_t ratio_A_size = segment_size / 2 - block_size * 10;
765+
size_t ratio_B_size = segment_size / 2 + block_size * 10;
766+
size_t ratio_C_size = segment_size + block_size;
767+
size_t ratio_D_size = segment_size * 1.25 + block_size;
768+
769+
auto run_until = [this](size_t size) -> seastar::future<> {
770+
return seastar::repeat([this, size] {
771+
size_t current_size = epm->background_process
772+
.main_cleaner->get_stat().data_stored;
773+
if (current_size >= size) {
774+
return seastar::futurize_invoke([] {
775+
return seastar::stop_iteration::yes;
776+
});
777+
} else {
778+
int num = (size - current_size) / block_size;
779+
return seastar::do_for_each(
780+
boost::make_counting_iterator(0),
781+
boost::make_counting_iterator(num),
782+
[this](auto) {
783+
// don't start background process to test the behavior
784+
// of generation changes during alloc new extents
785+
return allocate_sequentially(block_size, 1, false);
786+
}).then([] {
787+
return seastar::stop_iteration::no;
788+
});
789+
}
790+
});
791+
};
792+
793+
std::vector<extent_types_t> all_extent_types{
794+
extent_types_t::ROOT,
795+
extent_types_t::LADDR_INTERNAL,
796+
extent_types_t::LADDR_LEAF,
797+
extent_types_t::OMAP_INNER,
798+
extent_types_t::OMAP_LEAF,
799+
extent_types_t::ONODE_BLOCK_STAGED,
800+
extent_types_t::COLL_BLOCK,
801+
extent_types_t::OBJECT_DATA_BLOCK,
802+
extent_types_t::RETIRED_PLACEHOLDER,
803+
extent_types_t::ALLOC_INFO,
804+
extent_types_t::JOURNAL_TAIL,
805+
extent_types_t::TEST_BLOCK,
806+
extent_types_t::TEST_BLOCK_PHYSICAL,
807+
extent_types_t::BACKREF_INTERNAL,
808+
extent_types_t::BACKREF_LEAF
809+
};
810+
811+
std::vector<rewrite_gen_t> all_generations;
812+
for (auto i = INIT_GENERATION; i < REWRITE_GENERATIONS; i++) {
813+
all_generations.push_back(i);
814+
}
815+
816+
// input target-generation -> expected generation after the adjustment
817+
using generation_mapping_t = std::map<rewrite_gen_t, rewrite_gen_t>;
818+
std::map<extent_types_t, generation_mapping_t> expected_generations;
819+
820+
// this loop should be consistent with EPM::adjust_generation
821+
for (auto t : all_extent_types) {
822+
expected_generations[t] = {};
823+
if (!is_logical_type(t)) {
824+
for (auto gen : all_generations) {
825+
expected_generations[t][gen] = INLINE_GENERATION;
826+
}
827+
} else {
828+
if (get_extent_category(t) == data_category_t::METADATA) {
829+
expected_generations[t][INIT_GENERATION] = INLINE_GENERATION;
830+
} else {
831+
expected_generations[t][INIT_GENERATION] = OOL_GENERATION;
832+
}
833+
834+
for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
835+
expected_generations[t][i] = i;
836+
}
837+
}
838+
}
839+
840+
auto update_data_gen_mapping = [&](std::function<rewrite_gen_t(rewrite_gen_t)> func) {
841+
for (auto t : all_extent_types) {
842+
if (!is_logical_type(t)) {
843+
continue;
844+
}
845+
for (auto i = INIT_GENERATION + 1; i < REWRITE_GENERATIONS; i++) {
846+
expected_generations[t][i] = func(i);
847+
}
848+
}
849+
// since background process didn't start in allocate_sequentially
850+
// we update eviction mode manually.
851+
epm->background_process.maybe_update_eviction_mode();
852+
};
853+
854+
auto test_gen = [&](const char *caller) {
855+
for (auto t : all_extent_types) {
856+
for (auto gen : all_generations) {
857+
auto epm_gen = epm->adjust_generation(
858+
get_extent_category(t),
859+
t,
860+
placement_hint_t::HOT,
861+
gen);
862+
if (expected_generations[t][gen] != epm_gen) {
863+
logger().error("caller: {}, extent type: {}, input generation: {}, "
864+
"expected generation : {}, adjust result from EPM: {}",
865+
caller, t, gen, expected_generations[t][gen], epm_gen);
866+
}
867+
EXPECT_EQ(expected_generations[t][gen], epm_gen);
868+
}
869+
}
870+
};
871+
872+
// verify that no data should go to the cold tier
873+
update_data_gen_mapping([](rewrite_gen_t gen) -> rewrite_gen_t {
874+
if (gen == MIN_COLD_GENERATION) {
875+
return MIN_COLD_GENERATION - 1;
876+
} else {
877+
return gen;
878+
}
879+
});
880+
test_gen("init");
881+
882+
run_until(ratio_A_size).get();
883+
EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
884+
test_gen("exceed ratio A");
885+
epm->run_background_work_until_halt().get();
886+
887+
run_until(ratio_B_size).get();
888+
EXPECT_TRUE(epm->background_process.eviction_state.is_stop_mode());
889+
test_gen("exceed ratio B");
890+
epm->run_background_work_until_halt().get();
891+
892+
// verify that data may go to the cold tier
893+
run_until(ratio_C_size).get();
894+
update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
895+
EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
896+
test_gen("exceed ratio C");
897+
epm->run_background_work_until_halt().get();
898+
899+
// verify that data must go to the cold tier
900+
run_until(ratio_D_size).get();
901+
update_data_gen_mapping([](rewrite_gen_t gen) {
902+
if (gen >= MIN_REWRITE_GENERATION && gen < MIN_COLD_GENERATION) {
903+
return MIN_COLD_GENERATION;
904+
} else {
905+
return gen;
906+
}
907+
});
908+
EXPECT_TRUE(epm->background_process.eviction_state.is_fast_mode());
909+
test_gen("exceed ratio D");
910+
911+
auto main_size = epm->background_process.main_cleaner->get_stat().data_stored;
912+
auto cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
913+
EXPECT_EQ(cold_size, 0);
914+
epm->run_background_work_until_halt().get();
915+
auto new_main_size = epm->background_process.main_cleaner->get_stat().data_stored;
916+
auto new_cold_size = epm->background_process.cold_cleaner->get_stat().data_stored;
917+
EXPECT_GE(main_size, new_main_size);
918+
EXPECT_NE(new_cold_size, 0);
919+
920+
update_data_gen_mapping([](rewrite_gen_t gen) { return gen; });
921+
EXPECT_TRUE(epm->background_process.eviction_state.is_default_mode());
922+
test_gen("finish evict");
923+
});
924+
}
925+
734926
std::optional<TestBlockRef> map_existing_extent(
735927
test_transaction_t &t,
736928
laddr_t hint,
@@ -881,13 +1073,19 @@ struct transaction_manager_test_t :
8811073
struct tm_single_device_test_t :
8821074
public transaction_manager_test_t {
8831075

884-
tm_single_device_test_t() : transaction_manager_test_t(1) {}
1076+
tm_single_device_test_t() : transaction_manager_test_t(1, 0) {}
8851077
};
8861078

8871079
struct tm_multi_device_test_t :
8881080
public transaction_manager_test_t {
8891081

890-
tm_multi_device_test_t() : transaction_manager_test_t(3) {}
1082+
tm_multi_device_test_t() : transaction_manager_test_t(3, 0) {}
1083+
};
1084+
1085+
struct tm_multi_tier_device_test_t :
1086+
public transaction_manager_test_t {
1087+
1088+
tm_multi_tier_device_test_t() : transaction_manager_test_t(1, 2) {}
8911089
};
8921090

8931091
TEST_P(tm_single_device_test_t, basic)
@@ -1247,6 +1445,11 @@ TEST_P(tm_multi_device_test_t, random_writes_concurrent)
12471445
test_random_writes_concurrent();
12481446
}
12491447

1448+
TEST_P(tm_multi_tier_device_test_t, evict)
1449+
{
1450+
test_evict();
1451+
}
1452+
12501453
TEST_P(tm_single_device_test_t, parallel_extent_read)
12511454
{
12521455
test_parallel_extent_read();
@@ -1276,3 +1479,11 @@ INSTANTIATE_TEST_SUITE_P(
12761479
"segmented"
12771480
)
12781481
);
1482+
1483+
INSTANTIATE_TEST_SUITE_P(
1484+
transaction_manager_test,
1485+
tm_multi_tier_device_test_t,
1486+
::testing::Values (
1487+
"segmented"
1488+
)
1489+
);

0 commit comments

Comments
 (0)