]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
crimson/onode-staged-tree: generalize tree utilities
authorYingxin Cheng <yingxin.cheng@intel.com>
Thu, 11 Mar 2021 05:52:18 +0000 (13:52 +0800)
committerYingxin Cheng <yingxin.cheng@intel.com>
Thu, 11 Mar 2021 05:52:18 +0000 (13:52 +0800)
So TreeBuilder can be reused by different value implementations.

Signed-off-by: Yingxin Cheng <yingxin.cheng@intel.com>
src/crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h
src/test/crimson/seastore/onode_tree/test_staged_fltree.cc
src/test/crimson/seastore/onode_tree/test_value.h
src/tools/crimson/perf_staged_fltree.cc

index 867d3dfe33a3ca486d63a449092e1d26ea5a1206..962174bc47933dcab3b97bd01bb9324195559901 100644 (file)
@@ -16,7 +16,6 @@
 #include "crimson/common/log.h"
 #include "stages/key_layout.h"
 #include "tree.h"
-#include "test/crimson/seastore/onode_tree/test_value.h"
 
 /**
  * tree_utils.h
 
 namespace crimson::os::seastore::onode {
 
-using TestBtree = Btree<TestValue>;
+/**
+ * ValueItem template to work with tree utility classes:
+ *
+ * struct ValueItem {
+ *   using ValueType = ConcreteValueType;
+ *   <public members>
+ *
+ *   value_size_t get_payload_size() const;
+ *   void initialize(Transaction& t, ValueType& value) const;
+ *   void validate(ValueType& value) const;
+ *   static ValueItem create(std::size_t expected_size, std::size_t id);
+ * };
+ * std::ostream& operator<<(std::ostream& os, const ValueItem& item);
+ */
 
