From 456213a67104bba12e931072cee90e439fa014f7 Mon Sep 17 00:00:00 2001 From: Niko Nastonen Date: Wed, 9 Sep 2026 02:24:39 -0700 Subject: [PATCH] SDSTOR-25404: clear wbc for chunk selector --- conanfile.py | 2 +- src/CMakeLists.txt | 2 + src/include/homestore/index/wb_cache_base.hpp | 8 +- src/lib/device/chunk.cpp | 13 + src/lib/device/chunk.h | 5 + src/lib/index/wb_cache.cpp | 16 +- src/lib/index/wb_cache.hpp | 1 + src/tests/CMakeLists.txt | 9 + src/tests/btree_helpers/btree_test_helper.hpp | 2 +- .../test_common/homestore_test_common.hpp | 7 +- src/tests/test_index_chunk_selector.cpp | 333 ++++++++++++++++++ 11 files changed, 389 insertions(+), 9 deletions(-) create mode 100644 src/tests/test_index_chunk_selector.cpp diff --git a/conanfile.py b/conanfile.py index d02de6580..8e9f13c6c 100644 --- a/conanfile.py +++ b/conanfile.py @@ -9,7 +9,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "8.2.0" + version = "8.2.1" homepage = "https://github.com/eBay/Homestore" description = "HomeStore Storage Engine" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a3fb29416..d3d09c767 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -66,3 +66,5 @@ add_library(homestore STATIC ${HOMESTORE_OBJECTS} ) target_link_libraries(homestore ${COMMON_DEPS}) +target_compile_definitions(homestore PRIVATE _PRERELEASE) + diff --git a/src/include/homestore/index/wb_cache_base.hpp b/src/include/homestore/index/wb_cache_base.hpp index 96125651d..c8e8f8e9c 100644 --- a/src/include/homestore/index/wb_cache_base.hpp +++ b/src/include/homestore/index/wb_cache_base.hpp @@ -25,12 +25,13 @@ namespace homestore { class BtreeNode; +class Chunk; +struct CPContext; + using BtreeNodePtr = boost::intrusive_ptr< BtreeNode >; using BtreeNodeList = boost::container::small_vector< BtreeNodePtr, 3 >; using node_initializer_t = std::function< BtreeNodePtr(const IndexBufferPtr&) >; -struct CPContext; - class IndexWBCacheBase { public: virtual ~IndexWBCacheBase() = default; @@ -67,6 +68,9 @@ class IndexWBCacheBase { /// @return // virtual IndexBufferPtr copy_buffer(const IndexBufferPtr& cur_buf, const CPContext* context) const = 0; virtual void recover(sisl::byte_view sb) = 0; + + /// Remove entries for a specific chunk from wbc + virtual void evict_chunk_blkids(const Chunk& chunk) = 0; }; } // namespace homestore diff --git a/src/lib/device/chunk.cpp b/src/lib/device/chunk.cpp index 0e94a1f05..8e366224b 100644 --- a/src/lib/device/chunk.cpp +++ b/src/lib/device/chunk.cpp @@ -84,4 +84,17 @@ void Chunk::reset_block_allocator() { vdev_ptr->reset_chunk_blk_allocator(this); } +void Chunk::foreach_allocated_blk(std::function< void(blk_id const&) >&& cb) const { + auto* ba = blk_allocator(); + RELEASE_ASSERT(ba, "Blk allocator is null for chunk_id={}", chunk_id()); + + const auto total_blks = ba->get_total_blks(); + const auto cid = chunk_id(); + + for (blk_num_t blk_num{}; blk_num < total_blks; ++blk_num) { + blk_id blkid{blk_num, 1, cid}; + if (ba->is_blk_alloced(blkid, false)) { cb(blkid); } + } +} + } // namespace homestore diff --git a/src/lib/device/chunk.h b/src/lib/device/chunk.h index 67935e159..95b4e9dd4 100644 --- a/src/lib/device/chunk.h +++ b/src/lib/device/chunk.h @@ -16,8 +16,11 @@ #include "device/physical_dev.hpp" namespace homestore { + class BlkAllocator; class CP; +struct blk_id; + class Chunk { private: std::shared_mutex m_mgmt_mutex; @@ -74,6 +77,8 @@ class Chunk { float get_blk_usage_report_threshold() const { return blk_usage_report_threshold; } float get_blk_usage() const; + void foreach_allocated_blk(std::function< void(blk_id const&) >&& cb) const; + ////////////// Setters ///////////////////// void set_user_private(const sisl::blob& data); void set_block_allocator(cshared< BlkAllocator >& blkalloc) { diff --git a/src/lib/index/wb_cache.cpp b/src/lib/index/wb_cache.cpp index fe7942e82..b30d9333d 100644 --- a/src/lib/index/wb_cache.cpp +++ b/src/lib/index/wb_cache.cpp @@ -52,8 +52,13 @@ IndexWBCache::IndexWBCache(const std::shared_ptr< VirtualDev >& vdev, std::pair< return static_cast< IndexBtreeNode* >(node.get())->m_idx_buf->m_blkid; }, [](const sisl::CacheRecord& rec) -> bool { - const auto& hnode = (sisl::SingleEntryHashNode< BtreeNodePtr >&)rec; - return static_cast< IndexBtreeNode* >(hnode.m_value.get())->m_idx_buf->is_clean(); + const auto& hnode = static_cast< const sisl::SingleEntryHashNode< BtreeNodePtr >& >(rec); + const auto* idx_node = static_cast< const IndexBtreeNode* >(hnode.m_value.get()); + HS_REL_ASSERT(idx_node && idx_node->m_idx_buf, + "Cache evictor: invalid cache entry: idx_node={} m_idx_buf={}", + static_cast< const void* >(idx_node), + idx_node ? static_cast< const void* >(idx_node->m_idx_buf.get()) : nullptr); + return idx_node->m_idx_buf->is_clean(); }}, m_node_size{node_size}, m_meta_blk{sb.first} { @@ -1214,6 +1219,13 @@ void IndexWBCache::get_next_bufs_internal(IndexCPContext* cp_ctx, uint32_t max_c } } +void IndexWBCache::evict_chunk_blkids(const Chunk& chunk) { + chunk.foreach_allocated_blk([this](blk_id const& blkid) { + BtreeNodePtr node; + (void)m_cache.remove(blkid, node); + }); +} + /* IndexBtreeNode* IndexBtreeNode::convert(BtreeNode* bt_node) { return r_cast< IndexBtreeNode* >(bt_node->get_node_context()); diff --git a/src/lib/index/wb_cache.hpp b/src/lib/index/wb_cache.hpp index c3cc58391..9a2fe7344 100644 --- a/src/lib/index/wb_cache.hpp +++ b/src/lib/index/wb_cache.hpp @@ -57,6 +57,7 @@ class IndexWBCache : public IndexWBCacheBase { CPContext* cp_ctx) override; void free_buf(const IndexBufferPtr& buf, CPContext* cp_ctx) override; bool refresh_meta_buf(shared< MetaIndexBuffer >& meta_buf, CPContext* cp_ctx) override; + void evict_chunk_blkids(const Chunk& chunk) override; //////////////////// CP Related API section ///////////////////////////////// sisl::async::task< bool > async_cp_flush(IndexCPContext* context); diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 495cb86bb..3df43de65 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -86,6 +86,12 @@ if (${io_tests}) add_test(NAME IndexCrashRecovery COMMAND test_index_crash_recovery) set_property(TEST IndexCrashRecovery PROPERTY ENVIRONMENT "ASAN_OPTIONS=detect_stack_use_after_return=true") + set(TEST_RECOVERY_INDEX_SOURCE_FILES test_index_chunk_selector.cpp) + add_executable(test_index_chunk_selector ${TEST_RECOVERY_INDEX_SOURCE_FILES}) + target_link_libraries(test_index_chunk_selector homestore ${COMMON_TEST_DEPS} GTest::gtest) + add_test(NAME IndexChunkSelector COMMAND test_index_chunk_selector) + set_property(TEST IndexChunkSelector PROPERTY ENVIRONMENT "ASAN_OPTIONS=detect_stack_use_after_return=true") + add_executable(test_data_service) target_sources(test_data_service PRIVATE test_data_service.cpp) target_link_libraries(test_data_service homestore ${COMMON_TEST_DEPS} GTest::gmock) @@ -167,3 +173,6 @@ if (${non_coverage_build}) target_sources(index_btree_benchmark PRIVATE index_btree_benchmark.cpp) target_link_libraries(index_btree_benchmark homestore ${COMMON_TEST_DEPS} benchmark::benchmark) endif() + +target_compile_definitions(test_index_chunk_selector PRIVATE _PRERELEASE) + diff --git a/src/tests/btree_helpers/btree_test_helper.hpp b/src/tests/btree_helpers/btree_test_helper.hpp index 1ee8fbce0..7c5bfd26f 100644 --- a/src/tests/btree_helpers/btree_test_helper.hpp +++ b/src/tests/btree_helpers/btree_test_helper.hpp @@ -94,7 +94,7 @@ struct BtreeTestHelper { flip::FlipFrequency freq; freq.set_count(10000); freq.set_percent(100); - m_fc.inject_noreturn_flip(flip_name, {null_cond}, freq); + m_fc.inject_noreturn_flip(flip_name, std::array< flip::FlipCondition, 1 >{null_cond}, freq); m_bt->set_flip_point(flip_name); LOGINFO("Flip {} set", flip_name); } diff --git a/src/tests/test_common/homestore_test_common.hpp b/src/tests/test_common/homestore_test_common.hpp index 444bc857a..9a14b7c29 100644 --- a/src/tests/test_common/homestore_test_common.hpp +++ b/src/tests/test_common/homestore_test_common.hpp @@ -243,7 +243,8 @@ class HSTestHelper { flip::FlipCondition dont_care_cond; fc->create_condition("", flip::Operator::DONT_CARE, (int)1, &dont_care_cond); - fc->inject_retval_flip< long >("set_minimum_chunk_size", {dont_care_cond}, freq, chunk_size); + fc->inject_retval_flip< long >("set_minimum_chunk_size", std::array< flip::FlipCondition, 1 >{dont_care_cond}, + freq, chunk_size); #endif } @@ -253,7 +254,7 @@ class HSTestHelper { flip::FlipFrequency freq; freq.set_count(count); freq.set_percent(percent); - m_fc.inject_noreturn_flip(flip_name, {null_cond}, freq); + m_fc.inject_noreturn_flip(flip_name, std::array< flip::FlipCondition, 1 >{null_cond}, freq); LOGDEBUG("Flip {} set", flip_name); } @@ -262,7 +263,7 @@ class HSTestHelper { flip::FlipFrequency freq; freq.set_count(count); freq.set_percent(percent); - m_fc.inject_delay_flip(flip_name, {null_cond}, freq, delay_usec); + m_fc.inject_delay_flip(flip_name, std::array< flip::FlipCondition, 1 >{null_cond}, freq, delay_usec); LOGDEBUG("Flip {} set", flip_name); } diff --git a/src/tests/test_index_chunk_selector.cpp b/src/tests/test_index_chunk_selector.cpp new file mode 100644 index 000000000..47ce72859 --- /dev/null +++ b/src/tests/test_index_chunk_selector.cpp @@ -0,0 +1,333 @@ +#include +#include + +#include "device/device.h" +#include "device/chunk.h" +#include "common/homestore_config.hpp" +#include "common/resource_mgr.hpp" +#include "test_common/homestore_test_common.hpp" +#include "btree_helpers/btree_test_helper.hpp" +#include "btree_helpers/btree_test_kvs.hpp" +#include "btree_helpers/btree_decls.h" +#include "homestore/chunk_selector.hpp" +#include "blkalloc/blk_allocator.h" + +using namespace homestore; + +SISL_OPTIONS_ENABLE(logging, test_index_chunk_selector, iomgr, test_common_setup) + +SISL_OPTION_GROUP( + test_index_chunk_selector, + (num_iters, "", "num_iters", "number of iterations for rand ops", + ::cxxopts::value< uint32_t >()->default_value("500"), "number"), + (num_entries, "", "num_entries", "number of entries to test with", + ::cxxopts::value< uint32_t >()->default_value("5000"), "number"), + (run_time, "", "run_time", "run time for io", ::cxxopts::value< uint32_t >()->default_value("360000"), "seconds"), + (num_rounds, "", "num_rounds", "number of rounds to test with", + ::cxxopts::value< uint32_t >()->default_value("100"), "number"), + (num_entries_per_rounds, "", "num_entries_per_rounds", "number of entries per rounds", + ::cxxopts::value< uint32_t >()->default_value("40"), "number"), + (max_keys_in_node, "", "max_keys_in_node", "max_keys_in_node", ::cxxopts::value< uint32_t >()->default_value("20"), + ""), + (min_keys_in_node, "", "min_keys_in_node", "min_keys_in_node", ::cxxopts::value< uint32_t >()->default_value("6"), + ""), + (max_merge_level, "", "max_merge_level", "max merge level", ::cxxopts::value< uint8_t >()->default_value("1"), ""), + (disable_merge, "", "disable_merge", "disable_merge", ::cxxopts::value< bool >()->default_value("0"), ""), + (operation_list, "", "operation_list", "operation list instead of default created following by percentage", + ::cxxopts::value< std::vector< std::string > >(), "operations [...]"), + (preload_size, "", "preload_size", "number of entries to preload tree with", + ::cxxopts::value< uint32_t >()->default_value("1000"), "number"), + (init_device, "", "init_device", "init device", ::cxxopts::value< bool >()->default_value("1"), ""), + (load_from_file, "", "load_from_file", "load from file", ::cxxopts::value< bool >()->default_value("0"), ""), + (save_to_file, "", "save_to_file", "save to file", ::cxxopts::value< bool >()->default_value("0"), ""), + (cleanup_after_shutdown, "", "cleanup_after_shutdown", "cleanup after shutdown", + ::cxxopts::value< bool >()->default_value("1"), ""), + (print_keys_verbose_logging, "", "print_keys_verbose_logging", "print_keys_verbose_logging", + ::cxxopts::value< bool >()->default_value("0"), ""), + (seed, "", "seed", "random engine seed, use random if not defined", + ::cxxopts::value< uint64_t >()->default_value("0"), "number")) + +class SameChunkSelector : public ChunkSelector { +public: + void add_chunk(cshared< Chunk >& chunk) override { + if (!m_fixed_chunk) { m_fixed_chunk = chunk; } + m_chunks.push_back(chunk); + } + + void foreach_chunks(std::function< void(cshared< Chunk >&) >&& cb) override { + for (auto& c : m_chunks) { + cb(c); + } + } + + cshared< Chunk > select_chunk(blk_count_t, const blk_alloc_hints&) override { + HS_REL_ASSERT(m_fixed_chunk != nullptr, "SameChunkSelector: no chunk was added before select_chunk"); + return m_fixed_chunk; + } + +private: + shared< Chunk > m_fixed_chunk; + std::vector< shared< Chunk > > m_chunks; +}; + +struct DeterministicChunkSelectorWbcReuseTest : public test_common::HSTestHelper, + BtreeTestHelper< FixedLenBtree >, + public ::testing::Test { + using T = FixedLenBtree; + using K = typename T::KeyType; + using V = typename T::ValueType; + using BaseBt = typename T::BtreeType; + + std::shared_ptr< SameChunkSelector > m_same_chunk_selector; + + class InspectableBtree : public BaseBt { + public: + using BaseBt::BaseBt; + + std::set< bnodeid_t > collect_node_ids() const { + std::set< bnodeid_t > ids; + collect_node_ids_recurse(this->root_node_id(), ids); + return ids; + } + + void warm_all_nodes_into_wbc() { + auto ids = collect_node_ids(); + for (auto const node_id : ids) { + BtreeNodePtr node; + auto ret = this->read_node_impl(node_id, node); + ASSERT_EQ(ret, btree_status_t::success) << "failed to read node " << blk_id{node_id}.to_string(); + } + } + + BtreeNodePtr read_node_for_test(bnodeid_t node_id) { + BtreeNodePtr node; + auto ret = this->read_node_impl(node_id, node); + EXPECT_EQ(ret, btree_status_t::success); + return node; + } + + private: + void collect_node_ids_recurse(bnodeid_t node_id, std::set< bnodeid_t >& ids) const { + if ((node_id == empty_bnodeid) || ids.contains(node_id)) { return; } + + BtreeNodePtr node; + if ((this->read_node_impl(node_id, node) != btree_status_t::success) || node->is_node_deleted()) { return; } + + ids.insert(node_id); + + if (node->is_leaf()) { return; } + + for (uint32_t i = 0; i < node->total_entries(); ++i) { + BtreeLinkInfo child_info; + node->get_nth_value(i, &child_info, false /* copy */); + collect_node_ids_recurse(child_info.bnode_id(), ids); + } + + if (node->has_valid_edge()) { collect_node_ids_recurse(node->get_edge_value().bnode_id(), ids); } + } + }; + + class TestIndexServiceCallbacks : public IndexServiceCallbacks { + public: + explicit TestIndexServiceCallbacks(DeterministicChunkSelectorWbcReuseTest* test) : m_test(test) {} + + std::shared_ptr< IndexTableBase > on_index_table_found(superblk< index_table_sb >&& sb) override { + m_test->init_cfg(); + auto bt = std::make_shared< InspectableBtree >(std::move(sb), m_test->m_cfg); + m_test->m_last_recovered_bt = bt; + return bt; + } + + private: + DeterministicChunkSelectorWbcReuseTest* m_test; + }; + + DeterministicChunkSelectorWbcReuseTest() : ::testing::Test() { this->m_is_multi_threaded = false; } + + void init_cfg() { + this->m_cfg = BtreeConfig(hs()->index_service().node_size()); + this->m_cfg.m_leaf_node_type = T::leaf_node_type; + this->m_cfg.m_int_node_type = T::interior_node_type; + this->m_cfg.m_max_keys_in_node = 5; + this->m_cfg.m_min_keys_in_node = 2; + } + + void SetUp() override { + HS_SETTINGS_FACTORY().modifiable_settings([](auto& s) { + s.generic.cache_max_throttle_cnt = 10000; + s.generic.cp_timer_us = 0x8000000000000000; + s.resource_limits.dirty_buf_percent = 100; + HS_SETTINGS_FACTORY().save(); + }); + + m_same_chunk_selector = std::make_shared< SameChunkSelector >(); + + this->start_homestore("test_index_crash_recovery", + {{HS_SERVICE::META, {.size_pct = 10.0}}, + {HS_SERVICE::INDEX, + {.size_pct = 10.0, + .index_chunk_selector = m_same_chunk_selector, + .index_svc_cbs = new TestIndexServiceCallbacks(this)}}}, + nullptr, {}, true /* init_device */); + + BtreeTestHelper< FixedLenBtree >::SetUp(); + init_cfg(); + + ASSERT_NE(hs()->index_service().get_chunk_selector(), nullptr) + << "IndexService chunk selector was not installed"; + } + + void restart_homestore(uint32_t shutdown_delay_sec = 3) override { + this->params(HS_SERVICE::INDEX).index_svc_cbs = new TestIndexServiceCallbacks(this); + this->params(HS_SERVICE::INDEX).index_chunk_selector = m_same_chunk_selector; + test_common::HSTestHelper::restart_homestore(shutdown_delay_sec); + } + + void TearDown() override { + BtreeTestHelper< FixedLenBtree >::TearDown(); + this->shutdown_homestore(false); + } + + std::shared_ptr< InspectableBtree > create_btree() { + auto uuid = boost::uuids::random_generator()(); + auto parent_uuid = boost::uuids::random_generator()(); + auto bt = std::make_shared< InspectableBtree >(uuid, parent_uuid, 0, this->m_cfg); + hs()->index_service().add_index_table(bt); + return bt; + } + + void destroy_btree(std::shared_ptr< InspectableBtree >& bt) { + hs()->index_service().remove_index_table(bt); + ASSERT_EQ(homestore::detail::sync_get(bt->destroy()), btree_status_t::success); + bt.reset(); + } + + btree_status_t insert_key(std::shared_ptr< InspectableBtree >& bt, uint32_t key_num) { + K key{key_num}; + V value{V::generate_rand()}; + auto req = BtreeSinglePutRequest{&key, &value, btree_put_type::INSERT}; + req.enable_route_tracing(); + return bt->put(req); + } + + btree_status_t insert_range(std::shared_ptr< InspectableBtree >& bt, uint32_t begin, uint32_t end) { + for (uint32_t k = begin; k < end; ++k) { + auto ret = insert_key(bt, k); + if (ret != btree_status_t::success) { + LOGINFO("insert_range failed at key={} ret={}", k, enum_name(ret)); + return ret; + } + } + return btree_status_t::success; + } + + uint32_t assert_all_nodes_on_same_chunk(std::shared_ptr< InspectableBtree >& bt) { + auto ids = bt->collect_node_ids(); + HS_DBG_ASSERT_GT(ids.size(), 1u, "expected more than one node"); + + auto it = ids.begin(); + auto expected_chunk = blk_id{*it}.chunk_num(); + + for (auto const id : ids) { + auto const chunk_num = blk_id{id}.chunk_num(); + DEBUG_ASSERT_EQ(chunk_num, expected_chunk, "node {} landed on chunk {} but expected chunk {}", + blk_id{id}.to_string(), chunk_num, expected_chunk); + } + + return expected_chunk; + } + + void clear_chunk_bitmap(chunk_num_t chunk_num) { + Chunk* chunk = hs()->device_mgr()->get_chunk_mutable(chunk_num); + ASSERT_NE(chunk, nullptr) << "chunk " << chunk_num << " not found"; + + // Zero the alloc bitmap so the next btree reuses bt1 blkids + chunk->reset_block_allocator(); + } + + void evict_wbc_and_clear_chunk_async(chunk_num_t chunk_num) { + Chunk* chunk = hs()->device_mgr()->get_chunk_mutable(chunk_num); + ASSERT_NE(chunk, nullptr) << "chunk " << chunk_num << " not found"; + + auto done = std::make_shared< std::promise< void > >(); + auto fut = done->get_future(); + + iomanager.run_on_forget(iomgr::reactor_regex::random_worker, [chunk, done]() { + hs()->index_service().wb_cache().evict_chunk_blkids(*chunk); + chunk->reset_block_allocator(); + done->set_value(); + }); + + fut.wait(); + } + + std::shared_ptr< InspectableBtree > m_last_recovered_bt; +}; + +#ifdef _PRERELEASE + +TEST_F(DeterministicChunkSelectorWbcReuseTest, DestroyedBtreeStaleWbcEntriesCorruptNextBtreeOnSameChunk) { + // Create and populate bt1 + auto bt1 = create_btree(); + insert_range(bt1, 0, 256); + + // If this is enabled, we get a SegFault on do_flush_one_buf() + // test_common::HSTestHelper::trigger_cp(true); + + auto const bt1_node_ids = bt1->collect_node_ids(); + ASSERT_FALSE(bt1_node_ids.empty()); + + auto const bt1_chunk = assert_all_nodes_on_same_chunk(bt1); + + // Force bt1 nodes into wbc + bt1->warm_all_nodes_into_wbc(); + + // Destroy bt1 and leave stale entries in wbc + destroy_btree(bt1); + + Chunk* chunk = hs()->device_mgr()->get_chunk_mutable(bt1_chunk); + auto blk_alloc = chunk->blk_allocator(); + + // Allocator-state validation: blocks should be used + EXPECT_NE(blk_alloc->get_used_blks(), 0u); + EXPECT_NE(blk_alloc->available_blks(), blk_alloc->get_total_blks()); + + // Evict stale WBC entries for that chunk, then clear allocator state + evict_wbc_and_clear_chunk_async(bt1_chunk); + + // Allocator-state validation: everything should look free again + chunk = hs()->device_mgr()->get_chunk_mutable(bt1_chunk); + blk_alloc = chunk->blk_allocator(); + EXPECT_EQ(blk_alloc->get_used_blks(), 0u); + EXPECT_EQ(blk_alloc->available_blks(), blk_alloc->get_total_blks()); + + // Create bt2 on the same chunk with stale WBC entries still present; + // Duplicate insert should happen here on root creation + auto bt2 = create_btree(); + + ASSERT_EQ(blk_id{bt2->root_node_id()}.chunk_num(), bt1_chunk) + << "bt2 root did not land on the expected reused chunk"; + + ASSERT_TRUE(bt1_node_ids.contains(bt2->root_node_id())) + << "bt2 root did not immediately reuse a bt1 blkid; setup is not deterministic"; + + // The tree must remain usable + auto ret = insert_range(bt2, 100000, 100128); + ASSERT_EQ(ret, btree_status_t::success); +} + +#endif + +int main(int argc, char* argv[]) { + int parsed_argc{argc}; + ::testing::InitGoogleTest(&parsed_argc, argv); + SISL_OPTIONS_LOAD(parsed_argc, argv, logging, test_index_chunk_selector, iomgr, test_common_setup); + spdlog::set_pattern("[%D %T%z] [%^%L%$] [%t] %v"); + g_re.seed(std::chrono::system_clock::now().time_since_epoch().count()); + +#ifdef _PRERELEASE + return RUN_ALL_TESTS(); +#else + return 0; +#endif +} \ No newline at end of file