// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
+#include <random>
+
#include "test/crimson/gtest_seastar.h"
#include "crimson/os/seastore/segment_cleaner.h"
struct transaction_manager_test_t : public seastar_test_suite_t {
std::unique_ptr<SegmentManager> segment_manager;
- SegmentCleaner segment_cleaner;
- Journal journal;
- Cache cache;
+ std::unique_ptr<SegmentCleaner> segment_cleaner;
+ std::unique_ptr<Journal> journal;
+ std::unique_ptr<Cache> cache;
LBAManagerRef lba_manager;
- TransactionManager tm;
+ std::unique_ptr<TransactionManager> tm;
+
+ std::random_device rd;
+ std::mt19937 gen;
transaction_manager_test_t()
: segment_manager(create_ephemeral(segment_manager::DEFAULT_TEST_EPHEMERAL)),
- segment_cleaner(
- SegmentCleaner::config_t::default_from_segment_manager(
- *segment_manager)),
- journal(*segment_manager),
- cache(*segment_manager),
- lba_manager(
- lba_manager::create_lba_manager(*segment_manager, cache)),
- tm(*segment_manager, segment_cleaner, journal, cache, *lba_manager) {
- journal.set_segment_provider(&segment_cleaner);
- segment_cleaner.set_extent_callback(&tm);
+ gen(rd()) {
+ init();
+ }
+
+ void init() {
+ segment_cleaner = std::make_unique<SegmentCleaner>(
+ SegmentCleaner::config_t::default_from_segment_manager(
+ *segment_manager));
+ journal = std::make_unique<Journal>(*segment_manager);
+ cache = std::make_unique<Cache>(*segment_manager);
+ lba_manager = lba_manager::create_lba_manager(*segment_manager, *cache);
+ tm = std::make_unique<TransactionManager>(
+ *segment_manager, *segment_cleaner, *journal, *cache, *lba_manager);
+
+ journal->set_segment_provider(&*segment_cleaner);
+ segment_cleaner->set_extent_callback(&*tm);
+ }
+
+ void destroy() {
+ tm.reset();
+ lba_manager.reset();
+ cache.reset();
+ journal.reset();
+ segment_cleaner.reset();
+ }
+
+ laddr_t get_random_laddr(size_t block_size, laddr_t limit) {
+ return block_size *
+ std::uniform_int_distribution<>(0, (limit / block_size) - 1)(gen);
+ }
+
+ char get_random_contents() {
+ return static_cast<char>(std::uniform_int_distribution<>(0, 255)(gen));
}
seastar::future<> set_up_fut() final {
return segment_manager->init().safe_then([this] {
- return tm.mkfs();
+ return tm->mkfs();
}).safe_then([this] {
- return tm.mount();
+ return tm->mount();
}).handle_error(
crimson::ct_error::all_same_way([] {
ASSERT_FALSE("Unable to mount");
}
seastar::future<> tear_down_fut() final {
- return tm.close(
+ return tm->close(
).handle_error(
crimson::ct_error::all_same_way([] {
ASSERT_FALSE("Unable to close");
};
test_transaction_t create_transaction() {
- return { tm.create_transaction(), test_mappings };
+ return { tm->create_transaction(), test_mappings };
}
TestBlockRef alloc_extent(
laddr_t hint,
extent_len_t len,
char contents) {
- auto extent = tm.alloc_extent<TestBlock>(
+ auto extent = tm->alloc_extent<TestBlock>(
*(t.t),
hint,
len).unsafe_get0();
return extent;
}
+ TestBlockRef alloc_extent(
+ test_transaction_t &t,
+ laddr_t hint,
+ extent_len_t len) {
+ return alloc_extent(
+ t,
+ hint,
+ len,
+ get_random_contents());
+ }
+
void replay() {
- tm.close().unsafe_get();
- tm.mount().unsafe_get();
+ tm->close().unsafe_get();
+ auto next = segment_cleaner->get_next();
+ destroy();
+ init();
+ segment_cleaner->set_next(next);
+ tm->mount().unsafe_get();
}
void check_mappings() {
ceph_assert(t.mappings.count(addr));
ceph_assert(t.mappings[addr].desc.len == len);
- auto ret_list = tm.read_extents<TestBlock>(
+ auto ret_list = tm->read_extents<TestBlock>(
*t.t, addr, len
).unsafe_get0();
EXPECT_EQ(ret_list.size(), 1);
TestBlockRef ref) {
ceph_assert(t.mappings.count(ref->get_laddr()));
ceph_assert(t.mappings[ref->get_laddr()].desc.len == ref->get_length());
- auto ext = tm.get_mutable_extent(*t.t, ref)->cast<TestBlock>();
+ auto ext = tm->get_mutable_extent(*t.t, ref)->cast<TestBlock>();
EXPECT_EQ(ext->get_laddr(), ref->get_laddr());
EXPECT_EQ(ext->get_desc(), ref->get_desc());
- mutator.mutate(*ext);
+ mutator.mutate(*ext, gen);
t.mappings[ext->get_laddr()].update(ext->get_desc());
return ext;
}
void inc_ref(test_transaction_t &t, laddr_t offset) {
ceph_assert(t.mappings.count(offset));
ceph_assert(t.mappings[offset].refcount > 0);
- auto refcnt = tm.inc_ref(*t.t, offset).unsafe_get0();
+ auto refcnt = tm->inc_ref(*t.t, offset).unsafe_get0();
t.mappings[offset].refcount++;
EXPECT_EQ(refcnt, t.mappings[offset].refcount);
}
void dec_ref(test_transaction_t &t, laddr_t offset) {
ceph_assert(t.mappings.count(offset));
ceph_assert(t.mappings[offset].refcount > 0);
- auto refcnt = tm.dec_ref(*t.t, offset).unsafe_get0();
+ auto refcnt = tm->dec_ref(*t.t, offset).unsafe_get0();
t.mappings[offset].refcount--;
EXPECT_EQ(refcnt, t.mappings[offset].refcount);
if (t.mappings[offset].refcount == 0) {
}
void submit_transaction(test_transaction_t t) {
- tm.submit_transaction(std::move(t.t)).unsafe_get();
+ tm->submit_transaction(std::move(t.t)).unsafe_get();
test_mappings = t.mappings;
}
};
check_mappings();
});
}
+
+TEST_F(transaction_manager_test_t, random_writes)
+{
+ constexpr size_t TOTAL = 4<<20;
+ constexpr size_t BSIZE = 4<<10;
+ constexpr size_t PADDING_SIZE = 256<<10;
+ constexpr size_t BLOCKS = TOTAL / BSIZE;
+ run_async([this] {
+ for (unsigned i = 0; i < BLOCKS; ++i) {
+ auto t = create_transaction();
+ auto extent = alloc_extent(
+ t,
+ i * BSIZE,
+ BSIZE);
+ ASSERT_EQ(i * BSIZE, extent->get_laddr());
+ submit_transaction(std::move(t));
+ }
+
+ for (unsigned i = 0; i < 5; ++i) {
+ for (unsigned j = 0; j < 50; ++j) {
+ auto t = create_transaction();
+ for (unsigned k = 0; k < 2; ++k) {
+ auto ext = get_extent(
+ t,
+ get_random_laddr(BSIZE, TOTAL),
+ BSIZE);
+ auto mut = mutate_extent(t, ext);
+ // pad out transaction
+ auto padding = alloc_extent(
+ t,
+ TOTAL + (k * PADDING_SIZE),
+ PADDING_SIZE);
+ dec_ref(t, padding->get_laddr());
+ }
+ submit_transaction(std::move(t));
+ }
+ replay();
+ logger().debug("random_writes: checking");
+ check_mappings();
+ logger().debug("random_writes: done replaying/checking");
+ }
+ });
+}