test/crimson/seastore/test_transaction_manager: add random overwrite test

Also improves replay() by recycling all structures except for the
segment_manager.

Signed-off-by: Samuel Just <sjust@redhat.com>
This commit is contained in:
Samuel Just 2020-08-20 14:17:31 -07:00
parent 2fe1469c2b
commit 9ab4acd484
2 changed files with 118 additions and 33 deletions

View File

@ -126,20 +126,19 @@ struct test_block_mutator_t {
offset_distribution = std::uniform_int_distribution<uint16_t>(
0, TestBlock::SIZE - 1);
std::default_random_engine generator = std::default_random_engine(0);
std::uniform_int_distribution<uint16_t> length_distribution(uint16_t offset) {
return std::uniform_int_distribution<uint16_t>(
0, TestBlock::SIZE - offset - 1);
}
void mutate(TestBlock &block) {
auto offset = offset_distribution(generator);
template <typename generator_t>
void mutate(TestBlock &block, generator_t &gen) {
auto offset = offset_distribution(gen);
block.set_contents(
contents_distribution(generator),
contents_distribution(gen),
offset,
length_distribution(offset)(generator));
length_distribution(offset)(gen));
}
};

View File

@ -1,6 +1,8 @@
// -*- 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"
@ -47,31 +49,57 @@ std::ostream &operator<<(std::ostream &lhs, const test_extent_record_t &rhs) {
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");
@ -80,7 +108,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
}
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");
@ -135,7 +163,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
};
test_transaction_t create_transaction() {
return { tm.create_transaction(), test_mappings };
return { tm->create_transaction(), test_mappings };
}
TestBlockRef alloc_extent(
@ -143,7 +171,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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();
@ -154,9 +182,24 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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() {
@ -171,7 +214,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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);
@ -188,10 +231,10 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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;
}
@ -199,7 +242,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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);
}
@ -207,7 +250,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
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) {
@ -224,7 +267,7 @@ struct transaction_manager_test_t : public seastar_test_suite_t {
}
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;
}
};
@ -382,3 +425,46 @@ TEST_F(transaction_manager_test_t, cause_lba_split)
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");
}
});
}