--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#pragma once
+
+#include <cassert>
+#include <cstring>
+#include <random>
+#include <string>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+#include "crimson/common/log.h"
+#include "stages/key_layout.h"
+#include "tree.h"
+
+namespace crimson::os::seastore::onode {
+
+ghobject_t make_ghobj(
+ int shard, unsigned pool, unsigned crush,
+ std::string ns, std::string oid, unsigned snap, unsigned gen) {
+ assert(shard <= std::numeric_limits<shard_t>::max());
+ assert(shard >= std::numeric_limits<shard_t>::min());
+ assert(pool <= std::numeric_limits<pool_t>::max());
+ assert(crush <= std::numeric_limits<crush_hash_t>::max());
+ assert(snap <= std::numeric_limits<snap_t>::max());
+ assert(gen <= std::numeric_limits<gen_t>::max());
+ ghobject_t ghobj;
+ ghobj.shard_id.id = shard;
+ ghobj.hobj.pool = pool;
+ ghobj.hobj.set_hash(crush);
+ ghobj.hobj.nspace = ns;
+ ghobj.hobj.oid.name = oid;
+ ghobj.hobj.snap = snap;
+ ghobj.generation = gen;
+ return ghobj;
+}
+
+class Onodes {
+ public:
+ Onodes(size_t n) {
+ for (size_t i = 1; i <= n; ++i) {
+ auto p_onode = &create(i * 8);
+ onodes.push_back(p_onode);
+ }
+ }
+
+ Onodes(std::vector<size_t> sizes) {
+ for (auto& size : sizes) {
+ auto p_onode = &create(size);
+ onodes.push_back(p_onode);
+ }
+ }
+
+ ~Onodes() {
+ std::for_each(tracked_onodes.begin(), tracked_onodes.end(),
+ [] (onode_t* onode) {
+ std::free(onode);
+ });
+ }
+
+ const onode_t& create(size_t size) {
+ ceph_assert(size >= sizeof(onode_t) + sizeof(uint32_t));
+ uint32_t target = size * 137;
+ auto p_mem = (char*)std::malloc(size);
+ auto p_onode = (onode_t*)p_mem;
+ tracked_onodes.push_back(p_onode);
+ p_onode->size = size;
+ p_onode->id = id++;
+ p_mem += (size - sizeof(uint32_t));
+ std::memcpy(p_mem, &target, sizeof(uint32_t));
+ validate(*p_onode);
+ return *p_onode;
+ }
+
+ const onode_t& pick() const {
+ auto index = rd() % onodes.size();
+ return *onodes[index];
+ }
+
+ const onode_t& pick_largest() const {
+ return *onodes[onodes.size() - 1];
+ }
+
+ static void validate_cursor(
+ const Btree::Cursor& cursor, const ghobject_t& key, const onode_t& onode) {
+ ceph_assert(!cursor.is_end());
+ ceph_assert(cursor.get_ghobj() == key);
+ ceph_assert(cursor.value());
+ ceph_assert(cursor.value() != &onode);
+ ceph_assert(*cursor.value() == onode);
+ validate(*cursor.value());
+ }
+
+ private:
+ static void validate(const onode_t& node) {
+ auto p_target = (const char*)&node + node.size - sizeof(uint32_t);
+ uint32_t target;
+ std::memcpy(&target, p_target, sizeof(uint32_t));
+ ceph_assert(target == node.size * 137);
+ }
+
+ uint16_t id = 0;
+ mutable std::random_device rd;
+ std::vector<const onode_t*> onodes;
+ std::vector<onode_t*> tracked_onodes;
+};
+
+class KVPool {
+ struct kv_conf_t {
+ unsigned index2;
+ unsigned index1;
+ unsigned index0;
+ size_t ns_size;
+ size_t oid_size;
+ const onode_t* p_value;
+
+ ghobject_t get_ghobj() const {
+ assert(index1 < 10);
+ std::ostringstream os_ns;
+ os_ns << "ns" << index1;
+ unsigned current_size = (unsigned)os_ns.tellp();
+ assert(ns_size >= current_size);
+ os_ns << std::string(ns_size - current_size, '_');
+
+ std::ostringstream os_oid;
+ os_oid << "oid" << index1;
+ current_size = (unsigned)os_oid.tellp();
+ assert(oid_size >= current_size);
+ os_oid << std::string(oid_size - current_size, '_');
+
+ return make_ghobj(index2, index2, index2,
+ os_ns.str(), os_oid.str(), index0, index0);
+ }
+ };
+ using kv_vector_t = std::vector<kv_conf_t>;
+
+ public:
+ using kv_t = std::pair<ghobject_t, const onode_t*>;
+
+ KVPool(const std::vector<size_t>& str_sizes,
+ const std::vector<size_t>& onode_sizes,
+ const std::pair<unsigned, unsigned>& range2,
+ const std::pair<unsigned, unsigned>& range1,
+ const std::pair<unsigned, unsigned>& range0)
+ : str_sizes{str_sizes}, onodes{onode_sizes} {
+ ceph_assert(range2.first < range2.second);
+ ceph_assert(range2.second - 1 <= (unsigned)std::numeric_limits<shard_t>::max());
+ ceph_assert(range2.second - 1 <= std::numeric_limits<pool_t>::max());
+ ceph_assert(range2.second - 1 <= std::numeric_limits<crush_hash_t>::max());
+ ceph_assert(range1.first < range1.second);
+ ceph_assert(range1.second - 1 <= 9);
+ ceph_assert(range0.first < range0.second);
+ ceph_assert(range0.second - 1 <= std::numeric_limits<snap_t>::max());
+ ceph_assert(range0.second - 1 <= std::numeric_limits<gen_t>::max());
+ std::random_device rd;
+ for (unsigned i = range2.first; i < range2.second; ++i) {
+ for (unsigned j = range1.first; j < range1.second; ++j) {
+ auto ns_size = (unsigned)str_sizes[rd() % str_sizes.size()];
+ auto oid_size = (unsigned)str_sizes[rd() % str_sizes.size()];
+ for (unsigned k = range0.first; k < range0.second; ++k) {
+ kvs.emplace_back(kv_conf_t{i, j, k, ns_size, oid_size, &onodes.pick()});
+ }
+ }
+ }
+ random_kvs = kvs;
+ std::random_shuffle(random_kvs.begin(), random_kvs.end());
+ }
+
+ class iterator_t {
+ public:
+ iterator_t() = default;
+ iterator_t(const iterator_t&) = default;
+ iterator_t(iterator_t&&) = default;
+ iterator_t& operator=(const iterator_t&) = default;
+ iterator_t& operator=(iterator_t&&) = default;
+
+ kv_t get_kv() const {
+ assert(!is_end());
+ auto& conf = (*p_kvs)[i];
+ return std::make_pair(conf.get_ghobj(), conf.p_value);
+ }
+ bool is_end() const { return !p_kvs || i >= p_kvs->size(); }
+ size_t index() const { return i; }
+
+ iterator_t& operator++() {
+ assert(!is_end());
+ ++i;
+ return *this;
+ }
+
+ iterator_t operator++(int) {
+ iterator_t tmp = *this;
+ ++*this;
+ return tmp;
+ }
+
+ private:
+ iterator_t(const kv_vector_t& kvs) : p_kvs{&kvs} {}
+
+ const kv_vector_t* p_kvs = nullptr;
+ size_t i = 0;
+ friend class KVPool;
+ };
+
+ iterator_t begin() const {
+ return iterator_t(kvs);
+ }
+
+ iterator_t random_begin() const {
+ return iterator_t(random_kvs);
+ }
+
+ size_t size() const {
+ return kvs.size();
+ }
+
+ private:
+ std::vector<size_t> str_sizes;
+ Onodes onodes;
+ kv_vector_t kvs;
+ kv_vector_t random_kvs;
+};
+
+template <bool TRACK>
+class TreeBuilder {
+ public:
+ using ertr = Btree::btree_ertr;
+ template <class... ValuesT>
+ using future = ertr::future<ValuesT...>;
+
+ TreeBuilder(KVPool& kvs, NodeExtentManagerURef&& nm)
+ : kvs{kvs}, ref_t{make_transaction()}, t{*ref_t}, tree{std::move(nm)} {}
+
+ future<> bootstrap() {
+ return tree.mkfs(t);
+ }
+
+ future<> run() {
+ std::ostringstream oss;
+#ifndef NDEBUG
+ oss << "debug on, ";
+#else
+ oss << "debug off, ";
+#endif
+ if constexpr (TRACK) {
+ oss << "track on";
+ } else {
+ oss << "track off";
+ }
+ kv_iter = kvs.random_begin();
+ logger().warn("start inserting {} kvs ({}) ...", kvs.size(), oss.str());
+ auto start_time = mono_clock::now();
+ return crimson::do_until([this]() -> future<bool> {
+ if (kv_iter.is_end()) {
+ return ertr::make_ready_future<bool>(true);
+ }
+ auto [key, p_value] = kv_iter.get_kv();
+ logger().debug("[{}] {} -> {}", kv_iter.index(), key_hobj_t{key}, *p_value);
+ return tree.insert(t, key, *p_value).safe_then([this](auto ret) {
+ auto& [cursor, success] = ret;
+ assert(success == true);
+ if constexpr (TRACK) {
+ cursors.emplace_back(cursor);
+ }
+#ifndef NDEBUG
+ auto [key, p_value] = kv_iter.get_kv();
+ Onodes::validate_cursor(cursor, key, *p_value);
+ return tree.lower_bound(t, key).safe_then([this, cursor](auto cursor_) {
+ auto [key, p_value] = kv_iter.get_kv();
+ ceph_assert(cursor_.get_ghobj() == key);
+ ceph_assert(cursor_.value() == cursor.value());
+ ++kv_iter;
+ return ertr::make_ready_future<bool>(false);
+ });
+#else
+ ++kv_iter;
+ return ertr::make_ready_future<bool>(false);
+#endif
+ });
+ }).safe_then([this, start_time] {
+ std::chrono::duration<double> duration = mono_clock::now() - start_time;
+ logger().warn("Insert done! {}s", duration.count());
+ return tree.get_stats_slow(t);
+ }).safe_then([this](auto stats) {
+ logger().warn("{}", stats);
+ if (!cursors.empty()) {
+ logger().info("Verifing tracked cursors ...");
+ kv_iter = kvs.random_begin();
+ return seastar::do_with(
+ cursors.begin(), [this](auto& c_iter) {
+ return crimson::do_until([this, &c_iter]() -> future<bool> {
+ if (kv_iter.is_end()) {
+ logger().info("Verify done!");
+ return ertr::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.lower_bound(t, k).safe_then([this, &c_iter](auto cursor) {
+ auto [k, v] = kv_iter.get_kv();
+ Onodes::validate_cursor(cursor, k, *v);
+ // validate values in cursors keep intact
+ Onodes::validate_cursor(*c_iter, k, *v);
+ ++kv_iter;
+ ++c_iter;
+ return ertr::make_ready_future<bool>(false);
+ });
+ });
+ });
+ } else {
+ return ertr::now();
+ }
+ });
+ }
+
+ private:
+ static seastar::logger& logger() {
+ return crimson::get_logger(ceph_subsys_filestore);
+ }
+
+ KVPool& kvs;
+ TransactionRef ref_t;
+ Transaction& t;
+ Btree tree;
+ KVPool::iterator_t kv_iter;
+ std::vector<Btree::Cursor> cursors;
+};
+
+}
#include <array>
#include <cstring>
#include <memory>
-#include <random>
#include <set>
#include <sstream>
#include <vector>
#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
#include "crimson/os/seastore/onode_manager/staged-fltree/node_layout.h"
#include "crimson/os/seastore/onode_manager/staged-fltree/tree.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
#include "crimson/os/seastore/segment_manager.h"
#include "crimson/os/seastore/transaction_manager.h"
#include "test/crimson/gtest_seastar.h"
using namespace crimson::os::seastore::onode;
-using crimson::os::seastore::Cache;
-using crimson::os::seastore::Journal;
-using crimson::os::seastore::LBAManagerRef;
-using crimson::os::seastore::lba_manager::create_lba_manager;
-using crimson::os::seastore::SegmentCleaner;
-using crimson::os::seastore::SegmentManager;
-using crimson::os::seastore::segment_manager::create_ephemeral;
-using crimson::os::seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+namespace seastore = crimson::os::seastore;
+using seastore::lba_manager::create_lba_manager;
+using seastore::segment_manager::create_ephemeral;
+using seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+using sc_config_t = seastore::SegmentCleaner::config_t;
namespace {
[[maybe_unused]] seastar::logger& logger() {
return crimson::get_logger(ceph_subsys_test);
}
- ghobject_t make_ghobj(
- shard_t shard, pool_t pool, crush_hash_t crush,
- std::string ns, std::string oid, snap_t snap, gen_t gen) {
- ghobject_t ghobj;
- ghobj.shard_id.id = shard;
- ghobj.hobj.pool = pool;
- ghobj.hobj.set_hash(crush);
- ghobj.hobj.nspace = ns;
- ghobj.hobj.oid.name = oid;
- ghobj.hobj.snap = snap;
- ghobj.generation = gen;
- return ghobj;
- }
-
// return a key_view_t and its underlying memory buffer.
// the buffer needs to be freed manually.
std::pair<key_view_t, void*> build_key_view(const ghobject_t& hobj) {
return {key_view, p_mem};
}
-
- class Onodes {
- public:
- Onodes(size_t n) {
- for (size_t i = 1; i <= n; ++i) {
- auto p_onode = &create(i * 8);
- onodes.push_back(p_onode);
- }
- }
-
- Onodes(std::vector<size_t> sizes) {
- for (auto& size : sizes) {
- auto p_onode = &create(size);
- onodes.push_back(p_onode);
- }
- }
-
- ~Onodes() {
- std::for_each(tracked_onodes.begin(), tracked_onodes.end(),
- [] (onode_t* onode) {
- std::free(onode);
- });
- }
-
- const onode_t& create(size_t size) {
- assert(size >= sizeof(onode_t) + sizeof(uint32_t));
- uint32_t target = size * 137;
- auto p_mem = (char*)std::malloc(size);
- auto p_onode = (onode_t*)p_mem;
- tracked_onodes.push_back(p_onode);
- p_onode->size = size;
- p_onode->id = id++;
- p_mem += (size - sizeof(uint32_t));
- std::memcpy(p_mem, &target, sizeof(uint32_t));
- validate(*p_onode);
- return *p_onode;
- }
-
- const onode_t& pick() const {
- auto index = rd() % onodes.size();
- return *onodes[index];
- }
-
- const onode_t& pick_largest() const {
- return *onodes[onodes.size() - 1];
- }
-
- static void validate_cursor(
- const Btree::Cursor& cursor, const ghobject_t& key, const onode_t& onode) {
- assert(!cursor.is_end());
- assert(cursor.get_ghobj() == key);
- assert(cursor.value());
- assert(cursor.value() != &onode);
- assert(*cursor.value() == onode);
- validate(*cursor.value());
- }
-
- private:
- static void validate(const onode_t& node) {
- auto p_target = (const char*)&node + node.size - sizeof(uint32_t);
- uint32_t target;
- std::memcpy(&target, p_target, sizeof(uint32_t));
- assert(target == node.size * 137);
- }
-
- uint16_t id = 0;
- mutable std::random_device rd;
- std::vector<const onode_t*> onodes;
- std::vector<onode_t*> tracked_onodes;
- };
}
struct a_basic_test_t : public seastar_test_suite_t {};
std::pair<unsigned, unsigned> range_0,
std::string padding = "",
bool is_internal = false) {
+ assert(range_1.second <= 10);
std::set<ghobject_t> ret;
ghobject_t key;
for (unsigned i = range_2.first; i < range_2.second; ++i) {
});
}
-class KVPool {
- struct kv_conf_t {
- unsigned index_2;
- unsigned index_1;
- unsigned index_0;
- size_t ns_size;
- size_t oid_size;
- const onode_t* p_value;
-
- ghobject_t get_ghobj() const {
- assert(index_1 < 10);
- std::ostringstream os_ns;
- os_ns << "ns" << index_1;
- unsigned current_size = (unsigned)os_ns.tellp();
- assert(ns_size >= current_size);
- os_ns << std::string(ns_size - current_size, '_');
-
- std::ostringstream os_oid;
- os_oid << "oid" << index_1;
- current_size = (unsigned)os_oid.tellp();
- assert(oid_size >= current_size);
- os_oid << std::string(oid_size - current_size, '_');
-
- return make_ghobj(index_2, index_2, index_2,
- os_ns.str(), os_oid.str(), index_0, index_0);
- }
- };
- using kv_vector_t = std::vector<kv_conf_t>;
-
- public:
- using kv_t = std::pair<ghobject_t, const onode_t*>;
-
- KVPool(std::vector<size_t> str_sizes,
- std::vector<size_t> onode_sizes,
- std::pair<unsigned, unsigned> range_2,
- std::pair<unsigned, unsigned> range_1,
- std::pair<unsigned, unsigned> range_0)
- : str_sizes{str_sizes}, onodes{onode_sizes} {
- std::random_device rd;
- for (unsigned i = range_2.first; i < range_2.second; ++i) {
- for (unsigned j = range_1.first; j < range_1.second; ++j) {
- auto ns_size = (unsigned)str_sizes[rd() % str_sizes.size()];
- auto oid_size = (unsigned)str_sizes[rd() % str_sizes.size()];
- for (unsigned k = range_0.first; k < range_0.second; ++k) {
- kvs.emplace_back(kv_conf_t{i, j, k, ns_size, oid_size, &onodes.pick()});
- }
- }
- }
- random_kvs = kvs;
- std::random_shuffle(random_kvs.begin(), random_kvs.end());
- }
-
- class iterator_t {
- public:
- kv_t get_kv() const {
- assert(!is_end());
- auto& conf = (*p_kvs)[i];
- return std::make_pair(conf.get_ghobj(), conf.p_value);
- }
- bool is_end() const { return i >= p_kvs->size(); }
- size_t index() const { return i; }
-
- iterator_t& operator++() {
- assert(!is_end());
- ++i;
- return *this;
- }
-
- iterator_t operator++(int) {
- iterator_t tmp = *this;
- ++*this;
- return tmp;
- }
-
- private:
- iterator_t(const kv_vector_t& kvs) : p_kvs{&kvs} {}
-
- const kv_vector_t* p_kvs;
- size_t i = 0;
- friend class KVPool;
- };
-
- iterator_t begin() const {
- return iterator_t(kvs);
- }
-
- iterator_t random_begin() const {
- return iterator_t(random_kvs);
- }
-
- size_t size() const {
- return kvs.size();
- }
-
- kv_t make_kv(unsigned index_2, unsigned index_1, unsigned index_0,
- size_t ns_size, size_t oid_size, size_t onode_size) {
- auto& onode = onodes.create(onode_size);
- kv_conf_t conf{index_2, index_1, index_0, ns_size, oid_size, &onode};
- return std::make_pair(conf.get_ghobj(), &onode);
- }
-
- private:
- std::vector<size_t> str_sizes;
- Onodes onodes;
- kv_vector_t kvs;
- kv_vector_t random_kvs;
-};
-
struct d_seastore_tree_test_t : public seastar_test_suite_t {
- std::unique_ptr<SegmentManager> segment_manager;
- SegmentCleaner segment_cleaner;
- Journal journal;
- Cache cache;
- LBAManagerRef lba_manager;
- TransactionManager tm;
- NodeExtentManagerURef moved_nm;
- TransactionRef ref_t;
- Transaction& t;
- Btree tree;
+ std::unique_ptr<seastore::SegmentManager> segment_manager;
+ seastore::SegmentCleaner segment_cleaner;
+ seastore::Journal journal;
+ seastore::Cache cache;
+ seastore::LBAManagerRef lba_manager;
+ seastore::TransactionManager tm;
+ KVPool kvs;
+ TreeBuilder<true> tree;
d_seastore_tree_test_t()
: segment_manager(create_ephemeral(DEFAULT_TEST_EPHEMERAL)),
- segment_cleaner(SegmentCleaner::config_t::default_from_segment_manager(
- *segment_manager)),
+ segment_cleaner(sc_config_t::default_from_segment_manager(*segment_manager)),
journal(*segment_manager),
cache(*segment_manager),
lba_manager(create_lba_manager(*segment_manager, cache)),
tm(*segment_manager, segment_cleaner, journal, cache, *lba_manager),
- moved_nm{
+ kvs({8, 11, 64, 256, 301, 320},
+ {8, 16, 128, 512, 576, 640},
+ {0, 32}, {0, 10}, {0, 4}),
+ tree{kvs,
#if 0
- NodeExtentManager::create_dummy(false)},
+ NodeExtentManager::create_dummy(false)
#else
- NodeExtentManager::create_seastore(tm)},
+ NodeExtentManager::create_seastore(tm)
#endif
- ref_t{make_transaction()},
- t{*ref_t},
- tree{std::move(moved_nm)} {
+ } {
journal.set_segment_provider(&segment_cleaner);
segment_cleaner.set_extent_callback(&tm);
}
}).safe_then([this] {
return tm.mount();
}).safe_then([this] {
- return tree.mkfs(t);
+ return tree.bootstrap();
}).handle_error(
crimson::ct_error::all_same_way([] {
ASSERT_FALSE("Unable to mkfs");
TEST_F(d_seastore_tree_test_t, 6_random_insert_leaf_node)
{
- run_async([this] {
- KVPool kvs({8, 11, 64, 256, 301, 320},
- {8, 16, 128, 512, 576, 640},
- {0, 32}, {0, 10}, {0, 4});
- auto iter = kvs.random_begin();
- std::vector<Btree::Cursor> cursors;
- while (!iter.is_end()) {
- auto [key, p_value] = iter.get_kv();
- logger().info("[{}] {} -> {}", iter.index(), key_hobj_t{key}, *p_value);
- auto [cursor, success] = tree.insert(t, key, *p_value).unsafe_get0();
- assert(success == true);
-#if 1 // validate cursor tracking
- cursors.emplace_back(cursor);
-#endif
- Onodes::validate_cursor(cursor, key, *p_value);
- auto cursor_ = tree.lower_bound(t, key).unsafe_get0();
- assert(cursor_.get_ghobj() == key);
- assert(cursor_.value() == cursor.value());
- ++iter;
- }
-
- logger().info("Insert done!");
- logger().info("{}", tree.get_stats_slow(t).unsafe_get0());
-
- if (!cursors.empty()) {
- auto kv_iter = kvs.random_begin();
- auto c_iter = cursors.begin();
- while (!kv_iter.is_end()) {
- assert(c_iter != cursors.end());
- auto [k, v] = kv_iter.get_kv();
- // validate values in tree keep intact
- auto cursor = tree.lower_bound(t, k).unsafe_get0();
- Onodes::validate_cursor(cursor, k, *v);
- // validate values in cursors keep intact
- Onodes::validate_cursor(*c_iter, k, *v);
-
- ++kv_iter;
- ++c_iter;
- }
- }
+ run([this] {
+ return tree.run().handle_error(
+ crimson::ct_error::all_same_way([] {
+ ASSERT_FALSE("Test failed");
+ })
+ );
});
}
add_executable(perf_async_msgr perf_async_msgr.cc)
target_link_libraries(perf_async_msgr ceph-common global ${ALLOC_LIBS})
+
+add_executable(perf_staged_fltree perf_staged_fltree.cc)
+target_link_libraries(perf_staged_fltree crimson-seastore)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <boost/program_options.hpp>
+
+#include <seastar/core/app-template.hh>
+#include <seastar/core/thread.hh>
+
+#include "crimson/os/seastore/onode_manager/staged-fltree/tree_utils.h"
+#include "crimson/os/seastore/onode_manager/staged-fltree/node_extent_manager.h"
+#include "crimson/os/seastore/segment_manager.h"
+#include "crimson/os/seastore/transaction_manager.h"
+
+using namespace crimson::os::seastore::onode;
+namespace bpo = boost::program_options;
+namespace seastore = crimson::os::seastore;
+using seastore::lba_manager::create_lba_manager;
+using seastore::segment_manager::create_ephemeral;
+using seastore::segment_manager::DEFAULT_TEST_EPHEMERAL;
+using sc_config_t = seastore::SegmentCleaner::config_t;
+
+template <bool TRACK>
+class PerfTree {
+ public:
+ PerfTree(bool is_dummy,
+ const std::vector<size_t>& str_sizes,
+ const std::vector<size_t>& onode_sizes,
+ const std::pair<int, int>& range2,
+ const std::pair<unsigned, unsigned>& range1,
+ const std::pair<unsigned, unsigned>& range0)
+ : is_dummy{is_dummy},
+ segment_manager(create_ephemeral(DEFAULT_TEST_EPHEMERAL)),
+ segment_cleaner(sc_config_t::default_from_segment_manager(*segment_manager)),
+ journal(*segment_manager),
+ cache(*segment_manager),
+ lba_manager(create_lba_manager(*segment_manager, cache)),
+ tm(*segment_manager, segment_cleaner, journal, cache, *lba_manager),
+ kvs(str_sizes, onode_sizes, range2, range1, range0),
+ tree{kvs, is_dummy ? NodeExtentManager::create_dummy(true)
+ : NodeExtentManager::create_seastore(tm)} {
+ journal.set_segment_provider(&segment_cleaner);
+ segment_cleaner.set_extent_callback(&tm);
+ }
+
+ seastar::future<> run() {
+ return start().then([this] {
+ return tree.run().handle_error(
+ crimson::ct_error::all_same_way([] {
+ ceph_abort("runtime error");
+ })
+ );
+ }).then([this] {
+ return stop();
+ });
+ }
+
+ private:
+ seastar::future<> start() {
+ if (is_dummy) {
+ return tree.bootstrap().handle_error(
+ crimson::ct_error::all_same_way([] {
+ ceph_abort("Unable to mkfs");
+ })
+ );
+ } else {
+ return segment_manager->init().safe_then([this] {
+ return tm.mkfs();
+ }).safe_then([this] {
+ return tm.mount();
+ }).safe_then([this] {
+ return tree.bootstrap();
+ }).handle_error(
+ crimson::ct_error::all_same_way([] {
+ ceph_abort("Unable to mkfs");
+ })
+ );
+ }
+ }
+
+ seastar::future<> stop() {
+ if (is_dummy) {
+ return seastar::now();
+ } else {
+ return tm.close().handle_error(
+ crimson::ct_error::all_same_way([] {
+ ceph_abort("Unable to close");
+ })
+ );
+ }
+ }
+
+ bool is_dummy;
+ std::unique_ptr<seastore::SegmentManager> segment_manager;
+ seastore::SegmentCleaner segment_cleaner;
+ seastore::Journal journal;
+ seastore::Cache cache;
+ seastore::LBAManagerRef lba_manager;
+ seastore::TransactionManager tm;
+ KVPool kvs;
+ TreeBuilder<TRACK> tree;
+};
+
+template <bool TRACK>
+seastar::future<> run(const bpo::variables_map& config) {
+ return seastar::async([&config]{
+ auto backend = config["backend"].as<std::string>();
+ bool is_dummy;
+ if (backend == "dummy") {
+ is_dummy = true;
+ } else if (backend == "seastore") {
+ is_dummy = false;
+ } else {
+ ceph_abort(false && "invalid backend");
+ }
+ auto str_sizes = config["str-sizes"].as<std::vector<size_t>>();
+ auto onode_sizes = config["onode-sizes"].as<std::vector<size_t>>();
+ auto range2 = config["range2"].as<std::vector<int>>();
+ ceph_assert(range2.size() == 2);
+ auto range1 = config["range1"].as<std::vector<unsigned>>();
+ ceph_assert(range1.size() == 2);
+ auto range0 = config["range0"].as<std::vector<unsigned>>();
+ ceph_assert(range0.size() == 2);
+
+ PerfTree<TRACK> perf{is_dummy, str_sizes, onode_sizes,
+ {range2[0], range2[1]}, {range1[0], range1[1]}, {range0[0], range0[1]}};
+ perf.run().get0();
+ });
+}
+
+
+int main(int argc, char** argv)
+{
+ seastar::app_template app;
+ app.add_options()
+ ("backend", bpo::value<std::string>()->default_value("dummy"),
+ "tree backend: dummy, seastore")
+ ("tracked", bpo::value<bool>()->default_value(false),
+ "track inserted cursors")
+ ("str-sizes", bpo::value<std::vector<size_t>>()->default_value(
+ {8, 11, 64, 256, 301, 320}),
+ "sizes of ns/oid strings")
+ ("onode-sizes", bpo::value<std::vector<size_t>>()->default_value(
+ {8, 16, 128, 512, 576, 640}),
+ "sizes of onode")
+ ("range2", bpo::value<std::vector<int>>()->default_value(
+ {0, 128}),
+ "range of shard-pool-crush [a, b)")
+ ("range1", bpo::value<std::vector<unsigned>>()->default_value(
+ {0, 10}),
+ "range of ns-oid strings [a, b)")
+ ("range0", bpo::value<std::vector<unsigned>>()->default_value(
+ {0, 4}),
+ "range of snap-gen [a, b)");
+ return app.run(argc, argv, [&app] {
+ auto&& config = app.configuration();
+ auto tracked = config["tracked"].as<bool>();
+ if (tracked) {
+ return run<true>(config);
+ } else {
+ return run<false>(config);
+ }
+ });
+}