@@ -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 :
8811073struct 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
8871079struct 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
8931091TEST_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+
12501453TEST_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