1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 #include <boost/iterator/counting_iterator.hpp>
8 #include "test/crimson/gtest_seastar.h"
9 #include "test/crimson/seastore/transaction_manager_test_state.h"
11 #include "crimson/os/seastore/cache.h"
12 #include "crimson/os/seastore/transaction_manager.h"
13 #include "crimson/os/seastore/segment_manager/ephemeral.h"
14 #include "crimson/os/seastore/segment_manager.h"
16 #include "test/crimson/seastore/test_block.h"
18 using namespace crimson
;
19 using namespace crimson::os
;
20 using namespace crimson::os::seastore
;
23 [[maybe_unused
]] seastar::logger
& logger() {
24 return crimson::get_logger(ceph_subsys_test
);
28 struct test_extent_record_t
{
29 test_extent_desc_t desc
;
30 unsigned refcount
= 0;
31 test_extent_record_t() = default;
33 const test_extent_desc_t
&desc
,
34 unsigned refcount
) : desc(desc
), refcount(refcount
) {}
36 void update(const test_extent_desc_t
&to
) {
40 bool operator==(const test_extent_desc_t
&rhs
) const {
43 bool operator!=(const test_extent_desc_t
&rhs
) const {
49 struct fmt::formatter
<test_extent_record_t
> : fmt::formatter
<std::string_view
> {
50 template <typename FormatContext
>
51 auto format(const test_extent_record_t
& r
, FormatContext
& ctx
) const {
52 return fmt::format_to(ctx
.out(), "test_extent_record_t({}, refcount={})",
57 struct transaction_manager_test_t
:
58 public seastar_test_suite_t
,
61 std::random_device rd
;
64 transaction_manager_test_t(std::size_t num_main_devices
, std::size_t num_cold_devices
)
65 : TMTestState(num_main_devices
, num_cold_devices
), gen(rd()) {
68 laddr_t
get_random_laddr(size_t block_size
, laddr_t limit
) {
70 std::uniform_int_distribution
<>(0, (limit
/ block_size
) - 1)(gen
);
73 char get_random_contents() {
74 return static_cast<char>(std::uniform_int_distribution
<>(0, 255)(gen
));
77 seastar::future
<> set_up_fut() final
{
81 seastar::future
<> tear_down_fut() final
{
85 struct test_extents_t
: std::map
<laddr_t
, test_extent_record_t
> {
86 using delta_t
= std::map
<laddr_t
, std::optional
<test_extent_record_t
>>;
87 std::map
<laddr_t
, uint64_t> laddr_write_seq
;
89 struct delta_overlay_t
{
90 const test_extents_t
&extents
;
94 const test_extents_t
&extents
,
96 : extents(extents
), delta(delta
) {}
100 friend class test_extents_t
;
102 const delta_overlay_t
&parent
;
103 test_extents_t::const_iterator biter
;
104 delta_t::const_iterator oiter
;
105 std::optional
<std::pair
<laddr_t
, test_extent_record_t
>> cur
;
108 const delta_overlay_t
&parent
,
109 test_extents_t::const_iterator biter
,
110 delta_t::const_iterator oiter
)
111 : parent(parent
), biter(biter
), oiter(oiter
) {}
114 return biter
== parent
.extents
.end() ? L_ADDR_MAX
: biter
->first
;
118 return oiter
== parent
.delta
.end() ? L_ADDR_MAX
: oiter
->first
;
122 return oiter
== parent
.delta
.end() && biter
== parent
.extents
.end();
127 ((get_okey() < get_bkey()) && (oiter
->second
)) ||
128 (get_okey() > get_bkey());
134 auto okey
= get_okey();
135 auto bkey
= get_bkey();
138 std::pair
<laddr_t
, test_extent_record_t
>(*biter
) :
139 std::make_pair(okey
, *(oiter
->second
)));
143 while (!is_valid()) {
144 if (get_okey() < get_bkey()) {
145 assert(!oiter
->second
);
148 assert(get_okey() == get_bkey());
161 iterator(const iterator
&) = default;
162 iterator(iterator
&&) = default;
164 iterator
&operator++() {
167 if (get_bkey() < get_okey()) {
176 bool operator==(const iterator
&o
) const {
177 return o
.biter
== biter
&& o
.oiter
== oiter
;
179 bool operator!=(const iterator
&o
) const {
180 return !(*this == o
);
194 auto ret
= iterator
{*this, extents
.begin(), delta
.begin()};
200 auto ret
= iterator
{*this, extents
.end(), delta
.end()};
201 // adjust unnecessary
205 iterator
lower_bound(laddr_t l
) {
206 auto ret
= iterator
{*this, extents
.lower_bound(l
), delta
.lower_bound(l
)};
211 iterator
upper_bound(laddr_t l
) {
212 auto ret
= iterator
{*this, extents
.upper_bound(l
), delta
.upper_bound(l
)};
217 iterator
find(laddr_t l
) {
218 auto ret
= lower_bound(l
);
219 if (ret
== end() || ret
->first
!= l
) {
227 void check_available(
228 laddr_t addr
, extent_len_t len
, const delta_t
&delta
230 delta_overlay_t
overlay(*this, delta
);
231 for (const auto &i
: overlay
) {
232 if (i
.first
< addr
) {
233 EXPECT_FALSE(i
.first
+ i
.second
.desc
.len
> addr
);
235 EXPECT_FALSE(addr
+ len
> i
.first
);
244 delta_t
&delta
) const {
245 delta_overlay_t
overlay(*this, delta
);
246 auto iter
= overlay
.lower_bound(hint
);
249 if (iter
== overlay
.end() || iter
->first
> addr
) {
250 EXPECT_EQ(addr
, last
);
253 EXPECT_FALSE(iter
->first
- last
> len
);
254 last
= iter
->first
+ iter
->second
.desc
.len
;
259 std::optional
<test_extent_record_t
> &populate_delta(
260 laddr_t addr
, delta_t
&delta
, const test_extent_desc_t
*desc
) const {
261 auto diter
= delta
.find(addr
);
262 if (diter
!= delta
.end())
263 return diter
->second
;
265 auto iter
= find(addr
);
268 auto ret
= delta
.emplace(
269 std::make_pair(addr
, test_extent_record_t
{*desc
, 0}));
271 return ret
.first
->second
;
273 auto ret
= delta
.emplace(*iter
);
275 return ret
.first
->second
;
279 delta_overlay_t
get_overlay(const delta_t
&delta
) const {
280 return delta_overlay_t
{*this, delta
};
283 void insert(TestBlock
&extent
, delta_t
&delta
) const {
284 check_available(extent
.get_laddr(), extent
.get_length(), delta
);
285 delta
[extent
.get_laddr()] =
286 test_extent_record_t
{extent
.get_desc(), 1};
289 void alloced(laddr_t hint
, TestBlock
&extent
, delta_t
&delta
) const {
290 check_hint(hint
, extent
.get_laddr(), extent
.get_length(), delta
);
291 insert(extent
, delta
);
294 bool contains(laddr_t addr
, const delta_t
&delta
) const {
295 delta_overlay_t
overlay(*this, delta
);
296 return overlay
.find(addr
) != overlay
.end();
299 test_extent_record_t
get(laddr_t addr
, const delta_t
&delta
) const {
300 delta_overlay_t
overlay(*this, delta
);
301 auto iter
= overlay
.find(addr
);
302 assert(iter
!= overlay
.end());
308 const test_extent_desc_t
&desc
,
309 delta_t
&delta
) const {
310 auto &rec
= populate_delta(addr
, delta
, &desc
);
317 delta_t
&delta
) const {
318 auto &rec
= populate_delta(addr
, delta
, nullptr);
320 return ++rec
->refcount
;
325 delta_t
&delta
) const {
326 auto &rec
= populate_delta(addr
, delta
, nullptr);
328 assert(rec
->refcount
> 0);
330 if (rec
->refcount
== 0) {
331 delta
[addr
] = std::nullopt
;
334 return rec
->refcount
;
338 void consume(const delta_t
&delta
, const uint64_t write_seq
= 0) {
339 for (const auto &i
: delta
) {
341 if (laddr_write_seq
.find(i
.first
) == laddr_write_seq
.end() ||
342 laddr_write_seq
[i
.first
] <= write_seq
) {
343 (*this)[i
.first
] = *i
.second
;
344 laddr_write_seq
[i
.first
] = write_seq
;
354 struct test_transaction_t
{
356 test_extents_t::delta_t mapping_delta
;
359 test_transaction_t
create_transaction() {
360 return { create_mutate_transaction(), {} };
363 test_transaction_t
create_read_test_transaction() {
364 return {create_read_transaction(), {} };
367 test_transaction_t
create_weak_test_transaction() {
368 return { create_weak_transaction(), {} };
371 TestBlockRef
alloc_extent(
372 test_transaction_t
&t
,
376 auto extent
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
377 return tm
->alloc_extent
<TestBlock
>(trans
, hint
, len
);
379 extent
->set_contents(contents
);
380 EXPECT_FALSE(test_mappings
.contains(extent
->get_laddr(), t
.mapping_delta
));
381 EXPECT_EQ(len
, extent
->get_length());
382 test_mappings
.alloced(hint
, *extent
, t
.mapping_delta
);
386 TestBlockRef
alloc_extent(
387 test_transaction_t
&t
,
394 get_random_contents());
398 return epm
->check_usage();
402 EXPECT_TRUE(check_usage());
411 void check_mappings() {
412 auto t
= create_weak_test_transaction();
416 TestBlockRef
get_extent(
417 test_transaction_t
&t
,
420 ceph_assert(test_mappings
.contains(addr
, t
.mapping_delta
));
421 ceph_assert(test_mappings
.get(addr
, t
.mapping_delta
).desc
.len
== len
);
423 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
424 return tm
->read_extent
<TestBlock
>(trans
, addr
, len
);
426 EXPECT_EQ(addr
, ext
->get_laddr());
430 TestBlockRef
try_get_extent(
431 test_transaction_t
&t
,
433 ceph_assert(test_mappings
.contains(addr
, t
.mapping_delta
));
435 using ertr
= with_trans_ertr
<TransactionManager::read_extent_iertr
>;
436 using ret
= ertr::future
<TestBlockRef
>;
437 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
438 return tm
->read_extent
<TestBlock
>(trans
, addr
);
439 }).safe_then([](auto ext
) -> ret
{
440 return ertr::make_ready_future
<TestBlockRef
>(ext
);
442 [](const crimson::ct_error::eagain
&e
) {
443 return seastar::make_ready_future
<TestBlockRef
>();
445 crimson::ct_error::assert_all
{
446 "get_extent got invalid error"
450 EXPECT_EQ(addr
, ext
->get_laddr());
455 TestBlockRef
try_get_extent(
456 test_transaction_t
&t
,
459 ceph_assert(test_mappings
.contains(addr
, t
.mapping_delta
));
460 ceph_assert(test_mappings
.get(addr
, t
.mapping_delta
).desc
.len
== len
);
462 using ertr
= with_trans_ertr
<TransactionManager::read_extent_iertr
>;
463 using ret
= ertr::future
<TestBlockRef
>;
464 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
465 return tm
->read_extent
<TestBlock
>(trans
, addr
, len
);
466 }).safe_then([](auto ext
) -> ret
{
467 return ertr::make_ready_future
<TestBlockRef
>(ext
);
469 [](const crimson::ct_error::eagain
&e
) {
470 return seastar::make_ready_future
<TestBlockRef
>();
472 crimson::ct_error::assert_all
{
473 "get_extent got invalid error"
477 EXPECT_EQ(addr
, ext
->get_laddr());
482 TestBlockRef
try_read_pin(
483 test_transaction_t
&t
,
484 LBAMappingRef
&&pin
) {
485 using ertr
= with_trans_ertr
<TransactionManager::base_iertr
>;
486 using ret
= ertr::future
<TestBlockRef
>;
487 auto addr
= pin
->get_key();
488 auto ext
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
489 return tm
->read_pin
<TestBlock
>(trans
, std::move(pin
));
490 }).safe_then([](auto ext
) -> ret
{
491 return ertr::make_ready_future
<TestBlockRef
>(ext
);
493 [](const crimson::ct_error::eagain
&e
) {
494 return seastar::make_ready_future
<TestBlockRef
>();
496 crimson::ct_error::assert_all
{
497 "read_pin got invalid error"
501 EXPECT_EQ(addr
, ext
->get_laddr());
503 if (t
.t
->is_conflicted()) {
509 test_block_mutator_t mutator
;
510 TestBlockRef
mutate_extent(
511 test_transaction_t
&t
,
513 ceph_assert(test_mappings
.contains(ref
->get_laddr(), t
.mapping_delta
));
515 test_mappings
.get(ref
->get_laddr(), t
.mapping_delta
).desc
.len
==
518 auto ext
= tm
->get_mutable_extent(*t
.t
, ref
)->cast
<TestBlock
>();
519 EXPECT_EQ(ext
->get_laddr(), ref
->get_laddr());
520 EXPECT_EQ(ext
->get_desc(), ref
->get_desc());
521 mutator
.mutate(*ext
, gen
);
523 test_mappings
.update(ext
->get_laddr(), ext
->get_desc(), t
.mapping_delta
);
527 TestBlockRef
mutate_addr(
528 test_transaction_t
&t
,
531 auto ext
= get_extent(t
, offset
, length
);
532 mutate_extent(t
, ext
);
536 LBAMappingRef
get_pin(
537 test_transaction_t
&t
,
539 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
540 auto pin
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
541 return tm
->get_pin(trans
, offset
);
543 EXPECT_EQ(offset
, pin
->get_key());
547 LBAMappingRef
try_get_pin(
548 test_transaction_t
&t
,
550 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
551 using ertr
= with_trans_ertr
<TransactionManager::get_pin_iertr
>;
552 using ret
= ertr::future
<LBAMappingRef
>;
553 auto pin
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
554 return tm
->get_pin(trans
, offset
);
555 }).safe_then([](auto pin
) -> ret
{
556 return ertr::make_ready_future
<LBAMappingRef
>(std::move(pin
));
558 [](const crimson::ct_error::eagain
&e
) {
559 return seastar::make_ready_future
<LBAMappingRef
>();
561 crimson::ct_error::assert_all
{
562 "get_extent got invalid error"
566 EXPECT_EQ(offset
, pin
->get_key());
571 void inc_ref(test_transaction_t
&t
, laddr_t offset
) {
572 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
573 ceph_assert(test_mappings
.get(offset
, t
.mapping_delta
).refcount
> 0);
575 auto refcnt
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
576 return tm
->inc_ref(trans
, offset
);
578 auto check_refcnt
= test_mappings
.inc_ref(offset
, t
.mapping_delta
);
579 EXPECT_EQ(refcnt
, check_refcnt
);
582 void dec_ref(test_transaction_t
&t
, laddr_t offset
) {
583 ceph_assert(test_mappings
.contains(offset
, t
.mapping_delta
));
584 ceph_assert(test_mappings
.get(offset
, t
.mapping_delta
).refcount
> 0);
586 auto refcnt
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
587 return tm
->dec_ref(trans
, offset
);
589 auto check_refcnt
= test_mappings
.dec_ref(offset
, t
.mapping_delta
);
590 EXPECT_EQ(refcnt
, check_refcnt
);
592 logger().debug("dec_ref: {} at refcount 0", offset
);
595 void check_mappings(test_transaction_t
&t
) {
596 auto overlay
= test_mappings
.get_overlay(t
.mapping_delta
);
597 for (const auto &i
: overlay
) {
598 logger().debug("check_mappings: {}->{}", i
.first
, i
.second
);
599 auto ext
= get_extent(t
, i
.first
, i
.second
.desc
.len
);
600 EXPECT_EQ(i
.second
, ext
->get_desc());
604 [this, &overlay
](auto &t
) {
605 return lba_manager
->scan_mappings(
609 [iter
=overlay
.begin(), &overlay
](auto l
, auto p
, auto len
) mutable {
610 EXPECT_NE(iter
, overlay
.end());
612 "check_mappings: scan {}",
614 EXPECT_EQ(l
, iter
->first
);
618 (void)with_trans_intr(
621 return lba_manager
->check_child_trackers(t
);
625 bool try_submit_transaction(test_transaction_t t
) {
626 using ertr
= with_trans_ertr
<TransactionManager::submit_transaction_iertr
>;
627 using ret
= ertr::future
<bool>;
628 uint64_t write_seq
= 0;
629 bool success
= submit_transaction_fut_with_seq(*t
.t
630 ).safe_then([&write_seq
](auto seq
) -> ret
{
632 return ertr::make_ready_future
<bool>(true);
634 [](const crimson::ct_error::eagain
&e
) {
635 return seastar::make_ready_future
<bool>(false);
637 crimson::ct_error::assert_all
{
638 "try_submit_transaction hit invalid error"
640 ).then([this](auto ret
) {
641 return epm
->run_background_work_until_halt(
642 ).then([ret
] { return ret
; });
646 test_mappings
.consume(t
.mapping_delta
, write_seq
);
652 void submit_transaction(test_transaction_t
&&t
) {
653 bool success
= try_submit_transaction(std::move(t
));
654 EXPECT_TRUE(success
);
657 void submit_transaction_expect_conflict(test_transaction_t
&&t
) {
658 bool success
= try_submit_transaction(std::move(t
));
659 EXPECT_FALSE(success
);
662 auto allocate_sequentially(const size_t size
, const int num
, bool run_clean
= true) {
663 return repeat_eagain([this, size
, num
] {
664 return seastar::do_with(
665 create_transaction(),
666 [this, size
, num
](auto &t
) {
667 return with_trans_intr(
669 [&t
, this, size
, num
](auto &) {
670 return trans_intr::do_for_each(
671 boost::make_counting_iterator(0),
672 boost::make_counting_iterator(num
),
673 [&t
, this, size
](auto) {
674 return tm
->alloc_extent
<TestBlock
>(
675 *(t
.t
), L_ADDR_MIN
, size
676 ).si_then([&t
, this, size
](auto extent
) {
677 extent
->set_contents(get_random_contents());
679 test_mappings
.contains(extent
->get_laddr(), t
.mapping_delta
));
680 EXPECT_EQ(size
, extent
->get_length());
681 test_mappings
.alloced(extent
->get_laddr(), *extent
, t
.mapping_delta
);
682 return seastar::now();
684 }).si_then([&t
, this] {
685 return tm
->submit_transaction(*t
.t
);
687 }).safe_then([&t
, this] {
688 test_mappings
.consume(t
.mapping_delta
);
691 }).safe_then([this, run_clean
]() {
693 return epm
->run_background_work_until_halt();
695 return epm
->background_process
.trimmer
->trim();
698 crimson::ct_error::assert_all
{
699 "Invalid error in SeaStore::list_collections"
704 void test_parallel_extent_read() {
705 constexpr size_t TOTAL
= 4<<20;
706 constexpr size_t BSIZE
= 4<<10;
707 constexpr size_t BLOCKS
= TOTAL
/ BSIZE
;
709 for (unsigned i
= 0; i
< BLOCKS
; ++i
) {
710 auto t
= create_transaction();
711 auto extent
= alloc_extent(
715 ASSERT_EQ(i
* BSIZE
, extent
->get_laddr());
716 submit_transaction(std::move(t
));
720 create_read_test_transaction(),
722 return with_trans_intr(*(t
.t
), [this](auto &t
) {
723 return trans_intr::parallel_for_each(
724 boost::make_counting_iterator(0lu),
725 boost::make_counting_iterator(BLOCKS
),
727 return tm
->read_extent
<TestBlock
>(t
, i
* BSIZE
, BSIZE
729 return seastar::now();
737 void test_random_writes_concurrent() {
738 constexpr unsigned WRITE_STREAMS
= 256;
740 constexpr size_t TOTAL
= 4<<20;
741 constexpr size_t BSIZE
= 4<<10;
742 constexpr size_t BLOCKS
= TOTAL
/ BSIZE
;
745 boost::make_counting_iterator(0u),
746 boost::make_counting_iterator(WRITE_STREAMS
),
748 for (unsigned i
= idx
; i
< BLOCKS
; i
+= WRITE_STREAMS
) {
750 auto t
= create_transaction();
751 auto extent
= alloc_extent(
755 ASSERT_EQ(i
* BSIZE
, extent
->get_laddr());
756 if (try_submit_transaction(std::move(t
)))
763 unsigned failures
= 0;
764 seastar::parallel_for_each(
765 boost::make_counting_iterator(0u),
766 boost::make_counting_iterator(WRITE_STREAMS
),
768 return seastar::async([&] {
769 while (writes
< 300) {
770 auto t
= create_transaction();
771 auto ext
= try_get_extent(
773 get_random_laddr(BSIZE
, TOTAL
),
779 auto mut
= mutate_extent(t
, ext
);
780 auto success
= try_submit_transaction(std::move(t
));
782 failures
+= !success
;
787 logger().info("random_writes_concurrent: checking");
790 "random_writes_concurrent: {} suceeded, {} failed",
798 // only support segmented backend currently
799 ASSERT_EQ(epm
->get_main_backend_type(), backend_type_t::SEGMENTED
);
800 ASSERT_TRUE(epm
->background_process
.has_cold_tier());
801 constexpr size_t device_size
=
802 segment_manager::DEFAULT_TEST_EPHEMERAL
.size
;
803 constexpr size_t block_size
=
804 segment_manager::DEFAULT_TEST_EPHEMERAL
.block_size
;
805 constexpr size_t segment_size
=
806 segment_manager::DEFAULT_TEST_EPHEMERAL
.segment_size
;
807 ASSERT_GE(segment_size
, block_size
* 20);
810 // indicates there is no available segments to reclaim
811 double stop_ratio
= (double)segment_size
/ (double)device_size
/ 2;
813 double default_ratio
= stop_ratio
* 2;
815 double fast_ratio
= stop_ratio
* 2.5;
817 epm
->background_process
819 .init(stop_ratio
, default_ratio
, fast_ratio
);
821 // these variables are described in
822 // EPM::BackgroundProcess::eviction_state_t::maybe_update_eviction_mode
823 size_t ratio_A_size
= segment_size
/ 2 - block_size
* 10;
824 size_t ratio_B_size
= segment_size
/ 2 + block_size
* 10;
825 size_t ratio_C_size
= segment_size
+ block_size
;
826 size_t ratio_D_size
= segment_size
* 1.25 + block_size
;
828 auto run_until
= [this](size_t size
) -> seastar::future
<> {
829 return seastar::repeat([this, size
] {
830 size_t current_size
= epm
->background_process
831 .main_cleaner
->get_stat().data_stored
;
832 if (current_size
>= size
) {
833 return seastar::futurize_invoke([] {
834 return seastar::stop_iteration::yes
;
837 int num
= (size
- current_size
) / block_size
;
838 return seastar::do_for_each(
839 boost::make_counting_iterator(0),
840 boost::make_counting_iterator(num
),
842 // don't start background process to test the behavior
843 // of generation changes during alloc new extents
844 return allocate_sequentially(block_size
, 1, false);
846 return seastar::stop_iteration::no
;
852 std::vector
<extent_types_t
> all_extent_types
{
853 extent_types_t::ROOT
,
854 extent_types_t::LADDR_INTERNAL
,
855 extent_types_t::LADDR_LEAF
,
856 extent_types_t::OMAP_INNER
,
857 extent_types_t::OMAP_LEAF
,
858 extent_types_t::ONODE_BLOCK_STAGED
,
859 extent_types_t::COLL_BLOCK
,
860 extent_types_t::OBJECT_DATA_BLOCK
,
861 extent_types_t::RETIRED_PLACEHOLDER
,
862 extent_types_t::ALLOC_INFO
,
863 extent_types_t::JOURNAL_TAIL
,
864 extent_types_t::TEST_BLOCK
,
865 extent_types_t::TEST_BLOCK_PHYSICAL
,
866 extent_types_t::BACKREF_INTERNAL
,
867 extent_types_t::BACKREF_LEAF
870 std::vector
<rewrite_gen_t
> all_generations
;
871 for (auto i
= INIT_GENERATION
; i
< REWRITE_GENERATIONS
; i
++) {
872 all_generations
.push_back(i
);
875 // input target-generation -> expected generation after the adjustment
876 using generation_mapping_t
= std::map
<rewrite_gen_t
, rewrite_gen_t
>;
877 std::map
<extent_types_t
, generation_mapping_t
> expected_generations
;
879 // this loop should be consistent with EPM::adjust_generation
880 for (auto t
: all_extent_types
) {
881 expected_generations
[t
] = {};
882 if (!is_logical_type(t
)) {
883 for (auto gen
: all_generations
) {
884 expected_generations
[t
][gen
] = INLINE_GENERATION
;
887 if (get_extent_category(t
) == data_category_t::METADATA
) {
888 expected_generations
[t
][INIT_GENERATION
] = INLINE_GENERATION
;
890 expected_generations
[t
][INIT_GENERATION
] = OOL_GENERATION
;
893 for (auto i
= INIT_GENERATION
+ 1; i
< REWRITE_GENERATIONS
; i
++) {
894 expected_generations
[t
][i
] = i
;
899 auto update_data_gen_mapping
= [&](std::function
<rewrite_gen_t(rewrite_gen_t
)> func
) {
900 for (auto t
: all_extent_types
) {
901 if (!is_logical_type(t
)) {
904 for (auto i
= INIT_GENERATION
+ 1; i
< REWRITE_GENERATIONS
; i
++) {
905 expected_generations
[t
][i
] = func(i
);
908 // since background process didn't start in allocate_sequentially
909 // we update eviction mode manually.
910 epm
->background_process
.maybe_update_eviction_mode();
913 auto test_gen
= [&](const char *caller
) {
914 for (auto t
: all_extent_types
) {
915 for (auto gen
: all_generations
) {
916 auto epm_gen
= epm
->adjust_generation(
917 get_extent_category(t
),
919 placement_hint_t::HOT
,
921 if (expected_generations
[t
][gen
] != epm_gen
) {
922 logger().error("caller: {}, extent type: {}, input generation: {}, "
923 "expected generation : {}, adjust result from EPM: {}",
924 caller
, t
, gen
, expected_generations
[t
][gen
], epm_gen
);
926 EXPECT_EQ(expected_generations
[t
][gen
], epm_gen
);
931 // verify that no data should go to the cold tier
932 update_data_gen_mapping([](rewrite_gen_t gen
) -> rewrite_gen_t
{
933 if (gen
== MIN_COLD_GENERATION
) {
934 return MIN_COLD_GENERATION
- 1;
941 run_until(ratio_A_size
).get();
942 EXPECT_TRUE(epm
->background_process
.eviction_state
.is_stop_mode());
943 test_gen("exceed ratio A");
944 epm
->run_background_work_until_halt().get();
946 run_until(ratio_B_size
).get();
947 EXPECT_TRUE(epm
->background_process
.eviction_state
.is_stop_mode());
948 test_gen("exceed ratio B");
949 epm
->run_background_work_until_halt().get();
951 // verify that data may go to the cold tier
952 run_until(ratio_C_size
).get();
953 update_data_gen_mapping([](rewrite_gen_t gen
) { return gen
; });
954 EXPECT_TRUE(epm
->background_process
.eviction_state
.is_default_mode());
955 test_gen("exceed ratio C");
956 epm
->run_background_work_until_halt().get();
958 // verify that data must go to the cold tier
959 run_until(ratio_D_size
).get();
960 update_data_gen_mapping([](rewrite_gen_t gen
) {
961 if (gen
>= MIN_REWRITE_GENERATION
&& gen
< MIN_COLD_GENERATION
) {
962 return MIN_COLD_GENERATION
;
967 EXPECT_TRUE(epm
->background_process
.eviction_state
.is_fast_mode());
968 test_gen("exceed ratio D");
970 auto main_size
= epm
->background_process
.main_cleaner
->get_stat().data_stored
;
971 auto cold_size
= epm
->background_process
.cold_cleaner
->get_stat().data_stored
;
972 EXPECT_EQ(cold_size
, 0);
973 epm
->run_background_work_until_halt().get();
974 auto new_main_size
= epm
->background_process
.main_cleaner
->get_stat().data_stored
;
975 auto new_cold_size
= epm
->background_process
.cold_cleaner
->get_stat().data_stored
;
976 EXPECT_GE(main_size
, new_main_size
);
977 EXPECT_NE(new_cold_size
, 0);
979 update_data_gen_mapping([](rewrite_gen_t gen
) { return gen
; });
980 EXPECT_TRUE(epm
->background_process
.eviction_state
.is_default_mode());
981 test_gen("finish evict");
985 using remap_entry
= TransactionManager::remap_entry
;
986 LBAMappingRef
remap_pin(
987 test_transaction_t
&t
,
988 LBAMappingRef
&&opin
,
989 extent_len_t new_offset
,
990 extent_len_t new_len
) {
991 if (t
.t
->is_conflicted()) {
994 auto o_laddr
= opin
->get_key();
995 auto pin
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
996 return tm
->remap_pin
<TestBlock
>(
997 trans
, std::move(opin
), std::array
{
998 remap_entry(new_offset
, new_len
)}
999 ).si_then([](auto ret
) {
1000 return std::move(ret
[0]);
1002 }).handle_error(crimson::ct_error::eagain::handle([] {
1003 LBAMappingRef t
= nullptr;
1005 }), crimson::ct_error::pass_further_all
{}).unsafe_get0();
1006 if (t
.t
->is_conflicted()) {
1009 test_mappings
.dec_ref(o_laddr
, t
.mapping_delta
);
1010 EXPECT_FALSE(test_mappings
.contains(o_laddr
, t
.mapping_delta
));
1012 EXPECT_EQ(pin
->get_length(), new_len
);
1013 EXPECT_EQ(pin
->get_key(), o_laddr
+ new_offset
);
1015 auto extent
= try_read_pin(t
, pin
->duplicate());
1017 test_mappings
.alloced(pin
->get_key(), *extent
, t
.mapping_delta
);
1018 EXPECT_TRUE(extent
->is_exist_clean());
1020 ceph_assert(t
.t
->is_conflicted());
1026 using _overwrite_pin_iertr
= TransactionManager::get_pin_iertr
;
1027 using _overwrite_pin_ret
= _overwrite_pin_iertr::future
<
1028 std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>>;
1029 _overwrite_pin_ret
_overwrite_pin(
1031 LBAMappingRef
&&opin
,
1032 extent_len_t new_offset
,
1033 extent_len_t new_len
,
1034 ceph::bufferlist
&bl
) {
1035 auto o_laddr
= opin
->get_key();
1036 auto o_len
= opin
->get_length();
1037 if (new_offset
!= 0 && o_len
!= new_offset
+ new_len
) {
1038 return tm
->remap_pin
<TestBlock
, 2>(
1046 new_offset
+ new_len
,
1047 o_len
- new_offset
- new_len
)
1049 ).si_then([this, new_offset
, new_len
, o_laddr
, &t
, &bl
](auto ret
) {
1050 return tm
->alloc_extent
<TestBlock
>(t
, o_laddr
+ new_offset
, new_len
1051 ).si_then([this, ret
= std::move(ret
), new_len
,
1052 new_offset
, o_laddr
, &t
, &bl
](auto ext
) mutable {
1053 ceph_assert(ret
.size() == 2);
1054 auto iter
= bl
.cbegin();
1055 iter
.copy(new_len
, ext
->get_bptr().c_str());
1056 auto r_laddr
= o_laddr
+ new_offset
+ new_len
;
1057 // old pins expired after alloc new extent, need to get it.
1058 return tm
->get_pin(t
, o_laddr
1059 ).si_then([this, &t
, ext
= std::move(ext
), r_laddr
](auto lpin
) mutable {
1060 return tm
->get_pin(t
, r_laddr
1061 ).si_then([lpin
= std::move(lpin
), ext
= std::move(ext
)]
1062 (auto rpin
) mutable {
1063 return _overwrite_pin_iertr::make_ready_future
<
1064 std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>>(
1066 std::move(lpin
), std::move(ext
), std::move(rpin
)));
1071 } else if (new_offset
== 0 && o_len
!= new_offset
+ new_len
) {
1072 return tm
->remap_pin
<TestBlock
, 1>(
1077 new_offset
+ new_len
,
1078 o_len
- new_offset
- new_len
)
1080 ).si_then([this, new_offset
, new_len
, o_laddr
, &t
, &bl
](auto ret
) {
1081 return tm
->alloc_extent
<TestBlock
>(t
, o_laddr
+ new_offset
, new_len
1082 ).si_then([this, ret
= std::move(ret
), new_offset
, new_len
,
1083 o_laddr
, &t
, &bl
](auto ext
) mutable {
1084 ceph_assert(ret
.size() == 1);
1085 auto iter
= bl
.cbegin();
1086 iter
.copy(new_len
, ext
->get_bptr().c_str());
1087 auto r_laddr
= o_laddr
+ new_offset
+ new_len
;
1088 return tm
->get_pin(t
, r_laddr
1089 ).si_then([ext
= std::move(ext
)](auto rpin
) mutable {
1090 return _overwrite_pin_iertr::make_ready_future
<
1091 std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>>(
1093 nullptr, std::move(ext
), std::move(rpin
)));
1097 } else if (new_offset
!= 0 && o_len
== new_offset
+ new_len
) {
1098 return tm
->remap_pin
<TestBlock
, 1>(
1106 ).si_then([this, new_offset
, new_len
, o_laddr
, &t
, &bl
](auto ret
) {
1107 return tm
->alloc_extent
<TestBlock
>(t
, o_laddr
+ new_offset
, new_len
1108 ).si_then([this, ret
= std::move(ret
), new_len
, o_laddr
, &t
, &bl
]
1109 (auto ext
) mutable {
1110 ceph_assert(ret
.size() == 1);
1111 auto iter
= bl
.cbegin();
1112 iter
.copy(new_len
, ext
->get_bptr().c_str());
1113 return tm
->get_pin(t
, o_laddr
1114 ).si_then([ext
= std::move(ext
)](auto lpin
) mutable {
1115 return _overwrite_pin_iertr::make_ready_future
<
1116 std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>>(
1118 std::move(lpin
), std::move(ext
), nullptr));
1123 ceph_abort("impossible");
1124 return _overwrite_pin_iertr::make_ready_future
<
1125 std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>>(
1126 std::make_tuple(nullptr, nullptr, nullptr));
1130 using overwrite_pin_ret
= std::tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>;
1131 overwrite_pin_ret
overwrite_pin(
1132 test_transaction_t
&t
,
1133 LBAMappingRef
&&opin
,
1134 extent_len_t new_offset
,
1135 extent_len_t new_len
,
1136 ceph::bufferlist
&bl
) {
1137 if (t
.t
->is_conflicted()) {
1138 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1139 nullptr, nullptr, nullptr);
1141 auto o_laddr
= opin
->get_key();
1142 auto o_paddr
= opin
->get_val();
1143 auto o_len
= opin
->get_length();
1144 auto res
= with_trans_intr(*(t
.t
), [&](auto& trans
) {
1145 return _overwrite_pin(
1146 trans
, std::move(opin
), new_offset
, new_len
, bl
);
1147 }).handle_error(crimson::ct_error::eagain::handle([] {
1148 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1149 nullptr, nullptr, nullptr);
1150 }), crimson::ct_error::pass_further_all
{}).unsafe_get0();
1151 if (t
.t
->is_conflicted()) {
1152 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1153 nullptr, nullptr, nullptr);
1155 test_mappings
.dec_ref(o_laddr
, t
.mapping_delta
);
1156 EXPECT_FALSE(test_mappings
.contains(o_laddr
, t
.mapping_delta
));
1157 auto &[lpin
, ext
, rpin
] = res
;
1160 EXPECT_TRUE(lpin
|| rpin
);
1161 EXPECT_TRUE(o_len
> ext
->get_length());
1163 EXPECT_EQ(lpin
->get_key(), o_laddr
);
1164 EXPECT_EQ(lpin
->get_val(), o_paddr
);
1165 EXPECT_EQ(lpin
->get_length(), new_offset
);
1166 auto lext
= try_read_pin(t
, lpin
->duplicate());
1168 test_mappings
.alloced(lpin
->get_key(), *lext
, t
.mapping_delta
);
1169 EXPECT_TRUE(lext
->is_exist_clean());
1171 ceph_assert(t
.t
->is_conflicted());
1172 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1173 nullptr, nullptr, nullptr);
1176 EXPECT_EQ(ext
->get_laddr(), o_laddr
+ new_offset
);
1177 EXPECT_EQ(ext
->get_length(), new_len
);
1178 test_mappings
.alloced(ext
->get_laddr(), *ext
, t
.mapping_delta
);
1180 EXPECT_EQ(rpin
->get_key(), o_laddr
+ new_offset
+ new_len
);
1181 EXPECT_EQ(rpin
->get_val(), o_paddr
.add_offset(new_offset
)
1182 .add_offset(new_len
));
1183 EXPECT_EQ(rpin
->get_length(), o_len
- new_offset
- new_len
);
1184 auto rext
= try_read_pin(t
, rpin
->duplicate());
1186 test_mappings
.alloced(rpin
->get_key(), *rext
, t
.mapping_delta
);
1187 EXPECT_TRUE(rext
->is_exist_clean());
1189 ceph_assert(t
.t
->is_conflicted());
1190 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1191 nullptr, nullptr, nullptr);
1194 return std::make_tuple
<LBAMappingRef
, TestBlockRef
, LBAMappingRef
>(
1195 std::move(lpin
), std::move(ext
), std::move(rpin
));
1198 void test_remap_pin() {
1200 constexpr size_t l_offset
= 32 << 10;
1201 constexpr size_t l_len
= 32 << 10;
1202 constexpr size_t r_offset
= 64 << 10;
1203 constexpr size_t r_len
= 32 << 10;
1205 auto t
= create_transaction();
1206 auto lext
= alloc_extent(t
, l_offset
, l_len
);
1207 lext
->set_contents('l', 0, 16 << 10);
1208 auto rext
= alloc_extent(t
, r_offset
, r_len
);
1209 rext
->set_contents('r', 16 << 10, 16 << 10);
1210 submit_transaction(std::move(t
));
1213 auto t
= create_transaction();
1214 auto lpin
= get_pin(t
, l_offset
);
1215 auto rpin
= get_pin(t
, r_offset
);
1217 auto pin1
= remap_pin(t
, std::move(lpin
), 0, 16 << 10);
1219 auto pin2
= remap_pin(t
, std::move(pin1
), 0, 8 << 10);
1221 auto pin3
= remap_pin(t
, std::move(pin2
), 0, 4 << 10);
1223 auto lext
= get_extent(t
, pin3
->get_key(), pin3
->get_length());
1224 EXPECT_EQ('l', lext
->get_bptr().c_str()[0]);
1225 auto mlext
= mutate_extent(t
, lext
);
1226 ASSERT_TRUE(mlext
->is_exist_mutation_pending());
1227 ASSERT_TRUE(mlext
.get() == lext
.get());
1230 auto pin4
= remap_pin(t
, std::move(rpin
), 16 << 10, 16 << 10);
1232 auto pin5
= remap_pin(t
, std::move(pin4
), 8 << 10, 8 << 10);
1234 auto pin6
= remap_pin(t
, std::move(pin5
), 4 << 10, 4 << 10);
1236 auto rext
= get_extent(t
, pin6
->get_key(), pin6
->get_length());
1237 EXPECT_EQ('r', rext
->get_bptr().c_str()[0]);
1238 auto mrext
= mutate_extent(t
, rext
);
1239 ASSERT_TRUE(mrext
->is_exist_mutation_pending());
1240 ASSERT_TRUE(mrext
.get() == rext
.get());
1242 submit_transaction(std::move(t
));
1250 void test_overwrite_pin() {
1252 constexpr size_t m_offset
= 8 << 10;
1253 constexpr size_t m_len
= 56 << 10;
1254 constexpr size_t l_offset
= 64 << 10;
1255 constexpr size_t l_len
= 64 << 10;
1256 constexpr size_t r_offset
= 128 << 10;
1257 constexpr size_t r_len
= 64 << 10;
1259 auto t
= create_transaction();
1260 auto m_ext
= alloc_extent(t
, m_offset
, m_len
);
1261 m_ext
->set_contents('a', 0 << 10, 8 << 10);
1262 m_ext
->set_contents('b', 16 << 10, 4 << 10);
1263 m_ext
->set_contents('c', 36 << 10, 4 << 10);
1264 m_ext
->set_contents('d', 52 << 10, 4 << 10);
1266 auto l_ext
= alloc_extent(t
, l_offset
, l_len
);
1267 auto r_ext
= alloc_extent(t
, r_offset
, r_len
);
1268 submit_transaction(std::move(t
));
1271 auto t
= create_transaction();
1272 auto mpin
= get_pin(t
, m_offset
);
1273 auto lpin
= get_pin(t
, l_offset
);
1274 auto rpin
= get_pin(t
, r_offset
);
1276 bufferlist mbl1
, mbl2
, mbl3
;
1277 mbl1
.append(ceph::bufferptr(ceph::buffer::create(8 << 10, 0)));
1278 mbl2
.append(ceph::bufferptr(ceph::buffer::create(16 << 10, 0)));
1279 mbl3
.append(ceph::bufferptr(ceph::buffer::create(12 << 10, 0)));
1280 auto [mlp1
, mext1
, mrp1
] = overwrite_pin(
1281 t
, std::move(mpin
), 8 << 10 , 8 << 10, mbl1
);
1282 auto [mlp2
, mext2
, mrp2
] = overwrite_pin(
1283 t
, std::move(mrp1
), 4 << 10 , 16 << 10, mbl2
);
1284 auto [mlpin3
, me3
, mrpin3
] = overwrite_pin(
1285 t
, std::move(mrp2
), 4 << 10 , 12 << 10, mbl3
);
1286 auto mlext1
= get_extent(t
, mlp1
->get_key(), mlp1
->get_length());
1287 auto mlext2
= get_extent(t
, mlp2
->get_key(), mlp2
->get_length());
1288 auto mlext3
= get_extent(t
, mlpin3
->get_key(), mlpin3
->get_length());
1289 auto mrext3
= get_extent(t
, mrpin3
->get_key(), mrpin3
->get_length());
1290 EXPECT_EQ('a', mlext1
->get_bptr().c_str()[0]);
1291 EXPECT_EQ('b', mlext2
->get_bptr().c_str()[0]);
1292 EXPECT_EQ('c', mlext3
->get_bptr().c_str()[0]);
1293 EXPECT_EQ('d', mrext3
->get_bptr().c_str()[0]);
1294 auto mutate_mlext1
= mutate_extent(t
, mlext1
);
1295 auto mutate_mlext2
= mutate_extent(t
, mlext2
);
1296 auto mutate_mlext3
= mutate_extent(t
, mlext3
);
1297 auto mutate_mrext3
= mutate_extent(t
, mrext3
);
1298 ASSERT_TRUE(mutate_mlext1
->is_exist_mutation_pending());
1299 ASSERT_TRUE(mutate_mlext2
->is_exist_mutation_pending());
1300 ASSERT_TRUE(mutate_mlext3
->is_exist_mutation_pending());
1301 ASSERT_TRUE(mutate_mrext3
->is_exist_mutation_pending());
1302 ASSERT_TRUE(mutate_mlext1
.get() == mlext1
.get());
1303 ASSERT_TRUE(mutate_mlext2
.get() == mlext2
.get());
1304 ASSERT_TRUE(mutate_mlext3
.get() == mlext3
.get());
1305 ASSERT_TRUE(mutate_mrext3
.get() == mrext3
.get());
1307 bufferlist lbl1
, rbl1
;
1308 lbl1
.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
1309 auto [llp1
, lext1
, lrp1
] = overwrite_pin(
1310 t
, std::move(lpin
), 0 , 32 << 10, lbl1
);
1315 rbl1
.append(ceph::bufferptr(ceph::buffer::create(32 << 10, 0)));
1316 auto [rlp1
, rext1
, rrp1
] = overwrite_pin(
1317 t
, std::move(rpin
), 32 << 10 , 32 << 10, rbl1
);
1322 submit_transaction(std::move(t
));
1330 void test_remap_pin_concurrent() {
1332 constexpr unsigned REMAP_NUM
= 32;
1333 constexpr size_t offset
= 0;
1334 constexpr size_t length
= 256 << 10;
1336 auto t
= create_transaction();
1337 auto extent
= alloc_extent(t
, offset
, length
);
1338 ASSERT_EQ(length
, extent
->get_length());
1339 submit_transaction(std::move(t
));
1345 seastar::parallel_for_each(
1346 boost::make_counting_iterator(0u),
1347 boost::make_counting_iterator(REMAP_NUM
),
1349 return seastar::async([&] {
1350 uint32_t pieces
= std::uniform_int_distribution
<>(6, 31)(gen
);
1351 std::set
<uint32_t> split_points
;
1352 for (uint32_t i
= 0; i
< pieces
; i
++) {
1353 auto p
= std::uniform_int_distribution
<>(1, 256)(gen
);
1354 split_points
.insert(p
- p
% 4);
1357 auto t
= create_transaction();
1358 auto pin0
= try_get_pin(t
, offset
);
1359 if (!pin0
|| pin0
->get_length() != length
) {
1364 auto last_pin
= pin0
->duplicate();
1365 ASSERT_TRUE(!split_points
.empty());
1366 for (auto off
: split_points
) {
1367 if (off
== 0 || off
>= 255) {
1370 auto new_off
= (off
<< 10) - last_pin
->get_key();
1371 auto new_len
= last_pin
->get_length() - new_off
;
1372 //always remap right extent at new split_point
1373 auto pin
= remap_pin(t
, std::move(last_pin
), new_off
, new_len
);
1378 last_pin
= pin
->duplicate();
1380 auto last_ext
= try_get_extent(t
, last_pin
->get_key());
1382 auto last_ext1
= mutate_extent(t
, last_ext
);
1383 ASSERT_TRUE(last_ext1
->is_exist_mutation_pending());
1389 if (try_submit_transaction(std::move(t
))) {
1391 logger().info("transaction {} submit the transction",
1392 static_cast<void*>(t
.t
.get()));
1397 }).handle_exception([](std::exception_ptr e
) {
1398 logger().info("{}", e
);
1400 logger().info("test_remap_pin_concurrent: "
1401 "early_exit {} conflicted {} success {}",
1402 early_exit
, conflicted
, success
);
1403 ASSERT_TRUE(success
== 1);
1404 ASSERT_EQ(success
+ conflicted
+ early_exit
, REMAP_NUM
);
1410 void test_overwrite_pin_concurrent() {
1412 constexpr unsigned REMAP_NUM
= 32;
1413 constexpr size_t offset
= 0;
1414 constexpr size_t length
= 256 << 10;
1416 auto t
= create_transaction();
1417 auto extent
= alloc_extent(t
, offset
, length
);
1418 ASSERT_EQ(length
, extent
->get_length());
1419 submit_transaction(std::move(t
));
1425 seastar::parallel_for_each(
1426 boost::make_counting_iterator(0u),
1427 boost::make_counting_iterator(REMAP_NUM
),
1429 return seastar::async([&] {
1430 uint32_t pieces
= std::uniform_int_distribution
<>(6, 31)(gen
);
1431 if (pieces
% 2 == 1) {
1434 std::list
<uint32_t> split_points
;
1435 for (uint32_t i
= 0; i
< pieces
; i
++) {
1436 auto p
= std::uniform_int_distribution
<>(1, 120)(gen
);
1437 split_points
.push_back(p
- p
% 4);
1439 split_points
.sort();
1441 auto t
= create_transaction();
1442 auto pin0
= try_get_pin(t
, offset
);
1443 if (!pin0
|| pin0
->get_length() != length
) {
1448 auto empty_transaction
= true;
1449 auto last_rpin
= pin0
->duplicate();
1450 ASSERT_TRUE(!split_points
.empty());
1451 while(!split_points
.empty()) {
1452 // new overwrite area: start_off ~ end_off
1453 auto start_off
= split_points
.front();
1454 split_points
.pop_front();
1455 auto end_off
= split_points
.front();
1456 split_points
.pop_front();
1457 ASSERT_TRUE(start_off
<= end_off
);
1458 if (((end_off
<< 10) == pin0
->get_key() + pin0
->get_length())
1459 || (start_off
== end_off
)) {
1460 if (split_points
.empty() && empty_transaction
) {
1466 empty_transaction
= false;
1467 auto new_off
= (start_off
<< 10) - last_rpin
->get_key();
1468 auto new_len
= (end_off
- start_off
) << 10;
1470 bl
.append(ceph::bufferptr(ceph::buffer::create(new_len
, 0)));
1471 auto [lpin
, ext
, rpin
] = overwrite_pin(
1472 t
, last_rpin
->duplicate(), new_off
, new_len
, bl
);
1477 // lpin is nullptr might not cause by confliction,
1478 // it might just not exist.
1480 auto lext
= try_get_extent(t
, lpin
->get_key());
1485 if (get_random_contents() % 2 == 0) {
1486 auto lext1
= mutate_extent(t
, lext
);
1487 ASSERT_TRUE(lext1
->is_exist_mutation_pending());
1491 last_rpin
= rpin
->duplicate();
1493 auto last_rext
= try_get_extent(t
, last_rpin
->get_key());
1498 if (get_random_contents() % 2 == 0) {
1499 auto last_rext1
= mutate_extent(t
, last_rext
);
1500 ASSERT_TRUE(last_rext1
->is_exist_mutation_pending());
1503 if (try_submit_transaction(std::move(t
))) {
1505 logger().info("transaction {} submit the transction",
1506 static_cast<void*>(t
.t
.get()));
1511 }).handle_exception([](std::exception_ptr e
) {
1512 logger().info("{}", e
);
1514 logger().info("test_overwrite_pin_concurrent: "
1515 "early_exit {} conflicted {} success {}",
1516 early_exit
, conflicted
, success
);
1517 ASSERT_TRUE(success
== 1 || early_exit
== REMAP_NUM
);
1518 ASSERT_EQ(success
+ conflicted
+ early_exit
, REMAP_NUM
);
1525 struct tm_single_device_test_t
:
1526 public transaction_manager_test_t
{
1528 tm_single_device_test_t() : transaction_manager_test_t(1, 0) {}
1531 struct tm_multi_device_test_t
:
1532 public transaction_manager_test_t
{
1534 tm_multi_device_test_t() : transaction_manager_test_t(3, 0) {}
1537 struct tm_multi_tier_device_test_t
:
1538 public transaction_manager_test_t
{
1540 tm_multi_tier_device_test_t() : transaction_manager_test_t(1, 2) {}
1543 TEST_P(tm_single_device_test_t
, basic
)
1545 constexpr laddr_t SIZE
= 4096;
1547 constexpr laddr_t ADDR
= 0xFF * SIZE
;
1549 auto t
= create_transaction();
1550 auto extent
= alloc_extent(
1555 ASSERT_EQ(ADDR
, extent
->get_laddr());
1558 submit_transaction(std::move(t
));
1564 TEST_P(tm_single_device_test_t
, mutate
)
1566 constexpr laddr_t SIZE
= 4096;
1568 constexpr laddr_t ADDR
= 0xFF * SIZE
;
1570 auto t
= create_transaction();
1571 auto extent
= alloc_extent(
1576 ASSERT_EQ(ADDR
, extent
->get_laddr());
1579 submit_transaction(std::move(t
));
1582 ASSERT_TRUE(check_usage());
1585 auto t
= create_transaction();
1586 auto ext
= get_extent(
1590 auto mut
= mutate_extent(t
, ext
);
1593 submit_transaction(std::move(t
));
1596 ASSERT_TRUE(check_usage());
1602 TEST_P(tm_single_device_test_t
, allocate_lba_conflict
)
1604 constexpr laddr_t SIZE
= 4096;
1606 constexpr laddr_t ADDR
= 0xFF * SIZE
;
1607 constexpr laddr_t ADDR2
= 0xFE * SIZE
;
1608 auto t
= create_transaction();
1609 auto t2
= create_transaction();
1611 // These should conflict as they should both modify the lba root
1612 auto extent
= alloc_extent(
1617 ASSERT_EQ(ADDR
, extent
->get_laddr());
1621 auto extent2
= alloc_extent(
1626 ASSERT_EQ(ADDR2
, extent2
->get_laddr());
1630 submit_transaction(std::move(t2
));
1631 submit_transaction_expect_conflict(std::move(t
));
1635 TEST_P(tm_single_device_test_t
, mutate_lba_conflict
)
1637 constexpr laddr_t SIZE
= 4096;
1640 auto t
= create_transaction();
1641 for (unsigned i
= 0; i
< 300; ++i
) {
1642 auto extent
= alloc_extent(
1648 submit_transaction(std::move(t
));
1652 constexpr laddr_t ADDR
= 150 * SIZE
;
1654 auto t
= create_transaction();
1655 auto t2
= create_transaction();
1657 mutate_addr(t
, ADDR
, SIZE
);
1658 mutate_addr(t2
, ADDR
, SIZE
);
1660 submit_transaction(std::move(t
));
1661 submit_transaction_expect_conflict(std::move(t2
));
1666 auto t
= create_transaction();
1667 mutate_addr(t
, ADDR
, SIZE
);
1668 submit_transaction(std::move(t
));
1674 TEST_P(tm_single_device_test_t
, concurrent_mutate_lba_no_conflict
)
1676 constexpr laddr_t SIZE
= 4096;
1677 constexpr size_t NUM
= 500;
1678 constexpr laddr_t addr
= 0;
1679 constexpr laddr_t addr2
= SIZE
* (NUM
- 1);
1682 auto t
= create_transaction();
1683 for (unsigned i
= 0; i
< NUM
; ++i
) {
1684 auto extent
= alloc_extent(
1689 submit_transaction(std::move(t
));
1693 auto t
= create_transaction();
1694 auto t2
= create_transaction();
1696 mutate_addr(t
, addr
, SIZE
);
1697 mutate_addr(t2
, addr2
, SIZE
);
1699 submit_transaction(std::move(t
));
1700 submit_transaction(std::move(t2
));
1706 TEST_P(tm_single_device_test_t
, create_remove_same_transaction
)
1708 constexpr laddr_t SIZE
= 4096;
1710 constexpr laddr_t ADDR
= 0xFF * SIZE
;
1712 auto t
= create_transaction();
1713 auto extent
= alloc_extent(
1718 ASSERT_EQ(ADDR
, extent
->get_laddr());
1723 extent
= alloc_extent(
1729 submit_transaction(std::move(t
));
1737 TEST_P(tm_single_device_test_t
, split_merge_read_same_transaction
)
1739 constexpr laddr_t SIZE
= 4096;
1742 auto t
= create_transaction();
1743 for (unsigned i
= 0; i
< 300; ++i
) {
1744 auto extent
= alloc_extent(
1750 submit_transaction(std::move(t
));
1754 auto t
= create_transaction();
1755 for (unsigned i
= 0; i
< 240; ++i
) {
1761 submit_transaction(std::move(t
));
1767 TEST_P(tm_single_device_test_t
, inc_dec_ref
)
1769 constexpr laddr_t SIZE
= 4096;
1771 constexpr laddr_t ADDR
= 0xFF * SIZE
;
1773 auto t
= create_transaction();
1774 auto extent
= alloc_extent(
1779 ASSERT_EQ(ADDR
, extent
->get_laddr());
1782 submit_transaction(std::move(t
));
1787 auto t
= create_transaction();
1791 submit_transaction(std::move(t
));
1795 auto t
= create_transaction();
1799 submit_transaction(std::move(t
));
1804 auto t
= create_transaction();
1808 submit_transaction(std::move(t
));
1814 TEST_P(tm_single_device_test_t
, cause_lba_split
)
1816 constexpr laddr_t SIZE
= 4096;
1818 for (unsigned i
= 0; i
< 200; ++i
) {
1819 auto t
= create_transaction();
1820 auto extent
= alloc_extent(
1825 ASSERT_EQ(i
* SIZE
, extent
->get_laddr());
1826 submit_transaction(std::move(t
));
1832 TEST_P(tm_single_device_test_t
, random_writes
)
1834 constexpr size_t TOTAL
= 4<<20;
1835 constexpr size_t BSIZE
= 4<<10;
1836 constexpr size_t PADDING_SIZE
= 256<<10;
1837 constexpr size_t BLOCKS
= TOTAL
/ BSIZE
;
1839 for (unsigned i
= 0; i
< BLOCKS
; ++i
) {
1840 auto t
= create_transaction();
1841 auto extent
= alloc_extent(
1845 ASSERT_EQ(i
* BSIZE
, extent
->get_laddr());
1846 submit_transaction(std::move(t
));
1849 for (unsigned i
= 0; i
< 4; ++i
) {
1850 for (unsigned j
= 0; j
< 65; ++j
) {
1851 auto t
= create_transaction();
1852 for (unsigned k
= 0; k
< 2; ++k
) {
1853 auto ext
= get_extent(
1855 get_random_laddr(BSIZE
, TOTAL
),
1857 auto mut
= mutate_extent(t
, ext
);
1858 // pad out transaction
1859 auto padding
= alloc_extent(
1861 TOTAL
+ (k
* PADDING_SIZE
),
1863 dec_ref(t
, padding
->get_laddr());
1865 submit_transaction(std::move(t
));
1868 logger().info("random_writes: {} checking", i
);
1870 logger().info("random_writes: {} done replaying/checking", i
);
1875 TEST_P(tm_single_device_test_t
, find_hole_assert_trigger
)
1877 constexpr unsigned max
= 10;
1878 constexpr size_t BSIZE
= 4<<10;
1881 return seastar::parallel_for_each(
1882 boost::make_counting_iterator(0u),
1883 boost::make_counting_iterator(max
),
1884 [&, this](auto idx
) {
1885 return allocate_sequentially(BSIZE
, num
);
1890 TEST_P(tm_single_device_test_t
, remap_lazy_read
)
1892 constexpr laddr_t offset
= 0;
1893 constexpr size_t length
= 256 << 10;
1894 run_async([this, offset
] {
1896 auto t
= create_transaction();
1897 auto extent
= alloc_extent(
1902 ASSERT_EQ(offset
, extent
->get_laddr());
1904 submit_transaction(std::move(t
));
1909 auto t
= create_transaction();
1910 auto pin
= get_pin(t
, offset
);
1911 auto rpin
= remap_pin(t
, std::move(pin
), 0, 128 << 10);
1913 submit_transaction(std::move(t
));
1918 auto t
= create_transaction();
1919 auto pin
= get_pin(t
, offset
);
1921 bl
.append(ceph::bufferptr(ceph::buffer::create(64 << 10, 0)));
1922 auto [lpin
, ext
, rpin
] = overwrite_pin(
1923 t
, std::move(pin
), 4 << 10 , 64 << 10, bl
);
1925 submit_transaction(std::move(t
));
1932 TEST_P(tm_single_device_test_t
, random_writes_concurrent
)
1934 test_random_writes_concurrent();
1937 TEST_P(tm_multi_device_test_t
, random_writes_concurrent
)
1939 test_random_writes_concurrent();
1942 TEST_P(tm_multi_tier_device_test_t
, evict
)
1947 TEST_P(tm_single_device_test_t
, parallel_extent_read
)
1949 test_parallel_extent_read();
1952 TEST_P(tm_single_device_test_t
, test_remap_pin
)
1957 TEST_P(tm_single_device_test_t
, test_overwrite_pin
)
1959 test_overwrite_pin();
1962 TEST_P(tm_single_device_test_t
, test_remap_pin_concurrent
)
1964 test_remap_pin_concurrent();
1967 TEST_P(tm_single_device_test_t
, test_overwrite_pin_concurrent
)
1969 test_overwrite_pin_concurrent();
1972 INSTANTIATE_TEST_SUITE_P(
1973 transaction_manager_test
,
1974 tm_single_device_test_t
,
1981 INSTANTIATE_TEST_SUITE_P(
1982 transaction_manager_test
,
1983 tm_multi_device_test_t
,
1989 INSTANTIATE_TEST_SUITE_P(
1990 transaction_manager_test
,
1991 tm_multi_tier_device_test_t
,