crimson/seastore: convert onode unit test to use interruptible future.

Signed-off-by: chunmei-liu <chunmei.liu@intel.com>
This commit is contained in:
chunmei-liu 2021-07-27 18:23:25 -07:00
parent 6d0426468a
commit f61e94ff93
2 changed files with 216 additions and 155 deletions

View File

@ -71,7 +71,7 @@ struct fltree_onode_manager_test_t
virtual void _init() final {
TMTestState::_init();
manager.reset(new FLTreeOnodeManager(itm));
manager.reset(new FLTreeOnodeManager(*tm));
}
virtual void _destroy() final {
@ -112,18 +112,22 @@ struct fltree_onode_manager_test_t
void with_onode_write(iterator_t& it, F&& f) {
with_transaction([this, &it, f=std::move(f)] (auto& t) {
auto p_kv = *it;
auto onode = manager->get_or_create_onode(
t, p_kv->key).unsafe_get0();
auto onode = with_trans_intr(t, [&](auto &t) {
return manager->get_or_create_onode(t, p_kv->key);
}).unsafe_get0();
std::invoke(f, t, *onode, p_kv->value);
manager->write_dirty(t, {onode}).unsafe_get0();
with_trans_intr(t, [&](auto &t) {
return manager->write_dirty(t, {onode});
}).unsafe_get0();
});
}
void validate_onode(iterator_t& it) {
with_transaction([this, &it] (auto& t) {
auto p_kv = *it;
auto onode = manager->get_onode(
t, p_kv->key).unsafe_get0();
auto onode = with_trans_intr(t, [&](auto &t) {
return manager->get_onode(t, p_kv->key);
}).unsafe_get0();
p_kv->value.validate(*onode);
});
}
@ -131,8 +135,9 @@ struct fltree_onode_manager_test_t
void validate_erased(iterator_t& it) {
with_transaction([this, &it] (auto& t) {
auto p_kv = *it;
auto exist = manager->contains_onode(
t, p_kv->key).unsafe_get0();
auto exist = with_trans_intr(t, [&](auto &t) {
return manager->contains_onode(t, p_kv->key);
}).unsafe_get0();
ceph_assert(exist == false);
});
}
@ -159,15 +164,18 @@ struct fltree_onode_manager_test_t
const iterator_t& start, const iterator_t& end, F&& f) {
with_onodes_process(start, end,
[this, f=std::move(f)] (auto& t, auto& oids, auto& items) {
auto onodes = manager->get_or_create_onodes(
t, oids).unsafe_get0();
auto onodes = with_trans_intr(t, [&](auto &t) {
return manager->get_or_create_onodes(t, oids);
}).unsafe_get0();
for (auto tup : boost::combine(onodes, items)) {
OnodeRef onode;
onode_item_t* p_item;
boost::tie(onode, p_item) = tup;
std::invoke(f, t, *onode, *p_item);
}
manager->write_dirty(t, onodes).unsafe_get0();
with_trans_intr(t, [&](auto &t) {
return manager->write_dirty(t, onodes);
}).unsafe_get0();
});
}
@ -179,7 +187,9 @@ struct fltree_onode_manager_test_t
ghobject_t oid;
onode_item_t* p_item;
boost::tie(oid, p_item) = tup;
auto onode = manager->get_onode(t, oid).unsafe_get0();
auto onode = with_trans_intr(t, [&](auto &t) {
return manager->get_onode(t, oid);
}).unsafe_get0();
p_item->validate(*onode);
}
});
@ -190,8 +200,9 @@ struct fltree_onode_manager_test_t
with_onodes_process(start, end,
[this] (auto& t, auto& oids, auto& items) {
for (auto& oid : oids) {
auto exist = manager->contains_onode(
t, oid).unsafe_get0();
auto exist = with_trans_intr(t, [&](auto &t) {
return manager->contains_onode(t, oid);
}).unsafe_get0();
ceph_assert(exist == false);
}
});
@ -208,8 +219,9 @@ struct fltree_onode_manager_test_t
assert(start < oids[0]);
assert(oids[0] < end);
while (start != end) {
auto [list_ret, list_end] = manager->list_onodes(
t, start, end, LIST_LIMIT).unsafe_get0();
auto [list_ret, list_end] = with_trans_intr(t, [&](auto &t) {
return manager->list_onodes(t, start, end, LIST_LIMIT);
}).unsafe_get0();
listed_oids.insert(listed_oids.end(), list_ret.begin(), list_ret.end());
start = list_end;
}
@ -239,7 +251,9 @@ TEST_F(fltree_onode_manager_test_t, 1_single)
with_onode_write(iter, [this](auto& t, auto& onode, auto& item) {
OnodeRef onode_ref = &onode;
manager->erase_onode(t, onode_ref).unsafe_get0();
with_trans_intr(t, [&](auto &t) {
return manager->erase_onode(t, onode_ref);
}).unsafe_get0();
});
validate_erased(iter);
});
@ -283,7 +297,9 @@ TEST_F(fltree_onode_manager_test_t, 2_synthetic)
with_onodes_write(rd_start, rd_end,
[this](auto& t, auto& onode, auto& item) {
OnodeRef onode_ref = &onode;
manager->erase_onode(t, onode_ref).unsafe_get0();
with_trans_intr(t, [&](auto &t) {
return manager->erase_onode(t, onode_ref);
}).unsafe_get0();
});
validate_erased(rd_start, rd_end);
pool.erase_from_random(rd_start, rd_end);

