]> git.proxmox.com Git - ceph.git/blob - ceph/src/test/crimson/seastore/test_transaction_manager.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / test / crimson / seastore / test_transaction_manager.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include <random>
5
6 #include <boost/iterator/counting_iterator.hpp>
7
8 #include "test/crimson/gtest_seastar.h"
9 #include "test/crimson/seastore/transaction_manager_test_state.h"
10
11 #include "crimson/os/seastore/segment_cleaner.h"
12 #include "crimson/os/seastore/cache.h"
13 #include "crimson/os/seastore/transaction_manager.h"
14 #include "crimson/os/seastore/segment_manager/ephemeral.h"
15 #include "crimson/os/seastore/segment_manager.h"
16
17 #include "test/crimson/seastore/test_block.h"
18
19 using namespace crimson;
20 using namespace crimson::os;
21 using namespace crimson::os::seastore;
22
23 namespace {
24 [[maybe_unused]] seastar::logger& logger() {
25 return crimson::get_logger(ceph_subsys_test);
26 }
27 }
28
29 struct test_extent_record_t {
30 test_extent_desc_t desc;
31 unsigned refcount = 0;
32 test_extent_record_t() = default;
33 test_extent_record_t(
34 const test_extent_desc_t &desc,
35 unsigned refcount) : desc(desc), refcount(refcount) {}
36
37 void update(const test_extent_desc_t &to) {
38 desc = to;
39 }
40
41 bool operator==(const test_extent_desc_t &rhs) const {
42 return desc == rhs;
43 }
44 bool operator!=(const test_extent_desc_t &rhs) const {
45 return desc != rhs;
46 }
47 };
48
49 std::ostream &operator<<(std::ostream &lhs, const test_extent_record_t &rhs) {
50 return lhs << "test_extent_record_t(" << rhs.desc
51 << ", refcount=" << rhs.refcount << ")";
52 }
53
54 struct transaction_manager_test_t :
55 public seastar_test_suite_t,
56 TMTestState {
57
58 std::random_device rd;
59 std::mt19937 gen;
60
61 transaction_manager_test_t()
62 : gen(rd()) {
63 }
64
65 laddr_t get_random_laddr(size_t block_size, laddr_t limit) {
66 return block_size *
67 std::uniform_int_distribution<>(0, (limit / block_size) - 1)(gen);
68 }
69
70 char get_random_contents() {
71 return static_cast<char>(std::uniform_int_distribution<>(0, 255)(gen));
72 }
73
74 seastar::future<> set_up_fut() final {
75 return tm_setup();
76 }
77
78 seastar::future<> tear_down_fut() final {
79 return tm_teardown();
80 }
81
82 struct test_extents_t : std::map<laddr_t, test_extent_record_t> {
83 using delta_t = std::map<laddr_t, std::optional<test_extent_record_t>>;
84
85 struct delta_overlay_t {
86 const test_extents_t &extents;
87 const delta_t &delta;
88
89 delta_overlay_t(
90 const test_extents_t &extents,
91 const delta_t &delta)
92 : extents(extents), delta(delta) {}
93
94
95 class iterator {
96 friend class test_extents_t;
97
98 const delta_overlay_t &parent;
99 test_extents_t::const_iterator biter;
100 delta_t::const_iterator oiter;
101 std::optional<std::pair<laddr_t, test_extent_record_t>> cur;
102
103 iterator(
104 const delta_overlay_t &parent,
105 test_extents_t::const_iterator biter,
106 delta_t::const_iterator oiter)
107 : parent(parent), biter(biter), oiter(oiter) {}
108
109 laddr_t get_bkey() {
110 return biter == parent.extents.end() ? L_ADDR_MAX : biter->first;
111 }
112
113 laddr_t get_okey() {
114 return oiter == parent.delta.end() ? L_ADDR_MAX : oiter->first;
115 }
116
117 bool is_end() {
118 return oiter == parent.delta.end() && biter == parent.extents.end();
119 }
120
121 bool is_valid() {
122 return is_end() ||
123 ((get_okey() < get_bkey()) && (oiter->second)) ||
124 (get_okey() > get_bkey());
125 }
126
127 auto get_pair() {
128 assert(is_valid());
129 assert(!is_end());
130 auto okey = get_okey();
131 auto bkey = get_bkey();
132 return (
133 bkey < okey ?
134 std::pair<laddr_t, test_extent_record_t>(*biter) :
135 std::make_pair(okey, *(oiter->second)));
136 }
137
138 void adjust() {
139 while (!is_valid()) {
140 if (get_okey() < get_bkey()) {
141 assert(!oiter->second);
142 ++oiter;
143 } else {
144 assert(get_okey() == get_bkey());
145 ++biter;
146 }
147 }
148 assert(is_valid());
149 if (!is_end()) {
150 cur = get_pair();
151 } else {
152 cur = std::nullopt;
153 }
154 }
155
156 public:
157 iterator(const iterator &) = default;
158 iterator(iterator &&) = default;
159
160 iterator &operator++() {
161 assert(is_valid());
162 assert(!is_end());
163 if (get_bkey() < get_okey()) {
164 ++biter;
165 } else {
166 ++oiter;
167 }
168 adjust();
169 return *this;
170 }
171
172 bool operator==(const iterator &o) const {
173 return o.biter == biter && o.oiter == oiter;
174 }
175 bool operator!=(const iterator &o) const {
176 return !(*this == o);
177 }
178
179 auto operator*() {
180 assert(!is_end());
181 return *cur;
182 }
183 auto operator->() {
184 assert(!is_end());
185 return &*cur;
186 }
187 };
188
189 iterator begin() {
190 auto ret = iterator{*this, extents.begin(), delta.begin()};
191 ret.adjust();
192 return ret;
193 }
194
195 iterator end() {
196 auto ret = iterator{*this, extents.end(), delta.end()};
197 // adjust unnecessary
198 return ret;
199 }
200
201 iterator lower_bound(laddr_t l) {
202 auto ret = iterator{*this, extents.lower_bound(l), delta.lower_bound(l)};
203 ret.adjust();
204 return ret;
205 }
206
207 iterator upper_bound(laddr_t l) {
208 auto ret = iterator{*this, extents.upper_bound(l), delta.upper_bound(l)};
209 ret.adjust();
210 return ret;
211 }
212
213 iterator find(laddr_t l) {
214 auto ret = lower_bound(l);
215 if (ret == end() || ret->first != l) {
216 return end();
217 } else {
218 return ret;
219 }
220 }
221 };
222 private:
223 void check_available(
224 laddr_t addr, extent_len_t len, const delta_t &delta
225 ) const {
226 delta_overlay_t overlay(*this, delta);
227 for (const auto &i: overlay) {
228 if (i.first < addr) {
229 EXPECT_FALSE(i.first + i.second.desc.len > addr);
230 } else {
231 EXPECT_FALSE(addr + len > i.first);
232 }
233 }
234 }
235
236 void check_hint(
237 laddr_t hint,
238 laddr_t addr,
239 extent_len_t len,
240 delta_t &delta) const {
241 delta_overlay_t overlay(*this, delta);
242 auto iter = overlay.lower_bound(hint);
243 laddr_t last = hint;
244 while (true) {
245 if (iter == overlay.end() || iter->first > addr) {
246 EXPECT_EQ(addr, last);
247 break;
248 }
249 EXPECT_FALSE(iter->first - last > len);
250 last = iter->first + iter->second.desc.len;
251 ++iter;
252 }
253 }
254
255 std::optional<test_extent_record_t> &populate_delta(
256 laddr_t addr, delta_t &delta, const test_extent_desc_t *desc) const {
257 auto diter = delta.find(addr);
258 if (diter != delta.end())
259 return diter->second;
260
261 auto iter = find(addr);
262 if (iter == end()) {
263 assert(desc);
264 auto ret = delta.emplace(
265 std::make_pair(addr, test_extent_record_t{*desc, 0}));
266 assert(ret.second);
267 return ret.first->second;
268 } else {
269 auto ret = delta.emplace(*iter);
270 assert(ret.second);
271 return ret.first->second;
272 }
273 }
274 public:
275 delta_overlay_t get_overlay(const delta_t &delta) const {
276 return delta_overlay_t{*this, delta};
277 }
278
279 void insert(TestBlock &extent, delta_t &delta) const {
280 check_available(extent.get_laddr(), extent.get_length(), delta);
281 delta[extent.get_laddr()] =
282 test_extent_record_t{extent.get_desc(), 1};
283 }
284
285 void alloced(laddr_t hint, TestBlock &extent, delta_t &delta) const {
286 check_hint(hint, extent.get_laddr(), extent.get_length(), delta);
287 insert(extent, delta);
288 }
289
290 bool contains(laddr_t addr, const delta_t &delta) const {
291 delta_overlay_t overlay(*this, delta);
292 return overlay.find(addr) != overlay.end();
293 }
294
295 test_extent_record_t get(laddr_t addr, const delta_t &delta) const {
296 delta_overlay_t overlay(*this, delta);
297 auto iter = overlay.find(addr);
298 assert(iter != overlay.end());
299 return iter->second;
300 }
301
302 void update(
303 laddr_t addr,
304 const test_extent_desc_t &desc,
305 delta_t &delta) const {
306 auto &rec = populate_delta(addr, delta, &desc);
307 assert(rec);
308 rec->desc = desc;
309 }
310
311 int inc_ref(
312 laddr_t addr,
313 delta_t &delta) const {
314 auto &rec = populate_delta(addr, delta, nullptr);
315 assert(rec);
316 return ++rec->refcount;
317 }
318
319 int dec_ref(
320 laddr_t addr,
321 delta_t &delta) const {
322 auto &rec = populate_delta(addr, delta, nullptr);
323 assert(rec);
324 assert(rec->refcount > 0);
325 rec->refcount--;
326 if (rec->refcount == 0) {
327 delta[addr] = std::nullopt;
328 return 0;
329 } else {
330 return rec->refcount;
331 }
332 }
333
334 void consume(const delta_t &delta) {
335 for (const auto &i : delta) {
336 if (i.second) {
337 (*this)[i.first] = *i.second;
338 } else {
339 erase(i.first);
340 }
341 }
342 }
343
344 } test_mappings;
345
346 struct test_transaction_t {
347 TransactionRef t;
348 test_extents_t::delta_t mapping_delta;
349 };
350
351 test_transaction_t create_transaction() {
352 return { create_mutate_transaction(), {} };
353 }
354
355 test_transaction_t create_weak_test_transaction() {
356 return { create_weak_transaction(), {} };
357 }
358
359 TestBlockRef alloc_extent(
360 test_transaction_t &t,
361 laddr_t hint,
362 extent_len_t len,
363 char contents) {
364 auto extent = with_trans_intr(*(t.t), [&](auto& trans) {
365 return tm->alloc_extent<TestBlock>(trans, hint, len);
366 }).unsafe_get0();
367 extent->set_contents(contents);
368 EXPECT_FALSE(test_mappings.contains(extent->get_laddr(), t.mapping_delta));
369 EXPECT_EQ(len, extent->get_length());
370 test_mappings.alloced(hint, *extent, t.mapping_delta);
371 return extent;
372 }
373
374 TestBlockRef alloc_extent(
375 test_transaction_t &t,
376 laddr_t hint,
377 extent_len_t len) {
378 return alloc_extent(
379 t,
380 hint,
381 len,
382 get_random_contents());
383 }
384
385 bool check_usage() {
386 auto t = create_weak_test_transaction();
387 SpaceTrackerIRef tracker(segment_cleaner->get_empty_space_tracker());
388 with_trans_intr(
389 *t.t,
390 [this, &tracker](auto &t) {
391 return lba_manager->scan_mapped_space(
392 t,
393 [&tracker](auto offset, auto len) {
394 tracker->allocate(
395 offset.as_seg_paddr().get_segment_id(),
396 offset.as_seg_paddr().get_segment_off(),
397 len);
398 });
399 }).unsafe_get0();
400 return segment_cleaner->debug_check_space(*tracker);
401 }
402
403 void replay() {
404 logger().debug("{}: begin", __func__);
405 EXPECT_TRUE(check_usage());
406 restart();
407 logger().debug("{}: end", __func__);
408 }
409
410 void check() {
411 check_mappings();
412 check_usage();
413 }
414
415 void check_mappings() {
416 auto t = create_weak_test_transaction();
417 check_mappings(t);
418 }
419
420 TestBlockRef get_extent(
421 test_transaction_t &t,
422 laddr_t addr,
423 extent_len_t len) {
424 ceph_assert(test_mappings.contains(addr, t.mapping_delta));
425 ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
426
427 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
428 return tm->read_extent<TestBlock>(trans, addr, len);
429 }).unsafe_get0();
430 EXPECT_EQ(addr, ext->get_laddr());
431 return ext;
432 }
433
434 TestBlockRef try_get_extent(
435 test_transaction_t &t,
436 laddr_t addr,
437 extent_len_t len) {
438 ceph_assert(test_mappings.contains(addr, t.mapping_delta));
439 ceph_assert(test_mappings.get(addr, t.mapping_delta).desc.len == len);
440
441 using ertr = with_trans_ertr<TransactionManager::read_extent_iertr>;
442 using ret = ertr::future<TestBlockRef>;
443 auto ext = with_trans_intr(*(t.t), [&](auto& trans) {
444 return tm->read_extent<TestBlock>(trans, addr, len);
445 }).safe_then([](auto ext) -> ret {
446 return ertr::make_ready_future<TestBlockRef>(ext);
447 }).handle_error(
448 [](const crimson::ct_error::eagain &e) {
449 return seastar::make_ready_future<TestBlockRef>();
450 },
451 crimson::ct_error::assert_all{
452 "get_extent got invalid error"
453 }
454 ).get0();
455 if (ext) {
456 EXPECT_EQ(addr, ext->get_laddr());
457 }
458 return ext;
459 }
460
461 test_block_mutator_t mutator;
462 TestBlockRef mutate_extent(
463 test_transaction_t &t,
464 TestBlockRef ref) {
465 ceph_assert(test_mappings.contains(ref->get_laddr(), t.mapping_delta));
466 ceph_assert(
467 test_mappings.get(ref->get_laddr(), t.mapping_delta).desc.len ==
468 ref->get_length());
469
470 auto ext = tm->get_mutable_extent(*t.t, ref)->cast<TestBlock>();
471 EXPECT_EQ(ext->get_laddr(), ref->get_laddr());
472 EXPECT_EQ(ext->get_desc(), ref->get_desc());
473 mutator.mutate(*ext, gen);
474
475 test_mappings.update(ext->get_laddr(), ext->get_desc(), t.mapping_delta);
476 return ext;
477 }
478
479 TestBlockRef mutate_addr(
480 test_transaction_t &t,
481 laddr_t offset,
482 size_t length) {
483 auto ext = get_extent(t, offset, length);
484 mutate_extent(t, ext);
485 return ext;
486 }
487
488 void inc_ref(test_transaction_t &t, laddr_t offset) {
489 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
490 ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
491
492 auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
493 return tm->inc_ref(trans, offset);
494 }).unsafe_get0();
495 auto check_refcnt = test_mappings.inc_ref(offset, t.mapping_delta);
496 EXPECT_EQ(refcnt, check_refcnt);
497 }
498
499 void dec_ref(test_transaction_t &t, laddr_t offset) {
500 ceph_assert(test_mappings.contains(offset, t.mapping_delta));
501 ceph_assert(test_mappings.get(offset, t.mapping_delta).refcount > 0);
502
503 auto refcnt = with_trans_intr(*(t.t), [&](auto& trans) {
504 return tm->dec_ref(trans, offset);
505 }).unsafe_get0();
506 auto check_refcnt = test_mappings.dec_ref(offset, t.mapping_delta);
507 EXPECT_EQ(refcnt, check_refcnt);
508 if (refcnt == 0)
509 logger().debug("dec_ref: {} at refcount 0", offset);
510 }
511
512 void check_mappings(test_transaction_t &t) {
513 auto overlay = test_mappings.get_overlay(t.mapping_delta);
514 for (const auto &i: overlay) {
515 logger().debug("check_mappings: {}->{}", i.first, i.second);
516 auto ext = get_extent(t, i.first, i.second.desc.len);
517 EXPECT_EQ(i.second, ext->get_desc());
518 }
519 with_trans_intr(
520 *t.t,
521 [this, &overlay](auto &t) {
522 return lba_manager->scan_mappings(
523 t,
524 0,
525 L_ADDR_MAX,
526 [iter=overlay.begin(), &overlay](auto l, auto p, auto len) mutable {
527 EXPECT_NE(iter, overlay.end());
528 logger().debug(
529 "check_mappings: scan {}",
530 l);
531 EXPECT_EQ(l, iter->first);
532 ++iter;
533 });
534 }).unsafe_get0();
535 }
536
537 bool try_submit_transaction(test_transaction_t t) {
538 using ertr = with_trans_ertr<TransactionManager::submit_transaction_iertr>;
539 using ret = ertr::future<bool>;
540 bool success = submit_transaction_fut(*t.t
541 ).safe_then([]() -> ret {
542 return ertr::make_ready_future<bool>(true);
543 }).handle_error(
544 [](const crimson::ct_error::eagain &e) {
545 return seastar::make_ready_future<bool>(false);
546 },
547 crimson::ct_error::assert_all{
548 "try_submit_transaction hit invalid error"
549 }
550 ).then([this](auto ret) {
551 return segment_cleaner->run_until_halt().then([ret] { return ret; });
552 }).get0();
553
554 if (success) {
555 test_mappings.consume(t.mapping_delta);
556 }
557
558 return success;
559 }
560
561 void submit_transaction(test_transaction_t &&t) {
562 bool success = try_submit_transaction(std::move(t));
563 EXPECT_TRUE(success);
564 }
565
566 void submit_transaction_expect_conflict(test_transaction_t &&t) {
567 bool success = try_submit_transaction(std::move(t));
568 EXPECT_FALSE(success);
569 }
570
571 auto allocate_sequentially(const size_t& size, int &num) {
572 return repeat_eagain([&, this] {
573 return seastar::do_with(
574 create_transaction(),
575 [&, this](auto &t) {
576 return with_trans_intr(
577 *t.t,
578 [&, this](auto &) {
579 return trans_intr::do_for_each(
580 boost::make_counting_iterator(0),
581 boost::make_counting_iterator(num),
582 [&, this](auto) {
583 return tm->alloc_extent<TestBlock>(
584 *(t.t), L_ADDR_MIN, size
585 ).si_then([&, this](auto extent) {
586 extent->set_contents(get_random_contents());
587 EXPECT_FALSE(
588 test_mappings.contains(extent->get_laddr(), t.mapping_delta));
589 EXPECT_EQ(size, extent->get_length());
590 test_mappings.alloced(extent->get_laddr(), *extent, t.mapping_delta);
591 return seastar::now();
592 });
593 }).si_then([&t, this] {
594 return tm->submit_transaction(*t.t);
595 });
596 }).safe_then([&t, this] {
597 test_mappings.consume(t.mapping_delta);
598 });
599 });
600 }).safe_then([this]() {
601 return segment_cleaner->run_until_halt();
602 }).handle_error(
603 crimson::ct_error::assert_all{
604 "Invalid error in SeaStore::list_collections"
605 }
606 );
607 }
608 };
609
610 TEST_F(transaction_manager_test_t, basic)
611 {
612 constexpr laddr_t SIZE = 4096;
613 run_async([this] {
614 constexpr laddr_t ADDR = 0xFF * SIZE;
615 {
616 auto t = create_transaction();
617 auto extent = alloc_extent(
618 t,
619 ADDR,
620 SIZE,
621 'a');
622 ASSERT_EQ(ADDR, extent->get_laddr());
623 check_mappings(t);
624 check();
625 submit_transaction(std::move(t));
626 check();
627 }
628 });
629 }
630
631 TEST_F(transaction_manager_test_t, mutate)
632 {
633 constexpr laddr_t SIZE = 4096;
634 run_async([this] {
635 constexpr laddr_t ADDR = 0xFF * SIZE;
636 {
637 auto t = create_transaction();
638 auto extent = alloc_extent(
639 t,
640 ADDR,
641 SIZE,
642 'a');
643 ASSERT_EQ(ADDR, extent->get_laddr());
644 check_mappings(t);
645 check();
646 submit_transaction(std::move(t));
647 check();
648 }
649 ASSERT_TRUE(check_usage());
650 replay();
651 {
652 auto t = create_transaction();
653 auto ext = get_extent(
654 t,
655 ADDR,
656 SIZE);
657 auto mut = mutate_extent(t, ext);
658 check_mappings(t);
659 check();
660 submit_transaction(std::move(t));
661 check();
662 }
663 ASSERT_TRUE(check_usage());
664 replay();
665 check();
666 });
667 }
668
669 TEST_F(transaction_manager_test_t, allocate_lba_conflict)
670 {
671 constexpr laddr_t SIZE = 4096;
672 run_async([this] {
673 constexpr laddr_t ADDR = 0xFF * SIZE;
674 constexpr laddr_t ADDR2 = 0xFE * SIZE;
675 auto t = create_transaction();
676 auto t2 = create_transaction();
677
678 // These should conflict as they should both modify the lba root
679 auto extent = alloc_extent(
680 t,
681 ADDR,
682 SIZE,
683 'a');
684 ASSERT_EQ(ADDR, extent->get_laddr());
685 check_mappings(t);
686 check();
687
688 auto extent2 = alloc_extent(
689 t2,
690 ADDR2,
691 SIZE,
692 'a');
693 ASSERT_EQ(ADDR2, extent2->get_laddr());
694 check_mappings(t2);
695 extent2.reset();
696
697 submit_transaction(std::move(t2));
698 submit_transaction_expect_conflict(std::move(t));
699 });
700 }
701
702 TEST_F(transaction_manager_test_t, mutate_lba_conflict)
703 {
704 constexpr laddr_t SIZE = 4096;
705 run_async([this] {
706 {
707 auto t = create_transaction();
708 for (unsigned i = 0; i < 300; ++i) {
709 auto extent = alloc_extent(
710 t,
711 laddr_t(i * SIZE),
712 SIZE);
713 }
714 check_mappings(t);
715 submit_transaction(std::move(t));
716 check();
717 }
718
719 constexpr laddr_t ADDR = 150 * SIZE;
720 {
721 auto t = create_transaction();
722 auto t2 = create_transaction();
723
724 mutate_addr(t, ADDR, SIZE);
725 mutate_addr(t2, ADDR, SIZE);
726
727 submit_transaction(std::move(t));
728 submit_transaction_expect_conflict(std::move(t2));
729 }
730 check();
731
732 {
733 auto t = create_transaction();
734 mutate_addr(t, ADDR, SIZE);
735 submit_transaction(std::move(t));
736 }
737 check();
738 });
739 }
740
741 TEST_F(transaction_manager_test_t, concurrent_mutate_lba_no_conflict)
742 {
743 constexpr laddr_t SIZE = 4096;
744 constexpr size_t NUM = 500;
745 constexpr laddr_t addr = 0;
746 constexpr laddr_t addr2 = SIZE * (NUM - 1);
747 run_async([this] {
748 {
749 auto t = create_transaction();
750 for (unsigned i = 0; i < NUM; ++i) {
751 auto extent = alloc_extent(
752 t,
753 laddr_t(i * SIZE),
754 SIZE);
755 }
756 submit_transaction(std::move(t));
757 }
758
759 {
760 auto t = create_transaction();
761 auto t2 = create_transaction();
762
763 mutate_addr(t, addr, SIZE);
764 mutate_addr(t2, addr2, SIZE);
765
766 submit_transaction(std::move(t));
767 submit_transaction(std::move(t2));
768 }
769 check();
770 });
771 }
772
773 TEST_F(transaction_manager_test_t, create_remove_same_transaction)
774 {
775 constexpr laddr_t SIZE = 4096;
776 run_async([this] {
777 constexpr laddr_t ADDR = 0xFF * SIZE;
778 {
779 auto t = create_transaction();
780 auto extent = alloc_extent(
781 t,
782 ADDR,
783 SIZE,
784 'a');
785 ASSERT_EQ(ADDR, extent->get_laddr());
786 check_mappings(t);
787 dec_ref(t, ADDR);
788 check_mappings(t);
789
790 extent = alloc_extent(
791 t,
792 ADDR,
793 SIZE,
794 'a');
795
796 submit_transaction(std::move(t));
797 check();
798 }
799 replay();
800 check();
801 });
802 }
803
804 TEST_F(transaction_manager_test_t, split_merge_read_same_transaction)
805 {
806 constexpr laddr_t SIZE = 4096;
807 run_async([this] {
808 {
809 auto t = create_transaction();
810 for (unsigned i = 0; i < 300; ++i) {
811 auto extent = alloc_extent(
812 t,
813 laddr_t(i * SIZE),
814 SIZE);
815 }
816 check_mappings(t);
817 submit_transaction(std::move(t));
818 check();
819 }
820 {
821 auto t = create_transaction();
822 for (unsigned i = 0; i < 240; ++i) {
823 dec_ref(
824 t,
825 laddr_t(i * SIZE));
826 }
827 check_mappings(t);
828 submit_transaction(std::move(t));
829 check();
830 }
831 });
832 }
833
834 TEST_F(transaction_manager_test_t, inc_dec_ref)
835 {
836 constexpr laddr_t SIZE = 4096;
837 run_async([this] {
838 constexpr laddr_t ADDR = 0xFF * SIZE;
839 {
840 auto t = create_transaction();
841 auto extent = alloc_extent(
842 t,
843 ADDR,
844 SIZE,
845 'a');
846 ASSERT_EQ(ADDR, extent->get_laddr());
847 check_mappings(t);
848 check();
849 submit_transaction(std::move(t));
850 check();
851 }
852 replay();
853 {
854 auto t = create_transaction();
855 inc_ref(t, ADDR);
856 check_mappings(t);
857 check();
858 submit_transaction(std::move(t));
859 check();
860 }
861 {
862 auto t = create_transaction();
863 dec_ref(t, ADDR);
864 check_mappings(t);
865 check();
866 submit_transaction(std::move(t));
867 check();
868 }
869 replay();
870 {
871 auto t = create_transaction();
872 dec_ref(t, ADDR);
873 check_mappings(t);
874 check();
875 submit_transaction(std::move(t));
876 check();
877 }
878 });
879 }
880
881 TEST_F(transaction_manager_test_t, cause_lba_split)
882 {
883 constexpr laddr_t SIZE = 4096;
884 run_async([this] {
885 for (unsigned i = 0; i < 200; ++i) {
886 auto t = create_transaction();
887 auto extent = alloc_extent(
888 t,
889 i * SIZE,
890 SIZE,
891 (char)(i & 0xFF));
892 ASSERT_EQ(i * SIZE, extent->get_laddr());
893 submit_transaction(std::move(t));
894 }
895 check();
896 });
897 }
898
899 TEST_F(transaction_manager_test_t, random_writes)
900 {
901 constexpr size_t TOTAL = 4<<20;
902 constexpr size_t BSIZE = 4<<10;
903 constexpr size_t PADDING_SIZE = 256<<10;
904 constexpr size_t BLOCKS = TOTAL / BSIZE;
905 run_async([this] {
906 for (unsigned i = 0; i < BLOCKS; ++i) {
907 auto t = create_transaction();
908 auto extent = alloc_extent(
909 t,
910 i * BSIZE,
911 BSIZE);
912 ASSERT_EQ(i * BSIZE, extent->get_laddr());
913 submit_transaction(std::move(t));
914 }
915
916 for (unsigned i = 0; i < 4; ++i) {
917 for (unsigned j = 0; j < 65; ++j) {
918 auto t = create_transaction();
919 for (unsigned k = 0; k < 2; ++k) {
920 auto ext = get_extent(
921 t,
922 get_random_laddr(BSIZE, TOTAL),
923 BSIZE);
924 auto mut = mutate_extent(t, ext);
925 // pad out transaction
926 auto padding = alloc_extent(
927 t,
928 TOTAL + (k * PADDING_SIZE),
929 PADDING_SIZE);
930 dec_ref(t, padding->get_laddr());
931 }
932 submit_transaction(std::move(t));
933 }
934 replay();
935 logger().debug("random_writes: checking");
936 check();
937 logger().debug("random_writes: done replaying/checking");
938 }
939 });
940 }
941
942 TEST_F(transaction_manager_test_t, random_writes_concurrent)
943 {
944 constexpr unsigned WRITE_STREAMS = 256;
945
946 constexpr size_t TOTAL = 4<<20;
947 constexpr size_t BSIZE = 4<<10;
948 constexpr size_t BLOCKS = TOTAL / BSIZE;
949 run_async([this] {
950 seastar::parallel_for_each(
951 boost::make_counting_iterator(0u),
952 boost::make_counting_iterator(WRITE_STREAMS),
953 [&](auto idx) {
954 for (unsigned i = idx; i < BLOCKS; i += WRITE_STREAMS) {
955 while (true) {
956 auto t = create_transaction();
957 auto extent = alloc_extent(
958 t,
959 i * BSIZE,
960 BSIZE);
961 ASSERT_EQ(i * BSIZE, extent->get_laddr());
962 if (try_submit_transaction(std::move(t)))
963 break;
964 }
965 }
966 }).get0();
967
968 int writes = 0;
969 unsigned failures = 0;
970 seastar::parallel_for_each(
971 boost::make_counting_iterator(0u),
972 boost::make_counting_iterator(WRITE_STREAMS),
973 [&](auto) {
974 return seastar::async([&] {
975 while (writes < 300) {
976 auto t = create_transaction();
977 auto ext = try_get_extent(
978 t,
979 get_random_laddr(BSIZE, TOTAL),
980 BSIZE);
981 if (!ext){
982 failures++;
983 continue;
984 }
985 auto mut = mutate_extent(t, ext);
986 auto success = try_submit_transaction(std::move(t));
987 writes += success;
988 failures += !success;
989 }
990 });
991 }).get0();
992 replay();
993 logger().debug("random_writes: checking");
994 check();
995 logger().debug(
996 "random_writes: {} suceeded, {} failed",
997 writes,
998 failures
999 );
1000 });
1001 }
1002
1003 TEST_F(transaction_manager_test_t, find_hole_assert_trigger)
1004 {
1005 constexpr unsigned max = 10;
1006 constexpr size_t BSIZE = 4<<10;
1007 int num = 40;
1008 run([&, this] {
1009 return seastar::parallel_for_each(
1010 boost::make_counting_iterator(0u),
1011 boost::make_counting_iterator(max),
1012 [&, this](auto idx) {
1013 return allocate_sequentially(BSIZE, num);
1014 });
1015 });
1016
1017 }
1018