From 91b264a70b3eb399ea3b6ea93f5cc3f05cd51ba8 Mon Sep 17 00:00:00 2001 From: Yaming Kuang <1477567+yamingk@users.noreply.github.com> Date: Mon, 11 Sep 2023 15:32:17 -0700 Subject: [PATCH] SDSTOR-11237 SDSTOR-11255 AppendBlkAllocator and its checkpointing (#144) * SDSTOR-11237 AppendBlkAllocator initial commit * add data svc cp_flush * add test cases for read/write/cpflush --- conanfile.py | 2 +- src/include/homestore/blkdata_service.hpp | 8 +- src/include/homestore/homestore_decl.hpp | 12 +- src/include/homestore/superblk_handler.hpp | 4 +- src/lib/blkalloc/CMakeLists.txt | 3 +- src/lib/blkalloc/append_blk_allocator.cpp | 192 ++++++++++++ src/lib/blkalloc/append_blk_allocator.h | 112 +++++++ src/lib/blkalloc/blk_allocator.cpp | 6 +- src/lib/blkalloc/blk_allocator.h | 4 + src/lib/blkalloc/varsize_blk_allocator.h | 14 +- src/lib/blkdata_svc/CMakeLists.txt | 1 + src/lib/blkdata_svc/blkdata_service.cpp | 17 +- src/lib/blkdata_svc/data_svc_cp.cpp | 43 +++ src/lib/blkdata_svc/data_svc_cp.hpp | 38 +++ src/lib/device/chunk.cpp | 5 +- src/lib/device/chunk.h | 6 +- src/lib/device/device.h | 20 +- src/lib/device/device_manager.cpp | 4 +- src/lib/device/hs_super_blk.h | 2 +- src/lib/device/journal_vdev.cpp | 3 +- src/lib/device/physical_dev.hpp | 13 +- src/lib/device/round_robin_chunk_selector.cpp | 2 +- src/lib/device/virtual_dev.cpp | 56 +++- src/lib/device/virtual_dev.hpp | 25 +- src/lib/homestore.cpp | 7 +- src/lib/index/index_cp.cpp | 2 +- src/lib/index/index_service.cpp | 6 +- src/lib/index/wb_cache.cpp | 2 +- src/lib/logstore/log_store_service.cpp | 5 + src/lib/meta/meta_blk_service.cpp | 6 +- src/tests/CMakeLists.txt | 4 + src/tests/log_store_benchmark.cpp | 2 + src/tests/test_append_blkalloc.cpp | 288 ++++++++++++++++++ .../test_common/homestore_test_common.hpp | 78 ++++- src/tests/test_cp_mgr.cpp | 2 + src/tests/test_data_service.cpp | 82 ++--- src/tests/test_device_manager.cpp | 10 +- src/tests/test_index_btree.cpp | 32 +- src/tests/test_journal_vdev.cpp | 3 +- src/tests/test_log_store.cpp | 3 +- src/tests/test_meta_blk_mgr.cpp | 5 +- 41 files changed, 977 insertions(+), 152 deletions(-) create mode 100644 src/lib/blkalloc/append_blk_allocator.cpp create mode 100644 src/lib/blkalloc/append_blk_allocator.h create mode 100644 src/lib/blkdata_svc/data_svc_cp.cpp create mode 100644 src/lib/blkdata_svc/data_svc_cp.hpp create mode 100644 src/tests/test_append_blkalloc.cpp diff --git a/conanfile.py b/conanfile.py index 2d7b42dd9..b8f0e1ada 100644 --- a/conanfile.py +++ b/conanfile.py @@ -5,7 +5,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "4.2.0" + version = "4.2.1" homepage = "https://github.com/eBay/Homestore" description = "HomeStore Storage Engine" diff --git a/src/include/homestore/blkdata_service.hpp b/src/include/homestore/blkdata_service.hpp index 305a6f5ea..d82eaf7dd 100644 --- a/src/include/homestore/blkdata_service.hpp +++ b/src/include/homestore/blkdata_service.hpp @@ -48,7 +48,8 @@ class BlkDataService { * * @param size : size of this vdev */ - void create_vdev(uint64_t size); + void create_vdev(uint64_t size, homestore::blk_allocator_type_t alloc_type, + homestore::chunk_selector_type_t chunk_sel_type); /** * @brief : called during recovery to open existing vdev for data service @@ -131,6 +132,11 @@ class BlkDataService { */ BlkReadTracker* read_blk_tracker() { return m_blk_read_tracker.get(); } + /** + * @brief : start data service; + */ + void start(); + private: BlkAllocStatus alloc_blks(uint32_t size, const blk_alloc_hints& hints, std::vector< BlkId >& out_blkids); diff --git a/src/include/homestore/homestore_decl.hpp b/src/include/homestore/homestore_decl.hpp index de88d5df4..8dd859556 100644 --- a/src/include/homestore/homestore_decl.hpp +++ b/src/include/homestore/homestore_decl.hpp @@ -89,9 +89,11 @@ ENUM(io_flag, uint8_t, DIRECT_IO, // recommended mode READ_ONLY // Read-only mode for post-mortem checks ); -ENUM(blk_allocator_type_t, uint8_t, none, fixed, varsize); +ENUM(blk_allocator_type_t, uint8_t, none, fixed, varsize, append); ENUM(chunk_selector_type_t, uint8_t, // What are the options to select chunk to allocate a block + NONE, // Caller want nothing to be set ROUND_ROBIN, // Pick round robin + HEAP, // Heap chunk selector CUSTOM, // Controlled by the upper layer RANDOM, // Pick any chunk in uniformly random fashion MOST_AVAILABLE_SPACE, // Pick the most available space @@ -165,6 +167,8 @@ struct HS_SERVICE { struct hs_format_params { float size_pct; uint32_t num_chunks{1}; + blk_allocator_type_t alloc_type{blk_allocator_type_t::varsize}; + chunk_selector_type_t chunk_sel_type{chunk_selector_type_t::ROUND_ROBIN}; }; struct hs_input_params { @@ -212,8 +216,8 @@ struct cap_attrs { #endif ////////////// Misc /////////////////// -#define HOMESTORE_LOG_MODS \ - btree_structures, btree_nodes, btree_generics, btree, cache, device, blkalloc, vol_io_wd, volume, flip, cp, metablk, \ - indx_mgr, wbcache, logstore, replay, transient, IOMGR_LOG_MODS +#define HOMESTORE_LOG_MODS \ + btree_structures, btree_nodes, btree_generics, btree, cache, device, blkalloc, vol_io_wd, volume, flip, cp, \ + metablk, indx_mgr, wbcache, logstore, replay, transient, IOMGR_LOG_MODS } // namespace homestore diff --git a/src/include/homestore/superblk_handler.hpp b/src/include/homestore/superblk_handler.hpp index 7676af5da..d0c234dd9 100644 --- a/src/include/homestore/superblk_handler.hpp +++ b/src/include/homestore/superblk_handler.hpp @@ -30,7 +30,9 @@ class superblk { return ++s_count; } - superblk(const std::string& meta_name = "") { + superblk(const std::string& meta_name = "") { set_name(meta_name); } + + void set_name(const std::string& meta_name) { if (meta_name.empty()) { m_metablk_name = "meta_blk_" + std::to_string(next_count()); } else { diff --git a/src/lib/blkalloc/CMakeLists.txt b/src/lib/blkalloc/CMakeLists.txt index a5b9b41bb..54137942f 100644 --- a/src/lib/blkalloc/CMakeLists.txt +++ b/src/lib/blkalloc/CMakeLists.txt @@ -10,7 +10,8 @@ target_sources(hs_blkalloc PRIVATE fixed_blk_allocator.cpp varsize_blk_allocator.cpp blk_cache_queue.cpp + append_blk_allocator.cpp #blkalloc_cp.cpp ) target_link_libraries(hs_blkalloc ${COMMON_DEPS}) -add_dependencies(hs_blkalloc hs_common) \ No newline at end of file +add_dependencies(hs_blkalloc hs_common) diff --git a/src/lib/blkalloc/append_blk_allocator.cpp b/src/lib/blkalloc/append_blk_allocator.cpp new file mode 100644 index 000000000..f74ed8956 --- /dev/null +++ b/src/lib/blkalloc/append_blk_allocator.cpp @@ -0,0 +1,192 @@ +/********************************************************************************* + * Modifications Copyright 2017-2019 eBay Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * * + * *********************************************************************************/ +#include +#include + +#include "append_blk_allocator.h" +#include "checkpoint/cp.hpp" + +namespace homestore { + +AppendBlkAllocator::AppendBlkAllocator(const BlkAllocConfig& cfg, bool need_format, chunk_num_t id) : + BlkAllocator{cfg, id}, m_metrics{get_name().c_str()} { + // TODO: try to make all append_blk_allocator instances use same client type to reduce metablk's cache footprint; + meta_service().register_handler( + get_name(), + [this](meta_blk* mblk, sisl::byte_view buf, size_t size) { on_meta_blk_found(std::move(buf), (void*)mblk); }, + nullptr); + + if (need_format) { + m_freeable_nblks = available_blks(); + m_last_append_offset = 0; + + for (uint8_t i = 0; i < m_sb.size(); ++i) { + m_sb[i].set_name(get_name()); + m_sb[i].create(sizeof(append_blkalloc_ctx)); + m_sb[i]->is_dirty = false; + m_sb[i]->allocator_id = id; + m_sb[i]->last_append_offset = 0; + m_sb[i]->freeable_nblks = m_freeable_nblks; + } + } + + // for recovery boot, fields should be recovered from metablks; +} + +void AppendBlkAllocator::on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie) { + // TODO: also needs to initialize base class blkallocator for recovery path; + // load all dirty buffer from the same starting point; + m_sb[0].load(buf, meta_cookie); + for (uint8_t i = 1; i < m_sb.size(); ++i) { + m_sb[i].load(buf, meta_cookie); + } + + m_last_append_offset = m_sb[0]->last_append_offset; + m_freeable_nblks = m_sb[0]->freeable_nblks; + + HS_REL_ASSERT_EQ(m_sb[0]->magic, append_blkalloc_sb_magic, "Invalid AppendBlkAlloc metablk, magic mismatch"); + HS_REL_ASSERT_EQ(m_sb[0]->version, append_blkalloc_sb_version, "Invalid version of AppendBlkAllocator metablk"); +} + +// +// Every time buffer is being dirtied, it needs to be within CPGuard(). +// It garunteens either this dirty buffer is flushed in current cp or next cp as a whole; +// +// alloc a single block; +// +BlkAllocStatus AppendBlkAllocator::alloc(BlkId& bid) { + std::unique_lock lk(m_mtx); + if (available_blks() < 1) { + COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1); + LOGERROR("No space left to serve request nblks: 1, available_blks: {}", available_blks()); + return BlkAllocStatus::SPACE_FULL; + } + + bid.set(m_last_append_offset, 1, m_chunk_id); + + [[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard(); + ++m_last_append_offset; + --m_freeable_nblks; + set_dirty_offset(cur_cp->id() % MAX_CP_COUNT); + + COUNTER_INCREMENT(m_metrics, num_alloc, 1); + return BlkAllocStatus::SUCCESS; +} + +// +// For append blk allocator, the assumption is only one writer will append data on one chunk. +// If we want to change above design, we can open this api for vector allocation; +// +BlkAllocStatus AppendBlkAllocator::alloc(blk_count_t nblks, const blk_alloc_hints& hint, + std::vector< BlkId >& out_bids) { + std::unique_lock lk(m_mtx); + if (available_blks() < nblks) { + COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1); + LOGERROR("No space left to serve request nblks: {}, available_blks: {}", nblks, available_blks()); + return BlkAllocStatus::SPACE_FULL; + } else if (nblks > BlkId::max_blks_in_op()) { + // consumer(vdev) already handles this case. + COUNTER_INCREMENT(m_metrics, num_alloc_failure, 1); + LOGERROR("Can't serve request nblks: {} larger than max_blks_in_op: {}", nblks, BlkId::max_blks_in_op()); + return BlkAllocStatus::FAILED; + } + + // Push 1 blk to the vector which has all the requested nblks; + out_bids.emplace_back(m_last_append_offset, nblks, m_chunk_id); + + [[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard(); + m_last_append_offset += nblks; + m_freeable_nblks -= nblks; + + // it is garunteened dirty buffer always contains updates of current_cp or next_cp, it will + // never get dirty buffer from across updates; + set_dirty_offset(cur_cp->id() % MAX_CP_COUNT); + + COUNTER_INCREMENT(m_metrics, num_alloc, 1); + + return BlkAllocStatus::SUCCESS; +} + +// +// cp_flush doesn't need CPGuard as it is triggered by CPMgr which already handles the reference check; +// +// cp_flush should not block alloc/free; +// +void AppendBlkAllocator::cp_flush(CP* cp) { + const auto idx = cp->id(); + // check if current cp's context has dirty buffer already + if (m_sb[idx]->is_dirty) { + // write to metablk; + m_sb[cp->id()].write(); + + // clear this dirty buff's dirty flag; + clear_dirty_offset(idx); + } +} + +// updating current cp's dirty buffer context; +void AppendBlkAllocator::set_dirty_offset(const uint8_t idx) { + m_sb[idx]->is_dirty = true; + + m_sb[idx]->last_append_offset = m_last_append_offset; + m_sb[idx]->freeable_nblks = m_freeable_nblks; +} + +// clearing current cp context's dirty flag; +void AppendBlkAllocator::clear_dirty_offset(const uint8_t idx) { m_sb[idx]->is_dirty = false; } + +// +// free operation does: +// 1. book keeping "total freeable" space +// 2. if the blk being freed happens to be last block, move last_append_offset backwards accordingly; +// +void AppendBlkAllocator::free(const BlkId& bid) { + std::unique_lock lk(m_mtx); + [[maybe_unused]] auto cur_cp = hs()->cp_mgr().cp_guard(); + const auto n = bid.get_nblks(); + m_freeable_nblks += n; + if (bid.get_blk_num() + n == m_last_append_offset) { + // we are freeing the the last blk id, let's rewind. + m_last_append_offset -= n; + } + set_dirty_offset(cur_cp->id() % MAX_CP_COUNT); +} + +void AppendBlkAllocator::free(const std::vector< BlkId >& blk_ids) { + for (const auto b : blk_ids) { + this->free(b); + } +} + +blk_cap_t AppendBlkAllocator::available_blks() const { return get_total_blks() - get_used_blks(); } + +blk_cap_t AppendBlkAllocator::get_used_blks() const { return m_last_append_offset; } + +bool AppendBlkAllocator::is_blk_alloced(const BlkId& in_bid, bool) const { + // blk_num starts from 0; + return in_bid.get_blk_num() < get_used_blks(); +} + +std::string AppendBlkAllocator::get_name() const { return "AppendBlkAlloc_chunk_" + std::to_string(m_chunk_id); } + +std::string AppendBlkAllocator::to_string() const { + auto str = fmt::format("{}, last_append_offset: {}", get_name(), m_last_append_offset); + return str; +} + +blk_cap_t AppendBlkAllocator::get_freeable_nblks() const { return m_freeable_nblks; } + +} // namespace homestore diff --git a/src/lib/blkalloc/append_blk_allocator.h b/src/lib/blkalloc/append_blk_allocator.h new file mode 100644 index 000000000..ebfcdf61e --- /dev/null +++ b/src/lib/blkalloc/append_blk_allocator.h @@ -0,0 +1,112 @@ +/********************************************************************************* + * Modifications Copyright 2017-2019 eBay Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * * + * *********************************************************************************/ +#pragma once + +#include +#include "blk_allocator.h" +#include "common/homestore_assert.hpp" +#include "common/homestore_config.hpp" +#include +#include +#include +#include + +namespace homestore { +static constexpr uint64_t append_blkalloc_sb_magic{0xd0d0d02b}; +static constexpr uint64_t append_blkalloc_sb_version{0x1}; + +#pragma pack(1) +struct append_blkalloc_ctx { + uint64_t magic{append_blkalloc_sb_magic}; + uint32_t version{append_blkalloc_sb_version}; + bool is_dirty; // this field is needed for cp_flush, but not necessarily needed for persistence; + uint64_t allocator_id; + uint64_t freeable_nblks; + uint64_t last_append_offset; +}; +#pragma pack() + +class AppendBlkAllocMetrics : public sisl::MetricsGroup { +public: + explicit AppendBlkAllocMetrics(const char* inst_name) : sisl::MetricsGroup("AppendBlkAlloc", inst_name) { + REGISTER_COUNTER(num_alloc, "Number of blks alloc attempts"); + REGISTER_COUNTER(num_alloc_failure, "Number of blk alloc failures"); + + register_me_to_farm(); + } + + AppendBlkAllocMetrics(const AppendBlkAllocMetrics&) = delete; + AppendBlkAllocMetrics(AppendBlkAllocMetrics&&) noexcept = delete; + AppendBlkAllocMetrics& operator=(const AppendBlkAllocMetrics&) = delete; + AppendBlkAllocMetrics& operator=(AppendBlkAllocMetrics&&) noexcept = delete; + ~AppendBlkAllocMetrics() { deregister_me_from_farm(); } +}; + +// +// The assumption for AppendBlkAllocator: +// 1. Operations (alloc/free) are being called multiple threadeds +// 2. cp_flush will be triggered in a different thread +// +// Why do we want thread-safe AppendBlkAllocator: +// 1. one reason is it makes sense for AppendBlkAllocator to work on a nvme drive +// 2. for HDD, performance will drop significantly if alloc/write is being done in multi-threaded model, it is left for +// consumer to make choice; +// +class AppendBlkAllocator : public BlkAllocator { +public: + AppendBlkAllocator(const BlkAllocConfig& cfg, bool need_format, chunk_num_t id = 0); + + AppendBlkAllocator(const AppendBlkAllocator&) = delete; + AppendBlkAllocator(AppendBlkAllocator&&) noexcept = delete; + AppendBlkAllocator& operator=(const AppendBlkAllocator&) = delete; + AppendBlkAllocator& operator=(AppendBlkAllocator&&) noexcept = delete; + virtual ~AppendBlkAllocator() = default; + + BlkAllocStatus alloc(BlkId& bid) override; + BlkAllocStatus alloc(blk_count_t nblks, const blk_alloc_hints& hints, std::vector< BlkId >& out_blkid) override; + + void free(const std::vector< BlkId >& blk_ids) override; + void free(const BlkId& b) override; + + blk_cap_t available_blks() const override; + blk_cap_t get_used_blks() const override; + blk_cap_t get_freeable_nblks() const; + + bool is_blk_alloced(const BlkId& in_bid, bool use_lock = false) const override; + std::string to_string() const override; + + /// @brief : needs to be called with cp_guard(); + void set_dirty_offset(const uint8_t idx); + + /// @brief : clear dirty is best effort; + /// offset flush is idempotent; + void clear_dirty_offset(const uint8_t idx); + + void cp_flush(CP* cp) override; + +private: + std::string get_name() const; + void on_meta_blk_found(const sisl::byte_view& buf, void* meta_cookie); + +private: + std::mutex m_mtx; // thread_safe, TODO: open option for consumer to choose to go lockless; + uint64_t m_last_append_offset{0}; // last appended offset in blocks; + uint64_t m_freeable_nblks{0}; + AppendBlkAllocMetrics m_metrics; + std::array< superblk< append_blkalloc_ctx >, MAX_CP_COUNT > m_sb; +}; + +} // namespace homestore diff --git a/src/lib/blkalloc/blk_allocator.cpp b/src/lib/blkalloc/blk_allocator.cpp index bb959c5d2..4aa3269d0 100644 --- a/src/lib/blkalloc/blk_allocator.cpp +++ b/src/lib/blkalloc/blk_allocator.cpp @@ -14,7 +14,6 @@ * *********************************************************************************/ #include "blk_allocator.h" -// #include "blkalloc_cp.hpp" namespace homestore { BlkAllocator::BlkAllocator(const BlkAllocConfig& cfg, chunk_num_t id) : @@ -42,6 +41,11 @@ BlkAllocator::BlkAllocator(const BlkAllocConfig& cfg, chunk_num_t id) : } } +void BlkAllocator::cp_flush(CP*) { + // To be implemented; + LOGINFO("BitmapBlkAllocator cp_flush in not yet supported. "); +} + void BlkAllocator::set_disk_bm(std::unique_ptr< sisl::Bitset > recovered_bm) { BLKALLOC_LOG(INFO, "Persistent bitmap of size={} recovered", recovered_bm->size()); m_disk_bm = std::move(recovered_bm); diff --git a/src/lib/blkalloc/blk_allocator.h b/src/lib/blkalloc/blk_allocator.h index 1f676b79e..43f49842e 100644 --- a/src/lib/blkalloc/blk_allocator.h +++ b/src/lib/blkalloc/blk_allocator.h @@ -172,6 +172,7 @@ class BlkAllocPortion { * 2. It contains blks freed only upto that checkpoint. */ +class CP; class BlkAllocator { public: BlkAllocator(const BlkAllocConfig& cfg, chunk_num_t id = 0); @@ -190,6 +191,9 @@ class BlkAllocator { virtual bool is_blk_alloced(const BlkId& b, bool use_lock = false) const = 0; virtual std::string to_string() const = 0; + virtual void cp_flush(CP* cp); // TODO: it needs to be a pure virtual function after bitmap blkallocator is derived + // from base BlkAllocator; + sisl::Bitset* get_disk_bm_mutable() { set_disk_bm_dirty(); return m_disk_bm.get(); diff --git a/src/lib/blkalloc/varsize_blk_allocator.h b/src/lib/blkalloc/varsize_blk_allocator.h index 697afce89..7544fac55 100644 --- a/src/lib/blkalloc/varsize_blk_allocator.h +++ b/src/lib/blkalloc/varsize_blk_allocator.h @@ -233,17 +233,17 @@ class VarsizeBlkAllocator : public BlkAllocator { static std::queue< VarsizeBlkAllocator* > s_sweeper_queue; // Sweeper threads queue static std::unordered_set< VarsizeBlkAllocator* > s_block_allocators; // block allocators to be swept - static constexpr blk_num_t INVALID_PORTION_NUM{UINT_MAX}; // max of type blk_num_t + static constexpr blk_num_t INVALID_PORTION_NUM{UINT_MAX}; // max of type blk_num_t // per class sweeping logic - std::mutex m_mutex; // Mutex to protect regionstate & cb - std::condition_variable m_cv; // CV to signal thread - BlkAllocatorState m_state; // Current state of the blkallocator + std::mutex m_mutex; // Mutex to protect regionstate & cb + std::condition_variable m_cv; // CV to signal thread + BlkAllocatorState m_state; // Current state of the blkallocator - std::unique_ptr< sisl::Bitset > m_cache_bm; // Bitset representing entire blks in this allocator - std::unique_ptr< FreeBlkCache > m_fb_cache; // Free Blks cache + std::unique_ptr< sisl::Bitset > m_cache_bm; // Bitset representing entire blks in this allocator + std::unique_ptr< FreeBlkCache > m_fb_cache; // Free Blks cache - VarsizeBlkAllocConfig m_cfg; // Config for Varsize + VarsizeBlkAllocConfig m_cfg; // Config for Varsize std::vector< std::unique_ptr< BlkAllocSegment > > m_segments; // Lookup map for segment id - segment diff --git a/src/lib/blkdata_svc/CMakeLists.txt b/src/lib/blkdata_svc/CMakeLists.txt index e23de951f..3448cca0b 100644 --- a/src/lib/blkdata_svc/CMakeLists.txt +++ b/src/lib/blkdata_svc/CMakeLists.txt @@ -7,5 +7,6 @@ add_library(hs_datasvc OBJECT) target_sources(hs_datasvc PRIVATE blkdata_service.cpp blk_read_tracker.cpp + data_svc_cp.cpp ) target_link_libraries(hs_datasvc ${COMMON_DEPS}) diff --git a/src/lib/blkdata_svc/blkdata_service.cpp b/src/lib/blkdata_svc/blkdata_service.cpp index 45eb7fef5..da8d23941 100644 --- a/src/lib/blkdata_svc/blkdata_service.cpp +++ b/src/lib/blkdata_svc/blkdata_service.cpp @@ -22,6 +22,7 @@ #include "common/homestore_assert.hpp" #include "common/error.h" #include "blk_read_tracker.hpp" +#include "data_svc_cp.hpp" namespace homestore { @@ -31,7 +32,7 @@ BlkDataService::BlkDataService() { m_blk_read_tracker = std::make_unique< BlkRea BlkDataService::~BlkDataService() = default; // first-time boot path -void BlkDataService::create_vdev(uint64_t size) { +void BlkDataService::create_vdev(uint64_t size, blk_allocator_type_t alloc_type, chunk_selector_type_t chunk_sel_type) { const auto phys_page_size = hs()->device_mgr()->optimal_page_size(HSDevType::Data); hs_vdev_context vdev_ctx; @@ -43,14 +44,15 @@ void BlkDataService::create_vdev(uint64_t size) { .num_chunks = 1, .blk_size = phys_page_size, .dev_type = HSDevType::Data, + .alloc_type = alloc_type, + .chunk_sel_type = chunk_sel_type, .multi_pdev_opts = vdev_multi_pdev_opts_t::ALL_PDEV_STRIPED, .context_data = vdev_ctx.to_blob()}); } -// recovery path +// both first_time_boot and recovery path will come here shared< VirtualDev > BlkDataService::open_vdev(const vdev_info& vinfo, bool load_existing) { - m_vdev = std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, blk_allocator_type_t::varsize, - chunk_selector_type_t::ROUND_ROBIN, nullptr, true /* auto_recovery */); + m_vdev = std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, nullptr, true /* auto_recovery */); m_page_size = vinfo.blk_size; return m_vdev; } @@ -135,4 +137,11 @@ folly::Future< bool > BlkDataService::async_free_blk(const BlkId bid) { }); return f; } + +void BlkDataService::start() { + // Register to CP for flush dirty buffers underlying virtual device layer; + hs()->cp_mgr().register_consumer(cp_consumer_t::BLK_DATA_SVC, + std::move(std::make_unique< DataSvcCPCallbacks >(m_vdev))); +} + } // namespace homestore diff --git a/src/lib/blkdata_svc/data_svc_cp.cpp b/src/lib/blkdata_svc/data_svc_cp.cpp new file mode 100644 index 000000000..b76a4dc85 --- /dev/null +++ b/src/lib/blkdata_svc/data_svc_cp.cpp @@ -0,0 +1,43 @@ +/********************************************************************************* + * Modifications Copyright 2017-2019 eBay Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + *********************************************************************************/ +#include +#include "data_svc_cp.hpp" +#include "device/virtual_dev.hpp" + +namespace homestore { + +DataSvcCPCallbacks::DataSvcCPCallbacks(shared< VirtualDev > vdev) : m_vdev{vdev} {} + +std::unique_ptr< CPContext > DataSvcCPCallbacks::on_switchover_cp(CP* cur_cp, CP* new_cp) { + return m_vdev->create_cp_context(new_cp->id()); +} + +folly::Future< bool > DataSvcCPCallbacks::cp_flush(CP* cp) { + // Pick a CP Manager blocking IO fiber to execute the cp flush of vdev + // iomanager.run_on_forget(hs()->cp_mgr().pick_blocking_io_fiber(), [this, cp]() { + auto cp_ctx = s_cast< VDevCPContext* >(cp->context(cp_consumer_t::BLK_DATA_SVC)); + m_vdev->cp_flush(cp); // this is a blocking io call + cp_ctx->complete(true); + //}); + + return folly::makeFuture< bool >(true); +} + +void DataSvcCPCallbacks::cp_cleanup(CP* cp) { m_vdev->cp_cleanup(cp); } + +int DataSvcCPCallbacks::cp_progress_percent() { return m_vdev->cp_progress_percent(); } + +} // namespace homestore diff --git a/src/lib/blkdata_svc/data_svc_cp.hpp b/src/lib/blkdata_svc/data_svc_cp.hpp new file mode 100644 index 000000000..a72e9dda1 --- /dev/null +++ b/src/lib/blkdata_svc/data_svc_cp.hpp @@ -0,0 +1,38 @@ +/********************************************************************************* + * Modifications Copyright 2017-2019 eBay Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + *********************************************************************************/ +#pragma once +#include +#include +#include "checkpoint/cp.hpp" + +namespace homestore { + +class DataSvcCPCallbacks : public CPCallbacks { +public: + DataSvcCPCallbacks(shared< VirtualDev > vdev); + virtual ~DataSvcCPCallbacks() = default; + +public: + std::unique_ptr< CPContext > on_switchover_cp(CP* cur_cp, CP* new_cp) override; + folly::Future< bool > cp_flush(CP* cp) override; + void cp_cleanup(CP* cp) override; + int cp_progress_percent() override; + +private: + shared< VirtualDev > m_vdev; +}; + +} // namespace homestore diff --git a/src/lib/device/chunk.cpp b/src/lib/device/chunk.cpp index 2549658dc..f1ddbe097 100644 --- a/src/lib/device/chunk.cpp +++ b/src/lib/device/chunk.cpp @@ -16,12 +16,13 @@ #include "device/device.h" #include "device/physical_dev.hpp" #include "common/homestore_utils.hpp" +#include "blkalloc/blk_allocator.h" namespace homestore { Chunk::Chunk(PhysicalDev* pdev, const chunk_info& cinfo, uint32_t chunk_slot) : m_chunk_info{cinfo}, m_pdev{pdev}, m_chunk_slot{chunk_slot}, m_stream_id{pdev->chunk_to_stream_id(cinfo)} {} -void Chunk::cp_flush() {} +void Chunk::cp_flush(CP* cp) { blk_allocator_mutable()->cp_flush(cp); } std::string Chunk::to_string() const { return fmt::format("chunk_id={}, vdev_id={}, start_offset={}, size={}, end_of_chunk={}, slot_num_in_pdev={} " @@ -64,4 +65,4 @@ nlohmann::json Chunk::get_status([[maybe_unused]] int log_level) const { j["slot_alloced?"] = is_busy(); return j; } -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/chunk.h b/src/lib/device/chunk.h index e9bbfe952..8ca71a6c1 100644 --- a/src/lib/device/chunk.h +++ b/src/lib/device/chunk.h @@ -17,7 +17,7 @@ namespace homestore { class BlkAllocator; - +class CP; class Chunk { private: std::mutex m_mgmt_mutex; @@ -38,7 +38,7 @@ class Chunk { Chunk& operator=(Chunk&&) noexcept = delete; virtual ~Chunk() = default; - void cp_flush(); + void cp_flush(CP* cp); /////////////// Pointer Getters //////////////////// const PhysicalDev* physical_dev() const { return m_pdev; } @@ -72,4 +72,4 @@ class Chunk { private: void write_chunk_info(); }; -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/device.h b/src/lib/device/device.h index bdc340db3..ad8749576 100644 --- a/src/lib/device/device.h +++ b/src/lib/device/device.h @@ -47,9 +47,11 @@ struct vdev_info { uint8_t hs_dev_type{0}; // 26: PDev dev type (as in fast or data) uint8_t multi_pdev_choice{0}; // 27: Choice when multiple pdevs are present (vdev_multi_pdev_opts_t) char name[64]; // 28: Name of the vdev - uint16_t checksum{0}; // 94: Checksum of this entire block - uint8_t padding[162]{}; // 96: Pad to make it 256 bytes total - uint8_t user_private[user_private_size]{}; // 256: User specific information + uint16_t checksum{0}; // 92: Checksum of this entire Block + uint8_t alloc_type; // 94: Allocator type of this vdev + uint8_t chunk_sel_type; // 95: Chunk Selector type of this vdev_id + uint8_t padding[160]{}; // 96: Pad to make it 256 bytes total + uint8_t user_private[user_private_size]{}; // 128: User specific information uint32_t get_vdev_id() const { return vdev_id; } uint64_t get_size() const { return vdev_size; } @@ -83,6 +85,13 @@ struct vdev_info { static_assert(sizeof(vdev_info) <= vdev_info::size, "VDev info sizeof() mismatch"); +ENUM(chunk_selector_t, uint8_t, // What are the options to select chunk to allocate a block + ROUND_ROBIN, // Pick round robin + RANDOM, // Pick any chunk in uniformly random fashion + MOST_AVAILABLE_SPACE, // Pick the most available space + ALWAYS_CALLER_CONTROLLED // Expect the caller to always provide the specific chunkid +); + struct vdev_parameters { std::string vdev_name; // Name of the vdev uint64_t vdev_size; // Current Vdev size. @@ -91,6 +100,8 @@ struct vdev_parameters { // to number of pdevs evenly uint32_t blk_size; // Block size vdev operates on HSDevType dev_type; // Which physical device type this vdev belongs to (FAST or DATA) + blk_allocator_type_t alloc_type; // which allocator type this vdev wants to be with; + chunk_selector_type_t chunk_sel_type; // which chunk selector type this vdev wants to be with; vdev_multi_pdev_opts_t multi_pdev_opts; // How data to be placed on multiple vdevs sisl::blob context_data; // Context data about this vdev }; @@ -120,7 +131,6 @@ class DeviceManager { sisl::sparse_vector< shared< VirtualDev > > m_vdevs; // VDevs organized in array for quick lookup sisl::Bitset m_vdev_id_bm{hs_super_blk::MAX_VDEVS_IN_SYSTEM}; // Bitmap to keep track of vdev ids available vdev_create_cb_t m_vdev_create_cb; - // std::unique_ptr< ChunkManager > m_chunk_mgr; public: @@ -171,4 +181,4 @@ class DeviceManager { const std::vector< PhysicalDev* >& pdevs_by_type_internal(HSDevType dtype) const; }; // class DeviceManager -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/device_manager.cpp b/src/lib/device/device_manager.cpp index 064a37044..e9e16b550 100644 --- a/src/lib/device/device_manager.cpp +++ b/src/lib/device/device_manager.cpp @@ -340,6 +340,8 @@ static void populate_vdev_info(const vdev_parameters& vparam, uint32_t vdev_id, out_info->set_pdev_choice(vparam.multi_pdev_opts); out_info->set_name(vparam.vdev_name); out_info->set_user_private(vparam.context_data); + out_info->alloc_type = s_cast< uint8_t >(vparam.alloc_type); + out_info->chunk_sel_type = s_cast< uint8_t >(vparam.chunk_sel_type); out_info->compute_checksum(); } @@ -406,4 +408,4 @@ uint64_t hs_super_blk::chunk_super_block_size(const dev_info& dinfo) { return chunk_info_bitmap_size(dinfo) + (max_chunks_in_pdev(dinfo) * chunk_info::size); } -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/hs_super_blk.h b/src/lib/device/hs_super_blk.h index 7bc7ae112..e156ba94a 100644 --- a/src/lib/device/hs_super_blk.h +++ b/src/lib/device/hs_super_blk.h @@ -199,4 +199,4 @@ class hs_super_blk { } }; -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/journal_vdev.cpp b/src/lib/device/journal_vdev.cpp index 4f913352b..3ae7937dc 100644 --- a/src/lib/device/journal_vdev.cpp +++ b/src/lib/device/journal_vdev.cpp @@ -34,8 +34,7 @@ namespace homestore { JournalVirtualDev::JournalVirtualDev(DeviceManager& dmgr, const vdev_info& vinfo, vdev_event_cb_t event_cb) : - VirtualDev{dmgr, vinfo, blk_allocator_type_t::none, chunk_selector_type_t::ROUND_ROBIN, std::move(event_cb), - false /* is_auto_recovery */} {} + VirtualDev{dmgr, vinfo, std::move(event_cb), false /* is_auto_recovery */} {} off_t JournalVirtualDev::alloc_next_append_blk(size_t sz) { if (used_size() + sz > size()) { diff --git a/src/lib/device/physical_dev.hpp b/src/lib/device/physical_dev.hpp index c08d677bf..cb74d1ff0 100644 --- a/src/lib/device/physical_dev.hpp +++ b/src/lib/device/physical_dev.hpp @@ -123,13 +123,12 @@ class PhysicalDev { iomgr::io_device_ptr m_iodev; iomgr::DriveInterface* m_drive_iface; // Interface to do IO PhysicalDevMetrics m_metrics; - std::string m_devname; // Physical device path - HSDevType m_dev_type; // Device type - dev_info m_dev_info; // Input device info - pdev_info_header m_pdev_info; // Persistent information about this physical device - uint64_t m_devsize{0}; // Actual device size - bool m_super_blk_in_footer; // Indicate if the super blk is stored in the footer as well - + std::string m_devname; // Physical device path + HSDevType m_dev_type; // Device type + dev_info m_dev_info; // Input device info + pdev_info_header m_pdev_info; // Persistent information about this physical device + uint64_t m_devsize{0}; // Actual device size + bool m_super_blk_in_footer; // Indicate if the super blk is stored in the footer as well std::mutex m_chunk_op_mtx; // Mutex for all chunk related operations std::vector< Stream > m_streams; // List of streams in the system ChunkIntervalSet m_chunk_data_area; // Range of chunks data area created diff --git a/src/lib/device/round_robin_chunk_selector.cpp b/src/lib/device/round_robin_chunk_selector.cpp index 4d94fbb6b..50937668c 100644 --- a/src/lib/device/round_robin_chunk_selector.cpp +++ b/src/lib/device/round_robin_chunk_selector.cpp @@ -33,4 +33,4 @@ void RoundRobinChunkSelector::foreach_chunks(std::function< void(cshared< Chunk cb(chunk); } } -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/device/virtual_dev.cpp b/src/lib/device/virtual_dev.cpp index 4e3cc75dc..4580471c9 100644 --- a/src/lib/device/virtual_dev.cpp +++ b/src/lib/device/virtual_dev.cpp @@ -43,6 +43,7 @@ #include "common/homestore_assert.hpp" #include "common/homestore_utils.hpp" #include "blkalloc/varsize_blk_allocator.h" +#include "blkalloc/append_blk_allocator.h" SISL_LOGGING_DECL(device) @@ -69,23 +70,44 @@ static std::shared_ptr< BlkAllocator > create_blk_allocator(blk_allocator_type_t cfg.set_auto_recovery(is_auto_recovery); return std::make_shared< VarsizeBlkAllocator >(cfg, is_init, unique_id); } + case blk_allocator_type_t::append: { + BlkAllocConfig cfg{vblock_size, align_sz, size, std::string("append_chunk_") + std::to_string(unique_id)}; + cfg.set_auto_recovery(is_auto_recovery); + return std::make_shared< AppendBlkAllocator >(cfg, is_init, unique_id); + } case blk_allocator_type_t::none: default: return nullptr; } } -VirtualDev::VirtualDev(DeviceManager& dmgr, const vdev_info& vinfo, blk_allocator_type_t allocator_type, - chunk_selector_type_t chunk_selector, vdev_event_cb_t event_cb, bool is_auto_recovery) : +VirtualDev::VirtualDev(DeviceManager& dmgr, const vdev_info& vinfo, vdev_event_cb_t event_cb, bool is_auto_recovery) : m_vdev_info{vinfo}, m_dmgr{dmgr}, m_name{vinfo.name}, m_event_cb{std::move(event_cb)}, m_metrics{vinfo.name}, - m_allocator_type{allocator_type}, - m_chunk_selector_type{chunk_selector}, + m_allocator_type{vinfo.alloc_type}, + m_chunk_selector_type{vinfo.chunk_sel_type}, m_auto_recovery{is_auto_recovery} { - m_chunk_selector = std::make_unique< RoundRobinChunkSelector >(false /* dynamically add chunk */); + switch (m_chunk_selector_type) { + case chunk_selector_type_t::ROUND_ROBIN: { + m_chunk_selector = std::make_unique< RoundRobinChunkSelector >(false /* dynamically add chunk */); + break; + } + case chunk_selector_type_t::HEAP: { + // FIXME: change to HeapChunkSelector after it is ready; + m_chunk_selector = std::make_unique< RoundRobinChunkSelector >(false /* dynamically add chunk */); + break; + } + case chunk_selector_type_t::NONE: { + m_chunk_selector = nullptr; + break; + } + default: + LOGERROR("Unexpected chunk selector type: {}", m_chunk_selector_type); + m_chunk_selector = nullptr; + } } // TODO: Have an additional parameter for vdev to check if dynamic add chunk. If so, we need to take do an rcu for @@ -434,10 +456,6 @@ uint64_t VirtualDev::used_size() const { return (alloc_cnt * block_size()); } -void VirtualDev::cp_flush() { - m_chunk_selector->foreach_chunks([this](cshared< Chunk >& chunk) { chunk->cp_flush(); }); -} - std::vector< shared< Chunk > > VirtualDev::get_chunks() const { return m_all_chunks; } /*void VirtualDev::blkalloc_cp_start(const std::shared_ptr< blkalloc_cp >& ba_cp) { @@ -504,6 +522,26 @@ void VirtualDev::update_vdev_private(const sisl::blob& private_data) { hs_utils::iobuf_free(buf, sisl::buftag::superblk); } +///////////////////////// VirtualDev Checkpoint methods ///////////////////////////// + +VDevCPContext::VDevCPContext(cp_id_t cp_id) : CPContext(cp_id) {} + +std::unique_ptr< CPContext > VirtualDev::create_cp_context(cp_id_t cp_id) { + return std::make_unique< VDevCPContext >(cp_id); +} + +void VirtualDev::cp_flush(CP* cp) { + // pass down cp so that underlying componnents can get their customized CP context if needed; + m_chunk_selector->foreach_chunks([this, cp](cshared< Chunk >& chunk) { chunk->cp_flush(cp); }); +} + +// sync-ops during cp_flush, so return 100; +int VirtualDev::cp_progress_percent() { return 100; } + +void VirtualDev::cp_cleanup(CP*) { + // no-op; +} + ///////////////////////// VirtualDev Private Methods ///////////////////////////// uint64_t VirtualDev::to_dev_offset(const BlkId& b, Chunk** chunk) const { *chunk = m_dmgr.get_chunk_mutable(b.get_chunk_num()); diff --git a/src/lib/device/virtual_dev.hpp b/src/lib/device/virtual_dev.hpp index 0f5ec5324..5de36905f 100644 --- a/src/lib/device/virtual_dev.hpp +++ b/src/lib/device/virtual_dev.hpp @@ -31,6 +31,7 @@ #include #include +#include #include #include "device/device.h" #include "device/chunk_selector.hpp" @@ -96,8 +97,7 @@ class VirtualDev { bool m_auto_recovery; public: - VirtualDev(DeviceManager& dmgr, const vdev_info& vinfo, blk_allocator_type_t allocator_type, - chunk_selector_type_t chunk_selector, vdev_event_cb_t event_cb, bool is_auto_recovery); + VirtualDev(DeviceManager& dmgr, const vdev_info& vinfo, vdev_event_cb_t event_cb, bool is_auto_recovery); VirtualDev(const VirtualDev& other) = delete; VirtualDev& operator=(const VirtualDev& other) = delete; @@ -246,7 +246,19 @@ class VirtualDev { void submit_batch(); virtual void recovery_done(); - void cp_flush(); + + ////////////////////// Checkpointing related methods /////////////////////////// + /// @brief + /// + /// @param cp + void cp_flush(CP* cp); + + void cp_cleanup(CP* cp); + + /// @brief : percentage CP has been progressed, this api is normally used for cp watchdog; + int cp_progress_percent(); + + std::unique_ptr< CPContext > create_cp_context(cp_id_t cp_id); ////////////////////////// Standard Getters /////////////////////////////// virtual uint64_t available_blks() const; @@ -277,4 +289,11 @@ class VirtualDev { std::vector< BlkId >& out_blkid, Chunk* chunk); }; +// place holder for future needs in which components underlying virtualdev needs cp flush context; +class VDevCPContext : public CPContext { +public: + VDevCPContext(cp_id_t cp_id); + virtual ~VDevCPContext() = default; +}; + } // namespace homestore diff --git a/src/lib/homestore.cpp b/src/lib/homestore.cpp index f45c447ea..033c41611 100644 --- a/src/lib/homestore.cpp +++ b/src/lib/homestore.cpp @@ -126,7 +126,8 @@ void HomeStore::format_and_start(std::map< uint32_t, hs_format_params >&& format futs.emplace_back(m_log_service->create_vdev(pct_to_size(fparams.size_pct, HSDevType::Fast), LogStoreService::CTRL_LOG_FAMILY_IDX)); } else if ((svc_type & HS_SERVICE::DATA) && has_data_service()) { - m_data_service->create_vdev(pct_to_size(fparams.size_pct, HSDevType::Data)); + m_data_service->create_vdev(pct_to_size(fparams.size_pct, HSDevType::Data), fparams.alloc_type, + fparams.chunk_sel_type); } else if ((svc_type & HS_SERVICE::INDEX) && has_index_service()) { m_index_service->create_vdev(pct_to_size(fparams.size_pct, HSDevType::Fast)); } @@ -157,9 +158,11 @@ void HomeStore::do_start() { // In case of custom recovery, let consumer starts the recovery and it is consumer module's responsibilities to // start log store - if (has_log_service() && inp_params.auto_recovery) { m_log_service->start(is_first_time_boot()); } + if (has_log_service() && inp_params.auto_recovery) { m_log_service->start(is_first_time_boot() /* format */); } if (has_index_service()) { m_index_service->start(); } + + if (has_data_service()) { m_data_service->start(); } } void HomeStore::shutdown() { diff --git a/src/lib/index/index_cp.cpp b/src/lib/index/index_cp.cpp index 22d2b719f..3add1647e 100644 --- a/src/lib/index/index_cp.cpp +++ b/src/lib/index/index_cp.cpp @@ -17,4 +17,4 @@ void IndexCPCallbacks::cp_cleanup(CP* cp) {} int IndexCPCallbacks::cp_progress_percent() { return 100; } -} // namespace homestore \ No newline at end of file +} // namespace homestore diff --git a/src/lib/index/index_service.cpp b/src/lib/index/index_service.cpp index 972b058c9..4769a6d67 100644 --- a/src/lib/index/index_service.cpp +++ b/src/lib/index/index_service.cpp @@ -45,13 +45,15 @@ void IndexService::create_vdev(uint64_t size) { .num_chunks = 1, .blk_size = atomic_page_size, .dev_type = HSDevType::Fast, + .alloc_type = blk_allocator_type_t::fixed, + .chunk_sel_type = chunk_selector_type_t::ROUND_ROBIN, .multi_pdev_opts = vdev_multi_pdev_opts_t::ALL_PDEV_STRIPED, .context_data = vdev_ctx.to_blob()}); } shared< VirtualDev > IndexService::open_vdev(const vdev_info& vinfo, bool load_existing) { - m_vdev = std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, blk_allocator_type_t::fixed, - chunk_selector_type_t::ROUND_ROBIN, nullptr, true /* auto_recovery */); + m_vdev = + std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, nullptr /* event_cb */, true /* auto_recovery */); return m_vdev; } diff --git a/src/lib/index/wb_cache.cpp b/src/lib/index/wb_cache.cpp index e6fd793e6..86e9a50f1 100644 --- a/src/lib/index/wb_cache.cpp +++ b/src/lib/index/wb_cache.cpp @@ -313,7 +313,7 @@ void IndexWBCache::free_btree_blks_and_flush(IndexCPContext* cp_ctx) { // Pick a CP Manager blocking IO fiber to execute the cp flush of vdev iomanager.run_on_forget(hs()->cp_mgr().pick_blocking_io_fiber(), [this, cp_ctx]() { LOGTRACEMOD(wbcache, "Initiating CP flush"); - m_vdev->cp_flush(); // This is a blocking io call + m_vdev->cp_flush(nullptr); // This is a blocking io call cp_ctx->complete(true); }); } diff --git a/src/lib/logstore/log_store_service.cpp b/src/lib/logstore/log_store_service.cpp index 9ee191837..fa2cdf60e 100644 --- a/src/lib/logstore/log_store_service.cpp +++ b/src/lib/logstore/log_store_service.cpp @@ -57,12 +57,17 @@ folly::Future< bool > LogStoreService::create_vdev(uint64_t size, logstore_famil hs_ctx.type = hs_vdev_type_t::CTRL_LOGDEV_VDEV; } + // reason we set alloc_type/chunk_sel_type here instead of by homestore logstore service consumer is because + // consumer doesn't care or understands the underlying alloc/chunkSel for this service, if this changes in the + // future, we can let consumer set it by then; auto vdev = hs()->device_mgr()->create_vdev(vdev_parameters{.vdev_name = name, .vdev_size = size, .num_chunks = 1, .blk_size = atomic_page_size, .dev_type = HSDevType::Fast, + .alloc_type = blk_allocator_type_t::none, + .chunk_sel_type = chunk_selector_type_t::ROUND_ROBIN, .multi_pdev_opts = vdev_multi_pdev_opts_t::ALL_PDEV_STRIPED, .context_data = hs_ctx.to_blob()}); diff --git a/src/lib/meta/meta_blk_service.cpp b/src/lib/meta/meta_blk_service.cpp index 98423ef7f..eda967e14 100644 --- a/src/lib/meta/meta_blk_service.cpp +++ b/src/lib/meta/meta_blk_service.cpp @@ -58,13 +58,15 @@ void MetaBlkService::create_vdev(uint64_t size) { .num_chunks = 1, .blk_size = phys_page_size, .dev_type = HSDevType::Fast, + .alloc_type = blk_allocator_type_t::varsize, + .chunk_sel_type = chunk_selector_type_t::ROUND_ROBIN, .multi_pdev_opts = vdev_multi_pdev_opts_t::ALL_PDEV_STRIPED, .context_data = meta_ctx.to_blob()}); } shared< VirtualDev > MetaBlkService::open_vdev(const vdev_info& vinfo, bool load_existing) { - m_sb_vdev = std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, blk_allocator_type_t::varsize, - chunk_selector_type_t::ROUND_ROBIN, nullptr, false /* auto_recovery */); + m_sb_vdev = + std::make_shared< VirtualDev >(*(hs()->device_mgr()), vinfo, nullptr /* event_cb */, false /* auto_recovery */); if (load_existing) { /* get the blkid of homestore super block */ diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index fd0f7582b..13bc7267a 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt @@ -51,6 +51,10 @@ if (${build_nonio_tests}) target_link_libraries(test_device_manager homestore ${COMMON_TEST_DEPS} GTest::gmock) add_test(NAME DeviceManager COMMAND test_device_manager) + add_executable(test_append_blk_allocator) + target_sources(test_append_blk_allocator PRIVATE test_append_blkalloc.cpp) + target_link_libraries(test_append_blk_allocator homestore ${COMMON_TEST_DEPS} GTest::gmock) + add_test(NAME AppendBlkAlloc COMMAND test_append_blk_allocator) endif() can_build_io_tests(io_tests) diff --git a/src/tests/log_store_benchmark.cpp b/src/tests/log_store_benchmark.cpp index 234b01428..086633428 100644 --- a/src/tests/log_store_benchmark.cpp +++ b/src/tests/log_store_benchmark.cpp @@ -37,6 +37,8 @@ using namespace homestore; SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) std::vector< std::string > test_common::HSTestHelper::s_dev_names; +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; SISL_OPTIONS_ENABLE(logging, log_store_benchmark, iomgr, test_common_setup) SISL_OPTION_GROUP(log_store_benchmark, diff --git a/src/tests/test_append_blkalloc.cpp b/src/tests/test_append_blkalloc.cpp new file mode 100644 index 000000000..27125f810 --- /dev/null +++ b/src/tests/test_append_blkalloc.cpp @@ -0,0 +1,288 @@ +/********************************************************************************* + * Modifications Copyright 2017-2019 eBay Inc. + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + *********************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "blkalloc/append_blk_allocator.h" +#include "common/homestore_assert.hpp" +#include "common/homestore_config.hpp" +#include "test_common/homestore_test_common.hpp" +#include + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// // +// This test is to test data serice with append block allocator with heap chunk selector // +// // +//////////////////////////////////////////////////////////////////////////////////////////////////// + +RCU_REGISTER_INIT +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; +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; + +constexpr uint64_t Ki{1024}; +constexpr uint64_t Mi{Ki * Ki}; +constexpr uint64_t Gi{Ki * Mi}; + +struct Param { + uint64_t num_io; + uint64_t run_time; +}; + +static Param gp; + +class AppendBlkAllocatorTest : public testing::Test { +public: + BlkDataService& inst() { return homestore::data_service(); } + + virtual void SetUp() override { + test_common::HSTestHelper::set_data_svc_allocator(homestore::blk_allocator_type_t::append); + test_common::HSTestHelper::set_data_svc_chunk_selector(homestore::chunk_selector_type_t::HEAP); + } + + void finish_and_notify() { + { + std::lock_guard lk(this->m_mtx); + this->m_io_job_done = true; + } + // notify any one who is waiting for this write to complete; + this->m_cv.notify_one(); + } + + void free(sisl::sg_list& sg) { test_common::HSTestHelper::free(sg); } + + // + // this api is for caller who is not interested with the write buffer and blkids; + // + void write_io(uint64_t io_size, uint32_t num_iovs = 1) { + auto sg = std::make_shared< sisl::sg_list >(); + write_sgs(io_size, sg, num_iovs).thenValue([this, sg](auto) { + free(*sg); + finish_and_notify(); + }); + } + + void wait_for_all_io_complete() { + std::unique_lock lk(m_mtx); + m_cv.wait(lk, [this] { return this->m_io_job_done; }); + } + + void write_io_verify(const uint64_t io_size) { + auto sg_write_ptr = std::make_shared< sisl::sg_list >(); + auto sg_read_ptr = std::make_shared< sisl::sg_list >(); + + write_sgs(io_size, sg_write_ptr, 1 /* num_iovs */) + .thenValue([sg_write_ptr, sg_read_ptr, this](const std::vector< BlkId >& out_bids) mutable { + // this will be called in write io completion cb; + LOGINFO("after_write_cb: Write completed;"); + + HS_DBG_ASSERT_EQ(out_bids.size(), 1); + + const auto num_iovs = out_bids.size(); + + for (auto i = 0ul; i < num_iovs; ++i) { + struct iovec iov; + iov.iov_len = out_bids[i].get_nblks() * inst().get_page_size(); + iov.iov_base = iomanager.iobuf_alloc(512, iov.iov_len); + sg_read_ptr->iovs.push_back(iov); + sg_read_ptr->size += iov.iov_len; + } + + LOGINFO("Step 2: async read on blkid: {}", out_bids[0].to_string()); + return inst().async_read(out_bids[0], *sg_read_ptr, sg_read_ptr->size); + }) + .thenValue([this, sg_write_ptr, sg_read_ptr](auto) mutable { + const auto equal = test_common::HSTestHelper::compare(*sg_read_ptr, *sg_write_ptr); + assert(equal); + + LOGINFO("Read completed;"); + free(*sg_write_ptr); + free(*sg_read_ptr); + + this->finish_and_notify(); + }); + } + + void write_io_free_blk(const uint64_t io_size) { + std::shared_ptr< sisl::sg_list > sg_write_ptr = std::make_shared< sisl::sg_list >(); + + auto futs = write_sgs(io_size, sg_write_ptr, 1 /* num_iovs */) + .thenValue([sg_write_ptr, this](const std::vector< BlkId >& out_bids) { + LOGINFO("after_write_cb: Write completed;"); + free(*sg_write_ptr); + + std::vector< folly::Future< bool > > futs; + for (const auto& free_bid : out_bids) { + LOGINFO("Step 2: started async_free_blk: {}", free_bid.to_string()); + auto f = inst().async_free_blk(free_bid); + futs.emplace_back(std::move(f)); + } + return futs; + }); + + folly::collectAllUnsafe(futs).then([this](auto) { + LOGINFO("completed async_free_blks"); + this->finish_and_notify(); + }); + } + +private: + // + // call this api when caller needs the write buffer and blkids; + // caller is responsible to free the sg buffer; + // + // caller should be responsible to call free(sg) to free the iobuf allocated in iovs, + // normally it should be freed in after_write_cb; + // + folly::Future< std::vector< BlkId > > write_sgs(uint64_t io_size, cshared< sisl::sg_list >& sg, uint32_t num_iovs) { + // TODO: What if iov_len is not multiple of 4Ki? + HS_DBG_ASSERT_EQ(io_size % (4 * Ki * num_iovs), 0, "Expecting iov_len : {} to be multiple of {}.", + io_size / num_iovs, 4 * Ki); + const auto iov_len = io_size / num_iovs; + for (auto i = 0ul; i < num_iovs; ++i) { + struct iovec iov; + iov.iov_len = iov_len; + iov.iov_base = iomanager.iobuf_alloc(512, iov_len); + test_common::HSTestHelper::fill_data_buf(r_cast< uint8_t* >(iov.iov_base), iov.iov_len); + sg->iovs.push_back(iov); + sg->size += iov_len; + } + + auto out_bids_ptr = std::make_shared< std::vector< BlkId > >(); + return inst() + .async_alloc_write(*(sg.get()), blk_alloc_hints{}, *out_bids_ptr, false /* part_of_batch*/) + .thenValue([sg, this, out_bids_ptr](bool success) { + assert(success); + for (const auto& bid : *out_bids_ptr) { + LOGINFO("bid: {}", bid.to_string()); + } + return folly::makeFuture< std::vector< BlkId > >(std::move(*out_bids_ptr)); + }); + } + +private: + std::mutex m_mtx; + std::condition_variable m_cv; + bool m_io_job_done{false}; +}; + +TEST_F(AppendBlkAllocatorTest, TestBasicWrite) { + LOGINFO("Step 0: Starting homestore."); + test_common::HSTestHelper::start_homestore("test_append_blkalloc", 5.0 /* meta */, 0 /* data_log */, + 0 /* ctrl_log */, 80.0 /* data */, 0 /* index */, nullptr, + false /* recovery */, nullptr, false /* default ds type */); + + // start io in worker thread; + const auto io_size = 4 * Ki; + LOGINFO("Step 1: run on worker thread to schedule write for {} Bytes.", io_size); + iomanager.run_on_forget(iomgr::reactor_regex::random_worker, [this, io_size]() { this->write_io(io_size); }); + + LOGINFO("Step 2: Wait for I/O to complete."); + wait_for_all_io_complete(); + + LOGINFO("Step 3: I/O completed, do shutdown."); + test_common::HSTestHelper::shutdown_homestore(); +} + +TEST_F(AppendBlkAllocatorTest, TestWriteThenReadVerify) { + LOGINFO("Step 0: Starting homestore."); + test_common::HSTestHelper::start_homestore("test_append_blkalloc", 5.0, 0, 0, 80.0, 0, nullptr, false, nullptr, + false /* default ds type */); + + // start io in worker thread; + auto io_size = 4 * Ki; + LOGINFO("Step 1: run on worker thread to schedule write for {} Bytes.", io_size); + iomanager.run_on_forget(iomgr::reactor_regex::random_worker, [this, io_size]() { this->write_io_verify(io_size); }); + + LOGINFO("Step 3: Wait for I/O to complete."); + wait_for_all_io_complete(); + + LOGINFO("Step 4: I/O completed, do shutdown."); + test_common::HSTestHelper::shutdown_homestore(); +} + +TEST_F(AppendBlkAllocatorTest, TestWriteThenFreeBlk) { + LOGINFO("Step 0: Starting homestore."); + test_common::HSTestHelper::start_homestore("test_append_blkalloc", 5.0, 0, 0, 80.0, 0, nullptr, false, nullptr, + false /* default ds type */); + + // start io in worker thread; + auto io_size = 4 * Mi; + LOGINFO("Step 1: run on worker thread to schedule write for {} Bytes, then free blk.", io_size); + iomanager.run_on_forget(iomgr::reactor_regex::random_worker, + [this, io_size]() { this->write_io_free_blk(io_size); }); + + LOGINFO("Step 3: Wait for I/O to complete."); + wait_for_all_io_complete(); + + LOGINFO("Step 4: I/O completed, do shutdown."); + test_common::HSTestHelper::shutdown_homestore(); +} + +TEST_F(AppendBlkAllocatorTest, TestCPFlush) { + LOGINFO("Step 0: Starting homestore."); + test_common::HSTestHelper::start_homestore("test_append_blkalloc", 5.0, 0, 0, 80.0, 0, nullptr, false, nullptr, + false /* default ds type */); + const auto io_size = 4 * Ki; + LOGINFO("Step 1: run on worker thread to schedule write for {} Bytes.", io_size); + iomanager.run_on_forget(iomgr::reactor_regex::random_worker, [this, io_size]() { this->write_io(io_size); }); + + LOGINFO("Step 2: Wait for I/O to complete."); + wait_for_all_io_complete(); + + test_common::HSTestHelper::trigger_cp(true /* wait */); + + LOGINFO("Step 4: I/O completed, do shutdown."); + test_common::HSTestHelper::shutdown_homestore(); +} + +SISL_OPTION_GROUP(test_append_blkalloc, + (run_time, "", "run_time", "running time in seconds", + ::cxxopts::value< uint64_t >()->default_value("30"), "number"), + (num_io, "", "num_io", "number of io", ::cxxopts::value< uint64_t >()->default_value("300"), + "number")); + +int main(int argc, char* argv[]) { + int parsed_argc{argc}; + ::testing::InitGoogleTest(&parsed_argc, argv); + SISL_OPTIONS_LOAD(parsed_argc, argv, logging, test_append_blkalloc, iomgr, test_common_setup); + sisl::logging::SetLogger("test_append_blkalloc"); + spdlog::set_pattern("[%D %T%z] [%^%l%$] [%n] [%t] %v"); + + gp.run_time = SISL_OPTIONS["run_time"].as< uint64_t >(); + gp.num_io = SISL_OPTIONS["num_io"].as< uint64_t >(); + + return RUN_ALL_TESTS(); +} diff --git a/src/tests/test_common/homestore_test_common.hpp b/src/tests/test_common/homestore_test_common.hpp index 457a82ca6..4fe02ea59 100644 --- a/src/tests/test_common/homestore_test_common.hpp +++ b/src/tests/test_common/homestore_test_common.hpp @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include const std::string SPDK_ENV_VAR_STRING{"USER_WANT_SPDK"}; const std::string HTTP_SVC_ENV_VAR_STRING{"USER_WANT_HTTP_OFF"}; @@ -93,11 +95,24 @@ class HSTestHelper { } static std::vector< std::string > s_dev_names; + static blk_allocator_type_t s_ds_alloc_type; + static chunk_selector_type_t s_ds_chunk_sel_type; public: + static void set_data_svc_allocator(blk_allocator_type_t alloc_type) { s_ds_alloc_type = alloc_type; } + static void set_data_svc_chunk_selector(chunk_selector_type_t chunk_sel_type) { + s_ds_chunk_sel_type = chunk_sel_type; + } + static void start_homestore(const std::string& test_name, float meta_pct, float data_log_pct, float ctrl_log_pct, float data_pct, float index_pct, hs_before_services_starting_cb_t cb, - bool restart = false, std::unique_ptr< IndexServiceCallbacks > index_svc_cb = nullptr) { + bool restart = false, std::unique_ptr< IndexServiceCallbacks > index_svc_cb = nullptr, + bool default_data_svc_alloc_type = true) { + if (default_data_svc_alloc_type) { + set_data_svc_allocator(homestore::blk_allocator_type_t::varsize); + set_data_svc_chunk_selector(homestore::chunk_selector_type_t::ROUND_ROBIN); + } + 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 nthreads = SISL_OPTIONS["num_threads"].as< uint32_t >(); @@ -168,7 +183,9 @@ class HSTestHelper { {HS_SERVICE::META, hs_format_params{.size_pct = meta_pct}}, {HS_SERVICE::LOG_REPLICATED, hs_format_params{.size_pct = data_log_pct}}, {HS_SERVICE::LOG_LOCAL, hs_format_params{.size_pct = ctrl_log_pct}}, - {HS_SERVICE::DATA, hs_format_params{.size_pct = data_pct}}, + {HS_SERVICE::DATA, + hs_format_params{ + .size_pct = data_pct, .alloc_type = s_ds_alloc_type, .chunk_sel_type = s_ds_chunk_sel_type}}, {HS_SERVICE::INDEX, hs_format_params{.size_pct = index_pct}}, }); } @@ -182,6 +199,63 @@ class HSTestHelper { if (cleanup) { remove_files(s_dev_names); } s_dev_names.clear(); } + + static void fill_data_buf(uint8_t* buf, uint64_t size) { + for (uint64_t i = 0ul; i < size; ++i) { + *(buf + i) = (i % 256); + } + } + + static bool compare(const sisl::sg_list& sg1, const sisl::sg_list& sg2) { + if ((sg2.size != sg1.size)) { + LOGINFO("sg_list of sg1 size: {} mismatch with sg2 size: {}, ", sg1.size, sg2.size); + return false; + } + + if (sg2.iovs.size() != sg1.iovs.size()) { + LOGINFO("sg_list num of iovs mismatch: sg1: {}, sg2: {}", sg1.iovs.size(), sg2.iovs.size()); + return false; + } + + const auto num_iovs = sg2.iovs.size(); + for (auto i = 0ul; i < num_iovs; ++i) { + if (sg2.iovs[i].iov_len != sg1.iovs[i].iov_len) { + LOGINFO("iov_len of iov[{}] mismatch, sg1: {}, sg2: {}", i, sg1.iovs[i].iov_len, sg2.iovs[i].iov_len); + return false; + } + auto ret = std::memcmp(sg2.iovs[i].iov_base, sg1.iovs[i].iov_base, sg1.iovs[i].iov_len); + if (ret != 0) { + LOGINFO("memcmp return false for iovs[{}] between sg1 and sg2.", i); + return false; + } + } + + return true; + } + + static void free(sisl::sg_list& sg) { + for (auto x : sg.iovs) { + iomanager.iobuf_free(s_cast< uint8_t* >(x.iov_base)); + x.iov_base = nullptr; + x.iov_len = 0; + } + + sg.size = 0; + } + + static void trigger_cp(bool wait) { + auto fut = homestore::hs()->cp_mgr().trigger_cp_flush(true /* force */); + auto on_complete = [&](auto success) { + HS_REL_ASSERT_EQ(success, true, "CP Flush failed"); + LOGINFO("CP Flush completed"); + }; + + if (wait) { + on_complete(std::move(fut).get()); + } else { + std::move(fut).thenValue(on_complete); + } + } }; } // namespace test_common diff --git a/src/tests/test_cp_mgr.cpp b/src/tests/test_cp_mgr.cpp index d49e2d2e7..6558f8f59 100644 --- a/src/tests/test_cp_mgr.cpp +++ b/src/tests/test_cp_mgr.cpp @@ -32,6 +32,8 @@ 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; +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; SISL_OPTION_GROUP(test_cp_mgr, (num_records, "", "num_records", "number of record to test", diff --git a/src/tests/test_data_service.cpp b/src/tests/test_data_service.cpp index 069c261c4..e9e91c8a7 100644 --- a/src/tests/test_data_service.cpp +++ b/src/tests/test_data_service.cpp @@ -36,6 +36,12 @@ #include +//////////////////////////////////////////////////////////////////////////// +// // +// This test is to test data serice with varsize block allocator // +// // +//////////////////////////////////////////////////////////////////////////// + using namespace homestore; RCU_REGISTER_INIT @@ -44,6 +50,8 @@ 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; +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; constexpr uint64_t Ki{1024}; constexpr uint64_t Mi{Ki * Ki}; @@ -56,8 +64,6 @@ struct Param { static Param gp; -static const std::string DATA_SVC_FILE_PREFIX{"/tmp/test_data_service_"}; - ENUM(DataSvcOp, uint8_t, WRITE, READ, FREE_BLK, COMMIT_BLK, RESERVE_STREAM, ALLOC_STREAM, FREE_STREAM) typedef std::function< void(std::error_condition err, std::shared_ptr< std::vector< BlkId > > out_bids) > @@ -69,22 +75,14 @@ class BlkDataServiceTest : public testing::Test { return homestore::data_service(); } - void free_sg_buf(sisl::sg_list& sg) { - for (auto x : sg.iovs) { - iomanager.iobuf_free(s_cast< uint8_t* >(x.iov_base)); - x.iov_base = nullptr; - x.iov_len = 0; - } - - sg.size = 0; - } - void print_bids(const std::vector< BlkId >& out_bids) { for (auto i = 0ul; i < out_bids.size(); ++i) { LOGINFO("bid[{}]: {}", i, out_bids[i].to_string()); } } + void free(sisl::sg_list& sg) { test_common::HSTestHelper::free(sg); } + // free_blk after read completes void write_read_free_blk(uint64_t io_size) { auto sg_write_ptr = std::make_shared< sisl::sg_list >(); @@ -95,7 +93,7 @@ class BlkDataServiceTest : public testing::Test { .thenValue([this, sg_write_ptr, test_blkid_ptr](const std::vector< BlkId >& out_bids) { LOGINFO("after_write_cb: Write completed;"); // sg_write buffer is no longer needed; - free_sg_buf(*sg_write_ptr); + free(*sg_write_ptr); LOGINFO("Write blk ids: "); print_bids(out_bids); @@ -116,7 +114,7 @@ class BlkDataServiceTest : public testing::Test { }) .thenValue([this, sg_read_ptr, test_blkid_ptr](auto) { LOGINFO("read completed;"); - free_sg_buf(*sg_read_ptr); + free(*sg_read_ptr); return inst().async_free_blk(*test_blkid_ptr); }) .thenValue([this, test_blkid_ptr](auto) { @@ -137,7 +135,7 @@ class BlkDataServiceTest : public testing::Test { // a free blk; LOGINFO("after_write_cb: Write completed;"); - free_sg_buf(*sg_write_ptr); // sg_write buffer is no longer needed; + free(*sg_write_ptr); // sg_write buffer is no longer needed; LOGINFO("Write blk ids: "); print_bids(out_bids); @@ -164,7 +162,7 @@ class BlkDataServiceTest : public testing::Test { HS_DBG_ASSERT_EQ(m_free_blk_done.load(), true, "free blk callback should not be called before read blk completes"); - free_sg_buf(*sg_read_ptr); + free(*sg_read_ptr); this->finish_and_notify(); }); @@ -183,7 +181,7 @@ class BlkDataServiceTest : public testing::Test { auto futs = write_sgs(io_size, sg_write_ptr, 1 /* num_iovs */) .thenValue([sg_write_ptr, this](const std::vector< BlkId >& out_bids) { LOGINFO("after_write_cb: Write completed;"); - free_sg_buf(*sg_write_ptr); + free(*sg_write_ptr); std::vector< folly::Future< bool > > futs; for (const auto& free_bid : out_bids) { @@ -226,58 +224,24 @@ class BlkDataServiceTest : public testing::Test { return inst().async_read(out_bids[0], *sg_read_ptr, sg_read_ptr->size); }) .thenValue([this, sg_write_ptr, sg_read_ptr](auto) mutable { - assert(verify_read(*sg_read_ptr, *sg_write_ptr)); + const auto equal = test_common::HSTestHelper::compare(*sg_read_ptr, *sg_write_ptr); + assert(equal); LOGINFO("Read completed;"); - free_sg_buf(*sg_write_ptr); - free_sg_buf(*sg_read_ptr); + free(*sg_write_ptr); + free(*sg_read_ptr); this->finish_and_notify(); }); } - bool verify_read(const sisl::sg_list& read_sg, const sisl::sg_list& write_sg) { - if ((write_sg.size != read_sg.size)) { - LOGINFO("sg_list of read size: {} mismatch with write size: {}, ", read_sg.size, write_sg.size); - return false; - } - - if (write_sg.iovs.size() != read_sg.iovs.size()) { - LOGINFO("sg_list num of iovs mismatch: read: {}, write: {}", read_sg.iovs.size(), write_sg.iovs.size()); - return false; - } - - const auto num_iovs = write_sg.iovs.size(); - for (auto i = 0ul; i < num_iovs; ++i) { - if (write_sg.iovs[i].iov_len != read_sg.iovs[i].iov_len) { - LOGINFO("iov_len of iov[{}] mismatch, read: {}, write: {}", i, read_sg.iovs[i].iov_len, - write_sg.iovs[i].iov_len); - return false; - } - auto ret = std::memcmp(write_sg.iovs[i].iov_base, read_sg.iovs[i].iov_base, read_sg.iovs[i].iov_len); - if (ret != 0) { - LOGINFO("memcmp return false for iovs[{}] between read and write.", i); - return false; - } - } - - LOGINFO("verify_read passed! data size: {}, num_iovs: {}", read_sg.size, read_sg.iovs.size()); - return true; - } - - void fill_data_buf(uint8_t* buf, uint64_t size) { - for (uint64_t i = 0ul; i < size; ++i) { - *(buf + i) = (i % 256); - } - } - // // this api is for caller who is not interested with the write buffer and blkids; // void write_io(uint64_t io_size, uint32_t num_iovs = 1) { auto sg = std::make_shared< sisl::sg_list >(); write_sgs(io_size, sg, num_iovs).thenValue([this, sg](auto) { - free_sg_buf(*sg); + free(*sg); finish_and_notify(); }); } @@ -301,8 +265,8 @@ class BlkDataServiceTest : public testing::Test { // call this api when caller needs the write buffer and blkids; // caller is responsible to free the sg buffer; // - // caller should be responsible to call free_sg_buf(sg) to free the iobuf allocated in iovs, normally it should be - // freed in after_write_cb; + // caller should be responsible to call free(sg) to free the iobuf allocated in iovs, + // normally it should be freed in after_write_cb; // folly::Future< std::vector< BlkId > > write_sgs(uint64_t io_size, cshared< sisl::sg_list >& sg, uint32_t num_iovs) { // TODO: What if iov_len is not multiple of 4Ki? @@ -313,7 +277,7 @@ class BlkDataServiceTest : public testing::Test { struct iovec iov; iov.iov_len = iov_len; iov.iov_base = iomanager.iobuf_alloc(512, iov_len); - fill_data_buf(r_cast< uint8_t* >(iov.iov_base), iov.iov_len); + test_common::HSTestHelper::fill_data_buf(r_cast< uint8_t* >(iov.iov_base), iov.iov_len); sg->iovs.push_back(iov); sg->size += iov_len; } diff --git a/src/tests/test_device_manager.cpp b/src/tests/test_device_manager.cpp index 93a95c61d..c6b1b32c1 100644 --- a/src/tests/test_device_manager.cpp +++ b/src/tests/test_device_manager.cpp @@ -71,9 +71,11 @@ class DeviceMgrTest : public ::testing::Test { ioenvironment.with_iomgr(iomgr::iomgr_params{.num_threads = 1, .is_spdk = is_spdk}); m_dmgr = std::make_unique< homestore::DeviceManager >( m_dev_infos, [this](const homestore::vdev_info& vinfo, bool load_existing) { - return std::make_shared< homestore::VirtualDev >(*m_dmgr, vinfo, homestore::blk_allocator_type_t::fixed, - homestore::chunk_selector_type_t::ROUND_ROBIN, nullptr, - false); + vdev_info vinfo_tmp = vinfo; + vinfo_tmp.alloc_type = s_cast< uint8_t >(homestore::blk_allocator_type_t::fixed); + vinfo_tmp.chunk_sel_type = s_cast< uint8_t >(homestore::chunk_selector_type_t::ROUND_ROBIN); + + return std::make_shared< homestore::VirtualDev >(*m_dmgr, vinfo_tmp, nullptr /* event_cb */, false); }); m_dmgr->is_first_time_boot() ? m_dmgr->format_devices() : m_dmgr->load_devices(); m_pdevs = m_dmgr->get_pdevs_by_dev_type(homestore::HSDevType::Data); @@ -154,6 +156,8 @@ TEST_F(DeviceMgrTest, StripedVDevCreation) { .num_chunks = uint32_cast(m_pdevs.size() * 2), .blk_size = 4096, .dev_type = HSDevType::Data, + .alloc_type = blk_allocator_type_t::none, + .chunk_sel_type = chunk_selector_type_t::NONE, .multi_pdev_opts = vdev_multi_pdev_opts_t::ALL_PDEV_STRIPED, .context_data = sisl::blob{}}); m_vdevs.push_back(std::move(vdev)); diff --git a/src/tests/test_index_btree.cpp b/src/tests/test_index_btree.cpp index 8f1e8376d..1d1aefb39 100644 --- a/src/tests/test_index_btree.cpp +++ b/src/tests/test_index_btree.cpp @@ -40,8 +40,10 @@ 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 increase num_entries to 65k as io mgr page size is 512 and its slow. +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; + SISL_OPTION_GROUP(test_index_btree, (num_iters, "", "num_iters", "number of iterations for rand ops", ::cxxopts::value< uint32_t >()->default_value("65536"), "number"), @@ -294,20 +296,6 @@ struct BtreeTest : public testing::Test { void print(const std::string& file = "") const { m_bt->print_tree(file); } - void trigger_cp(bool wait) { - auto fut = homestore::hs()->cp_mgr().trigger_cp_flush(true /* force */); - auto on_complete = [&](auto success) { - ASSERT_EQ(success, true) << "CP Flush failed"; - LOGINFO("CP Flush completed"); - }; - - if (wait) { - on_complete(std::move(fut).get()); - } else { - std::move(fut).thenValue(on_complete); - } - } - void destroy_btree() { auto cpg = hs()->cp_mgr().cp_guard(); auto op_context = (void*)cpg->context(cp_consumer_t::INDEX_SVC); @@ -472,7 +460,7 @@ TYPED_TEST(BtreeTest, CpFlush) { this->print(std::string("before.txt")); LOGINFO("Trigger checkpoint flush."); - this->trigger_cp(true /* wait */); + test_common::HSTestHelper::trigger_cp(true /* wait */); LOGINFO("Query {} entries and validate with pagination of 75 entries", num_entries); this->query_validate(0, num_entries - 1, 75); @@ -505,22 +493,22 @@ TYPED_TEST(BtreeTest, MultipleCpFlush) { this->put(i, btree_put_type::INSERT_ONLY_IF_NOT_EXISTS); if (i % 500 == 0) { LOGINFO("Trigger checkpoint flush wait=false."); - this->trigger_cp(false /* wait */); + test_common::HSTestHelper::trigger_cp(false /* wait */); } } LOGINFO("Trigger checkpoint flush wait=false."); - this->trigger_cp(false /* wait */); + test_common::HSTestHelper::trigger_cp(false /* wait */); for (uint32_t i = num_entries / 2; i < num_entries; ++i) { this->put(i, btree_put_type::INSERT_ONLY_IF_NOT_EXISTS); } LOGINFO("Trigger checkpoint flush wait=false."); - this->trigger_cp(false /* wait */); + test_common::HSTestHelper::trigger_cp(false /* wait */); LOGINFO("Trigger checkpoint flush wait=true."); - this->trigger_cp(true /* wait */); + test_common::HSTestHelper::trigger_cp(true /* wait */); LOGINFO("Query {} entries and validate with pagination of 75 entries", num_entries); this->query_validate(0, num_entries - 1, 75); @@ -559,7 +547,7 @@ TYPED_TEST(BtreeTest, ThreadedCpFlush) { auto cp_flush_thread = std::thread([this, &stop_cp_flush] { while (!stop_cp_flush) { LOGINFO("Trigger checkpoint flush wait=false."); - this->trigger_cp(false /* wait */); + test_common::HSTestHelper::trigger_cp(false /* wait */); std::this_thread::sleep_for(std::chrono::seconds{1}); } }); @@ -569,7 +557,7 @@ TYPED_TEST(BtreeTest, ThreadedCpFlush) { cp_flush_thread.join(); LOGINFO("Trigger checkpoint flush wait=true."); - this->trigger_cp(true /* wait */); + test_common::HSTestHelper::trigger_cp(true /* wait */); LOGINFO("Query {} entries and validate with pagination of 75 entries", num_entries); this->query_validate(0, num_entries - 1, 75); diff --git a/src/tests/test_journal_vdev.cpp b/src/tests/test_journal_vdev.cpp index 1e8d820a0..eff4c7b7f 100644 --- a/src/tests/test_journal_vdev.cpp +++ b/src/tests/test_journal_vdev.cpp @@ -44,7 +44,8 @@ SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) SISL_OPTIONS_ENABLE(logging, test_vdev, iomgr, test_common_setup) std::vector< std::string > test_common::HSTestHelper::s_dev_names; - +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; struct Param { uint64_t num_io; uint64_t run_time; diff --git a/src/tests/test_log_store.cpp b/src/tests/test_log_store.cpp index f996fd830..8e95a99a8 100644 --- a/src/tests/test_log_store.cpp +++ b/src/tests/test_log_store.cpp @@ -56,7 +56,8 @@ using namespace homestore; RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) std::vector< std::string > test_common::HSTestHelper::s_dev_names; - +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; struct test_log_data { test_log_data() = default; test_log_data(const test_log_data&) = delete; diff --git a/src/tests/test_meta_blk_mgr.cpp b/src/tests/test_meta_blk_mgr.cpp index a3a959d67..f194c67cd 100644 --- a/src/tests/test_meta_blk_mgr.cpp +++ b/src/tests/test_meta_blk_mgr.cpp @@ -48,7 +48,8 @@ using namespace homestore; RCU_REGISTER_INIT SISL_LOGGING_INIT(HOMESTORE_LOG_MODS) std::vector< std::string > test_common::HSTestHelper::s_dev_names; - +blk_allocator_type_t test_common::HSTestHelper::s_ds_alloc_type; +chunk_selector_type_t test_common::HSTestHelper::s_ds_chunk_sel_type; SISL_OPTIONS_ENABLE(logging, test_meta_blk_mgr, iomgr, test_common_setup) SISL_LOGGING_DECL(test_meta_blk_mgr) @@ -363,7 +364,7 @@ class VMetaBlkMgrTest : public ::testing::Test { iomanager.iobuf_free(buf); } else { if (unaligned_addr) { - delete[] (buf - unaligned_shift); + delete[](buf - unaligned_shift); } else { delete[] buf; }