View File

@ -22,6 +22,30 @@
using namespace crimson::os::seastore::onode;
#define INTR(fun, t) \
with_trans_intr( \
t, \
[&] (auto &tr) { \
return fun(tr); \
} \
)
#define INTR_R(fun, t, args...) \
with_trans_intr( \
t, \
[&] (auto &tr) { \
return fun(tr, args); \
} \
)
#define INTR_WITH_PARAM(fun, c, b, v) \
with_trans_intr( \
c.t, \
[=] (auto &t) { \
return fun(c, b, v); \
} \
)
namespace {
constexpr bool IS_DUMMY_SYNC = false;
using DummyManager = DummyNodeExtentManager<IS_DUMMY_SYNC>;
@ -145,22 +169,22 @@ TEST_F(a_basic_test_t, 2_node_sizes)
ValueBuilderImpl<UnboundedValue> vb;
context_t c{*nm, vb, *t};
std::array<std::pair<NodeImplURef, NodeExtentMutable>, 16> nodes = {
InternalNode0::allocate(c, false, 1u).unsafe_get0().make_pair(),
InternalNode1::allocate(c, false, 1u).unsafe_get0().make_pair(),
InternalNode2::allocate(c, false, 1u).unsafe_get0().make_pair(),
InternalNode3::allocate(c, false, 1u).unsafe_get0().make_pair(),
InternalNode0::allocate(c, true, 1u).unsafe_get0().make_pair(),
InternalNode1::allocate(c, true, 1u).unsafe_get0().make_pair(),
InternalNode2::allocate(c, true, 1u).unsafe_get0().make_pair(),
InternalNode3::allocate(c, true, 1u).unsafe_get0().make_pair(),
LeafNode0::allocate(c, false, 0u).unsafe_get0().make_pair(),
LeafNode1::allocate(c, false, 0u).unsafe_get0().make_pair(),
LeafNode2::allocate(c, false, 0u).unsafe_get0().make_pair(),
LeafNode3::allocate(c, false, 0u).unsafe_get0().make_pair(),
LeafNode0::allocate(c, true, 0u).unsafe_get0().make_pair(),
LeafNode1::allocate(c, true, 0u).unsafe_get0().make_pair(),
LeafNode2::allocate(c, true, 0u).unsafe_get0().make_pair(),
LeafNode3::allocate(c, true, 0u).unsafe_get0().make_pair()
INTR_WITH_PARAM(InternalNode0::allocate, c, false, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode1::allocate, c, false, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode2::allocate, c, false, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode3::allocate, c, false, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode0::allocate, c, true, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode1::allocate, c, true, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode2::allocate, c, true, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(InternalNode3::allocate, c, true, 1u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode0::allocate, c, false, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode1::allocate, c, false, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode2::allocate, c, false, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode3::allocate, c, false, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode0::allocate, c, true, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode1::allocate, c, true, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode2::allocate, c, true, 0u).unsafe_get0().make_pair(),
INTR_WITH_PARAM(LeafNode3::allocate, c, true, 0u).unsafe_get0().make_pair()
};
std::ostringstream oss;
oss << "\nallocated nodes:";
@ -204,20 +228,21 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
"\nrandomized leaf node insert:\n");
auto key_s = ghobject_t();
auto key_e = ghobject_t::get_max();
ASSERT_TRUE(tree.find(t, key_s).unsafe_get0().is_end());
ASSERT_TRUE(tree.begin(t).unsafe_get0().is_end());
ASSERT_TRUE(tree.last(t).unsafe_get0().is_end());
ASSERT_TRUE(INTR_R(tree.find, t, key_s).unsafe_get0().is_end());
ASSERT_TRUE(INTR(tree.begin, t).unsafe_get0().is_end());
ASSERT_TRUE(INTR(tree.last, t).unsafe_get0().is_end());
std::map<ghobject_t,
std::tuple<test_item_t, UnboundedBtree::Cursor>> insert_history;
auto f_validate_insert_new = [this, &insert_history] (
const ghobject_t& key, const test_item_t& value) {
auto [cursor, success] = tree.insert(
t, key, {value.get_payload_size()}).unsafe_get0();
auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
auto [cursor, success] = INTR_R(tree.insert,
t, key, conf).unsafe_get0();
initialize_cursor_from_item(t, key, value, cursor, success);
insert_history.emplace(key, std::make_tuple(value, cursor));
auto cursor_ = tree.find(t, key).unsafe_get0();
auto cursor_ = INTR_R(tree.find, t, key).unsafe_get0();
ceph_assert(cursor_ != tree.end());
ceph_assert(cursor_.value() == cursor.value());
validate_cursor_from_item(key, value, cursor_);
@ -225,12 +250,12 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
};
auto f_validate_erase = [this, &insert_history] (const ghobject_t& key) {
auto cursor_erase = tree.find(t, key).unsafe_get0();
auto cursor_next = cursor_erase.get_next(t).unsafe_get0();
auto cursor_ret = tree.erase(t, cursor_erase).unsafe_get0();
auto cursor_erase = INTR_R(tree.find, t, key).unsafe_get0();
auto cursor_next = INTR(cursor_erase.get_next, t).unsafe_get0();
auto cursor_ret = INTR_R(tree.erase, t, cursor_erase).unsafe_get0();
ceph_assert(cursor_erase.is_end());
ceph_assert(cursor_ret == cursor_next);
auto cursor_lb = tree.lower_bound(t, key).unsafe_get0();
auto cursor_lb = INTR_R(tree.lower_bound, t, key).unsafe_get0();
ceph_assert(cursor_lb == cursor_next);
auto it = insert_history.find(key);
ceph_assert(std::get<1>(it->second).is_end());
@ -253,18 +278,19 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
// validate lookup
{
auto cursor1_s = tree.lower_bound(t, key_s).unsafe_get0();
auto cursor1_s = INTR_R(tree.lower_bound, t, key_s).unsafe_get0();
ASSERT_EQ(cursor1_s.get_ghobj(), key1);
ASSERT_EQ(cursor1_s.value(), test_value1);
auto cursor1_e = tree.lower_bound(t, key_e).unsafe_get0();
auto cursor1_e = INTR_R(tree.lower_bound, t, key_e).unsafe_get0();
ASSERT_TRUE(cursor1_e.is_end());
}
// insert the same key1 with a different value
{
auto value1_dup = values.pick();
auto [cursor1_dup, ret1_dup] = tree.insert(
t, key1, {value1_dup.get_payload_size()}).unsafe_get0();
auto conf = UnboundedBtree::tree_value_config_t{value1_dup.get_payload_size()};
auto [cursor1_dup, ret1_dup] = INTR_R(tree.insert,
t, key1, conf).unsafe_get0();
ASSERT_FALSE(ret1_dup);
validate_cursor_from_item(key1, value1, cursor1_dup);
}
@ -345,28 +371,28 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
std::for_each(kvs.begin(), kvs.end(), [&f_insert_erase_insert] (auto& kv) {
f_insert_erase_insert(kv.first, kv.second);
});
ASSERT_EQ(tree.height(t).unsafe_get0(), 1);
ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
ASSERT_FALSE(tree.test_is_clean());
for (auto& [k, val] : insert_history) {
auto& [v, c] = val;
// validate values in tree keep intact
auto cursor = tree.find(t, k).unsafe_get0();
auto cursor = INTR_R(tree.find, t, k).unsafe_get0();
EXPECT_NE(cursor, tree.end());
validate_cursor_from_item(k, v, cursor);
// validate values in cursors keep intact
validate_cursor_from_item(k, v, c);
}
{
auto cursor = tree.lower_bound(t, key_s).unsafe_get0();
auto cursor = INTR_R(tree.lower_bound, t, key_s).unsafe_get0();
validate_cursor_from_item(smallest_key, smallest_value, cursor);
}
{
auto cursor = tree.begin(t).unsafe_get0();
auto cursor = INTR(tree.begin, t).unsafe_get0();
validate_cursor_from_item(smallest_key, smallest_value, cursor);
}
{
auto cursor = tree.last(t).unsafe_get0();
auto cursor = INTR(tree.last, t).unsafe_get0();
validate_cursor_from_item(largest_key, largest_value, cursor);
}
@ -381,11 +407,11 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
std::sort(kvs.begin(), kvs.end(), [](auto& l, auto& r) {
return l.first < r.first;
});
auto cursor = tree.begin(t).unsafe_get0();
auto cursor = INTR(tree.begin, t).unsafe_get0();
for (auto& [k, v] : kvs) {
ASSERT_FALSE(cursor.is_end());
validate_cursor_from_item(k, v, cursor);
cursor = cursor.get_next(t).unsafe_get0();
cursor = INTR(cursor.get_next, t).unsafe_get0();
}
ASSERT_TRUE(cursor.is_end());
}
@ -397,12 +423,12 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_erase_leaf_node)
// randomized erase until empty
std::random_shuffle(kvs.begin(), kvs.end());
for (auto& [k, v] : kvs) {
auto e_size = tree.erase(t, k).unsafe_get0();
auto e_size = INTR_R(tree.erase, t, k).unsafe_get0();
ASSERT_EQ(e_size, 1);
}
auto cursor = tree.begin(t).unsafe_get0();
auto cursor = INTR(tree.begin, t).unsafe_get0();
ASSERT_TRUE(cursor.is_end());
ASSERT_EQ(tree.height(t).unsafe_get0(), 1);
ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
});
}
@ -457,7 +483,7 @@ class TestTree {
auto value = values.create(value_size);
insert_tree(key, value).get0();
}
ASSERT_EQ(tree.height(t).unsafe_get0(), 1);
ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
ASSERT_FALSE(tree.test_is_clean());
//std::ostringstream oss;
//tree.dump(t, oss);
@ -479,7 +505,7 @@ class TestTree {
++key_iter;
++value_iter;
}
ASSERT_EQ(tree.height(t).unsafe_get0(), 1);
ASSERT_EQ(INTR(tree.height, t).unsafe_get0(), 1);
ASSERT_FALSE(tree.test_is_clean());
//std::ostringstream oss;
//tree.dump(t, oss);
@ -499,12 +525,13 @@ class TestTree {
UnboundedBtree tree_clone(std::move(ref_dummy));
auto ref_t_clone = make_test_transaction();
Transaction& t_clone = *ref_t_clone;
tree_clone.test_clone_from(t_clone, t, tree).unsafe_get0();
INTR_R(tree_clone.test_clone_from, t_clone, t, tree).unsafe_get0();
// insert and split
logger().info("\n\nINSERT-SPLIT {}:", key_hobj_t(key));
auto [cursor, success] = tree_clone.insert(
t_clone, key, {value.get_payload_size()}).unsafe_get0();
auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
auto [cursor, success] = INTR_R(tree_clone.insert,
t_clone, key, conf).unsafe_get0();
initialize_cursor_from_item(t, key, value, cursor, success);
{
@ -512,15 +539,15 @@ class TestTree {
tree_clone.dump(t_clone, oss);
logger().info("dump new root:\n{}", oss.str());
}
EXPECT_EQ(tree_clone.height(t_clone).unsafe_get0(), 2);
EXPECT_EQ(INTR(tree_clone.height, t_clone).unsafe_get0(), 2);
for (auto& [k, val] : insert_history) {
auto& [v, c] = val;
auto result = tree_clone.find(t_clone, k).unsafe_get0();
auto result = INTR_R(tree_clone.find, t_clone, k).unsafe_get0();
EXPECT_NE(result, tree_clone.end());
validate_cursor_from_item(k, v, result);
}
auto result = tree_clone.find(t_clone, key).unsafe_get0();
auto result = INTR_R(tree_clone.find, t_clone, key).unsafe_get0();
EXPECT_NE(result, tree_clone.end());
validate_cursor_from_item(key, value, result);
EXPECT_TRUE(last_split.match(expected));
@ -528,11 +555,11 @@ class TestTree {
// erase and merge
logger().info("\n\nERASE-MERGE {}:", key_hobj_t(key));
auto nxt_cursor = cursor.erase<true>(t_clone).unsafe_get0();
auto nxt_cursor = INTR(cursor.erase<true>, t_clone).unsafe_get0();
{
// track root again to dump
auto begin = tree_clone.begin(t_clone).unsafe_get0();
auto begin = INTR(tree_clone.begin, t_clone).unsafe_get0();
std::ignore = begin;
std::ostringstream oss;
tree_clone.dump(t_clone, oss);
@ -550,12 +577,11 @@ class TestTree {
for (auto& [k, val] : insert_history) {
auto& [v, c] = val;
auto result = tree_clone.find(t_clone, k).unsafe_get0();
auto result = INTR_R(tree_clone.find, t_clone, k).unsafe_get0();
EXPECT_NE(result, tree_clone.end());
validate_cursor_from_item(k, v, result);
}
EXPECT_EQ(tree_clone.height(t_clone).unsafe_get0(), 1);
EXPECT_EQ(INTR(tree_clone.height, t_clone).unsafe_get0(), 1);
EXPECT_EQ(p_dummy->size(), 1);
});
}
@ -567,8 +593,9 @@ class TestTree {
private:
seastar::future<> insert_tree(const ghobject_t& key, const test_item_t& value) {
return seastar::async([this, &key, &value] {
auto [cursor, success] = tree.insert(
t, key, {value.get_payload_size()}).unsafe_get0();
auto conf = UnboundedBtree::tree_value_config_t{value.get_payload_size()};
auto [cursor, success] = INTR_R(tree.insert,
t, key, conf).unsafe_get0();
initialize_cursor_from_item(t, key, value, cursor, success);
insert_history.emplace(key, std::make_tuple(value, cursor));
});
@ -835,10 +862,10 @@ class DummyChildPool {
build_name();
return search_position_t::end();
}
eagain_future<> retire_extent(context_t) override {
eagain_ifuture<> retire_extent(context_t) override {
assert(!_is_extent_retired);
_is_extent_retired = true;
return seastar::now();
return eagain_iertr::now();
}
protected:
@ -871,7 +898,7 @@ class DummyChildPool {
ceph_abort("impossible path"); }
search_position_t merge(NodeExtentMutable&, NodeImpl&, match_stage_t, extent_len_t) override {
ceph_abort("impossible path"); }
eagain_future<NodeExtentMutable> rebuild_extent(context_t) override {
eagain_ifuture<NodeExtentMutable> rebuild_extent(context_t) override {
ceph_abort("impossible path"); }
node_stats_t get_stats() const override {
ceph_abort("impossible path"); }
@ -913,7 +940,7 @@ class DummyChildPool {
key_view_t get_pivot_key() const { return *impl->get_pivot_index(); }
eagain_future<> populate_split(
eagain_ifuture<> populate_split(
context_t c, std::set<Ref<DummyChild>>& splitable_nodes) {
ceph_assert(can_split());
ceph_assert(splitable_nodes.find(this) != splitable_nodes.end());
@ -941,10 +968,10 @@ class DummyChildPool {
}
Ref<Node> this_ref = this;
return apply_split_to_parent(
c, std::move(this_ref), std::move(right_child), false);
c, std::move(this_ref), std::move(right_child), false);
}
eagain_future<> insert_and_split(
eagain_ifuture<> insert_and_split(
context_t c, const ghobject_t& insert_key,
std::set<Ref<DummyChild>>& splitable_nodes) {
const auto& keys = impl->get_keys();
@ -964,9 +991,9 @@ class DummyChildPool {
return fut;
}
eagain_future<> merge(context_t c, Ref<DummyChild>&& this_ref) {
eagain_ifuture<> merge(context_t c, Ref<DummyChild>&& this_ref) {
return parent_info().ptr->get_child_peers(c, parent_info().position
).safe_then([c, this_ref = std::move(this_ref), this] (auto lr_nodes) mutable {
).si_then([c, this_ref = std::move(this_ref), this] (auto lr_nodes) mutable {
auto& [lnode, rnode] = lr_nodes;
if (rnode) {
lnode.reset();
@ -985,7 +1012,7 @@ class DummyChildPool {
});
}
eagain_future<> fix_key(context_t c, const ghobject_t& new_key) {
eagain_ifuture<> fix_key(context_t c, const ghobject_t& new_key) {
const auto& keys = impl->get_keys();
ceph_assert(keys.size() == 1);
assert(impl->is_level_tail() == false);
@ -1015,24 +1042,24 @@ class DummyChildPool {
return create(keys, is_level_tail, seed++, pool);
}
static eagain_future<Ref<DummyChild>> create_initial(
static eagain_ifuture<Ref<DummyChild>> create_initial(
context_t c, const std::set<ghobject_t>& keys,
DummyChildPool& pool, RootNodeTracker& root_tracker) {
auto initial = create_new(keys, true, pool);
return c.nm.get_super(c.t, root_tracker
).handle_error(
eagain_ertr::pass_further{},
).handle_error_interruptible(
eagain_iertr::pass_further{},
crimson::ct_error::assert_all{"Invalid error during create_initial()"}
).safe_then([c, &pool, initial](auto super) {
).si_then([c, &pool, initial](auto super) {
initial->make_root_new(c, std::move(super));
return initial->upgrade_root(c).safe_then([initial] {
return initial->upgrade_root(c).si_then([initial] {
return initial;
});
});
}
protected:
eagain_future<> test_clone_non_root(
eagain_ifuture<> test_clone_non_root(
context_t, Ref<InternalNode> new_parent) const override {
ceph_assert(!is_root());
auto p_pool_clone = pool.pool_clone_in_progress;
@ -1040,18 +1067,18 @@ class DummyChildPool {
auto clone = create(
impl->get_keys(), impl->is_level_tail(), impl->laddr(), *p_pool_clone);
clone->as_child(parent_info().position, new_parent);
return seastar::now();
return eagain_iertr::now();
}
eagain_future<Ref<tree_cursor_t>> lookup_smallest(context_t) override {
eagain_ifuture<Ref<tree_cursor_t>> lookup_smallest(context_t) override {
ceph_abort("impossible path"); }
eagain_future<Ref<tree_cursor_t>> lookup_largest(context_t) override {
eagain_ifuture<Ref<tree_cursor_t>> lookup_largest(context_t) override {
ceph_abort("impossible path"); }
eagain_future<> test_clone_root(context_t, RootNodeTracker&) const override {
eagain_ifuture<> test_clone_root(context_t, RootNodeTracker&) const override {
ceph_abort("impossible path"); }
eagain_future<search_result_t> lower_bound_tracked(
eagain_ifuture<search_result_t> lower_bound_tracked(
context_t, const key_hobj_t&, MatchHistory&) override {
ceph_abort("impossible path"); }
eagain_future<> do_get_tree_stats(context_t, tree_stats_t&) override {
eagain_ifuture<> do_get_tree_stats(context_t, tree_stats_t&) override {
ceph_abort("impossible path"); }
bool is_tracking() const override { return false; }
void track_merge(Ref<Node>, match_stage_t, search_position_t&) override {
@ -1065,7 +1092,7 @@ class DummyChildPool {
bool can_split() const { return impl->get_keys().size() > 1; }
static eagain_future<> do_merge(
static eagain_ifuture<> do_merge(
context_t c, Ref<DummyChild>&& left, Ref<DummyChild>&& right, bool stole_key) {
assert(right->use_count() == 1);
assert(left->impl->get_keys().size() == 1);
@ -1094,37 +1121,38 @@ class DummyChildPool {
eagain_future<> build_tree(const std::set<ghobject_t>& keys) {
reset();
// create tree
auto ref_dummy = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
p_dummy = static_cast<DummyManager*>(ref_dummy.get());
p_btree.emplace(std::move(ref_dummy));
return DummyChild::create_initial(get_context(), keys, *this, *p_btree->root_tracker
).safe_then([this](auto initial_child) {
// split
splitable_nodes.insert(initial_child);
return crimson::repeat([this] ()
-> eagain_future<seastar::stop_iteration> {
if (splitable_nodes.empty()) {
return seastar::make_ready_future<seastar::stop_iteration>(
seastar::stop_iteration::yes);
}
auto index = rd() % splitable_nodes.size();
auto iter = splitable_nodes.begin();
std::advance(iter, index);
Ref<DummyChild> child = *iter;
return child->populate_split(get_context(), splitable_nodes
).safe_then([] {
return seastar::stop_iteration::no;
return with_trans_intr(get_context().t, [this, &keys] (auto &tr) {
return DummyChild::create_initial(get_context(), keys, *this, *p_btree->root_tracker
).si_then([this](auto initial_child) {
// split
splitable_nodes.insert(initial_child);
return trans_intr::repeat([this] ()
-> eagain_ifuture<seastar::stop_iteration> {
if (splitable_nodes.empty()) {
return seastar::make_ready_future<seastar::stop_iteration>(
seastar::stop_iteration::yes);
}
auto index = rd() % splitable_nodes.size();
auto iter = splitable_nodes.begin();
std::advance(iter, index);
Ref<DummyChild> child = *iter;
return child->populate_split(get_context(), splitable_nodes
).si_then([] {
return seastar::stop_iteration::no;
});
});
});
}).safe_then([this] {
}).si_then([this] {
//std::ostringstream oss;
//p_btree->dump(t(), oss);
//logger().info("\n{}\n", oss.str());
return p_btree->height(t());
}).safe_then([](auto height) {
ceph_assert(height == 2);
return p_btree->height(t());
}).si_then([](auto height) {
ceph_assert(height == 2);
});
});
}
@ -1137,14 +1165,17 @@ class DummyChildPool {
// insert and split
logger().info("\n\nINSERT-SPLIT {} at pos({}):", key_hobj_t(key), pos);
auto node_to_split = pool_clone.get_node_by_pos(pos);
node_to_split->insert_and_split(
pool_clone.get_context(), key, pool_clone.splitable_nodes).unsafe_get0();
with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
return node_to_split->insert_and_split(
pool_clone.get_context(), key, pool_clone.splitable_nodes);
}).unsafe_get0();
{
std::ostringstream oss;
pool_clone.p_btree->dump(pool_clone.t(), oss);
logger().info("dump new root:\n{}", oss.str());
}
EXPECT_EQ(pool_clone.p_btree->height(pool_clone.t()).unsafe_get0(), 3);
auto &pt = pool_clone.t();
EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 3);
EXPECT_TRUE(last_split.match(expected));
EXPECT_EQ(pool_clone.p_dummy->size(), 3);
@ -1152,9 +1183,12 @@ class DummyChildPool {
auto pivot_key = node_to_split->get_pivot_key();
logger().info("\n\nERASE-MERGE {}:", node_to_split->get_name());
assert(pivot_key.compare_to(key_hobj_t(key)) == MatchKindCMP::EQ);
node_to_split->merge(
pool_clone.get_context(), std::move(node_to_split)).unsafe_get0();
EXPECT_EQ(pool_clone.p_btree->height(pool_clone.t()).unsafe_get0(), 2);
with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
return node_to_split->merge(
pool_clone.get_context(), std::move(node_to_split));
}).unsafe_get0();
auto &pt2 = pool_clone.t();
EXPECT_EQ(INTR(pool_clone.p_btree->height ,pt2).unsafe_get0(), 2);
EXPECT_EQ(pool_clone.p_dummy->size(), 1);
});
}
@ -1170,23 +1204,30 @@ class DummyChildPool {
auto old_key = node_to_fix->get_pivot_key().to_ghobj();
logger().info("\n\nFIX pos({}) from {} to {}, expect_split={}:",
pos, node_to_fix->get_name(), key_hobj_t(new_key), expect_split);
node_to_fix->fix_key(pool_clone.get_context(), new_key).unsafe_get0();
with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
return node_to_fix->fix_key(pool_clone.get_context(), new_key);
}).unsafe_get0();
if (expect_split) {
std::ostringstream oss;
pool_clone.p_btree->dump(pool_clone.t(), oss);
logger().info("dump new root:\n{}", oss.str());
EXPECT_EQ(pool_clone.p_btree->height(pool_clone.t()).unsafe_get0(), 3);
auto &pt = pool_clone.t();
EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 3);
EXPECT_EQ(pool_clone.p_dummy->size(), 3);
} else {
EXPECT_EQ(pool_clone.p_btree->height(pool_clone.t()).unsafe_get0(), 2);
auto &pt = pool_clone.t();
EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 2);
EXPECT_EQ(pool_clone.p_dummy->size(), 1);
}
// fix back
logger().info("\n\nFIX pos({}) from {} back to {}:",
pos, node_to_fix->get_name(), key_hobj_t(old_key));
node_to_fix->fix_key(pool_clone.get_context(), old_key).unsafe_get0();
EXPECT_EQ(pool_clone.p_btree->height(pool_clone.t()).unsafe_get0(), 2);
with_trans_intr(pool_clone.get_context().t, [&] (auto &t) {
return node_to_fix->fix_key(pool_clone.get_context(), old_key);
}).unsafe_get0();
auto &pt = pool_clone.t();
EXPECT_EQ(INTR(pool_clone.p_btree->height, pt).unsafe_get0(), 2);
EXPECT_EQ(pool_clone.p_dummy->size(), 1);
});
}
@ -1197,8 +1238,10 @@ class DummyChildPool {
auto ref_dummy = NodeExtentManager::create_dummy(IS_DUMMY_SYNC);
pool_clone.p_dummy = static_cast<DummyManager*>(ref_dummy.get());
pool_clone.p_btree.emplace(std::move(ref_dummy));
pool_clone.p_btree->test_clone_from(
pool_clone.t(), t(), *p_btree).unsafe_get0();
auto &pt = pool_clone.t();
[[maybe_unused]] auto &tr = t();
INTR_R(pool_clone.p_btree->test_clone_from,
pt, tr, *p_btree).unsafe_get0();
pool_clone_in_progress = nullptr;
}
@ -1528,14 +1571,14 @@ TEST_F(d_seastore_tm_test_t, 6_random_tree_insert_erase)
{8, 11, 64, 256, 301, 320},
{8, 16, 128, 512, 576, 640},
{0, 16}, {0, 10}, {0, 4});
auto moved_nm = (TEST_SEASTORE ? NodeExtentManager::create_seastore(itm)
auto moved_nm = (TEST_SEASTORE ? NodeExtentManager::create_seastore(*tm)
: NodeExtentManager::create_dummy(IS_DUMMY_SYNC));
auto p_nm = moved_nm.get();
auto tree = std::make_unique<TreeBuilder<TRACK_CURSORS, BoundedValue>>(
kvs, std::move(moved_nm));
{
auto t = create_mutate_transaction();
tree->bootstrap(*t).unsafe_get();
INTR(tree->bootstrap, *t).unsafe_get();
submit_transaction(std::move(t));
segment_cleaner->run_until_halt().get0();
}
@ -1543,69 +1586,71 @@ TEST_F(d_seastore_tm_test_t, 6_random_tree_insert_erase)
// test insert
{
auto t = create_mutate_transaction();
tree->insert(*t).unsafe_get();
INTR(tree->insert, *t).unsafe_get();
submit_transaction(std::move(t));
segment_cleaner->run_until_halt().get0();
}
{
auto t = create_read_transaction();
tree->get_stats(*t).unsafe_get();
INTR(tree->get_stats, *t).unsafe_get();
}
if constexpr (TEST_SEASTORE) {
logger().info("seastore replay insert begin");
restart();
tree->reload(NodeExtentManager::create_seastore(itm));
tree->reload(NodeExtentManager::create_seastore(*tm));
logger().info("seastore replay insert end");
}
{
// Note: create_weak_transaction() can also work, but too slow.
auto t = create_read_transaction();
tree->validate(*t).unsafe_get();
INTR(tree->validate, *t).unsafe_get();
}
// test erase 3/4
{
auto t = create_mutate_transaction();
tree->erase(*t, kvs.size() / 4 * 3).unsafe_get();
auto size = kvs.size() / 4 * 3;
INTR_R(tree->erase, *t, size).unsafe_get();
submit_transaction(std::move(t));
segment_cleaner->run_until_halt().get0();
}
{
auto t = create_read_transaction();
tree->get_stats(*t).unsafe_get();
INTR(tree->get_stats, *t).unsafe_get();
}
if constexpr (TEST_SEASTORE) {
logger().info("seastore replay erase-1 begin");
restart();
tree->reload(NodeExtentManager::create_seastore(itm));
tree->reload(NodeExtentManager::create_seastore(*tm));
logger().info("seastore replay erase-1 end");
}
{
auto t = create_read_transaction();
tree->validate(*t).unsafe_get();
INTR(tree->validate, *t).unsafe_get();
}
// test erase remaining
{
auto t = create_mutate_transaction();
tree->erase(*t, kvs.size()).unsafe_get();
auto size = kvs.size();
INTR_R(tree->erase, *t, size).unsafe_get();
submit_transaction(std::move(t));
segment_cleaner->run_until_halt().get0();
}
{
auto t = create_read_transaction();
tree->get_stats(*t).unsafe_get();
INTR(tree->get_stats, *t).unsafe_get();
}
if constexpr (TEST_SEASTORE) {
logger().info("seastore replay erase-2 begin");
restart();
tree->reload(NodeExtentManager::create_seastore(itm));
tree->reload(NodeExtentManager::create_seastore(*tm));
logger().info("seastore replay erase-2 end");
}
{
auto t = create_read_transaction();
tree->validate(*t).unsafe_get();
EXPECT_EQ(tree->height(*t).unsafe_get0(), 1);
INTR(tree->validate, *t).unsafe_get();
EXPECT_EQ(INTR(tree->height, *t).unsafe_get0(), 1);
}
if constexpr (!TEST_SEASTORE) {
@ -1627,7 +1672,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
{8, 16, 128, 576, 992, 1200},
{0, 8}, {0, 10}, {0, 4});
auto moved_nm = NodeExtentManager::create_seastore(
itm, L_ADDR_MIN, EAGAIN_PROBABILITY);
*tm, L_ADDR_MIN, EAGAIN_PROBABILITY);
auto p_nm = static_cast<SeastoreNodeExtentManager<true>*>(moved_nm.get());
auto tree = std::make_unique<TreeBuilder<TRACK_CURSORS, ExtendedValue>>(
kvs, std::move(moved_nm));
@ -1641,7 +1686,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
return seastar::do_with(
create_mutate_transaction(),
[this, &tree](auto &t) {
return tree->bootstrap(*t
return INTR(tree->bootstrap, *t
).safe_then([this, &t] {
return submit_transaction_fut(*t);
});
@ -1660,7 +1705,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
return seastar::do_with(
create_mutate_transaction(),
[this, &tree, &iter](auto &t) {
return tree->insert_one(*t, iter
return INTR_R(tree->insert_one, *t, iter
).safe_then([this, &t](auto cursor) {
cursor.invalidate();
return submit_transaction_fut(*t);
@ -1675,7 +1720,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
{
p_nm->set_generate_eagain(false);
auto t = create_read_transaction();
tree->get_stats(*t).unsafe_get0();
INTR(tree->get_stats, *t).unsafe_get0();
p_nm->set_generate_eagain(true);
}
@ -1688,7 +1733,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
repeat_eagain2([this, &tree, &num_ops_eagain, &iter] {
++num_ops_eagain;
auto t = create_read_transaction();
return tree->validate_one(*t, iter
return INTR_R(tree->validate_one, *t, iter
).safe_then([t=std::move(t)]{});
}).get0();
++iter;
@ -1707,7 +1752,7 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
return seastar::do_with(
create_mutate_transaction(),
[this, &tree, &iter](auto &t) {
return tree->erase_one(*t, iter
return INTR_R(tree->erase_one, *t, iter
).safe_then([this, &t] () mutable {
return submit_transaction_fut(*t);
});
@ -1722,9 +1767,9 @@ TEST_F(d_seastore_tm_test_t, 7_tree_insert_erase_eagain)
{
p_nm->set_generate_eagain(false);
auto t = create_read_transaction();
tree->get_stats(*t).unsafe_get0();
tree->validate(*t).unsafe_get0();
EXPECT_EQ(tree->height(*t).unsafe_get0(), 1);
INTR(tree->get_stats, *t).unsafe_get0();
INTR(tree->validate, *t).unsafe_get0();
EXPECT_EQ(INTR(tree->height,*t).unsafe_get0(), 1);
}
// we can adjust EAGAIN_PROBABILITY to get a proper eagain_rate