-struct value_item_t {
-  value_size_t size;
-  TestValue::id_t id;
-  TestValue::magic_t magic;
+template <typename ValueItem>
+void initialize_cursor_from_item(
+    Transaction& t,
+    const ghobject_t& key,
+    const ValueItem& item,
+    typename Btree<typename ValueItem::ValueType>::Cursor& cursor,
+    bool insert_success) {
+  ceph_assert(insert_success);
+  ceph_assert(!cursor.is_end());
+  ceph_assert(cursor.get_ghobj() == key);
+  auto tree_value = cursor.value();
+  item.initialize(t, tree_value);
+}
 
-  TestBtree::tree_value_config_t get_config() const {
-    assert(size > sizeof(value_header_t));
-    return {static_cast<value_size_t>(size - sizeof(value_header_t))};
-  }
-};
-inline std::ostream& operator<<(std::ostream& os, const value_item_t& item) {
-  return os << "ValueItem(#" << item.id << ", " << item.size << "B)";
+
+template <typename ValueItem>
+void validate_cursor_from_item(
+    const ghobject_t& key,
+    const ValueItem& item,
+    typename Btree<typename ValueItem::ValueType>::Cursor& cursor) {
+  ceph_assert(!cursor.is_end());
+  ceph_assert(cursor.get_ghobj() == key);
+  auto value = cursor.value();
+  item.validate(value);
 }
 
+template <typename ValueItem>
 class Values {
  public:
   Values(size_t n) {
@@ -60,48 +85,22 @@ class Values {
 
   ~Values() = default;
 
-  value_item_t create(size_t _size) {
-    ceph_assert(_size <= std::numeric_limits<value_size_t>::max());
-    ceph_assert(_size > sizeof(value_header_t));
-    value_size_t size = _size;
-    auto current_id = id++;
-    return value_item_t{size, current_id, (TestValue::magic_t)current_id * 137};
+  ValueItem create(size_t size) {
+    return ValueItem::create(size, id++);
   }
 
-  value_item_t pick() const {
+  ValueItem pick() const {
     auto index = rd() % values.size();
     return values[index];
   }
 
-  static void initialize_cursor(
-      Transaction& t,
-      TestBtree::Cursor& cursor,
-      const value_item_t& item) {
-    ceph_assert(!cursor.is_end());
-    auto value = cursor.value();
-    ceph_assert(value.get_payload_size() + sizeof(value_header_t) == item.size);
-    value.set_id_replayable(t, item.id);
-    value.set_tail_magic_replayable(t, item.magic);
-  }
-
-  static void validate_cursor(
-      TestBtree::Cursor& cursor,
-      const ghobject_t& key,
-      const value_item_t& item) {
-    ceph_assert(!cursor.is_end());
-    ceph_assert(cursor.get_ghobj() == key);
-    auto value = cursor.value();
-    ceph_assert(value.get_payload_size() + sizeof(value_header_t) == item.size);
-    ceph_assert(value.get_id() == item.id);
-    ceph_assert(value.get_tail_magic() == item.magic);
-  }
-
  private:
-  TestValue::id_t id = 0;
+  std::size_t id = 0;
   mutable std::random_device rd;
-  std::vector<value_item_t> values;
+  std::vector<ValueItem> values;
 };
 
+template <typename ValueItem>
 class KVPool {
   struct kv_conf_t {
     index_t index2;
@@ -109,7 +108,7 @@ class KVPool {
     index_t index0;
     size_t ns_size;
     size_t oid_size;
-    value_item_t value;
+    ValueItem value;
 
     ghobject_t get_ghobj() const {
       assert(index1 < 10);
@@ -137,7 +136,7 @@ class KVPool {
   using kv_vector_t = std::vector<kv_conf_t>;
 
  public:
-  using kv_t = std::pair<ghobject_t, value_item_t>;
+  using kv_t = std::pair<ghobject_t, ValueItem>;
 
   KVPool(const std::vector<size_t>& str_sizes,
          const std::vector<size_t>& value_sizes,
@@ -224,19 +223,21 @@ class KVPool {
 
  private:
   std::vector<size_t> str_sizes;
-  Values values;
+  Values<ValueItem> values;
   kv_vector_t kvs;
   kv_vector_t random_kvs;
 };
 
-template <bool TRACK>
+template <bool TRACK, typename ValueItem>
 class TreeBuilder {
  public:
-  using ertr = TestBtree::btree_ertr;
+  using BtreeImpl = Btree<typename ValueItem::ValueType>;
+  using BtreeCursor = typename BtreeImpl::Cursor;
+  using ertr = typename BtreeImpl::btree_ertr;
   template <class ValueT=void>
-  using future = ertr::future<ValueT>;
+  using future = typename ertr::template future<ValueT>;
 
-  TreeBuilder(KVPool& kvs, NodeExtentManagerURef&& nm)
+  TreeBuilder(KVPool<ValueItem>& kvs, NodeExtentManagerURef&& nm)
       : kvs{kvs} {
     tree.emplace(std::move(nm));
   }
@@ -265,39 +266,39 @@ class TreeBuilder {
 
   future<> insert(Transaction& t) {
     kv_iter = kvs.random_begin();
-    auto cursors = seastar::make_lw_shared<std::vector<TestBtree::Cursor>>();
+    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]() -> future<bool> {
       if (kv_iter.is_end()) {
-        return ertr::make_ready_future<bool>(true);
+        return ertr::template make_ready_future<bool>(true);
       }
       auto [key, value] = kv_iter.get_kv();
       logger().debug("[{}] {} -> {}", kv_iter.index(), key_hobj_t{key}, value);
-      return tree->insert(t, key, value.get_config()
-      ).safe_then([&t, this, cursors, value](auto ret) {
+      return tree->insert(
+          t, key, {value.get_payload_size()}
+      ).safe_then([&t, this, cursors, key, value](auto ret) {
         auto& [cursor, success] = ret;
-        assert(success == true);
-        Values::initialize_cursor(t, cursor, value);
+        initialize_cursor_from_item(t, key, value, cursor, success);
         if constexpr (TRACK) {
           cursors->emplace_back(cursor);
         }
 #ifndef NDEBUG
         auto [key, value] = kv_iter.get_kv();
-        Values::validate_cursor(cursor, key, value);
+        validate_cursor_from_item(key, value, cursor);
         return tree->find(t, key
         ).safe_then([this, cursor](auto cursor_) mutable {
           assert(!cursor_.is_end());
           auto [key, value] = kv_iter.get_kv();
           ceph_assert(cursor_.get_ghobj() == key);
           ceph_assert(cursor_.value() == cursor.value());
-          Values::validate_cursor(cursor_, key, value);
+          validate_cursor_from_item(key, value, cursor_);
           ++kv_iter;
-          return ertr::make_ready_future<bool>(false);
+          return ertr::template make_ready_future<bool>(false);
         });
 #else
         ++kv_iter;
-        return ertr::make_ready_future<bool>(false);
+        return ertr::template make_ready_future<bool>(false);
 #endif
       });
     }).safe_then([&t, this, start_time, cursors] {
@@ -311,19 +312,19 @@ class TreeBuilder {
           return crimson::do_until([&t, this, &c_iter, cursors]() -> future<bool> {
             if (kv_iter.is_end()) {
               logger().info("Verify done!");
-              return ertr::make_ready_future<bool>(true);
+              return ertr::template make_ready_future<bool>(true);
             }
             assert(c_iter != cursors->end());
             auto [k, v] = kv_iter.get_kv();
             // validate values in tree keep intact
             return tree->find(t, k).safe_then([this, &c_iter](auto cursor) {
               auto [k, v] = kv_iter.get_kv();
-              Values::validate_cursor(cursor, k, v);
+              validate_cursor_from_item(k, v, cursor);
               // validate values in cursors keep intact
-              Values::validate_cursor(*c_iter, k, v);
+              validate_cursor_from_item(k, v, *c_iter);
               ++kv_iter;
               ++c_iter;
-              return ertr::make_ready_future<bool>(false);
+              return ertr::template make_ready_future<bool>(false);
             });
           });
         });
@@ -351,7 +352,7 @@ class TreeBuilder {
       while (!iter.is_end()) {
         auto [k, v] = iter.get_kv();
         auto cursor = tree->find(t, k).unsafe_get0();
-        Values::validate_cursor(cursor, k, v);
+        validate_cursor_from_item(k, v, cursor);
         ++iter;
       }
 
@@ -361,7 +362,7 @@ class TreeBuilder {
       while (!iter.is_end()) {
         assert(!cursor.is_end());
         auto [k, v] = iter.get_kv();
-        Values::validate_cursor(cursor, k, v);
+        validate_cursor_from_item(k, v, cursor);
         cursor = cursor.get_next(t).unsafe_get0();
         ++iter;
       }
@@ -376,9 +377,9 @@ class TreeBuilder {
     return crimson::get_logger(ceph_subsys_filestore);
   }
 
-  KVPool& kvs;
-  std::optional<TestBtree> tree;
-  KVPool::iterator_t kv_iter;
+  KVPool<ValueItem>& kvs;
+  std::optional<BtreeImpl> tree;
+  typename KVPool<ValueItem>::iterator_t kv_iter;
 };
 
 }
index 61344951ff835f5f4525c41fa25bc0c0bc0e3dbd..7f06c41d310acec81a112a87598699a39db72626 100644 (file)
@@ -169,6 +169,8 @@ TEST_F(a_basic_test_t, 2_node_sizes)
   });
 }
 
+using TestBtree = Btree<TestValue>;
+
 struct b_dummy_tree_test_t : public seastar_test_suite_t {
   NodeExtentManagerURef moved_nm;
   TransactionRef ref_t;
@@ -206,23 +208,21 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_leaf_node)
     ASSERT_TRUE(tree.last(t).unsafe_get0().is_end());
 
     std::vector<std::tuple<ghobject_t,
-                           value_item_t,
+                           test_item_t,
                            TestBtree::Cursor>> insert_history;
     auto f_validate_insert_new = [this, &insert_history] (
-        const ghobject_t& key, const value_item_t& value) {
+        const ghobject_t& key, const test_item_t& value) {
       auto [cursor, success] = tree.insert(
-          t, key, value.get_config()).unsafe_get0();
-      ceph_assert(success);
-      ceph_assert(cursor.get_ghobj() == key);
-      Values::initialize_cursor(t, cursor, value);
+          t, key, {value.get_payload_size()}).unsafe_get0();
+      initialize_cursor_from_item(t, key, value, cursor, success);
       insert_history.emplace_back(key, value, cursor);
       auto cursor_ = tree.find(t, key).unsafe_get0();
       ceph_assert(cursor_ != tree.end());
       ceph_assert(cursor_.value() == cursor.value());
-      Values::validate_cursor(cursor_, key, value);
+      validate_cursor_from_item(key, value, cursor_);
       return cursor.value();
     };
-    auto values = Values(15);
+    auto values = Values<test_item_t>(15);
 
     // insert key1, value1 at STAGE_LEFT
     auto key1 = make_ghobj(3, 3, 3, "ns3", "oid3", 3, 3);
@@ -242,9 +242,9 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_leaf_node)
     {
       auto value1_dup = values.pick();
       auto [cursor1_dup, ret1_dup] = tree.insert(
-          t, key1, value1_dup.get_config()).unsafe_get0();
+          t, key1, {value1_dup.get_payload_size()}).unsafe_get0();
       ASSERT_FALSE(ret1_dup);
-      Values::validate_cursor(cursor1_dup, key1, value1);
+      validate_cursor_from_item(key1, value1, cursor1_dup);
     }
 
     // insert key2, value2 to key1's left at STAGE_LEFT
@@ -300,7 +300,7 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_leaf_node)
     f_validate_insert_new(key11, value11);
 
     // insert key, value randomly until a perfect 3-ary tree is formed
-    std::vector<std::pair<ghobject_t, value_item_t>> kvs{
+    std::vector<std::pair<ghobject_t, test_item_t>> kvs{
       {make_ghobj(2, 2, 2, "ns2", "oid2", 2, 2), values.pick()},
       {make_ghobj(2, 2, 2, "ns2", "oid2", 4, 4), values.pick()},
       {make_ghobj(2, 2, 2, "ns3", "oid3", 4, 4), values.pick()},
@@ -330,21 +330,21 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_leaf_node)
       // validate values in tree keep intact
       auto cursor = tree.find(t, k).unsafe_get0();
       EXPECT_NE(cursor, tree.end());
-      Values::validate_cursor(cursor, k, v);
+      validate_cursor_from_item(k, v, cursor);
       // validate values in cursors keep intact
-      Values::validate_cursor(c, k, v);
+      validate_cursor_from_item(k, v, c);
     }
     {
       auto cursor = tree.lower_bound(t, key_s).unsafe_get0();
-      Values::validate_cursor(cursor, smallest_key, smallest_value);
+      validate_cursor_from_item(smallest_key, smallest_value, cursor);
     }
     {
       auto cursor = tree.begin(t).unsafe_get0();
-      Values::validate_cursor(cursor, smallest_key, smallest_value);
+      validate_cursor_from_item(smallest_key, smallest_value, cursor);
     }
     {
       auto cursor = tree.last(t).unsafe_get0();
-      Values::validate_cursor(cursor, largest_key, largest_value);
+      validate_cursor_from_item(largest_key, largest_value, cursor);
     }
 
     // validate range query
@@ -360,7 +360,7 @@ TEST_F(b_dummy_tree_test_t, 3_random_insert_leaf_node)
       auto cursor = tree.begin(t).unsafe_get0();
       for (auto& [k, v] : kvs) {
         ASSERT_FALSE(cursor.is_end());
-        Values::validate_cursor(cursor, k, v);
+        validate_cursor_from_item(k, v, cursor);
         cursor = cursor.get_next(t).unsafe_get0();
       }
       ASSERT_TRUE(cursor.is_end());
@@ -432,7 +432,7 @@ class TestTree {
   }
 
   seastar::future<> build_tree(
-      const std::vector<ghobject_t>& keys, const std::vector<value_item_t>& values) {
+      const std::vector<ghobject_t>& keys, const std::vector<test_item_t>& values) {
     return seastar::async([this, keys, values] {
       tree.mkfs(t).unsafe_get0();
       //logger().info("\n---------------------------------------------"
@@ -453,7 +453,7 @@ class TestTree {
     });
   }
 
-  seastar::future<> split(const ghobject_t& key, const value_item_t& value,
+  seastar::future<> split(const ghobject_t& key, const test_item_t& value,
                           const split_expectation_t& expected) {
     return seastar::async([this, key, value, expected] {
       TestBtree tree_clone(NodeExtentManager::create_dummy(IS_DUMMY_SYNC));
@@ -463,10 +463,8 @@ class TestTree {
 
       logger().info("insert {}:", key_hobj_t(key));
       auto [cursor, success] = tree_clone.insert(
-          t_clone, key, value.get_config()).unsafe_get0();
-      ASSERT_TRUE(success);
-      ASSERT_EQ(cursor.get_ghobj(), key);
-      Values::initialize_cursor(t_clone, cursor, value);
+          t_clone, key, {value.get_payload_size()}).unsafe_get0();
+      initialize_cursor_from_item(t, key, value, cursor, success);
 
       std::ostringstream oss;
       tree_clone.dump(t_clone, oss);
@@ -476,27 +474,25 @@ class TestTree {
       for (auto& [k, v, c] : insert_history) {
         auto result = tree_clone.find(t_clone, k).unsafe_get0();
         EXPECT_NE(result, tree_clone.end());
-        Values::validate_cursor(result, k, v);
+        validate_cursor_from_item(k, v, result);
       }
       auto result = tree_clone.find(t_clone, key).unsafe_get0();
       EXPECT_NE(result, tree_clone.end());
-      Values::validate_cursor(result, key, value);
+      validate_cursor_from_item(key, value, result);
       EXPECT_TRUE(last_split.match(expected));
     });
   }
 
-  value_item_t create_value(size_t size) {
+  test_item_t create_value(size_t size) {
     return values.create(size);
   }
 
  private:
-  seastar::future<> insert_tree(const ghobject_t& key, const value_item_t& value) {
+  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_config()).unsafe_get0();
-      ASSERT_TRUE(success);
-      ASSERT_EQ(cursor.get_ghobj(), key);
-      Values::initialize_cursor(t, cursor, value);
+          t, key, {value.get_payload_size()}).unsafe_get0();
+      initialize_cursor_from_item(t, key, value, cursor, success);
       insert_history.emplace_back(key, value, cursor);
     });
   }
@@ -507,9 +503,9 @@ class TestTree {
   ValueBuilderImpl<TestValue> vb;
   context_t c;
   TestBtree tree;
-  Values values;
+  Values<test_item_t> values;
   std::vector<std::tuple<
-    ghobject_t, value_item_t, TestBtree::Cursor>> insert_history;
+    ghobject_t, test_item_t, TestBtree::Cursor>> insert_history;
 };
 
 struct c_dummy_test_t : public seastar_test_suite_t {};
@@ -661,7 +657,7 @@ TEST_F(c_dummy_test_t, 4_split_leaf_node)
       std::vector<ghobject_t> keys = {
         make_ghobj(2, 2, 2, "ns3", "oid3", 3, 3),
         make_ghobj(3, 3, 3, "ns3", "oid3", 3, 3)};
-      std::vector<value_item_t> values = {
+      std::vector<test_item_t> values = {
         test.create_value(1360),
         test.create_value(1632)};
       test.build_tree(keys, values).get0();
@@ -1207,10 +1203,10 @@ TEST_F(d_seastore_tm_test_t, 6_random_insert_leaf_node)
   run_async([this] {
     constexpr bool TEST_SEASTORE = true;
     constexpr bool TRACK_CURSORS = true;
-    KVPool kvs{{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>>(kvs,
+    KVPool<test_item_t> kvs{{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)));
     {
index 35ec25f1a9e5984b138a4b65b1fcbac00f023a18..bf2a15abb1241e5856da949e04fee2e7bad5e4ff 100644 (file)
@@ -140,4 +140,43 @@ class TestValue final : public Value {
   }
 };
 
+struct test_item_t {
+  using ValueType = TestValue;
+
+  value_size_t size;
+  TestValue::id_t id;
+  TestValue::magic_t magic;
+
+  value_size_t get_payload_size() const {
+    assert(size > sizeof(value_header_t));
+    return {static_cast<value_size_t>(size - sizeof(value_header_t))};
+  }
+
+  void initialize(Transaction& t, TestValue& value) const {
+    ceph_assert(value.get_payload_size() + sizeof(value_header_t) == size);
+    value.set_id_replayable(t, id);
+    value.set_tail_magic_replayable(t, magic);
+  }
+
+  void validate(TestValue& value) const {
+    ceph_assert(value.get_payload_size() + sizeof(value_header_t) == size);
+    ceph_assert(value.get_id() == id);
+    ceph_assert(value.get_tail_magic() == magic);
+  }
+
+  static test_item_t create(std::size_t _size, std::size_t _id) {
+    ceph_assert(_size <= std::numeric_limits<value_size_t>::max());
+    ceph_assert(_size > sizeof(value_header_t));
+    value_size_t size = _size;
+
+    ceph_assert(_id <= std::numeric_limits<TestValue::id_t>::max());
+    TestValue::id_t id = _id;
+
+    return {size, id, (TestValue::magic_t)id * 137};
+  }
+};
+inline std::ostream& operator<<(std::ostream& os, const test_item_t& item) {
+  return os << "TestItem(#" << item.id << ", " << item.size << "B)";
+}
+
 }
index 14f8635086418f88f3321dbfba508256dbd5b2f3..074fbd923fd4b200f7c65cd2374e482adc941d0b 100644 (file)
@@ -9,6 +9,8 @@
 #include "crimson/common/log.h"
 #include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
 #include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
+
+#include "test/crimson/seastore/onode_tree/test_value.h"
 #include "test/crimson/seastore/transaction_manager_test_state.h"
 
 using namespace crimson::os::seastore::onode;
@@ -23,10 +25,10 @@ class PerfTree : public TMTestState {
  public:
   PerfTree(bool is_dummy) : is_dummy{is_dummy} {}
 
-  seastar::future<> run(KVPool& kvs) {
+  seastar::future<> run(KVPool<test_item_t>& kvs) {
     return tm_setup().then([this, &kvs] {
       return seastar::async([this, &kvs] {
-        auto tree = std::make_unique<TreeBuilder<TRACK>>(kvs,
+        auto tree = std::make_unique<TreeBuilder<TRACK, test_item_t>>(kvs,
             (is_dummy ? NodeExtentManager::create_dummy(true)
                       : NodeExtentManager::create_seastore(*tm)));
         {
@@ -84,10 +86,10 @@ seastar::future<> run(const bpo::variables_map& config) {
     auto range0 = config["range0"].as<std::vector<unsigned>>();
     ceph_assert(range0.size() == 2);
 
-    KVPool kvs{str_sizes, onode_sizes,
-               {range2[0], range2[1]},
-               {range1[0], range1[1]},
-               {range0[0], range0[1]}};
+    KVPool<test_item_t> kvs{str_sizes, onode_sizes,
+                            {range2[0], range2[1]},
+                            {range1[0], range1[1]},
+                            {range0[0], range0[1]}};
     PerfTree<TRACK> perf{is_dummy};
     perf.run(kvs).get0();
   });