std::random_shuffle(random_p_kvs.begin(), random_p_kvs.end());
}
+ void erase_from_random(iterator_t begin, iterator_t end) {
+ random_p_kvs.erase(begin, end);
+ kv_vector_t new_kvs;
+ for (auto p_kv : random_p_kvs) {
+ new_kvs.emplace_back(*p_kv);
+ }
+ std::sort(new_kvs.begin(), new_kvs.end(), [](auto& l, auto& r) {
+ return l.key < r.key;
+ });
+
+ kvs.swap(new_kvs);
+ serial_p_kvs.resize(kvs.size());
+ random_p_kvs.resize(kvs.size());
+ init();
+ }
+
static KVPool create_raw_range(
const std::vector<size_t>& str_sizes,
const std::vector<size_t>& value_sizes,
private:
KVPool(kv_vector_t&& _kvs)
: kvs(std::move(_kvs)), serial_p_kvs(kvs.size()), random_p_kvs(kvs.size()) {
+ init();
+ }
+
+ void init() {
std::transform(kvs.begin(), kvs.end(), serial_p_kvs.begin(),
[] (kv_t& item) { return &item; });
std::transform(kvs.begin(), kvs.end(), random_p_kvs.begin(),
auto cursors = seastar::make_lw_shared<std::vector<BtreeCursor>>();
logger().warn("start inserting {} kvs ...", kvs.size());
auto start_time = mono_clock::now();
- return crimson::do_until([&t, this, cursors, ref_kv_iter]() -> future<bool> {
+ return crimson::do_until([&t, this, cursors, ref_kv_iter,
+ start_time]() -> future<bool> {
if (*ref_kv_iter == kvs.random_end()) {
+ std::chrono::duration<double> duration = mono_clock::now() - start_time;
+ logger().warn("Insert done! {}s", duration.count());
return ertr::template make_ready_future<bool>(true);
}
auto p_kv = **ref_kv_iter;
- logger().debug("[{}] {} -> {}",
+ logger().debug("[{}] insert {} -> {}",
(*ref_kv_iter) - kvs.random_begin(),
key_hobj_t{p_kv->key},
p_kv->value);
return ertr::template make_ready_future<bool>(false);
#endif
});
- }).safe_then([&t, this, start_time, cursors, ref_kv_iter] {
- std::chrono::duration<double> duration = mono_clock::now() - start_time;
- logger().warn("Insert done! {}s", duration.count());
+ }).safe_then([&t, this, cursors, ref_kv_iter] {
if (!cursors->empty()) {
logger().info("Verifing tracked cursors ...");
*ref_kv_iter = kvs.random_begin();
});
}
+ future<> erase(Transaction& t, std::size_t erase_size) {
+ assert(erase_size <= kvs.size());
+ kvs.shuffle();
+ auto begin = kvs.random_begin();
+ auto end = begin + erase_size;
+ auto ref_kv_iter = seastar::make_lw_shared<iterator_t>();
+ auto cursors = seastar::make_lw_shared<std::map<ghobject_t, BtreeCursor>>();
+ return ertr::now().safe_then([&t, this, cursors, ref_kv_iter] {
+ if constexpr (TRACK) {
+ logger().info("Tracking cursors before erase ...");
+ *ref_kv_iter = kvs.begin();
+ auto start_time = mono_clock::now();
+ return crimson::do_until([&t, this, cursors, ref_kv_iter, start_time] () -> future<bool> {
+ if (*ref_kv_iter == kvs.end()) {
+ std::chrono::duration<double> duration = mono_clock::now() - start_time;
+ logger().info("Track done! {}s", duration.count());
+ return ertr::template make_ready_future<bool>(true);
+ }
+ auto p_kv = **ref_kv_iter;
+ return tree->find(t, p_kv->key).safe_then([this, cursors, ref_kv_iter](auto cursor) {
+ auto p_kv = **ref_kv_iter;
+ validate_cursor_from_item(p_kv->key, p_kv->value, cursor);
+ cursors->emplace(p_kv->key, cursor);
+ ++(*ref_kv_iter);
+ return ertr::template make_ready_future<bool>(false);
+ });
+ });
+ } else {
+ return ertr::now();
+ }
+ }).safe_then([&t, this, ref_kv_iter, begin, end] {
+ *ref_kv_iter = begin;
+ logger().warn("start erasing {}/{} kvs ...", end - begin, kvs.size());
+ auto start_time = mono_clock::now();
+ return crimson::do_until([&t, this, ref_kv_iter,
+ start_time, begin, end] () -> future<bool> {
+ if (*ref_kv_iter == end) {
+ std::chrono::duration<double> duration = mono_clock::now() - start_time;
+ logger().warn("Erase done! {}s", duration.count());
+ return ertr::template make_ready_future<bool>(true);
+ }
+ auto p_kv = **ref_kv_iter;
+ logger().debug("[{}] erase {} -> {}",
+ (*ref_kv_iter) - begin,
+ key_hobj_t{p_kv->key},
+ p_kv->value);
+ return tree->erase(t, p_kv->key).safe_then([&t, this, ref_kv_iter] (auto size) {
+ ceph_assert(size == 1);
+#ifndef NDEBUG
+ auto p_kv = **ref_kv_iter;
+ return tree->contains(t, p_kv->key).safe_then([ref_kv_iter] (bool ret) {
+ ceph_assert(ret == false);
+ ++(*ref_kv_iter);
+ return ertr::template make_ready_future<bool>(false);
+ });
+#else
+ ++(*ref_kv_iter);
+ return ertr::template make_ready_future<bool>(false);
+#endif
+ });
+ });
+ }).safe_then([this, cursors, ref_kv_iter, begin, end] {
+ if constexpr (TRACK) {
+ logger().info("Verifing tracked cursors ...");
+ *ref_kv_iter = begin;
+ while (*ref_kv_iter != end) {
+ auto p_kv = **ref_kv_iter;
+ auto c_it = cursors->find(p_kv->key);
+ ceph_assert(c_it != cursors->end());
+ ceph_assert(c_it->second.is_end());
+ cursors->erase(c_it);
+ ++(*ref_kv_iter);
+ }
+ }
+ kvs.erase_from_random(begin, end);
+ if constexpr (TRACK) {
+ *ref_kv_iter = kvs.begin();
+ for (auto& [k, c] : *cursors) {
+ assert(*ref_kv_iter != kvs.end());
+ auto p_kv = **ref_kv_iter;
+ validate_cursor_from_item(p_kv->key, p_kv->value, c);
+ ++(*ref_kv_iter);
+ }
+ logger().info("Verify done!");
+ }
+ });
+ }
+
future<> get_stats(Transaction& t) {
return tree->get_stats_slow(t
).safe_then([this](auto stats) {
});
}
+ future<std::size_t> height(Transaction& t) {
+ return tree->height(t);
+ }
+
void reload(NodeExtentManagerURef&& nm) {
tree.emplace(std::move(nm));
}
future<> validate(Transaction& t) {
return seastar::async([this, &t] {
- logger().info("Verifing insertion ...");
+ logger().info("Verifing inserted ...");
for (auto& p_kv : kvs) {
auto cursor = tree->find(t, p_kv->key).unsafe_get0();
validate_cursor_from_item(p_kv->key, p_kv->value, cursor);
}
};
-TEST_F(d_seastore_tm_test_t, 6_random_insert_leaf_node)
+TEST_F(d_seastore_tm_test_t, 6_random_tree_insert_erase)
{
run_async([this] {
constexpr bool TEST_SEASTORE = true;
{8, 11, 64, 256, 301, 320},
{8, 16, 128, 512, 576, 640},
{0, 32}, {0, 10}, {0, 4});
- auto tree = std::make_unique<TreeBuilder<TRACK_CURSORS, test_item_t>>(kvs,
- (TEST_SEASTORE ? NodeExtentManager::create_seastore(*tm)
- : NodeExtentManager::create_dummy(IS_DUMMY_SYNC)));
+ 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, test_item_t>>(
+ kvs, std::move(moved_nm));
{
auto t = tm->create_transaction();
tree->bootstrap(*t).unsafe_get();
tm->submit_transaction(std::move(t)).unsafe_get();
segment_cleaner->run_until_halt().get0();
}
+
+ // test insert
{
auto t = tm->create_transaction();
tree->insert(*t).unsafe_get();
{
auto t = tm->create_transaction();
tree->get_stats(*t).unsafe_get();
+ }
+ if constexpr (TEST_SEASTORE) {
+ logger().info("seastore replay insert begin");
+ restart();
+ tree->reload(NodeExtentManager::create_seastore(*tm));
+ logger().info("seastore replay insert end");
+ }
+ {
+ // Note: tm->create_weak_transaction() can also work, but too slow.
+ auto t = tm->create_transaction();
+ tree->validate(*t).unsafe_get();
+ }
+
+ // test erase 3/4
+ {
+ auto t = tm->create_transaction();
+ tree->erase(*t, kvs.size() / 4 * 3).unsafe_get();
tm->submit_transaction(std::move(t)).unsafe_get();
segment_cleaner->run_until_halt().get0();
}
+ {
+ auto t = tm->create_transaction();
+ tree->get_stats(*t).unsafe_get();
+ }
if constexpr (TEST_SEASTORE) {
- logger().info("seastore replay begin");
+ logger().info("seastore replay erase-1 begin");
restart();
tree->reload(NodeExtentManager::create_seastore(*tm));
- logger().info("seastore replay end");
+ logger().info("seastore replay erase-1 end");
+ }
+ {
+ auto t = tm->create_transaction();
+ tree->validate(*t).unsafe_get();
+ }
+
+ // test erase remaining
+ {
+ auto t = tm->create_transaction();
+ tree->erase(*t, kvs.size()).unsafe_get();
+ tm->submit_transaction(std::move(t)).unsafe_get();
+ segment_cleaner->run_until_halt().get0();
+ }
+ {
+ auto t = tm->create_transaction();
+ tree->get_stats(*t).unsafe_get();
+ }
+ if constexpr (TEST_SEASTORE) {
+ logger().info("seastore replay erase-2 begin");
+ restart();
+ tree->reload(NodeExtentManager::create_seastore(*tm));
+ logger().info("seastore replay erase-2 end");
}
{
- // Note: tm->create_weak_transaction() can also work, but too slow.
auto t = tm->create_transaction();
tree->validate(*t).unsafe_get();
+ EXPECT_EQ(tree->height(*t).unsafe_get0(), 1);
+ }
+
+ if constexpr (!TEST_SEASTORE) {
+ auto p_dummy = static_cast<DummyManager*>(p_nm);
+ EXPECT_EQ(p_dummy->size(), 1);
}
tree.reset();
});