From 13a873b0300937d93a4ba5bb50480526dd8bedcc Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Wed, 29 May 2024 14:06:57 -0700 Subject: [PATCH 1/9] add root node. bin/test_index_recovery --num_entries=18 --seed=0 --flip_list=index_parent_root --enable_crash crash root --- src/include/homestore/btree/btree.hpp | 3 +++ src/include/homestore/btree/btree.ipp | 8 +++++++ .../btree/detail/btree_mutate_impl.ipp | 24 +++++++++++++++++++ src/include/homestore/index/wb_cache_base.hpp | 1 + 4 files changed, 36 insertions(+) diff --git a/src/include/homestore/btree/btree.hpp b/src/include/homestore/btree/btree.hpp index f7a82b14a..693994f54 100644 --- a/src/include/homestore/btree/btree.hpp +++ b/src/include/homestore/btree/btree.hpp @@ -70,6 +70,7 @@ class Btree { protected: mutable iomgr::FiberManagerLib::shared_mutex m_btree_lock; BtreeLinkInfo m_root_node_info; + BtreeLinkInfo m_super_node_info; BtreeMetrics m_metrics; std::atomic< bool > m_destroyed{false}; @@ -123,6 +124,7 @@ class Btree { uint64_t root_link_version() const; void set_root_node_info(const BtreeLinkInfo& info); + void set_super_node_info(const BtreeLinkInfo& info); // static void set_io_flip(); // static void set_error_flip(); @@ -138,6 +140,7 @@ class Btree { protected: /////////////////////////// Methods the underlying store is expected to handle /////////////////////////// + virtual void retrieve_root_node() = 0; virtual BtreeNodePtr alloc_node(bool is_leaf) = 0; virtual BtreeNode* init_node(uint8_t* node_buf, bnodeid_t id, bool init_buf, bool is_leaf) const; virtual btree_status_t read_node_impl(bnodeid_t id, BtreeNodePtr& node) const = 0; diff --git a/src/include/homestore/btree/btree.ipp b/src/include/homestore/btree/btree.ipp index 73279d138..8be0e2e91 100644 --- a/src/include/homestore/btree/btree.ipp +++ b/src/include/homestore/btree/btree.ipp @@ -48,6 +48,10 @@ template < typename K, typename V > void Btree< K, V >::set_root_node_info(const BtreeLinkInfo& info) { m_root_node_info = info; } +template < typename K, typename V > +void Btree< K, V >::set_super_node_info(const BtreeLinkInfo& info) { + m_super_node_info = info; +} template < typename K, typename V > std::pair< btree_status_t, uint64_t > Btree< K, V >::destroy_btree(void* context) { @@ -341,6 +345,10 @@ template < typename K, typename V > bnodeid_t Btree< K, V >::root_node_id() const { return m_root_node_info.bnode_id(); } +template < typename K, typename V > +bnodeid_t Btree< K, V >::super_node_id() const { + return m_super_node_info.bnode_id(); +} template < typename K, typename V > uint64_t Btree< K, V >::root_link_version() const { diff --git a/src/include/homestore/btree/detail/btree_mutate_impl.ipp b/src/include/homestore/btree/detail/btree_mutate_impl.ipp index 209b35558..267abe45c 100644 --- a/src/include/homestore/btree/detail/btree_mutate_impl.ipp +++ b/src/include/homestore/btree/detail/btree_mutate_impl.ipp @@ -202,15 +202,24 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { K split_key; BtreeNodePtr child_node = nullptr; btree_status_t ret = btree_status_t::success; + btree_status_t ret2 = btree_status_t::success; BtreeNodePtr root; BtreeNodePtr new_root; + BtreeNodePtr super_node; m_btree_lock.lock(); ret = read_and_lock_node(m_root_node_info.bnode_id(), root, locktype_t::WRITE, locktype_t::WRITE, req.m_op_context); + ret2 = read_and_lock_node(m_super_node_info.bnode_id(), super_node, locktype_t::WRITE, locktype_t::WRITE, + req.m_op_context); if (ret != btree_status_t::success) { goto done; } + if (ret2 != btree_status_t::success) { + unlock_node(root, locktype_t::WRITE); + goto done; + } if (!is_split_needed(root, req)) { unlock_node(root, locktype_t::WRITE); + unlock_node(super_node, locktype_t::WRITE); goto done; } @@ -218,6 +227,7 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { if (new_root == nullptr) { ret = btree_status_t::space_not_avail; unlock_node(root, locktype_t::WRITE); + unlock_node(super_node, locktype_t::WRITE); goto done; } new_root->set_level(root->level() + 1); @@ -240,10 +250,13 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { root = std::move(child_node); on_root_changed(root, req.m_op_context); // Revert it back unlock_node(root, locktype_t::WRITE); + } else { + // LOGINFO(" split happens for new root {}", root->node_id()); if (req.route_tracing) { append_route_trace(req, child_node, btree_event_t::SPLIT); } m_root_node_info = BtreeLinkInfo{root->node_id(), root->link_version()}; unlock_node(child_node, locktype_t::WRITE); + unlock_node(super_node, locktype_t::WRITE); COUNTER_INCREMENT(m_metrics, btree_depth, 1); } @@ -301,6 +314,17 @@ btree_status_t Btree< K, V >::split_node(const BtreeNodePtr& parent_node, const template < typename K, typename V > template < typename ReqT > +// bool Btree< K, V >::is_split_needed(const BtreeNodePtr& node, ReqT& req) const { +// if (!node->is_leaf()) { // if internal node, size is atmost one additional entry, size of K/V +// return node->total_entries() > 2; +// } else if constexpr (std::is_same_v< ReqT, BtreeRangePutRequest< K > >) { +// return node->total_entries() > 2; +// } else if constexpr (std::is_same_v< ReqT, BtreeSinglePutRequest >) { +// return node->total_entries() > 2; +// } else { +// return false; +// } +// } bool Btree< K, V >::is_split_needed(const BtreeNodePtr& node, ReqT& req) const { if (!node->is_leaf()) { // if internal node, size is atmost one additional entry, size of K/V return !node->has_room_for_put(btree_put_type::UPSERT, K::get_max_size(), BtreeLinkInfo::get_fixed_size()); diff --git a/src/include/homestore/index/wb_cache_base.hpp b/src/include/homestore/index/wb_cache_base.hpp index d576c2b6b..71076666d 100644 --- a/src/include/homestore/index/wb_cache_base.hpp +++ b/src/include/homestore/index/wb_cache_base.hpp @@ -46,6 +46,7 @@ class IndexWBCacheBase { virtual void write_buf(const BtreeNodePtr& node, const IndexBufferPtr& buf, CPContext* context) = 0; virtual void read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t&& node_initializer) = 0; + virtual std::pair get_root(bnodeid_t super_node_id) = 0; virtual bool get_writable_buf(const BtreeNodePtr& node, CPContext* context) = 0; From 1c845aac7c4092491755e7f6bb70c13f15c2dfd4 Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Tue, 14 May 2024 12:26:50 -0700 Subject: [PATCH 2/9] Staged commit for Index crash recovery --- .../homestore/btree/detail/btree_node_mgr.ipp | 2 +- src/include/homestore/index/wb_cache_base.hpp | 2 +- src/lib/index/wb_cache.cpp | 63 +++++++++++++++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/include/homestore/btree/detail/btree_node_mgr.ipp b/src/include/homestore/btree/detail/btree_node_mgr.ipp index a5b0317de..c4ddfa22f 100644 --- a/src/include/homestore/btree/detail/btree_node_mgr.ipp +++ b/src/include/homestore/btree/detail/btree_node_mgr.ipp @@ -35,7 +35,7 @@ btree_status_t Btree< K, V >::create_root_node(void* op_context) { if (root == nullptr) { return btree_status_t::space_not_avail; } root->set_level(0u); - auto ret = write_node(root, op_context); + auto const ret = write_node(root, op_context); if (ret != btree_status_t::success) { free_node(root, locktype_t::NONE, op_context); return btree_status_t::space_not_avail; diff --git a/src/include/homestore/index/wb_cache_base.hpp b/src/include/homestore/index/wb_cache_base.hpp index 71076666d..f8d7690c4 100644 --- a/src/include/homestore/index/wb_cache_base.hpp +++ b/src/include/homestore/index/wb_cache_base.hpp @@ -46,7 +46,7 @@ class IndexWBCacheBase { virtual void write_buf(const BtreeNodePtr& node, const IndexBufferPtr& buf, CPContext* context) = 0; virtual void read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t&& node_initializer) = 0; - virtual std::pair get_root(bnodeid_t super_node_id) = 0; + virtual std::pair< bnodeid_t, uint64_t > get_root(bnodeid_t super_node_id) = 0; virtual bool get_writable_buf(const BtreeNodePtr& node, CPContext* context) = 0; diff --git a/src/lib/index/wb_cache.cpp b/src/lib/index/wb_cache.cpp index 013eb0da5..b780f3de5 100644 --- a/src/lib/index/wb_cache.cpp +++ b/src/lib/index/wb_cache.cpp @@ -87,6 +87,69 @@ void IndexWBCache::start_flush_threads() { } } +void IndexWBCache::recover_new_nodes(sisl::byte_view sb) { + // If sb is empty, its possible a first time boot. + if ((sb.bytes() == nullptr) || (sb.size() == 0)) { return; } + + auto cpg = hs()->cp_mgr().cp_guard(); + auto cp_ctx = r_cast< IndexCPContext* >(cpg.context(cp_consumer_t::INDEX_SVC)); + cp_id_t cur_cp_id = cpg->id(); + + auto const* new_blks_sb = r_cast< IndexCPContext::new_blks_sb_t const* >(sb.bytes()); + if (new_blks_sb->cp_id != cur_cp_id) { + // On clean shutdown, cp_id would be lesser than the current cp_id, in that case ignore this sb + HS_DBG_ASSERT_LT(new_blks_sb->cp_id, cur_cp_id, "Persisted cp in wbcache_sb is more than current cp"); + return; + } + + LOGINFOMOD(wbcache, "Prior to restart allocated {} new blks, validating if they need to be persisted", + new_blks_sb->num_blks); + + std::unordered_map< BlkId, sisl::io_blob_safe > cached_inplace_nodes; + for (auto i = 0u; i < new_blks_sb->num_blks; ++i) { + auto const& [inplace_p, new_p] = new_blks_sb->blks[i]; + auto const inplace_blkid = BlkId{inplace_p.first, (blk_count_t)1, inplace_p.second}; + auto const new_blkid = BlkId{new_p.first, (blk_count_t)1, new_p.second}; + + // Read the new btree node + sisl::io_blob_safe node_buf(m_node_size, 512); + m_vdev->sync_read(r_cast< char* >(node_buf.bytes()), m_node_size, new_blkid); + + // Invalid node indicates it was never written during cp_flush prior to unclean shutdown, ignore the blkid + if (!IndexTableBase::is_valid_btree_node(node_buf)) { continue; } + + // Read the inplace node and find out if they have same cp_id as new_blks. If so, the inplace node is also + // written and in that case the new_node should be retained. It means the first part of dependency chain was + // already persisted prior to unclean shutdown. If its not written, we can discard the new_node. + // Note: There can be multiple new_blks point to the same in-place node, so we keep them cached. + auto it = cached_inplace_nodes.find(inplace_blkid); + if (it == cached_inplace_nodes.end()) { + sisl::io_blob_safe inplace_buf(m_node_size, 512); + m_vdev->sync_read(r_cast< char* >(inplace_buf.bytes()), m_node_size, inplace_blkid); + + if (!IndexTableBase::is_valid_btree_node(inplace_buf)) { + HS_LOG_ASSERT(false, "Inplace node is invalid btree node at blkid={}, should not happen", + inplace_blkid); + continue; + } + bool happened; + std::tie(it, happened) = + cached_inplace_nodes.emplace(std::make_pair(inplace_blkid, std::move(inplace_buf))); + } + + if (IndexTableBase::modified_cp_id(it->second) == cur_cp_id) { + LOGDEBUGMOD(wbcache, "Inplace node={} has been written prior to unclean shutdown, retaining new_node={} ", + inplace_blkid.to_string(), new_blkid.to_string()); + // Put them in current cp, to support unclean shutdown during recovery + cp_ctx->track_new_blk(inplace_blkid, new_blkid); + m_vdev->commit_blk(new_blkid); + } else { + LOGDEBUGMOD(wbcache, "Inplace node={} was not written prior to unclean shutdowm, so discarding new_node={}", + inplace_blkid.to_string(), new_blkid.to_string()); + } + } +} + BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { auto cpg = cp_mgr().cp_guard(); auto cp_ctx = r_cast< IndexCPContext* >(cpg.context(cp_consumer_t::INDEX_SVC)); From c9f2aee9c248b2c60e1907dde9e267e52289b04e Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Wed, 29 May 2024 14:05:30 -0700 Subject: [PATCH 3/9] Revamp of Index Writeback Cache crash recovery --- src/include/homestore/btree/btree.hpp | 3 - src/include/homestore/btree/btree.ipp | 8 -- .../btree/detail/btree_mutate_impl.ipp | 24 ----- .../homestore/btree/detail/btree_node_mgr.ipp | 2 +- src/lib/index/wb_cache.cpp | 91 ++++++------------- 5 files changed, 27 insertions(+), 101 deletions(-) diff --git a/src/include/homestore/btree/btree.hpp b/src/include/homestore/btree/btree.hpp index 693994f54..f7a82b14a 100644 --- a/src/include/homestore/btree/btree.hpp +++ b/src/include/homestore/btree/btree.hpp @@ -70,7 +70,6 @@ class Btree { protected: mutable iomgr::FiberManagerLib::shared_mutex m_btree_lock; BtreeLinkInfo m_root_node_info; - BtreeLinkInfo m_super_node_info; BtreeMetrics m_metrics; std::atomic< bool > m_destroyed{false}; @@ -124,7 +123,6 @@ class Btree { uint64_t root_link_version() const; void set_root_node_info(const BtreeLinkInfo& info); - void set_super_node_info(const BtreeLinkInfo& info); // static void set_io_flip(); // static void set_error_flip(); @@ -140,7 +138,6 @@ class Btree { protected: /////////////////////////// Methods the underlying store is expected to handle /////////////////////////// - virtual void retrieve_root_node() = 0; virtual BtreeNodePtr alloc_node(bool is_leaf) = 0; virtual BtreeNode* init_node(uint8_t* node_buf, bnodeid_t id, bool init_buf, bool is_leaf) const; virtual btree_status_t read_node_impl(bnodeid_t id, BtreeNodePtr& node) const = 0; diff --git a/src/include/homestore/btree/btree.ipp b/src/include/homestore/btree/btree.ipp index 8be0e2e91..73279d138 100644 --- a/src/include/homestore/btree/btree.ipp +++ b/src/include/homestore/btree/btree.ipp @@ -48,10 +48,6 @@ template < typename K, typename V > void Btree< K, V >::set_root_node_info(const BtreeLinkInfo& info) { m_root_node_info = info; } -template < typename K, typename V > -void Btree< K, V >::set_super_node_info(const BtreeLinkInfo& info) { - m_super_node_info = info; -} template < typename K, typename V > std::pair< btree_status_t, uint64_t > Btree< K, V >::destroy_btree(void* context) { @@ -345,10 +341,6 @@ template < typename K, typename V > bnodeid_t Btree< K, V >::root_node_id() const { return m_root_node_info.bnode_id(); } -template < typename K, typename V > -bnodeid_t Btree< K, V >::super_node_id() const { - return m_super_node_info.bnode_id(); -} template < typename K, typename V > uint64_t Btree< K, V >::root_link_version() const { diff --git a/src/include/homestore/btree/detail/btree_mutate_impl.ipp b/src/include/homestore/btree/detail/btree_mutate_impl.ipp index 267abe45c..209b35558 100644 --- a/src/include/homestore/btree/detail/btree_mutate_impl.ipp +++ b/src/include/homestore/btree/detail/btree_mutate_impl.ipp @@ -202,24 +202,15 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { K split_key; BtreeNodePtr child_node = nullptr; btree_status_t ret = btree_status_t::success; - btree_status_t ret2 = btree_status_t::success; BtreeNodePtr root; BtreeNodePtr new_root; - BtreeNodePtr super_node; m_btree_lock.lock(); ret = read_and_lock_node(m_root_node_info.bnode_id(), root, locktype_t::WRITE, locktype_t::WRITE, req.m_op_context); - ret2 = read_and_lock_node(m_super_node_info.bnode_id(), super_node, locktype_t::WRITE, locktype_t::WRITE, - req.m_op_context); if (ret != btree_status_t::success) { goto done; } - if (ret2 != btree_status_t::success) { - unlock_node(root, locktype_t::WRITE); - goto done; - } if (!is_split_needed(root, req)) { unlock_node(root, locktype_t::WRITE); - unlock_node(super_node, locktype_t::WRITE); goto done; } @@ -227,7 +218,6 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { if (new_root == nullptr) { ret = btree_status_t::space_not_avail; unlock_node(root, locktype_t::WRITE); - unlock_node(super_node, locktype_t::WRITE); goto done; } new_root->set_level(root->level() + 1); @@ -250,13 +240,10 @@ btree_status_t Btree< K, V >::check_split_root(ReqT& req) { root = std::move(child_node); on_root_changed(root, req.m_op_context); // Revert it back unlock_node(root, locktype_t::WRITE); - } else { - // LOGINFO(" split happens for new root {}", root->node_id()); if (req.route_tracing) { append_route_trace(req, child_node, btree_event_t::SPLIT); } m_root_node_info = BtreeLinkInfo{root->node_id(), root->link_version()}; unlock_node(child_node, locktype_t::WRITE); - unlock_node(super_node, locktype_t::WRITE); COUNTER_INCREMENT(m_metrics, btree_depth, 1); } @@ -314,17 +301,6 @@ btree_status_t Btree< K, V >::split_node(const BtreeNodePtr& parent_node, const template < typename K, typename V > template < typename ReqT > -// bool Btree< K, V >::is_split_needed(const BtreeNodePtr& node, ReqT& req) const { -// if (!node->is_leaf()) { // if internal node, size is atmost one additional entry, size of K/V -// return node->total_entries() > 2; -// } else if constexpr (std::is_same_v< ReqT, BtreeRangePutRequest< K > >) { -// return node->total_entries() > 2; -// } else if constexpr (std::is_same_v< ReqT, BtreeSinglePutRequest >) { -// return node->total_entries() > 2; -// } else { -// return false; -// } -// } bool Btree< K, V >::is_split_needed(const BtreeNodePtr& node, ReqT& req) const { if (!node->is_leaf()) { // if internal node, size is atmost one additional entry, size of K/V return !node->has_room_for_put(btree_put_type::UPSERT, K::get_max_size(), BtreeLinkInfo::get_fixed_size()); diff --git a/src/include/homestore/btree/detail/btree_node_mgr.ipp b/src/include/homestore/btree/detail/btree_node_mgr.ipp index c4ddfa22f..a5b0317de 100644 --- a/src/include/homestore/btree/detail/btree_node_mgr.ipp +++ b/src/include/homestore/btree/detail/btree_node_mgr.ipp @@ -35,7 +35,7 @@ btree_status_t Btree< K, V >::create_root_node(void* op_context) { if (root == nullptr) { return btree_status_t::space_not_avail; } root->set_level(0u); - auto const ret = write_node(root, op_context); + auto ret = write_node(root, op_context); if (ret != btree_status_t::success) { free_node(root, locktype_t::NONE, op_context); return btree_status_t::space_not_avail; diff --git a/src/lib/index/wb_cache.cpp b/src/lib/index/wb_cache.cpp index b780f3de5..5012a7d02 100644 --- a/src/lib/index/wb_cache.cpp +++ b/src/lib/index/wb_cache.cpp @@ -34,6 +34,12 @@ SISL_LOGGING_DECL(wbcache) namespace homestore { +void usage_log(const IndexBufferPtr& buf, const std::string& msg) { + /*LOGINFO("{}: Buf={} state={} create/dirty_cp={}/{} down_wait#={} use_count={}", msg, + voidptr_cast(const_cast< IndexBuffer* >(buf.get())), int_cast(buf->state()), buf->m_created_cp_id, + buf->m_dirtied_cp_id, buf->m_wait_for_down_buffers.get(), buf.use_count());*/ +} + IndexWBCacheBase& wb_cache() { return index_service().wb_cache(); } IndexWBCache::IndexWBCache(const std::shared_ptr< VirtualDev >& vdev, std::pair< meta_blk*, sisl::byte_view > sb, @@ -87,69 +93,6 @@ void IndexWBCache::start_flush_threads() { } } -void IndexWBCache::recover_new_nodes(sisl::byte_view sb) { - // If sb is empty, its possible a first time boot. - if ((sb.bytes() == nullptr) || (sb.size() == 0)) { return; } - - auto cpg = hs()->cp_mgr().cp_guard(); - auto cp_ctx = r_cast< IndexCPContext* >(cpg.context(cp_consumer_t::INDEX_SVC)); - cp_id_t cur_cp_id = cpg->id(); - - auto const* new_blks_sb = r_cast< IndexCPContext::new_blks_sb_t const* >(sb.bytes()); - if (new_blks_sb->cp_id != cur_cp_id) { - // On clean shutdown, cp_id would be lesser than the current cp_id, in that case ignore this sb - HS_DBG_ASSERT_LT(new_blks_sb->cp_id, cur_cp_id, "Persisted cp in wbcache_sb is more than current cp"); - return; - } - - LOGINFOMOD(wbcache, "Prior to restart allocated {} new blks, validating if they need to be persisted", - new_blks_sb->num_blks); - - std::unordered_map< BlkId, sisl::io_blob_safe > cached_inplace_nodes; - for (auto i = 0u; i < new_blks_sb->num_blks; ++i) { - auto const& [inplace_p, new_p] = new_blks_sb->blks[i]; - auto const inplace_blkid = BlkId{inplace_p.first, (blk_count_t)1, inplace_p.second}; - auto const new_blkid = BlkId{new_p.first, (blk_count_t)1, new_p.second}; - - // Read the new btree node - sisl::io_blob_safe node_buf(m_node_size, 512); - m_vdev->sync_read(r_cast< char* >(node_buf.bytes()), m_node_size, new_blkid); - - // Invalid node indicates it was never written during cp_flush prior to unclean shutdown, ignore the blkid - if (!IndexTableBase::is_valid_btree_node(node_buf)) { continue; } - - // Read the inplace node and find out if they have same cp_id as new_blks. If so, the inplace node is also - // written and in that case the new_node should be retained. It means the first part of dependency chain was - // already persisted prior to unclean shutdown. If its not written, we can discard the new_node. - // Note: There can be multiple new_blks point to the same in-place node, so we keep them cached. - auto it = cached_inplace_nodes.find(inplace_blkid); - if (it == cached_inplace_nodes.end()) { - sisl::io_blob_safe inplace_buf(m_node_size, 512); - m_vdev->sync_read(r_cast< char* >(inplace_buf.bytes()), m_node_size, inplace_blkid); - - if (!IndexTableBase::is_valid_btree_node(inplace_buf)) { - HS_LOG_ASSERT(false, "Inplace node is invalid btree node at blkid={}, should not happen", - inplace_blkid); - continue; - } - bool happened; - std::tie(it, happened) = - cached_inplace_nodes.emplace(std::make_pair(inplace_blkid, std::move(inplace_buf))); - } - - if (IndexTableBase::modified_cp_id(it->second) == cur_cp_id) { - LOGDEBUGMOD(wbcache, "Inplace node={} has been written prior to unclean shutdown, retaining new_node={} ", - inplace_blkid.to_string(), new_blkid.to_string()); - // Put them in current cp, to support unclean shutdown during recovery - cp_ctx->track_new_blk(inplace_blkid, new_blkid); - m_vdev->commit_blk(new_blkid); - } else { - LOGDEBUGMOD(wbcache, "Inplace node={} was not written prior to unclean shutdowm, so discarding new_node={}", - inplace_blkid.to_string(), new_blkid.to_string()); - } - } -} - BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { auto cpg = cp_mgr().cp_guard(); auto cp_ctx = r_cast< IndexCPContext* >(cpg.context(cp_consumer_t::INDEX_SVC)); @@ -169,6 +112,8 @@ BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { bool done = m_cache.insert(node); HS_REL_ASSERT_EQ(done, true, "Unable to add alloc'd node to cache, low memory or duplicate inserts?"); + usage_log(idx_buf, "After init"); + // The entire index is updated in the commit path, so we alloc the blk and commit them right away auto alloc_status = m_vdev->commit_blk(blkid); // if any error happens when committing the blk to index service, we should assert and crash @@ -178,8 +123,12 @@ BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { void IndexWBCache::write_buf(const BtreeNodePtr& node, const IndexBufferPtr& buf, CPContext* cp_ctx) { // TODO upsert always returns false even if it succeeds. - if (node != nullptr) { m_cache.upsert(node); } + if (node != nullptr) { + m_cache.upsert(node); + usage_log(buf, "After inserting to cache"); + } r_cast< IndexCPContext* >(cp_ctx)->add_to_dirty_list(buf); + usage_log(buf, "After adding to dirty list"); resource_mgr().inc_dirty_buf_size(m_node_size); } @@ -188,7 +137,10 @@ void IndexWBCache::read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t retry: // Check if the blkid is already in cache, if not load and put it into the cache - if (m_cache.get(blkid, node)) { return; } + if (m_cache.get(blkid, node)) { + usage_log(static_cast< IndexBtreeNode* >(node.get())->m_idx_buf, "After read cache hit"); + return; + } // Read the buffer from virtual device auto idx_buf = std::make_shared< IndexBuffer >(blkid, m_node_size, m_vdev->align_size()); @@ -203,6 +155,7 @@ void IndexWBCache::read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t // There is a race between 2 concurrent reads from vdev and other party won the race. Re-read from cache goto retry; } + usage_log(idx_buf, "After read cache miss and insert to cache"); } bool IndexWBCache::get_writable_buf(const BtreeNodePtr& node, CPContext* context) { @@ -225,6 +178,9 @@ bool IndexWBCache::get_writable_buf(const BtreeNodePtr& node, CPContext* context new_buf->m_created_cp_id = idx_buf->m_created_cp_id; std::memcpy(new_buf->raw_buffer(), idx_buf->raw_buffer(), m_node_size); + usage_log(idx_buf, "Forced copy - Current Dirty buf"); + usage_log(new_buf, "Forced copy - New clean buf"); + node->update_phys_buf(new_buf->raw_buffer()); LOGTRACEMOD(wbcache, "cp={} cur_buf={} for node={} is dirtied by cp={} copying new_buf={}", icp_ctx->id(), static_cast< void* >(idx_buf.get()), node->node_id(), idx_buf->m_dirtied_cp_id, @@ -405,12 +361,16 @@ void IndexWBCache::link_buf(IndexBufferPtr const& up_buf, IndexBufferPtr const& #ifndef NDEBUG real_up_buf->m_down_buffers.emplace_back(down_buf); #endif + + usage_log(real_up_buf, "After link buf - Up buf"); + usage_log(down_buf, "After link buf - Down buf"); } void IndexWBCache::free_buf(const IndexBufferPtr& buf, CPContext* cp_ctx) { BtreeNodePtr node; bool done = m_cache.remove(buf->m_blkid, node); HS_REL_ASSERT_EQ(done, true, "Race on cache removal of btree blkid?"); + usage_log(buf, "After free buf - remove from cache"); resource_mgr().inc_free_blk(m_node_size); m_vdev->free_blk(buf->m_blkid, s_cast< VDevCPContext* >(cp_ctx)); @@ -605,6 +565,7 @@ std::pair< IndexBufferPtr, bool > IndexWBCache::on_buf_flush_done_internal(Index #endif buf->set_state(index_buf_state_t::CLEAN); + usage_log(buf, "After finishing flush"); if (cp_ctx->m_dirty_buf_count.decrement_testz()) { return std::make_pair(nullptr, false); } else { From ac81454bb420fea7f6ad3bc9dda5e50375449126 Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Wed, 29 May 2024 22:12:09 -0700 Subject: [PATCH 4/9] Added testhelper restart_homestore to prevent repeating parameters and incorrect usage of format/init devices --- src/tests/test_meta_blk_mgr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests/test_meta_blk_mgr.cpp b/src/tests/test_meta_blk_mgr.cpp index 89372995a..428e8ae35 100644 --- a/src/tests/test_meta_blk_mgr.cpp +++ b/src/tests/test_meta_blk_mgr.cpp @@ -129,7 +129,7 @@ class VMetaBlkMgrTest : public ::testing::Test { test_common::HSTestHelper::start_homestore("test_meta_blk_mgr", {{HS_SERVICE::META, {.size_pct = 85.0}}}); } - void TearDown() override{}; + void TearDown() override {}; public: [[nodiscard]] uint64_t get_elapsed_time(const Clock::time_point& start) { From d086b8f537fad0bab4b00e3334ed8c0b0d6a9584 Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Mon, 10 Jun 2024 14:04:37 -0700 Subject: [PATCH 5/9] Resolved merge conflicts --- conanfile.py | 2 +- src/tests/test_common/homestore_test_common.hpp | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/conanfile.py b/conanfile.py index 6787c8b9c..f9a137fec 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "6.4.20" + version = "6.4.21" homepage = "https://github.com/eBay/Homestore" description = "HomeStore Storage Engine" topics = ("ebay", "nublox") diff --git a/src/tests/test_common/homestore_test_common.hpp b/src/tests/test_common/homestore_test_common.hpp index acf447e76..f7f759506 100644 --- a/src/tests/test_common/homestore_test_common.hpp +++ b/src/tests/test_common/homestore_test_common.hpp @@ -239,9 +239,7 @@ class HSTestHelper { for (const auto& name : devs) { // TODO:: Add support for fast and data devices in device_list token.devs_.emplace_back(name, - token.devs_.empty() - ? homestore::HSDevType::Fast - : homestore::HSDevType::Data); // First device is fast device + homestore::HSDevType::Data); // First device is fast device } } else { LOGINFO("Taking input dev_list: {}", From 6d524178ca6305c2d41c7448f7550b4b586c9808 Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Mon, 10 Jun 2024 17:32:46 -0700 Subject: [PATCH 6/9] Clean device restart on no journal update fails, fixed that --- src/lib/index/wb_cache.cpp | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/lib/index/wb_cache.cpp b/src/lib/index/wb_cache.cpp index 5012a7d02..013eb0da5 100644 --- a/src/lib/index/wb_cache.cpp +++ b/src/lib/index/wb_cache.cpp @@ -34,12 +34,6 @@ SISL_LOGGING_DECL(wbcache) namespace homestore { -void usage_log(const IndexBufferPtr& buf, const std::string& msg) { - /*LOGINFO("{}: Buf={} state={} create/dirty_cp={}/{} down_wait#={} use_count={}", msg, - voidptr_cast(const_cast< IndexBuffer* >(buf.get())), int_cast(buf->state()), buf->m_created_cp_id, - buf->m_dirtied_cp_id, buf->m_wait_for_down_buffers.get(), buf.use_count());*/ -} - IndexWBCacheBase& wb_cache() { return index_service().wb_cache(); } IndexWBCache::IndexWBCache(const std::shared_ptr< VirtualDev >& vdev, std::pair< meta_blk*, sisl::byte_view > sb, @@ -112,8 +106,6 @@ BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { bool done = m_cache.insert(node); HS_REL_ASSERT_EQ(done, true, "Unable to add alloc'd node to cache, low memory or duplicate inserts?"); - usage_log(idx_buf, "After init"); - // The entire index is updated in the commit path, so we alloc the blk and commit them right away auto alloc_status = m_vdev->commit_blk(blkid); // if any error happens when committing the blk to index service, we should assert and crash @@ -123,12 +115,8 @@ BtreeNodePtr IndexWBCache::alloc_buf(node_initializer_t&& node_initializer) { void IndexWBCache::write_buf(const BtreeNodePtr& node, const IndexBufferPtr& buf, CPContext* cp_ctx) { // TODO upsert always returns false even if it succeeds. - if (node != nullptr) { - m_cache.upsert(node); - usage_log(buf, "After inserting to cache"); - } + if (node != nullptr) { m_cache.upsert(node); } r_cast< IndexCPContext* >(cp_ctx)->add_to_dirty_list(buf); - usage_log(buf, "After adding to dirty list"); resource_mgr().inc_dirty_buf_size(m_node_size); } @@ -137,10 +125,7 @@ void IndexWBCache::read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t retry: // Check if the blkid is already in cache, if not load and put it into the cache - if (m_cache.get(blkid, node)) { - usage_log(static_cast< IndexBtreeNode* >(node.get())->m_idx_buf, "After read cache hit"); - return; - } + if (m_cache.get(blkid, node)) { return; } // Read the buffer from virtual device auto idx_buf = std::make_shared< IndexBuffer >(blkid, m_node_size, m_vdev->align_size()); @@ -155,7 +140,6 @@ void IndexWBCache::read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t // There is a race between 2 concurrent reads from vdev and other party won the race. Re-read from cache goto retry; } - usage_log(idx_buf, "After read cache miss and insert to cache"); } bool IndexWBCache::get_writable_buf(const BtreeNodePtr& node, CPContext* context) { @@ -178,9 +162,6 @@ bool IndexWBCache::get_writable_buf(const BtreeNodePtr& node, CPContext* context new_buf->m_created_cp_id = idx_buf->m_created_cp_id; std::memcpy(new_buf->raw_buffer(), idx_buf->raw_buffer(), m_node_size); - usage_log(idx_buf, "Forced copy - Current Dirty buf"); - usage_log(new_buf, "Forced copy - New clean buf"); - node->update_phys_buf(new_buf->raw_buffer()); LOGTRACEMOD(wbcache, "cp={} cur_buf={} for node={} is dirtied by cp={} copying new_buf={}", icp_ctx->id(), static_cast< void* >(idx_buf.get()), node->node_id(), idx_buf->m_dirtied_cp_id, @@ -361,16 +342,12 @@ void IndexWBCache::link_buf(IndexBufferPtr const& up_buf, IndexBufferPtr const& #ifndef NDEBUG real_up_buf->m_down_buffers.emplace_back(down_buf); #endif - - usage_log(real_up_buf, "After link buf - Up buf"); - usage_log(down_buf, "After link buf - Down buf"); } void IndexWBCache::free_buf(const IndexBufferPtr& buf, CPContext* cp_ctx) { BtreeNodePtr node; bool done = m_cache.remove(buf->m_blkid, node); HS_REL_ASSERT_EQ(done, true, "Race on cache removal of btree blkid?"); - usage_log(buf, "After free buf - remove from cache"); resource_mgr().inc_free_blk(m_node_size); m_vdev->free_blk(buf->m_blkid, s_cast< VDevCPContext* >(cp_ctx)); @@ -565,7 +542,6 @@ std::pair< IndexBufferPtr, bool > IndexWBCache::on_buf_flush_done_internal(Index #endif buf->set_state(index_buf_state_t::CLEAN); - usage_log(buf, "After finishing flush"); if (cp_ctx->m_dirty_buf_count.decrement_testz()) { return std::make_pair(nullptr, false); } else { From 8a31db05dcaf803ac0cb4ab2bb25a1b3d4befc30 Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Thu, 13 Jun 2024 19:07:01 -0700 Subject: [PATCH 7/9] Updated all homestore test helpers to be non-static and store context to avoid boiler plate in tests code --- src/tests/btree_helpers/btree_test_helper.hpp | 131 ++++--- src/tests/btree_helpers/shadow_map.hpp | 50 ++- src/tests/index_btree_benchmark.cpp | 35 +- src/tests/log_store_benchmark.cpp | 8 +- src/tests/test_append_blkalloc.cpp | 14 +- .../test_common/homestore_test_common.hpp | 369 ++++++++++-------- src/tests/test_common/hs_repl_test_common.hpp | 13 +- src/tests/test_cp_mgr.cpp | 9 +- src/tests/test_data_service.cpp | 14 +- src/tests/test_home_raft_logstore.cpp | 15 +- src/tests/test_index_btree.cpp | 25 +- src/tests/test_journal_vdev.cpp | 37 +- src/tests/test_log_dev.cpp | 32 +- src/tests/test_log_store.cpp | 5 +- src/tests/test_meta_blk_mgr.cpp | 28 +- src/tests/test_raft_repl_dev.cpp | 32 +- src/tests/test_solo_repl_dev.cpp | 9 +- 17 files changed, 441 insertions(+), 385 deletions(-) diff --git a/src/tests/btree_helpers/btree_test_helper.hpp b/src/tests/btree_helpers/btree_test_helper.hpp index e0be5230e..950884903 100644 --- a/src/tests/btree_helpers/btree_test_helper.hpp +++ b/src/tests/btree_helpers/btree_test_helper.hpp @@ -100,60 +100,63 @@ struct BtreeTestHelper { } #endif void preload(uint32_t preload_size) { - if (preload_size) { - const auto n_fibers = std::min(preload_size, (uint32_t)m_fibers.size()); - const auto chunk_size = preload_size / n_fibers; - const auto last_chunk_size = preload_size % chunk_size ?: chunk_size; - auto test_count = n_fibers; - - for (std::size_t i = 0; i < n_fibers; ++i) { - const auto start_range = i * chunk_size; - const auto end_range = start_range + ((i == n_fibers - 1) ? last_chunk_size : chunk_size); - auto fiber_id = i; - iomanager.run_on_forget( - m_fibers[i], [this, start_range, end_range, &test_count, fiber_id, preload_size]() { - double progress_interval = - (double)(end_range - start_range) / 20; // 5% of the total number of iterations - double progress_thresh = progress_interval; // threshold for progress interval - double elapsed_time, progress_percent, last_progress_time = 0; - auto m_start_time = Clock::now(); - - for (uint32_t i = start_range; i < end_range; i++) { - put(i, btree_put_type::INSERT); - if (fiber_id == 0) { - elapsed_time = get_elapsed_time_sec(m_start_time); - progress_percent = (double)(i - start_range) / (end_range - start_range) * 100; - - // check progress every 5% of the total number of iterations or every 30 seconds - bool print_time = false; - if (i >= progress_thresh) { - progress_thresh += progress_interval; - print_time = true; - } - if (elapsed_time - last_progress_time > 30) { - last_progress_time = elapsed_time; - print_time = true; - } - if (print_time) { - LOGINFO("Progress: iterations completed ({:.2f}%)- Elapsed time: {:.0f} seconds- " - "populated entries: {} ({:.2f}%)", - progress_percent, elapsed_time, m_shadow_map.size(), - m_shadow_map.size() * 100.0 / preload_size); - } - } + if (preload_size == 0) { + LOGINFO("Preload Skipped"); + return; + } + + const auto n_fibers = std::min(preload_size, (uint32_t)m_fibers.size()); + const auto chunk_size = preload_size / n_fibers; + const auto last_chunk_size = preload_size % chunk_size ?: chunk_size; + auto test_count = n_fibers; + + for (std::size_t i = 0; i < n_fibers; ++i) { + const auto start_range = i * chunk_size; + const auto end_range = start_range + ((i == n_fibers - 1) ? last_chunk_size : chunk_size) - 1; + auto fiber_id = i; + iomanager.run_on_forget(m_fibers[i], [this, start_range, end_range, &test_count, fiber_id, preload_size]() { + double progress_interval = + (double)(end_range - start_range) / 20; // 5% of the total number of iterations + double progress_thresh = progress_interval; // threshold for progress interval + double elapsed_time, progress_percent, last_progress_time = 0; + auto m_start_time = Clock::now(); + + for (uint32_t i = start_range; i < end_range; i++) { + put(i, btree_put_type::INSERT); + if (fiber_id == 0) { + elapsed_time = get_elapsed_time_sec(m_start_time); + progress_percent = (double)(i - start_range) / (end_range - start_range) * 100; + + // check progress every 5% of the total number of iterations or every 30 seconds + bool print_time = false; + if (i >= progress_thresh) { + progress_thresh += progress_interval; + print_time = true; + } + if (elapsed_time - last_progress_time > 30) { + last_progress_time = elapsed_time; + print_time = true; } - { - std::unique_lock lg(m_test_done_mtx); - if (--test_count == 0) { m_test_done_cv.notify_one(); } + if (print_time) { + LOGINFO("Progress: iterations completed ({:.2f}%)- Elapsed time: {:.0f} seconds- " + "populated entries: {} ({:.2f}%)", + progress_percent, elapsed_time, m_shadow_map.size(), + m_shadow_map.size() * 100.0 / preload_size); } - }); - } + } + } + { + std::unique_lock lg(m_test_done_mtx); + if (--test_count == 0) { m_test_done_cv.notify_one(); } + } + }); + } - { - std::unique_lock< std::mutex > lk(m_test_done_mtx); - m_test_done_cv.wait(lk, [&]() { return test_count == 0; }); - } + { + std::unique_lock< std::mutex > lk(m_test_done_mtx); + m_test_done_cv.wait(lk, [&]() { return test_count == 0; }); } + LOGINFO("Preload Done"); } @@ -171,6 +174,18 @@ struct BtreeTestHelper { do_put(start_k, btree_put_type::INSERT, V::generate_rand()); } + void force_upsert(uint64_t k) { + auto existing_v = std::make_unique< V >(); + K key = K{k}; + V value = V::generate_rand(); + auto sreq = BtreeSinglePutRequest{&key, &value, btree_put_type::UPSERT, existing_v.get()}; + sreq.enable_route_tracing(); + + auto const ret = m_bt->put(sreq); + ASSERT_EQ(ret, btree_status_t::success) << "Upsert key=" << k << " failed with error=" << enum_name(ret); + m_shadow_map.force_put(k, value); + } + void range_put(uint32_t start_k, uint32_t end_k, V const& value, bool update) { K start_key = K{start_k}; K end_key = K{end_k}; @@ -346,7 +361,16 @@ struct BtreeTestHelper { } void multi_op_execute(const std::vector< std::pair< std::string, int > >& op_list, bool skip_preload = false) { - if (!skip_preload) { preload(SISL_OPTIONS["preload_size"].as< uint32_t >()); } + if (!skip_preload) { + auto preload_size = SISL_OPTIONS["preload_size"].as< uint32_t >(); + auto const num_entries = SISL_OPTIONS["num_entries"].as< uint32_t >(); + if (preload_size > num_entries / 2) { + LOGWARN("Preload size={} is more than half of num_entries, setting preload_size to {}", preload_size, + num_entries / 2); + preload_size = num_entries / 2; + } + preload(preload_size); + } run_in_parallel(op_list); } @@ -388,13 +412,12 @@ struct BtreeTestHelper { K key = K{k}; auto sreq = BtreeSinglePutRequest{&key, &value, put_type, existing_v.get()}; sreq.enable_route_tracing(); - // bool done = (m_bt->put(sreq) == btree_status_t::success); bool done = expect_success ? (m_bt->put(sreq) == btree_status_t::success) : m_bt->put(sreq) == btree_status_t::put_failed; if (put_type == btree_put_type::INSERT) { ASSERT_EQ(done, !m_shadow_map.exists(key)); - } else { + } else if (put_type == btree_put_type::UPDATE) { ASSERT_EQ(done, m_shadow_map.exists(key)); } if (expect_success) { m_shadow_map.put_and_check(key, value, *existing_v, done); } @@ -488,7 +511,7 @@ struct BtreeTestHelper { LOGINFO("ALL parallel jobs joined"); } - std::vector< std::pair< std::string, int > > build_op_list(std::vector< std::string >& input_ops) { + std::vector< std::pair< std::string, int > > build_op_list(std::vector< std::string > const& input_ops) { std::vector< std::pair< std::string, int > > ops; int total = std::accumulate(input_ops.begin(), input_ops.end(), 0, [](int sum, const auto& str) { std::vector< std::string > tokens; diff --git a/src/tests/btree_helpers/shadow_map.hpp b/src/tests/btree_helpers/shadow_map.hpp index e2c86bfda..a91dd39b8 100644 --- a/src/tests/btree_helpers/shadow_map.hpp +++ b/src/tests/btree_helpers/shadow_map.hpp @@ -8,11 +8,12 @@ class ShadowMap { private: std::map< K, V > m_map; RangeScheduler m_range_scheduler; + uint32_t m_max_keys; using mutex = iomgr::FiberManagerLib::shared_mutex; mutex m_mutex; public: - ShadowMap(uint32_t num_keys) : m_range_scheduler(num_keys) {} + ShadowMap(uint32_t num_keys) : m_range_scheduler(num_keys), m_max_keys{num_keys} {} void put_and_check(const K& key, const V& val, const V& old_val, bool expected_success) { std::lock_guard lock{m_mutex}; @@ -24,6 +25,12 @@ class ShadowMap { m_range_scheduler.put_key(key.key()); } + void force_put(const K& key, const V& val) { + std::lock_guard lock{m_mutex}; + m_map.insert_or_assign(key, val); + m_range_scheduler.put_key(key.key()); + } + void range_upsert(uint64_t start_k, uint32_t count, const V& val) { std::lock_guard lock{m_mutex}; for (uint32_t i{0}; i < count; ++i) { @@ -58,6 +65,8 @@ class ShadowMap { return std::pair(start_it->first, it->first); } + uint32_t max_keys() const { return m_max_keys; } + bool exists(const K& key) const { std::lock_guard lock{m_mutex}; return m_map.find(key) != m_map.end(); @@ -128,6 +137,38 @@ class ShadowMap { m_range_scheduler.remove_keys(start_key.key(), end_key.key()); } + std::vector< std::pair< K, bool > > diff(ShadowMap< K, V > const& other) { + auto it1 = m_map.begin(); + auto it2 = other.m_map.begin(); + std::vector< std::pair< K, bool > > ret_diff; + + while ((it1 != m_map.end()) && (it2 != m_map.end())) { + auto const x = it1->first.compare(it2->first); + if (x == 0) { + ++it1; + ++it2; + } else if (x < 0) { + // Has in current map, add it to addition + ret_diff.emplace_back(it1->first, true /* addition */); + ++it1; + } else { + ret_diff.emplace_back(it2->first, false /* addition */); + ++it2; + } + } + + while (it1 != m_map.end()) { + ret_diff.emplace_back(it1->first, true /* addition */); + ++it1; + } + + while (it2 != other.m_map.end()) { + ret_diff.emplace_back(it1->first, false /* addition */); + ++it2; + } + return ret_diff; + } + mutex& guard() { return m_mutex; } std::map< K, V >& map() { return m_map; } const std::map< K, V >& map_const() const { return m_map; } @@ -144,12 +185,11 @@ class ShadowMap { const int key_width = 20; // Format the key-value pairs and insert them into the result string - ss << std::left << std::setw(key_width) << "KEY" - << " " - << "VaLUE" << '\n'; + ss << std::left << std::setw(key_width) << "KEY" << " " << "VaLUE" << '\n'; foreach ([&](const auto& key, const auto& value) { ss << std::left << std::setw(key_width) << key.to_string() << " " << value.to_string() << '\n'; - }); + }) + ; result = ss.str(); return result; } diff --git a/src/tests/index_btree_benchmark.cpp b/src/tests/index_btree_benchmark.cpp index d36bea643..02cb065f9 100644 --- a/src/tests/index_btree_benchmark.cpp +++ b/src/tests/index_btree_benchmark.cpp @@ -29,7 +29,7 @@ using namespace homestore; -#define INDEX_BETREE_BENCHMARK(BTREE_TYPE) \ +#define INDEX_BTREE_BENCHMARK(BTREE_TYPE) \ BENCHMARK(run_benchmark< BTREE_TYPE >) \ ->Setup(BM_Setup< BTREE_TYPE >) \ ->Teardown(BM_Teardown< BTREE_TYPE >) \ @@ -38,12 +38,9 @@ using namespace homestore; ->Name(#BTREE_TYPE); // this is used to splite the setup and teardown from the benchmark to get a more accurate result -void* globle_helper{nullptr}; - -#define GET_BENCHMARK_HELPER(BTREE_TYPE) static_cast< IndexBtreeBenchmark< BTREE_TYPE >* >(globle_helper) +void* g_btree_helper{nullptr}; SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; SISL_OPTIONS_ENABLE(logging, index_btree_benchmark, iomgr, test_common_setup) SISL_OPTION_GROUP(index_btree_benchmark, @@ -69,8 +66,8 @@ struct IndexBtreeBenchmark : public BtreeTestHelper< TestType > { ~IndexBtreeBenchmark() { TearDown(); } void SetUp() { - test_common::HSTestHelper::start_homestore( - "index_btree_benchmark", {{HS_SERVICE::META, {.size_pct = 10.0}}, {HS_SERVICE::INDEX, {.size_pct = 70.0}}}); + m_helper.start_homestore("index_btree_benchmark", + {{HS_SERVICE::META, {.size_pct = 10.0}}, {HS_SERVICE::INDEX, {.size_pct = 70.0}}}); this->m_cfg = BtreeConfig(hs()->index_service().node_size()); this->m_is_multi_threaded = true; @@ -87,30 +84,32 @@ struct IndexBtreeBenchmark : public BtreeTestHelper< TestType > { void TearDown() { BtreeTestHelper< TestType >::TearDown(); - test_common::HSTestHelper::shutdown_homestore(); + m_helper.shutdown_homestore(); } void run_benchmark() { this->run_in_parallel(m_op_list); } private: + test_common::HSTestHelper m_helper; std::vector< std::pair< std::string, int > > m_op_list; }; template < class BenchmarkType > void BM_Setup(const benchmark::State& state) { - globle_helper = new IndexBtreeBenchmark< BenchmarkType >(); - auto helper = GET_BENCHMARK_HELPER(BenchmarkType); + g_btree_helper = new IndexBtreeBenchmark< BenchmarkType >(); + auto helper = s_cast< IndexBtreeBenchmark< BenchmarkType >* >(g_btree_helper); helper->preload(SISL_OPTIONS["preload_size"].as< uint32_t >()); } template < class BenchmarkType > void BM_Teardown(const benchmark::State& state) { - delete GET_BENCHMARK_HELPER(BenchmarkType); + auto helper = s_cast< IndexBtreeBenchmark< BenchmarkType >* >(g_btree_helper); + delete helper; } template < class BenchmarkType > void add_custom_counter(benchmark::State& state) { - auto helper = GET_BENCHMARK_HELPER(BenchmarkType); + auto helper = s_cast< IndexBtreeBenchmark< BenchmarkType >* >(g_btree_helper); auto totol_ops = helper->get_op_num(); state.counters["thread_num"] = SISL_OPTIONS["num_threads"].as< uint32_t >(); state.counters["fiber_num"] = SISL_OPTIONS["num_fibers"].as< uint32_t >(); @@ -122,18 +121,18 @@ void add_custom_counter(benchmark::State& state) { template < class BenchmarkType > void run_benchmark(benchmark::State& state) { - auto helper = GET_BENCHMARK_HELPER(BenchmarkType); + auto helper = s_cast< IndexBtreeBenchmark< BenchmarkType >* >(g_btree_helper); for (auto _ : state) { helper->run_benchmark(); } add_custom_counter< BenchmarkType >(state); } -INDEX_BETREE_BENCHMARK(FixedLenBtree) -INDEX_BETREE_BENCHMARK(VarKeySizeBtree) -INDEX_BETREE_BENCHMARK(VarValueSizeBtree) -INDEX_BETREE_BENCHMARK(VarObjSizeBtree) -//INDEX_BETREE_BENCHMARK(PrefixIntervalBtree) +INDEX_BTREE_BENCHMARK(FixedLenBtree) +INDEX_BTREE_BENCHMARK(VarKeySizeBtree) +INDEX_BTREE_BENCHMARK(VarValueSizeBtree) +INDEX_BTREE_BENCHMARK(VarObjSizeBtree) +// INDEX_BTREE_BENCHMARK(PrefixIntervalBtree) int main(int argc, char** argv) { SISL_OPTIONS_LOAD(argc, argv, logging, index_btree_benchmark, iomgr, test_common_setup); diff --git a/src/tests/log_store_benchmark.cpp b/src/tests/log_store_benchmark.cpp index 20b81f302..c4e37fa25 100644 --- a/src/tests/log_store_benchmark.cpp +++ b/src/tests/log_store_benchmark.cpp @@ -36,7 +36,6 @@ using namespace homestore; SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; SISL_OPTIONS_ENABLE(logging, log_store_benchmark, iomgr, test_common_setup) SISL_OPTION_GROUP(log_store_benchmark, @@ -50,6 +49,7 @@ SISL_OPTION_GROUP(log_store_benchmark, ::cxxopts::value< uint32_t >()->default_value("1024"), "number")); static constexpr size_t ITERATIONS{100000}; +static test_common::HSTestHelper s_helper; class BenchLogStore { public: @@ -168,11 +168,11 @@ static void test_append(benchmark::State& state) { } static void setup() { - test_common::HSTestHelper::start_homestore( - "test_log_store_bench", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, {.size_pct = 87.0}}}); + s_helper.start_homestore("test_log_store_bench", + {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, {.size_pct = 87.0}}}); } -static void teardown() { test_common::HSTestHelper::shutdown_homestore(); } +static void teardown() { s_helper.shutdown_homestore(); } // BENCHMARK(test_append)->Iterations(10)->Threads(SISL_OPTIONS["num_threads"].as< uint32_t >()); BENCHMARK(test_append)->Iterations(1); diff --git a/src/tests/test_append_blkalloc.cpp b/src/tests/test_append_blkalloc.cpp index 2e03b8603..a1f3f515b 100644 --- a/src/tests/test_append_blkalloc.cpp +++ b/src/tests/test_append_blkalloc.cpp @@ -48,8 +48,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_append_blkalloc, iomgr, test_common_setup) SISL_LOGGING_DECL(test_append_blkalloc) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; - constexpr uint64_t Ki{1024}; constexpr uint64_t Mi{Ki * Ki}; constexpr uint64_t Gi{Ki * Mi}; @@ -66,16 +64,14 @@ class AppendBlkAllocatorTest : public testing::Test { BlkDataService& inst() { return homestore::data_service(); } virtual void SetUp() override { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_append_blkalloc", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::DATA, {.size_pct = 80.0, .blkalloc_type = homestore::blk_allocator_type_t::append, .num_chunks = 65000}}}); } - virtual void TearDown() override { test_common::HSTestHelper::shutdown_homestore(); } - - void restart_homestore() { test_common::HSTestHelper::restart_homestore(m_token); } + virtual void TearDown() override { m_helper.shutdown_homestore(); } void reset_io_job_done() { m_io_job_done = false; } @@ -186,11 +182,11 @@ class AppendBlkAllocatorTest : public testing::Test { }); } -private: +protected: std::mutex m_mtx; std::condition_variable m_cv; bool m_io_job_done{false}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; TEST_F(AppendBlkAllocatorTest, TestBasicWrite) { @@ -258,7 +254,7 @@ TEST_F(AppendBlkAllocatorTest, TestWriteThenRecovey) { test_common::HSTestHelper::trigger_cp(true /* wait */); LOGINFO("Step 4: cp completed, restart homestore."); - this->restart_homestore(); + m_helper.restart_homestore(); std::this_thread::sleep_for(std::chrono::seconds{3}); LOGINFO("Step 5: Restarted homestore with data service recovered"); diff --git a/src/tests/test_common/homestore_test_common.hpp b/src/tests/test_common/homestore_test_common.hpp index f7f759506..3200026c4 100644 --- a/src/tests/test_common/homestore_test_common.hpp +++ b/src/tests/test_common/homestore_test_common.hpp @@ -147,41 +147,7 @@ struct Waiter { }; class HSTestHelper { -private: - static void remove_files(const std::vector< std::string >& file_paths) { - for (const auto& fpath : file_paths) { - if (std::filesystem::exists(fpath)) { std::filesystem::remove(fpath); } - } - } - - static void init_files(const std::vector< std::string >& file_paths, uint64_t dev_size) { - remove_files(file_paths); - for (const auto& fpath : file_paths) { - std::ofstream ofs{fpath, std::ios::binary | std::ios::out | std::ios::trunc}; - std::filesystem::resize_file(fpath, dev_size); - } - } - - static void init_raw_devices(const std::vector< homestore::dev_info >& devs) { - auto const zero_size = hs_super_blk::first_block_size() * 1024; - std::vector< int > zeros(zero_size, 0); - for (auto const& dinfo : devs) { - if (!std::filesystem::exists(dinfo.dev_name)) { - HS_REL_ASSERT(false, "Device {} does not exist", dinfo.dev_name); - } - - auto fd = ::open(dinfo.dev_name.c_str(), O_RDWR, 0640); - HS_REL_ASSERT(fd != -1, "Failed to open device"); - - auto const write_sz = - pwrite(fd, zeros.data(), zero_size /* size */, hs_super_blk::first_block_offset() /* offset */); - HS_REL_ASSERT(write_sz == zero_size, "Failed to write to device"); - LOGINFO("Successfully zeroed the 1st {} bytes of device {}", zero_size, dinfo.dev_name); - ::close(fd); - } - } - - static std::vector< std::string > s_dev_names; + friend class HSReplTestHelper; public: struct test_params { @@ -207,147 +173,33 @@ class HSTestHelper { hs_before_services_starting_cb_t& cb() { return cb_; } }; - static test_token start_homestore(const std::string& test_name, std::map< uint32_t, test_params >&& svc_params, - hs_before_services_starting_cb_t cb = nullptr, - std::vector< homestore::dev_info > devs = {}, bool init_device = true) { - test_token token{.name_ = test_name, .svc_params_ = std::move(svc_params), .cb_ = cb, .devs_ = std::move(devs)}; - do_start_homestore(token, false /* fake_restart */, init_device); - return token; + virtual void start_homestore(const std::string& test_name, std::map< uint32_t, test_params >&& svc_params, + hs_before_services_starting_cb_t cb = nullptr, + std::vector< homestore::dev_info > devs = {}, bool init_device = true) { + m_token = + test_token{.name_ = test_name, .svc_params_ = std::move(svc_params), .cb_ = cb, .devs_ = std::move(devs)}; + do_start_homestore(false /* fake_restart */, init_device); } - static void restart_homestore(test_token& token, uint32_t shutdown_delay_sec = 5) { - do_start_homestore(token, true /* fake_restart*/, false /* init_device */, shutdown_delay_sec); + virtual void restart_homestore(uint32_t shutdown_delay_sec = 5) { + do_start_homestore(true /* fake_restart*/, false /* init_device */, shutdown_delay_sec); } - static void do_start_homestore(test_token& token, bool fake_restart = false, bool init_device = true, - uint32_t shutdown_delay_sec = 5) { - auto const ndevices = SISL_OPTIONS["num_devs"].as< uint32_t >(); - auto const dev_size = SISL_OPTIONS["dev_size_mb"].as< uint64_t >() * 1024 * 1024; - auto num_threads = SISL_OPTIONS["num_threads"].as< uint32_t >(); - auto num_fibers = SISL_OPTIONS["num_fibers"].as< uint32_t >(); - auto is_spdk = SISL_OPTIONS["spdk"].as< bool >(); - - if (fake_restart) { - shutdown_homestore(false); - std::this_thread::sleep_for(std::chrono::seconds{shutdown_delay_sec}); - } - - std::vector< homestore::dev_info > device_info; - if (!token.devs_.empty() || SISL_OPTIONS.count("device_list")) { - if (token.devs_.empty()) { - auto const devs = SISL_OPTIONS["device_list"].as< std::vector< std::string > >(); - for (const auto& name : devs) { - // TODO:: Add support for fast and data devices in device_list - token.devs_.emplace_back(name, - homestore::HSDevType::Data); // First device is fast device - } - } else { - LOGINFO("Taking input dev_list: {}", - std::accumulate(token.devs_.begin(), token.devs_.end(), std::string(""), - [](const std::string& s, const homestore::dev_info& dinfo) { - return s.empty() ? dinfo.dev_name : s + "," + dinfo.dev_name; - })); - } - if (init_device && !fake_restart) init_raw_devices(token.devs_); - } else { - /* create files */ - LOGINFO("creating {} device files with each of size {} ", ndevices, homestore::in_bytes(dev_size)); - for (uint32_t i{0}; i < ndevices; ++i) { - s_dev_names.emplace_back(std::string{"/tmp/" + token.name_ + "_" + std::to_string(i + 1)}); - } - - if (!fake_restart && init_device) { init_files(s_dev_names, dev_size); } - for (const auto& fname : s_dev_names) { - token.devs_.emplace_back(std::filesystem::canonical(fname).string(), - token.devs_.empty() - ? homestore::HSDevType::Fast - : homestore::HSDevType::Data); // First device is fast device - } - } - - if (is_spdk) { - LOGINFO("Spdk with more than 2 threads will cause overburden test systems, changing nthreads to 2"); - num_threads = 2; - } - - LOGINFO("Starting iomgr with {} threads, spdk: {}", num_threads, is_spdk); - ioenvironment.with_iomgr( - iomgr::iomgr_params{.num_threads = num_threads, .is_spdk = is_spdk, .num_fibers = 1 + num_fibers}); - - auto const http_port = SISL_OPTIONS["http_port"].as< int >(); - if (http_port != 0) { - set_fixed_http_port((http_port == -1) ? generate_random_http_port() : uint32_cast(http_port)); - ioenvironment.with_http_server(); - } - - const uint64_t app_mem_size = ((ndevices * dev_size) * 15) / 100; - LOGINFO("Initialize and start HomeStore with app_mem_size = {}", homestore::in_bytes(app_mem_size)); - - using namespace homestore; - auto hsi = HomeStore::instance(); - for (auto& [svc, tp] : token.svc_params_) { - if (svc == HS_SERVICE::DATA) { - hsi->with_data_service(tp.custom_chunk_selector); - } else if (svc == HS_SERVICE::INDEX) { - hsi->with_index_service(std::unique_ptr< IndexServiceCallbacks >(tp.index_svc_cbs)); - } else if ((svc == HS_SERVICE::LOG)) { - hsi->with_log_service(); - } else if (svc == HS_SERVICE::REPLICATION) { - hsi->with_repl_data_service(tp.repl_app, tp.custom_chunk_selector); - } - } -#ifdef _PRERELEASE - hsi->with_crash_simulator([copied_token = token](void) mutable { - LOGINFO("CrashSimulator::crash() is called - restarting homestore"); - HSTestHelper::restart_homestore(copied_token); - }); -#endif - - bool need_format = hsi->start(hs_input_params{.devices = token.devs_, .app_mem_size = app_mem_size}, token.cb_); - - // We need to set the min chunk size before homestore format - if (token.svc_params_.contains(HS_SERVICE::LOG) && token.svc_params_[HS_SERVICE::LOG].min_chunk_size != 0) { - set_min_chunk_size(token.svc_params_[HS_SERVICE::LOG].min_chunk_size); - } - - if (need_format) { - auto svc_params = token.svc_params_; - hsi->format_and_start( - {{HS_SERVICE::META, - {.dev_type = homestore::HSDevType::Fast, .size_pct = svc_params[HS_SERVICE::META].size_pct}}, - {HS_SERVICE::LOG, - {.dev_type = homestore::HSDevType::Fast, - .size_pct = svc_params[HS_SERVICE::LOG].size_pct, - .chunk_size = svc_params[HS_SERVICE::LOG].chunk_size, - .vdev_size_type = svc_params[HS_SERVICE::LOG].vdev_size_type}}, - {HS_SERVICE::DATA, - {.size_pct = svc_params[HS_SERVICE::DATA].size_pct, - .num_chunks = svc_params[HS_SERVICE::DATA].num_chunks, - .alloc_type = svc_params[HS_SERVICE::DATA].blkalloc_type, - .chunk_sel_type = svc_params[HS_SERVICE::DATA].custom_chunk_selector - ? chunk_selector_type_t::CUSTOM - : chunk_selector_type_t::ROUND_ROBIN}}, - {HS_SERVICE::INDEX, - {.dev_type = homestore::HSDevType::Fast, .size_pct = svc_params[HS_SERVICE::INDEX].size_pct}}, - {HS_SERVICE::REPLICATION, - {.size_pct = svc_params[HS_SERVICE::REPLICATION].size_pct, - .alloc_type = svc_params[HS_SERVICE::REPLICATION].blkalloc_type, - .chunk_sel_type = svc_params[HS_SERVICE::REPLICATION].custom_chunk_selector - ? chunk_selector_type_t::CUSTOM - : chunk_selector_type_t::ROUND_ROBIN}}}); - } - } - - static void shutdown_homestore(bool cleanup = true) { + virtual void shutdown_homestore(bool cleanup = true) { homestore::HomeStore::instance()->shutdown(); homestore::HomeStore::reset_instance(); iomanager.stop(); - if (cleanup) { remove_files(s_dev_names); } - s_dev_names.clear(); + if (cleanup) { remove_files(m_generated_devs); } } - static void set_min_chunk_size(uint64_t chunk_size) { + void change_start_cb(hs_before_services_starting_cb_t cb) { m_token.cb() = cb; } + void change_device_list(std::vector< homestore::dev_info > devs) { m_token.devs_ = std::move(devs); } + test_params& params(uint32_t svc) { return m_token.svc_params_[svc]; } + + void wait_for_crash_recovery() { m_crash_recovered.getFuture().get(); } + + void set_min_chunk_size(uint64_t chunk_size) { #ifdef _PRERELEASE LOGINFO("Set minimum chunk size {}", chunk_size); flip::FlipClient* fc = iomgr_flip::client_instance(); @@ -362,6 +214,26 @@ class HSTestHelper { #endif } +#ifdef _PRERELEASE + void set_basic_flip(const std::string flip_name, uint32_t count = 1, uint32_t percent = 100) { + flip::FlipCondition null_cond; + flip::FlipFrequency freq; + freq.set_count(count); + freq.set_percent(percent); + m_fc.inject_noreturn_flip(flip_name, {null_cond}, freq); + LOGDEBUG("Flip {} set", flip_name); + } + + void set_delay_flip(const std::string flip_name, uint64_t delay_usec, uint32_t count = 1, uint32_t percent = 100) { + flip::FlipCondition null_cond; + flip::FlipFrequency freq; + freq.set_count(count); + freq.set_percent(percent); + m_fc.inject_delay_flip(flip_name, {null_cond}, freq, delay_usec); + LOGDEBUG("Flip {} set", flip_name); + } +#endif + static void fill_data_buf(uint8_t* buf, uint64_t size, uint64_t pattern = 0) { uint64_t* ptr = r_cast< uint64_t* >(buf); for (uint64_t i = 0ul; i < size / sizeof(uint64_t); ++i) { @@ -457,5 +329,168 @@ class HSTestHelper { std::move(fut).thenValue(on_complete); } } + +private: + void do_start_homestore(bool fake_restart = false, bool init_device = true, uint32_t shutdown_delay_sec = 5) { + auto const ndevices = SISL_OPTIONS["num_devs"].as< uint32_t >(); + auto const dev_size = SISL_OPTIONS["dev_size_mb"].as< uint64_t >() * 1024 * 1024; + auto num_threads = SISL_OPTIONS["num_threads"].as< uint32_t >(); + auto num_fibers = SISL_OPTIONS["num_fibers"].as< uint32_t >(); + auto is_spdk = SISL_OPTIONS["spdk"].as< bool >(); + + if (fake_restart) { + // Fake restart, device list is unchanged. + shutdown_homestore(false); + std::this_thread::sleep_for(std::chrono::seconds{shutdown_delay_sec}); + } else if (SISL_OPTIONS.count("device_list")) { + // User has provided explicit device list, use that and initialize them + auto const devs = SISL_OPTIONS["device_list"].as< std::vector< std::string > >(); + for (const auto& name : devs) { + // iomgr::DriveInterface::emulate_drive_type(name, iomgr::drive_type::block_hdd); + m_token.devs_.emplace_back(name, homestore::HSDevType::Data); + } + + HS_REL_ASSERT_EQ(m_token.devs_.size() > 2, true, + "if not fake restart, we need at least 3 device to run the ut of simulating restart with " + "missing drive. current device num is {}", + m_token.devs_.size()); + + LOGINFO("Taking input dev_list: {}", + std::accumulate(m_token.devs_.begin(), m_token.devs_.end(), std::string(""), + [](const std::string& s, const homestore::dev_info& dinfo) { + return s.empty() ? dinfo.dev_name : s + "," + dinfo.dev_name; + })); + + if (init_device) { init_raw_devices(m_token.devs_); } + } else { + for (uint32_t i{0}; i < ndevices; ++i) { + m_generated_devs.emplace_back(std::string{"/tmp/" + m_token.name_ + "_" + std::to_string(i + 1)}); + } + if (init_device) { + LOGINFO("creating {} device files with each of size {} ", ndevices, homestore::in_bytes(dev_size)); + init_files(m_generated_devs, dev_size); + } + for (auto const& fname : m_generated_devs) { + m_token.devs_.emplace_back(std::filesystem::canonical(fname).string(), + m_token.devs_.empty() + ? homestore::HSDevType::Fast + : homestore::HSDevType::Data); // First device is fast device + } + } + + if (is_spdk) { + LOGINFO("Spdk with more than 2 threads will cause overburden test systems, changing nthreads to 2"); + num_threads = 2; + } + + LOGINFO("Starting iomgr with {} threads, spdk: {}", num_threads, is_spdk); + ioenvironment.with_iomgr( + iomgr::iomgr_params{.num_threads = num_threads, .is_spdk = is_spdk, .num_fibers = 1 + num_fibers}); + + auto const http_port = SISL_OPTIONS["http_port"].as< int >(); + if (http_port != 0) { + set_fixed_http_port((http_port == -1) ? generate_random_http_port() : uint32_cast(http_port)); + ioenvironment.with_http_server(); + } + + const uint64_t app_mem_size = ((ndevices * dev_size) * 15) / 100; + LOGINFO("Initialize and start HomeStore with app_mem_size = {}", homestore::in_bytes(app_mem_size)); + + using namespace homestore; + auto hsi = HomeStore::instance(); + for (auto& [svc, tp] : m_token.svc_params_) { + if (svc == HS_SERVICE::DATA) { + hsi->with_data_service(tp.custom_chunk_selector); + } else if (svc == HS_SERVICE::INDEX) { + hsi->with_index_service(std::unique_ptr< IndexServiceCallbacks >(tp.index_svc_cbs)); + } else if ((svc == HS_SERVICE::LOG)) { + hsi->with_log_service(); + } else if (svc == HS_SERVICE::REPLICATION) { + hsi->with_repl_data_service(tp.repl_app, tp.custom_chunk_selector); + } + } +#ifdef _PRERELEASE + hsi->with_crash_simulator([this](void) mutable { + LOGINFO("CrashSimulator::crash() is called - restarting homestore"); + this->restart_homestore(); + m_crash_recovered.setValue(); + }); +#endif + + bool need_format = + hsi->start(hs_input_params{.devices = m_token.devs_, .app_mem_size = app_mem_size}, m_token.cb_); + + // We need to set the min chunk size before homestore format + if (m_token.svc_params_.contains(HS_SERVICE::LOG) && m_token.svc_params_[HS_SERVICE::LOG].min_chunk_size != 0) { + set_min_chunk_size(m_token.svc_params_[HS_SERVICE::LOG].min_chunk_size); + } + + if (need_format) { + auto svc_params = m_token.svc_params_; + hsi->format_and_start( + {{HS_SERVICE::META, + {.dev_type = homestore::HSDevType::Fast, .size_pct = svc_params[HS_SERVICE::META].size_pct}}, + {HS_SERVICE::LOG, + {.dev_type = homestore::HSDevType::Fast, + .size_pct = svc_params[HS_SERVICE::LOG].size_pct, + .chunk_size = svc_params[HS_SERVICE::LOG].chunk_size, + .vdev_size_type = svc_params[HS_SERVICE::LOG].vdev_size_type}}, + {HS_SERVICE::DATA, + {.size_pct = svc_params[HS_SERVICE::DATA].size_pct, + .num_chunks = svc_params[HS_SERVICE::DATA].num_chunks, + .alloc_type = svc_params[HS_SERVICE::DATA].blkalloc_type, + .chunk_sel_type = svc_params[HS_SERVICE::DATA].custom_chunk_selector + ? chunk_selector_type_t::CUSTOM + : chunk_selector_type_t::ROUND_ROBIN}}, + {HS_SERVICE::INDEX, {.size_pct = svc_params[HS_SERVICE::INDEX].size_pct}}, + {HS_SERVICE::REPLICATION, + {.size_pct = svc_params[HS_SERVICE::REPLICATION].size_pct, + .alloc_type = svc_params[HS_SERVICE::REPLICATION].blkalloc_type, + .chunk_sel_type = svc_params[HS_SERVICE::REPLICATION].custom_chunk_selector + ? chunk_selector_type_t::CUSTOM + : chunk_selector_type_t::ROUND_ROBIN}}}); + } + } + + void remove_files(const std::vector< std::string >& file_paths) { + for (const auto& fpath : file_paths) { + if (std::filesystem::exists(fpath)) { std::filesystem::remove(fpath); } + } + } + + void init_files(const std::vector< std::string >& file_paths, uint64_t dev_size) { + remove_files(file_paths); + for (const auto& fpath : file_paths) { + std::ofstream ofs{fpath, std::ios::binary | std::ios::out | std::ios::trunc}; + std::filesystem::resize_file(fpath, dev_size); + } + } + + void init_raw_devices(const std::vector< homestore::dev_info >& devs) { + auto const zero_size = hs_super_blk::first_block_size() * 1024; + std::vector< int > zeros(zero_size, 0); + for (auto const& dinfo : devs) { + if (!std::filesystem::exists(dinfo.dev_name)) { + HS_REL_ASSERT(false, "Device {} does not exist", dinfo.dev_name); + } + + auto fd = ::open(dinfo.dev_name.c_str(), O_RDWR, 0640); + HS_REL_ASSERT(fd != -1, "Failed to open device"); + + auto const write_sz = + pwrite(fd, zeros.data(), zero_size /* size */, hs_super_blk::first_block_offset() /* offset */); + HS_REL_ASSERT(write_sz == zero_size, "Failed to write to device"); + LOGINFO("Successfully zeroed the 1st {} bytes of device {}", zero_size, dinfo.dev_name); + ::close(fd); + } + } + +protected: + test_token m_token; + std::vector< std::string > m_generated_devs; +#ifdef _PRERELEASE + flip::FlipClient m_fc{iomgr_flip::instance()}; + folly::Promise< folly::Unit > m_crash_recovered; +#endif }; -}; // namespace test_common +} // namespace test_common diff --git a/src/tests/test_common/hs_repl_test_common.hpp b/src/tests/test_common/hs_repl_test_common.hpp index f0e33ddea..356b58a6b 100644 --- a/src/tests/test_common/hs_repl_test_common.hpp +++ b/src/tests/test_common/hs_repl_test_common.hpp @@ -46,8 +46,6 @@ SISL_OPTION_GROUP(test_repl_common_setup, (replica_dev_list, "", "replica_dev_list", "Device list for all replicas", ::cxxopts::value< std::vector< std::string > >(), "path [...]")); -std::vector< std::string > test_common::HSTestHelper::s_dev_names; - using namespace homestore; namespace bip = boost::interprocess; @@ -56,7 +54,7 @@ namespace test_common { VENUM(ipc_packet_op_t, uint32_t, WAKE_UP = 0, CLEAN_EXIT = 1, UNCLEAN_EXIT = 2, PEER_GOING_DOWN = 3); ENUM(repl_test_phase_t, uint32_t, REGISTER, MEMBER_START, TEST_RUN, VALIDATE, CLEANUP); -class HSReplTestHelper { +class HSReplTestHelper : public HSTestHelper { protected: struct IPCData { bip::interprocess_mutex mtx_; @@ -210,7 +208,7 @@ class HSReplTestHelper { folly_ = std::make_unique< folly::Init >(&tmp_argc, &argv_, true); LOGINFO("Starting Homestore replica={}", replica_num_); - m_token = test_common::HSTestHelper::start_homestore( + start_homestore( name_ + std::to_string(replica_num_), {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::REPLICATION, {.size_pct = 60.0, .repl_app = std::make_unique< TestReplApplication >(*this)}}, @@ -222,7 +220,7 @@ class HSReplTestHelper { LOGINFO("Stopping Homestore replica={}", replica_num_); // sisl::GrpcAsyncClientWorker::shutdown_all(); // don't remove device if it is real drive; - test_common::HSTestHelper::shutdown_homestore(dev_list_.empty() /* cleanup */); + shutdown_homestore(dev_list_.empty() /* cleanup */); sisl::GrpcAsyncClientWorker::shutdown_all(); } @@ -233,14 +231,14 @@ class HSReplTestHelper { void restart(uint32_t shutdown_delay_secs = 5u) { m_token.params(HS_SERVICE::REPLICATION).repl_app = std::make_unique< TestReplApplication >(*this); - test_common::HSTestHelper::restart_homestore(m_token, shutdown_delay_secs); + restart_homestore(shutdown_delay_secs); } void restart_one_by_one() { exclusive_replica([this]() { LOGINFO("Restarting Homestore replica={}", replica_num_); m_token.params(HS_SERVICE::REPLICATION).repl_app = std::make_unique< TestReplApplication >(*this); - test_common::HSTestHelper::restart_homestore(m_token, 5u /* shutdown_delay_secs */); + restart_homestore(5u /* shutdown_delay_secs */); }); } @@ -364,6 +362,5 @@ class HSReplTestHelper { IPCData* ipc_data_; Runner io_runner_; - HSTestHelper::test_token m_token; }; } // namespace test_common diff --git a/src/tests/test_cp_mgr.cpp b/src/tests/test_cp_mgr.cpp index 33a0c77d4..5413a1a3b 100644 --- a/src/tests/test_cp_mgr.cpp +++ b/src/tests/test_cp_mgr.cpp @@ -31,7 +31,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_cp_mgr, iomgr, test_common_setup) SISL_LOGGING_DECL(test_cp_mgr) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; SISL_OPTION_GROUP(test_cp_mgr, (num_records, "", "num_records", "number of record to test", @@ -86,10 +85,11 @@ class TestCPCallbacks : public CPCallbacks { class TestCPMgr : public ::testing::Test { public: void SetUp() override { - test_common::HSTestHelper::start_homestore("test_cp", {{HS_SERVICE::META, {.size_pct = 85.0}}}); + m_helper.start_homestore("test_cp", {{HS_SERVICE::META, {.size_pct = 85.0}}}); hs()->cp_mgr().register_consumer(cp_consumer_t::HS_CLIENT, std::move(std::make_unique< TestCPCallbacks >())); } - void TearDown() override { test_common::HSTestHelper::shutdown_homestore(); } + + void TearDown() override { m_helper.shutdown_homestore(); } void simulate_io() { iomanager.run_on_forget(iomgr::reactor_regex::least_busy_worker, [this]() { @@ -140,6 +140,9 @@ class TestCPMgr : public ::testing::Test { std::move(fut).thenValue(on_complete); } } + +private: + test_common::HSTestHelper m_helper; }; TEST_F(TestCPMgr, cp_start_and_flush) { diff --git a/src/tests/test_data_service.cpp b/src/tests/test_data_service.cpp index 05452a0ab..9592da475 100644 --- a/src/tests/test_data_service.cpp +++ b/src/tests/test_data_service.cpp @@ -57,8 +57,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_data_service, iomgr, test_common_setup) SISL_LOGGING_DECL(test_data_service) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; - constexpr uint64_t Ki{1024}; constexpr uint64_t Mi{Ki * Ki}; constexpr uint64_t Gi{Ki * Mi}; @@ -83,8 +81,8 @@ class BlkDataServiceTest : public testing::Test { virtual void SetUp() override { m_blk_crc_map.clear(); - m_token = test_common::HSTestHelper::start_homestore( - "test_data_service", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::DATA, {.size_pct = 80.0}}}); + m_helper.start_homestore("test_data_service", + {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::DATA, {.size_pct = 80.0}}}); if (gp.min_io_size % homestore::data_service().get_blk_size() || gp.max_io_size % homestore::data_service().get_blk_size()) { @@ -95,7 +93,7 @@ class BlkDataServiceTest : public testing::Test { } } - virtual void TearDown() override { test_common::HSTestHelper::shutdown_homestore(); } + virtual void TearDown() override { m_helper.shutdown_homestore(); } void free(sisl::sg_list& sg) { test_common::HSTestHelper::free(sg); } @@ -328,8 +326,8 @@ class BlkDataServiceTest : public testing::Test { } // restart with the given drive list - m_token.devs_ = start_with_devices; - test_common::HSTestHelper::restart_homestore(m_token); + m_helper.change_device_list(start_with_devices); + m_helper.restart_homestore(); LOGINFO("Step 4: read the blk from missing data drive"); auto sg = std::make_shared< sisl::sg_list >(); @@ -827,7 +825,7 @@ class BlkDataServiceTest : public testing::Test { std::unordered_set< uint64_t > m_outstanding_free_bid; std::atomic< uint64_t > m_outstanding_io_cnt{0}; std::atomic< uint64_t > m_total_io_comp_cnt{0}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; // diff --git a/src/tests/test_home_raft_logstore.cpp b/src/tests/test_home_raft_logstore.cpp index ab230be40..b4349e6b8 100644 --- a/src/tests/test_home_raft_logstore.cpp +++ b/src/tests/test_home_raft_logstore.cpp @@ -16,7 +16,6 @@ static constexpr uint32_t g_max_logsize{512}; static std::random_device g_rd{}; static std::default_random_engine g_re{g_rd()}; static std::uniform_int_distribution< uint32_t > g_randlogsize_generator{2, g_max_logsize}; -std::vector< std::string > test_common::HSTestHelper::s_dev_names; static constexpr std::array< const char, 62 > alphanum{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', @@ -174,8 +173,8 @@ class RaftLogStoreClient { class TestRaftLogStore : public ::testing::Test { public: void SetUp() { - m_token = test_common::HSTestHelper::start_homestore( - "test_home_raft_log_store", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, {.size_pct = 72.0}}}); + m_helper.start_homestore("test_home_raft_log_store", + {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, {.size_pct = 72.0}}}); m_leader_store.m_rls = std::make_unique< HomeRaftLogStore >(); m_leader_store.m_store_id = m_leader_store.m_rls->logstore_id(); m_leader_store.m_logdev_id = m_leader_store.m_rls->logdev_id(); @@ -189,26 +188,26 @@ class TestRaftLogStore : public ::testing::Test { m_leader_store.m_rls.reset(); m_follower_store.m_rls.reset(); - m_token.cb_ = [this]() { + m_helper.change_start_cb([this]() { m_leader_store.m_rls = std::make_unique< HomeRaftLogStore >(m_leader_store.m_logdev_id, m_leader_store.m_store_id); m_follower_store.m_rls = std::make_unique< HomeRaftLogStore >(m_follower_store.m_logdev_id, m_follower_store.m_store_id); - }; + }); - test_common::HSTestHelper::restart_homestore(m_token); + m_helper.restart_homestore(); } virtual void TearDown() override { m_leader_store.m_rls.reset(); m_follower_store.m_rls.reset(); - test_common::HSTestHelper::shutdown_homestore(); + m_helper.shutdown_homestore(); } protected: RaftLogStoreClient m_leader_store; RaftLogStoreClient m_follower_store; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; TEST_F(TestRaftLogStore, lifecycle_test) { diff --git a/src/tests/test_index_btree.cpp b/src/tests/test_index_btree.cpp index 6429525ee..68e6bbee0 100644 --- a/src/tests/test_index_btree.cpp +++ b/src/tests/test_index_btree.cpp @@ -31,8 +31,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_index_btree, iomgr, test_common_setup) SISL_LOGGING_DECL(test_index_btree) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; - // TODO Add tests to do write,remove after recovery. // TODO Test with var len key with io mgr page size is 512. @@ -85,7 +83,7 @@ struct BtreeTest : public BtreeTestHelper< TestType >, public ::testing::Test { BtreeTest() : testing::Test() {} void SetUp() override { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_index_btree", {{HS_SERVICE::META, {.size_pct = 10.0}}, {HS_SERVICE::INDEX, {.size_pct = 70.0, .index_svc_cbs = new TestIndexServiceCallbacks(this)}}}); @@ -112,14 +110,14 @@ struct BtreeTest : public BtreeTestHelper< TestType >, public ::testing::Test { void TearDown() override { BtreeTestHelper< TestType >::TearDown(); - test_common::HSTestHelper::shutdown_homestore(false); + m_helper.shutdown_homestore(false); this->m_bt.reset(); log_obj_life_counter(); } void restart_homestore() { - m_token.params(HS_SERVICE::INDEX).index_svc_cbs = new TestIndexServiceCallbacks(this); - test_common::HSTestHelper::restart_homestore(m_token); + m_helper.params(HS_SERVICE::INDEX).index_svc_cbs = new TestIndexServiceCallbacks(this); + m_helper.restart_homestore(); } void destroy_btree() { @@ -130,7 +128,7 @@ struct BtreeTest : public BtreeTestHelper< TestType >, public ::testing::Test { this->m_bt.reset(); } - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; using BtreeTypes = testing::Types< FixedLenBtree, VarKeySizeBtree, VarValueSizeBtree, VarObjSizeBtree >; @@ -420,7 +418,6 @@ TYPED_TEST(BtreeTest, ThreadedCpFlush) { } template < typename TestType > - struct BtreeConcurrentTest : public BtreeTestHelper< TestType >, public ::testing::Test { using T = TestType; using K = typename TestType::KeyType; @@ -445,10 +442,13 @@ struct BtreeConcurrentTest : public BtreeTestHelper< TestType >, public ::testin BtreeConcurrentTest() : testing::Test() { this->m_is_multi_threaded = true; } - void restart_homestore() { test_common::HSTestHelper::restart_homestore(m_token); } + void restart_homestore() { + m_helper.params(HS_SERVICE::INDEX).index_svc_cbs = new TestIndexServiceCallbacks(this); + m_helper.restart_homestore(); + } void SetUp() override { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_index_btree", {{HS_SERVICE::META, {.size_pct = 10.0}}, {HS_SERVICE::INDEX, {.size_pct = 70.0, .index_svc_cbs = new TestIndexServiceCallbacks(this)}}}, @@ -478,6 +478,7 @@ struct BtreeConcurrentTest : public BtreeTestHelper< TestType >, public ::testin hs()->index_service().add_index_table(this->m_bt); LOGINFO("Added index table to index service"); } + void populate_shadow_map() { this->m_shadow_map.load(m_shadow_filename); ASSERT_EQ(this->m_shadow_map.size(), this->m_bt->count_keys(this->m_bt->root_node_id())) @@ -500,12 +501,12 @@ struct BtreeConcurrentTest : public BtreeTestHelper< TestType >, public ::testin LOGINFO("Teardown with Root bnode_id {} tree size: {}", this->m_bt->root_node_id(), this->m_bt->count_keys(this->m_bt->root_node_id())); BtreeTestHelper< TestType >::TearDown(); - test_common::HSTestHelper::shutdown_homestore(false); + m_helper.shutdown_homestore(false); } private: const std::string m_shadow_filename = "/tmp/shadow_map.txt"; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; TYPED_TEST_SUITE(BtreeConcurrentTest, BtreeTypes); diff --git a/src/tests/test_journal_vdev.cpp b/src/tests/test_journal_vdev.cpp index ab489ff96..8a06911cf 100644 --- a/src/tests/test_journal_vdev.cpp +++ b/src/tests/test_journal_vdev.cpp @@ -44,7 +44,6 @@ RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_journal_vdev, iomgr, test_common_setup) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; struct Param { uint64_t num_io; @@ -66,28 +65,24 @@ constexpr uint32_t dma_alignment = 512; class VDevJournalIOTest : public ::testing::Test { public: - const std::map< uint32_t, test_common::HSTestHelper::test_params > svc_params = {}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; virtual void SetUp() override { auto const ndevices = SISL_OPTIONS["num_devs"].as< uint32_t >(); auto const dev_size = SISL_OPTIONS["dev_size_mb"].as< uint64_t >() * 1024 * 1024; - m_token = - test_common::HSTestHelper::start_homestore("test_journal_vdev", - { - {HS_SERVICE::META, {.size_pct = 15.0}}, - {HS_SERVICE::LOG, - {.size_pct = 50.0, - .chunk_size = 16 * 1024 * 1024, - .min_chunk_size = 16 * 1024 * 1024, - .vdev_size_type = vdev_size_type_t::VDEV_SIZE_DYNAMIC}}, - }, - nullptr /* starting_cb */); - } - - virtual void TearDown() override { test_common::HSTestHelper::shutdown_homestore(); } - - void restart_homestore() { test_common::HSTestHelper::restart_homestore(m_token); } + m_helper.start_homestore("test_journal_vdev", + { + {HS_SERVICE::META, {.size_pct = 15.0}}, + {HS_SERVICE::LOG, + {.size_pct = 50.0, + .chunk_size = 16 * 1024 * 1024, + .min_chunk_size = 16 * 1024 * 1024, + .vdev_size_type = vdev_size_type_t::VDEV_SIZE_DYNAMIC}}, + }, + nullptr /* starting_cb */); + } + + virtual void TearDown() override { m_helper.shutdown_homestore(); } }; class JournalDescriptorTest { @@ -456,7 +451,7 @@ TEST_F(VDevJournalIOTest, Recovery) { } // Restart homestore. - restart_homestore(); + m_helper.restart_homestore(); // Restore the offsets after restart. for (auto& t : tests) { @@ -502,7 +497,7 @@ TEST_F(VDevJournalIOTest, MultipleChunkTest) { auto restart_restore = [&]() { test.save(); - restart_homestore(); + m_helper.restart_homestore(); test.restore(); log_dev_jd = test.vdev_jd(); }; diff --git a/src/tests/test_log_dev.cpp b/src/tests/test_log_dev.cpp index 5a6a3c04b..ada551940 100644 --- a/src/tests/test_log_dev.cpp +++ b/src/tests/test_log_dev.cpp @@ -45,8 +45,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_log_dev, iomgr, test_common_setup) SISL_LOGGING_DECL(test_log_dev) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; - struct test_log_data { test_log_data() = default; test_log_data(const test_log_data&) = delete; @@ -68,8 +66,7 @@ struct test_log_data { class LogDevTest : public ::testing::Test { public: - const std::map< uint32_t, test_common::HSTestHelper::test_params > svc_params = {}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; static constexpr uint32_t max_data_size = 1024; uint64_t s_max_flush_multiple = 0; @@ -86,24 +83,23 @@ class LogDevTest : public ::testing::Test { HS_SETTINGS_FACTORY().save(); if (restart) { - m_token.cb() = starting_cb; - test_common::HSTestHelper::restart_homestore(m_token); + m_helper.change_start_cb(starting_cb); + m_helper.restart_homestore(); } else { - m_token = test_common::HSTestHelper::start_homestore( - "test_log_dev", - { - {HS_SERVICE::META, {.size_pct = 15.0}}, - {HS_SERVICE::LOG, - {.size_pct = 50.0, - .chunk_size = 8 * 1024 * 1024, - .min_chunk_size = 8 * 1024 * 1024, - .vdev_size_type = vdev_size_type_t::VDEV_SIZE_DYNAMIC}}, - }, - starting_cb); + m_helper.start_homestore("test_log_dev", + { + {HS_SERVICE::META, {.size_pct = 15.0}}, + {HS_SERVICE::LOG, + {.size_pct = 50.0, + .chunk_size = 8 * 1024 * 1024, + .min_chunk_size = 8 * 1024 * 1024, + .vdev_size_type = vdev_size_type_t::VDEV_SIZE_DYNAMIC}}, + }, + starting_cb); } } - virtual void TearDown() override { test_common::HSTestHelper::shutdown_homestore(); } + virtual void TearDown() override { m_helper.shutdown_homestore(); } test_log_data* prepare_data(const logstore_seq_num_t lsn, bool& io_memory, uint32_t fixed_size = 0) { static thread_local std::random_device rd{}; diff --git a/src/tests/test_log_store.cpp b/src/tests/test_log_store.cpp index 93ab5b212..7ed6cf2d8 100644 --- a/src/tests/test_log_store.cpp +++ b/src/tests/test_log_store.cpp @@ -54,7 +54,6 @@ using namespace homestore; RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; struct test_log_data { test_log_data() = default; @@ -522,7 +521,7 @@ class SampleDB { } void shutdown(bool cleanup = true) { - test_common::HSTestHelper::shutdown_homestore(cleanup); + m_helper.shutdown_homestore(cleanup); if (cleanup) { m_log_store_clients.clear(); m_highest_log_idx.clear(); @@ -559,7 +558,7 @@ class SampleDB { test_log_store_comp_cb_t m_io_closure; std::vector< std::unique_ptr< SampleLogStoreClient > > m_log_store_clients; std::map< logdev_id_t, std::atomic< logid_t > > m_highest_log_idx; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; const std::string SampleDB::s_fpath_root{"/tmp/log_store_dev_"}; diff --git a/src/tests/test_meta_blk_mgr.cpp b/src/tests/test_meta_blk_mgr.cpp index 428e8ae35..f065b1a56 100644 --- a/src/tests/test_meta_blk_mgr.cpp +++ b/src/tests/test_meta_blk_mgr.cpp @@ -50,7 +50,6 @@ using namespace homestore; RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; SISL_OPTIONS_ENABLE(logging, test_meta_blk_mgr, iomgr, test_common_setup) @@ -113,7 +112,7 @@ class VMetaBlkMgrTest : public ::testing::Test { std::vector< meta_sub_type > actual_on_complete_cb_order; std::vector< void* > cookies; bool enable_dependency_chain{false}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; VMetaBlkMgrTest() = default; VMetaBlkMgrTest(const VMetaBlkMgrTest&) = delete; @@ -124,10 +123,7 @@ class VMetaBlkMgrTest : public ::testing::Test { virtual ~VMetaBlkMgrTest() override = default; protected: - void SetUp() override { - m_token = - test_common::HSTestHelper::start_homestore("test_meta_blk_mgr", {{HS_SERVICE::META, {.size_pct = 85.0}}}); - } + void SetUp() override { m_helper.start_homestore("test_meta_blk_mgr", {{HS_SERVICE::META, {.size_pct = 85.0}}}); } void TearDown() override {}; @@ -147,12 +143,12 @@ class VMetaBlkMgrTest : public ::testing::Test { } void restart_homestore() { - auto before_services_starting_cb = [this]() { + m_helper.change_start_cb([this]() { register_client(); - if (enable_dependency_chain) { register_client_inlcuding_dependencies(); } - }; - m_token.cb_ = before_services_starting_cb; - test_common::HSTestHelper::restart_homestore(m_token); + if (enable_dependency_chain) { register_client_including_dependencies(); } + }); + + m_helper.restart_homestore(); } uint64_t io_cnt() const { return m_update_cnt + m_wrt_cnt + m_rm_cnt; } @@ -571,7 +567,7 @@ class VMetaBlkMgrTest : public ::testing::Test { m_write_sbs.clear(); m_cb_blks.clear(); } - test_common::HSTestHelper::shutdown_homestore(); + m_helper.shutdown_homestore(); } void reset_counters() { @@ -599,7 +595,7 @@ class VMetaBlkMgrTest : public ::testing::Test { [this](bool success) { HS_DBG_ASSERT_EQ(success, true); }); } - void register_client_inlcuding_dependencies() { + void register_client_including_dependencies() { enable_dependency_chain = true; m_mbm = &(meta_service()); m_total_wrt_sz = m_mbm->used_size(); @@ -668,7 +664,7 @@ class VMetaBlkMgrTest : public ::testing::Test { [this](bool success) { actual_on_complete_cb_order.push_back("F"); }, false); } - void deregister_client_inlcuding_dependencies() { + void deregister_client_including_dependencies() { enable_dependency_chain = false; m_mbm->deregister_handler("A"); @@ -752,7 +748,7 @@ TEST_F(VMetaBlkMgrTest, single_read_test) { TEST_F(VMetaBlkMgrTest, random_dependency_test) { reset_counters(); m_start_time = Clock::now(); - this->register_client_inlcuding_dependencies(); + this->register_client_including_dependencies(); // add sub super block out of order uint8_t* buf = iomanager.iobuf_alloc(512, 1); @@ -805,7 +801,7 @@ TEST_F(VMetaBlkMgrTest, random_dependency_test) { EXPECT_TRUE(actual_first_cb_order_map["E"] < actual_first_cb_order_map["B"]); EXPECT_TRUE(actual_first_cb_order_map["F"] < actual_first_cb_order_map["C"]); - this->deregister_client_inlcuding_dependencies(); + this->deregister_client_including_dependencies(); this->shutdown(); } diff --git a/src/tests/test_raft_repl_dev.cpp b/src/tests/test_raft_repl_dev.cpp index 97c118edd..c6a326b7c 100644 --- a/src/tests/test_raft_repl_dev.cpp +++ b/src/tests/test_raft_repl_dev.cpp @@ -295,26 +295,6 @@ class RaftReplDevTest : public testing::Test { shared< TestReplicatedDB > pick_one_db() { return dbs_[0]; } -#ifdef _PRERELEASE - void set_basic_flip(const std::string flip_name, uint32_t count = 1, uint32_t percent = 100) { - flip::FlipCondition null_cond; - flip::FlipFrequency freq; - freq.set_count(count); - freq.set_percent(percent); - m_fc.inject_noreturn_flip(flip_name, {null_cond}, freq); - LOGDEBUG("Flip {} set", flip_name); - } - - void set_delay_flip(const std::string flip_name, uint64_t delay_usec, uint32_t count = 1, uint32_t percent = 100) { - flip::FlipCondition null_cond; - flip::FlipFrequency freq; - freq.set_count(count); - freq.set_percent(percent); - m_fc.inject_delay_flip(flip_name, {null_cond}, freq, delay_usec); - LOGDEBUG("Flip {} set", flip_name); - } -#endif - void assign_leader(uint16_t replica) { LOGINFO("Switch the leader to replica_num = {}", replica); if (g_helper->replica_num() == replica) { @@ -468,7 +448,7 @@ TEST_F(RaftReplDevTest, Follower_Fetch_OnActive_ReplicaGroup) { if (g_helper->replica_num() != 0) { LOGINFO("Set flip to fake fetch data request on data channel"); - set_basic_flip("drop_push_data_request"); + g_helper->set_basic_flip("drop_push_data_request"); } this->write_on_leader(100, true /* wait_for_commit */); @@ -528,9 +508,9 @@ TEST_F(RaftReplDevTest, Follower_Reject_Append) { if (g_helper->replica_num() != 0) { LOGINFO("Set flip to fake reject append entries in both data and raft channels. We slow down data channel " "occassionally so that raft channel reject can be hit"); - set_basic_flip("fake_reject_append_data_channel", 5, 10); - set_basic_flip("fake_reject_append_raft_channel", 10, 100); - set_delay_flip("slow_down_data_channel", 10000ull, 10, 10); + g_helper->set_basic_flip("fake_reject_append_data_channel", 5, 10); + g_helper->set_basic_flip("fake_reject_append_raft_channel", 10, 100); + g_helper->set_delay_flip("slow_down_data_channel", 10000ull, 10, 10); } LOGINFO("Write to leader and then wait for all the commits on all replica despite drop/slow_down"); @@ -603,7 +583,7 @@ TEST_F(RaftReplDevTest, Drop_Raft_Entry_Switch_Leader) { if (g_helper->replica_num() == 2) { LOGINFO("Set flip to fake drop append entries in raft channel of replica=2"); - set_basic_flip("fake_drop_append_raft_channel", 2, 75); + test_common::HSTestHelper::set_basic_flip("fake_drop_append_raft_channel", 2, 75); } uint64_t exp_entries = SISL_OPTIONS["num_io"].as< uint64_t >(); @@ -614,7 +594,7 @@ TEST_F(RaftReplDevTest, Drop_Raft_Entry_Switch_Leader) { if (g_helper->replica_num() == 2) { LOGINFO("Set flip to fake drop append entries in raft channel of replica=2 again"); - set_basic_flip("fake_drop_append_raft_channel", 1, 100); + test_common::HSTestHelper::set_basic_flip("fake_drop_append_raft_channel", 1, 100); } else { g_helper->sync_dataset_size(1); if (g_helper->replica_num() == 0) { this->write_on_leader(); } diff --git a/src/tests/test_solo_repl_dev.cpp b/src/tests/test_solo_repl_dev.cpp index bee5cd9c1..a401ba0d3 100644 --- a/src/tests/test_solo_repl_dev.cpp +++ b/src/tests/test_solo_repl_dev.cpp @@ -51,7 +51,6 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_solo_repl_dev, iomgr, test_common_setup) SISL_LOGGING_DECL(test_solo_repl_dev) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; static thread_local std::random_device g_rd{}; static thread_local std::default_random_engine g_re{g_rd()}; static uint32_t g_block_size; @@ -154,11 +153,11 @@ class SoloReplDevTest : public testing::Test { shared< ReplDev > m_repl_dev2; uuid_t m_uuid1; uuid_t m_uuid2; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; public: virtual void SetUp() override { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_solo_repl_dev", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::REPLICATION, {.size_pct = 60.0, .repl_app = std::make_unique< Application >(*this)}}, @@ -175,13 +174,13 @@ class SoloReplDevTest : public testing::Test { virtual void TearDown() override { m_repl_dev1.reset(); m_repl_dev2.reset(); - test_common::HSTestHelper::shutdown_homestore(); + m_helper.shutdown_homestore(); } void restart() { m_repl_dev1.reset(); m_repl_dev2.reset(); - test_common::HSTestHelper::restart_homestore(m_token); + m_helper.restart_homestore(); m_repl_dev1 = hs()->repl_service().get_repl_dev(m_uuid1).value(); m_repl_dev2 = hs()->repl_service().get_repl_dev(m_uuid2).value(); From c444a3a3c19e27e40b1412cde837a593d4f7127c Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Thu, 20 Jun 2024 18:27:26 -0700 Subject: [PATCH 8/9] Added missing test changes --- src/include/homestore/index/wb_cache_base.hpp | 1 - src/tests/test_log_store.cpp | 9 +++++---- src/tests/test_log_store_long_run.cpp | 13 ++++++------- src/tests/test_raft_repl_dev.cpp | 2 +- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/include/homestore/index/wb_cache_base.hpp b/src/include/homestore/index/wb_cache_base.hpp index f8d7690c4..d576c2b6b 100644 --- a/src/include/homestore/index/wb_cache_base.hpp +++ b/src/include/homestore/index/wb_cache_base.hpp @@ -46,7 +46,6 @@ class IndexWBCacheBase { virtual void write_buf(const BtreeNodePtr& node, const IndexBufferPtr& buf, CPContext* context) = 0; virtual void read_buf(bnodeid_t id, BtreeNodePtr& node, node_initializer_t&& node_initializer) = 0; - virtual std::pair< bnodeid_t, uint64_t > get_root(bnodeid_t super_node_id) = 0; virtual bool get_writable_buf(const BtreeNodePtr& node, CPContext* context) = 0; diff --git a/src/tests/test_log_store.cpp b/src/tests/test_log_store.cpp index 7ed6cf2d8..d2cadd5f3 100644 --- a/src/tests/test_log_store.cpp +++ b/src/tests/test_log_store.cpp @@ -474,7 +474,8 @@ class SampleDB { for (auto& lsc : m_log_store_clients) { lsc->flush(); } - m_token.cb_ = [this, n_log_stores]() { + + m_helper.change_start_cb([this, n_log_stores]() { HS_SETTINGS_FACTORY().modifiable_settings([](auto& s) { // Disable flush and resource mgr timer in UT. s.logstore.flush_timer_frequency_us = 0; @@ -489,10 +490,10 @@ class SampleDB { .open_log_store(client->m_logdev_id, client->m_store_id, false /* append_mode */) .thenValue([i, this, client](auto log_store) { client->set_log_store(log_store); }); } - }; - test_common::HSTestHelper::restart_homestore(m_token); + }); + m_helper.restart_homestore(); } else { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_log_store", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, diff --git a/src/tests/test_log_store_long_run.cpp b/src/tests/test_log_store_long_run.cpp index b2aeea160..a5e6009ba 100644 --- a/src/tests/test_log_store_long_run.cpp +++ b/src/tests/test_log_store_long_run.cpp @@ -54,7 +54,6 @@ using namespace homestore; RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) -std::vector< std::string > test_common::HSTestHelper::s_dev_names; struct test_log_data { test_log_data() = default; @@ -302,7 +301,7 @@ class LogStoreLongRun : public ::testing::Test { for (auto& lsc : m_log_store_clients) { lsc->flush(); } - m_token.cb_ = [this, n_log_stores]() { + m_helper.change_start_cb([this, n_log_stores]() { HS_SETTINGS_FACTORY().modifiable_settings([](auto& s) { // Disable flush and resource mgr timer in UT. s.logstore.flush_timer_frequency_us = 0; @@ -316,10 +315,10 @@ class LogStoreLongRun : public ::testing::Test { .open_log_store(client->m_logdev_id, client->m_store_id, false /* append_mode */) .thenValue([i, this, client](auto log_store) { client->set_log_store(log_store); }); } - }; - test_common::HSTestHelper::restart_homestore(m_token); + }); + m_helper.restart_homestore(); } else { - m_token = test_common::HSTestHelper::start_homestore( + m_helper.start_homestore( "test_log_store_long_run", {{HS_SERVICE::META, {.size_pct = 5.0}}, {HS_SERVICE::LOG, @@ -345,7 +344,7 @@ class LogStoreLongRun : public ::testing::Test { } void shutdown(bool cleanup = true) { - test_common::HSTestHelper::shutdown_homestore(cleanup); + m_helper.shutdown_homestore(cleanup); if (cleanup) { m_log_store_clients.clear(); m_highest_log_idx.clear(); @@ -560,7 +559,7 @@ class LogStoreLongRun : public ::testing::Test { uint32_t m_batch_size{1}; std::random_device rd{}; std::default_random_engine re{rd()}; - test_common::HSTestHelper::test_token m_token; + test_common::HSTestHelper m_helper; }; TEST_F(LogStoreLongRun, LongRunning) { diff --git a/src/tests/test_raft_repl_dev.cpp b/src/tests/test_raft_repl_dev.cpp index c6a326b7c..ac6aaae5c 100644 --- a/src/tests/test_raft_repl_dev.cpp +++ b/src/tests/test_raft_repl_dev.cpp @@ -676,7 +676,7 @@ TEST_F(RaftReplDevTest, GCReplReqs) { if (g_helper->replica_num() != 0) { LOGINFO("Set flip to fake fetch data request on data channel"); - set_basic_flip("drop_push_data_request"); + g_helper->set_basic_flip("drop_push_data_request"); } this->write_on_leader(100 /* num_entries */, true /* wait_for_commit */); From 525707ef97ff5a33809aa4bbb4088b27bbb3d3bf Mon Sep 17 00:00:00 2001 From: Harihara Kadayam Date: Fri, 21 Jun 2024 11:32:29 -0700 Subject: [PATCH 9/9] Addressed review comments --- src/tests/test_common/homestore_test_common.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/test_common/homestore_test_common.hpp b/src/tests/test_common/homestore_test_common.hpp index 3200026c4..b82ae2466 100644 --- a/src/tests/test_common/homestore_test_common.hpp +++ b/src/tests/test_common/homestore_test_common.hpp @@ -197,7 +197,9 @@ class HSTestHelper { void change_device_list(std::vector< homestore::dev_info > devs) { m_token.devs_ = std::move(devs); } test_params& params(uint32_t svc) { return m_token.svc_params_[svc]; } +#ifdef _PRERELEASE void wait_for_crash_recovery() { m_crash_recovered.getFuture().get(); } +#endif void set_min_chunk_size(uint64_t chunk_size) { #ifdef _PRERELEASE @@ -347,14 +349,12 @@ class HSTestHelper { auto const devs = SISL_OPTIONS["device_list"].as< std::vector< std::string > >(); for (const auto& name : devs) { // iomgr::DriveInterface::emulate_drive_type(name, iomgr::drive_type::block_hdd); - m_token.devs_.emplace_back(name, homestore::HSDevType::Data); + m_token.devs_.emplace_back(name, + m_token.devs_.empty() + ? homestore::HSDevType::Fast + : homestore::HSDevType::Data); // First device is fast device } - HS_REL_ASSERT_EQ(m_token.devs_.size() > 2, true, - "if not fake restart, we need at least 3 device to run the ut of simulating restart with " - "missing drive. current device num is {}", - m_token.devs_.size()); - LOGINFO("Taking input dev_list: {}", std::accumulate(m_token.devs_.begin(), m_token.devs_.end(), std::string(""), [](const std::string& s, const homestore::dev_info& dinfo) {