diff --git a/conanfile.py b/conanfile.py index 69b9b3853..4dd8eb1c8 100644 --- a/conanfile.py +++ b/conanfile.py @@ -2,7 +2,7 @@ class HomestoreConan(ConanFile): name = "homestore" - version = "3.6.7" + version = "3.6.8" homepage = "https://github.corp.ebay.com/SDS/homestore" description = "HomeStore" diff --git a/src/api/meta_interface.hpp b/src/api/meta_interface.hpp index f9656a56b..5f8244a6f 100644 --- a/src/api/meta_interface.hpp +++ b/src/api/meta_interface.hpp @@ -43,6 +43,10 @@ class BlkBuffer; template < typename Buffer > class BlkStore; +// The offset of vol_name is needed for metablk service to translate the vol_name from buffer of metablk_VOLUME for +// example in get_status +#define VOL_NAME_OFFSET 48 + typedef homestore::BlkStore< BlkBuffer > blk_store_t; // each subsystem could receive callbacks multiple times @@ -89,7 +93,6 @@ class MetaBlkMgr { sisl::blob m_compress_info; MetablkMetrics m_metrics; bool m_inited{false}; - sisl::sobject_ptr m_sobject; public: MetaBlkMgr(const char* const name = "MetaBlkStore"); @@ -221,8 +224,8 @@ class MetaBlkMgr { [[nodiscard]] uint32_t get_align_size() const; [[nodiscard]] sisl::status_response get_status(const sisl::status_request& request); - - sisl::sobject_ptr sobject() { return m_sobject; } + [[nodiscard]] sisl::status_response get_status_metablk(const sisl::status_request& request, std::string type = ""); + void create_sobject(meta_sub_type type); public: /*********************** static public function **********************/ @@ -345,9 +348,11 @@ class MetaBlkMgr { [[nodiscard]] uint64_t get_min_compress_size() const; [[nodiscard]] uint64_t get_max_compress_memory_size() const; [[nodiscard]] uint64_t get_init_compress_memory_size() const; + public: [[nodiscard]] uint32_t get_compress_ratio_limit() const; [[nodiscard]] bool get_skip_hdr_check() const; + private: [[nodiscard]] bool compress_feature_on() const; diff --git a/src/engine/common/homestore_utils.cpp b/src/engine/common/homestore_utils.cpp index adeb1575e..fa60cbb3a 100644 --- a/src/engine/common/homestore_utils.cpp +++ b/src/engine/common/homestore_utils.cpp @@ -15,6 +15,9 @@ *********************************************************************************/ #include "homestore_utils.hpp" #include "homestore_assert.hpp" +#include +#include +#include namespace homestore { uint8_t* hs_utils::iobuf_alloc(const size_t size, const sisl::buftag tag, const size_t alignment) { @@ -73,5 +76,49 @@ sisl::byte_array hs_utils::extract_byte_array(const sisl::byte_view& b, const bo return (is_aligned_needed) ? b.extract(alignment) : b.extract(0); }; +std::string hs_utils::encodeBase64(const uint8_t* first, std::size_t size) { + using Base64FromBinary = boost::archive::iterators::base64_from_binary< + boost::archive::iterators::transform_width< const char*, // sequence of chars + 6, // get view of 6 bit + 8 // from sequence of 8 bit + > >; + std::vector< unsigned char > bytes{first, first + size}; + std::size_t bytes_to_pad = (3 - size % 3) % 3; + if (bytes_to_pad > 0) { bytes.resize(bytes.size() + bytes_to_pad, 0); } + std::string encoded{Base64FromBinary{bytes.data()}, Base64FromBinary{bytes.data() + (bytes.size() - bytes_to_pad)}}; + + return encoded.append(bytes_to_pad, '='); +} + +std::string hs_utils::encodeBase64(const sisl::byte_view& b){ + return encodeBase64(b.bytes(), b.size()); +} + +template +void hs_utils::decodeBase64(const std::string &encoded_data, T out) +{ + using BinaryFromBase64 = boost::archive::iterators::transform_width< + boost::archive::iterators::binary_from_base64, + 8, // get a view of 8 bit + 6 // from a sequence of 6 bit + >; + auto unpadded_data = encoded_data; + const auto bytes_to_pad = std::count(begin(encoded_data), end(encoded_data), '='); + std::replace(begin(unpadded_data), end(unpadded_data), '=', 'A'); // A_64 == \0 + + std::string decoded_data{BinaryFromBase64{begin(unpadded_data)}, + BinaryFromBase64{begin(unpadded_data) + unpadded_data.length()}}; + + decoded_data.erase(end(decoded_data) - bytes_to_pad, end(decoded_data)); + std::copy(begin(decoded_data), end(decoded_data), out); +} + +std::string hs_utils::decodeBase64(const std::string &encoded_data) +{ + std::string rv; + decodeBase64(encoded_data, std::back_inserter(rv)); + return rv; +} + size_t hs_utils::m_btree_mempool_size; } // namespace homestore diff --git a/src/engine/common/homestore_utils.hpp b/src/engine/common/homestore_utils.hpp index 6aadd0dde..b1313df96 100644 --- a/src/engine/common/homestore_utils.hpp +++ b/src/engine/common/homestore_utils.hpp @@ -38,6 +38,10 @@ class hs_utils { static sisl::byte_array make_byte_array(const uint64_t size, const bool is_aligned_needed, const sisl::buftag tag, const size_t alignment); static hs_uuid_t gen_system_uuid(); + static std::string encodeBase64(const uint8_t* first, std::size_t size); + static std::string encodeBase64(const sisl::byte_view& b); + template static void decodeBase64(const std::string &encoded_data, T out); + static std::string decodeBase64(const std::string &encoded_data); }; static constexpr hs_uuid_t INVALID_SYSTEM_UUID{0}; diff --git a/src/engine/meta/meta_blks_mgr.cpp b/src/engine/meta/meta_blks_mgr.cpp index 382e10f86..14d3c5f88 100644 --- a/src/engine/meta/meta_blks_mgr.cpp +++ b/src/engine/meta/meta_blks_mgr.cpp @@ -90,10 +90,8 @@ void MetaBlkMgr::start(blk_store_t* sb_blk_store, const sb_blkstore_blob* blob, HS_REL_ASSERT_GT(get_page_size(), META_BLK_HDR_MAX_SZ); HS_REL_ASSERT_GT(get_page_size(), MAX_BLK_OVF_HDR_MAX_SZ); - - auto hs = HomeStoreBase::safe_instance(); - m_sobject = hs->sobject_mgr()->create_object("module", "MetaBlkMgr", - std::bind(&MetaBlkMgr::get_status, this, std::placeholders::_1)); + HomeStoreBase::safe_instance()->sobject_mgr()->create_object( + "module", "MetaBlkMgr", std::bind(&MetaBlkMgr::get_status, this, std::placeholders::_1)); reset_self_recover(); alloc_compress_buf(get_init_compress_memory_size()); @@ -261,6 +259,8 @@ bool MetaBlkMgr::scan_and_load_meta_blks(meta_blk_map_t& meta_blks, ovf_hdr_map_ HS_DBG_ASSERT_EQ(mblk->hdr.h.bid.to_integer(), bid.to_integer(), "{}, bid mismatch: {} : {} ", mblk->hdr.h.type, mblk->hdr.h.bid.to_string(), bid.to_string()); + create_sobject(mblk->hdr.h.type); + if (prev_meta_bid.to_integer() != mblk->hdr.h.prev_bid.to_integer()) { // recover from previous crash during remove_sub_sb; HS_LOG(INFO, metablk, "[type={}], Recovering fromp previous crash. Fixing prev linkage.", mblk->hdr.h.type); @@ -399,6 +399,14 @@ void MetaBlkMgr::register_handler(const meta_sub_type type, const meta_blk_found HS_LOG(INFO, metablk, "[type={}] registered with do_crc: {}", type, do_crc); } +void MetaBlkMgr::create_sobject(meta_sub_type type) { + if (m_sub_info[type].meta_bids.size() == 1) { + HomeStoreBase::safe_instance()->sobject_mgr()->create_object( + "MetaBlk", "MetaBlk_" + type, + std::bind(&MetaBlkMgr::get_status_metablk, this, std::placeholders::_1, "MetaBlk_" + type)); + } +} + void MetaBlkMgr::add_sub_sb(const meta_sub_type type, const void* context_data, const uint64_t sz, void*& cookie) { std::lock_guard< decltype(m_meta_mtx) > lg(m_meta_mtx); HS_REL_ASSERT_EQ(m_inited, true, "accessing metablk store before init is not allowed."); @@ -412,6 +420,7 @@ void MetaBlkMgr::add_sub_sb(const meta_sub_type type, const void* context_data, // add meta_bid to in-memory for reverse mapping; m_sub_info[type].meta_bids.insert(meta_bid.to_integer()); + create_sobject(type); #ifdef _PRERELEASE uint32_t crc{0}; @@ -1435,9 +1444,92 @@ bool MetaBlkMgr::sanity_check(const bool check_ovf_chain) { // sisl::status_response MetaBlkMgr::get_status(const sisl::status_request& request) { sisl::status_response response; - std::string dummy_client; - response.json = populate_json(request.verbose_level, m_meta_blks, m_ovf_blk_hdrs, m_last_mblk_id.get(), m_sub_info, - m_self_recover, dummy_client); + nlohmann::json j; + { + std::lock_guard< decltype(m_meta_mtx) > lg{m_meta_mtx}; + j["ssb"] = m_ssb ? m_ssb->to_string() : ""; + j["self_recovery"] = m_self_recover; + j["last_mid"] = m_last_mblk_id->to_string(); + j["compression"] = compress_feature_on() ? "On" : "Off"; + } + + response.json = j; + return response; +} +sisl::status_response MetaBlkMgr::get_status_metablk(const sisl::status_request& request, std::string child_type) { + + auto log_level{request.verbose_level}; + sisl::status_response response; + std::string metablk_type = request.json.contains("name") ? request.obj_name : child_type; + + nlohmann::json& j = response.json; + { + std::lock_guard< decltype(m_meta_mtx) > lg{m_meta_mtx}; + std::string metablk_name_template{"MetaBlk_"}; + auto pos{metablk_name_template.size()}; + auto client = metablk_type.substr(pos); + if (m_sub_info.find(client) == m_sub_info.end()) { + j["error"] = "no client " + client + " found"; + return response; + } + for (auto& x : m_sub_info) { + if (!client.empty() && client.compare(x.first)) { continue; } + + j[x.first]["type"] = x.first; + j[x.first]["do_crc"] = x.second.do_crc; + j[x.first]["cb"] = x.second.cb ? "registered valid cb" : "nullptr"; + j[x.first]["comp_cb"] = x.second.comp_cb ? "registered valid cb" : "nullptr"; + j[x.first]["num_meta_bids"] = x.second.meta_bids.size(); + if (log_level >= 2 && log_level <= 3) { + size_t bid_cnt{0}; + uint32_t start_sub_type = request.json.contains("start_sub_type_id") + ? std::stoul(request.json["start_sub_type_id"].get< std::string >()) + : 0; + uint32_t end_sub_type = request.json.contains("end_sub_type_id") + ? std::stoul(request.json["end_sub_type_id"].get< std::string >()) + : UINT32_MAX; + + for (const auto& y : x.second.meta_bids) { + if (bid_cnt < start_sub_type || bid_cnt > end_sub_type) { + bid_cnt++; + continue; + } + BlkId bid(y); + auto it = m_meta_blks.find(y); + std::string jname = "content"; + sisl::byte_array buf; + if (client == "VOLUME" || log_level == 3) { + if (it == m_meta_blks.end()) { + j["error"] = fmt::format( + "Expecting meta_bid: {} to be found in meta blks cache. Corruption detected!", + bid.to_string()); + return response; + } + + if (it == m_meta_blks.end()) { + LOGERROR("bid: {} not found in meta blk cache, corruption detected!", y); + continue; + } + + buf = read_sub_sb_internal(it->second); + if (client == "VOLUME") { jname = std::string((const char*)(buf->bytes + VOL_NAME_OFFSET)); } + } + + if (log_level == 2) { + // dump bid if log level is 2 or dump to file is not possible; + j[x.first]["[" + std::to_string(bid_cnt) + "] " + jname] = bid.to_string(); + + } else if (log_level == 3) { // log_level >= 3 and can dump to file + // dump the whole data buffer to file + j[x.first]["[" + std::to_string(bid_cnt) + "] " + jname] = + hs_utils::encodeBase64(buf->bytes, buf->size); + } + + ++bid_cnt; + } + } + } + } return response; } @@ -1524,7 +1616,7 @@ nlohmann::json MetaBlkMgr::populate_json(const int log_level, meta_blk_map_t& me const std::string file_path{fmt::format("{}/{}_{}", dump_dir, x.first, bid_cnt)}; std::ofstream f{file_path}; f.write(reinterpret_cast< const char* >(buf->bytes), buf->size); - j[x.first]["meta_bids"][std::to_string(bid_cnt)] = file_path; + j[x.first]["content"][std::to_string(bid_cnt)] = hs_utils::encodeBase64(buf->bytes, buf->size); free_space -= buf->size; } diff --git a/src/engine/meta/test_meta_blk_mgr.cpp b/src/engine/meta/test_meta_blk_mgr.cpp index a1d13c235..779f0f74d 100644 --- a/src/engine/meta/test_meta_blk_mgr.cpp +++ b/src/engine/meta/test_meta_blk_mgr.cpp @@ -111,8 +111,8 @@ static void start_homestore(const uint32_t ndevices, const uint64_t dev_size, co params.min_virtual_page_size = 4096; params.app_mem_size = app_mem_size; params.data_devices = device_info; - params.init_done_cb = [& tl_start_mutex = start_mutex, &tl_cv = cv, &tl_inited = inited](std::error_condition err, - const out_params& params) { + params.init_done_cb = [&tl_start_mutex = start_mutex, &tl_cv = cv, &tl_inited = inited](std::error_condition err, + const out_params& params) { LOGINFO("HomeBlks Init completed"); { std::unique_lock< std::mutex > lk{tl_start_mutex}; @@ -126,7 +126,7 @@ static void start_homestore(const uint32_t ndevices, const uint64_t dev_size, co if (SISL_OPTIONS.count("http_port")) { test_common::set_fixed_http_port(SISL_OPTIONS["http_port"].as< uint32_t >()); - }else { + } else { test_common::set_random_http_port(); } VolInterface::init(params); @@ -159,14 +159,14 @@ class VMetaBlkMgrTest : public ::testing::Test { void SetUp() override{}; void TearDown() override{}; -public: +public: [[nodiscard]] uint64_t get_elapsed_time(const Clock::time_point& start) { const std::chrono::seconds sec{std::chrono::duration_cast< std::chrono::seconds >(Clock::now() - start)}; return sec.count(); } - [[nodiscard]] bool keep_running() { + [[nodiscard]] bool keep_running() { HS_DBG_ASSERT(m_mbm->get_size() >= m_mbm->get_used_size(), "total size:{} less than used size: {}", m_mbm->get_size(), m_mbm->get_used_size()); const auto free_size{m_mbm->get_size() - m_mbm->get_used_size()}; @@ -208,9 +208,7 @@ class VMetaBlkMgrTest : public ::testing::Test { } } - [[nodiscard]] uint64_t total_size_written(const void* const cookie) { - return m_mbm->get_meta_size(cookie); - } + [[nodiscard]] uint64_t total_size_written(const void* const cookie) { return m_mbm->get_meta_size(cookie); } void do_write_to_full() { static constexpr uint64_t blkstore_overhead = 4 * 1024ul * 1024ul; // 4MB @@ -428,7 +426,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; } @@ -506,7 +504,7 @@ class VMetaBlkMgrTest : public ::testing::Test { } } - [[nodiscard]] bool do_aligned() const { + [[nodiscard]] bool do_aligned() const { static thread_local std::random_device rd; static thread_local std::default_random_engine re{rd()}; std::uniform_int_distribution< uint8_t > aligned_rand{0, 1}; @@ -555,16 +553,14 @@ class VMetaBlkMgrTest : public ::testing::Test { } } - [[nodiscard]] uint64_t total_op_cnt() const { - return m_update_cnt + m_wrt_cnt + m_rm_cnt; - } + [[nodiscard]] uint64_t total_op_cnt() const { return m_update_cnt + m_wrt_cnt + m_rm_cnt; } [[nodiscard]] uint32_t write_ratio() const { if (m_wrt_cnt == 0) return 0; return (100 * m_wrt_cnt) / total_op_cnt(); } - [[nodiscard]] uint32_t update_ratio() const { + [[nodiscard]] uint32_t update_ratio() const { if (m_update_cnt == 0) return 0; return (100 * m_update_cnt) / total_op_cnt(); } @@ -574,7 +570,7 @@ class VMetaBlkMgrTest : public ::testing::Test { return false; } - [[nodiscard]] bool do_write() const { + [[nodiscard]] bool do_write() const { if (write_ratio() < gp.per_write) { return true; } return false; } @@ -620,15 +616,16 @@ class VMetaBlkMgrTest : public ::testing::Test { HS_REL_ASSERT_EQ(m_mbm->get_size() - m_total_wrt_sz, m_mbm->get_available_blks() * m_mbm->get_page_size()); m_mbm->deregister_handler(mtype); - m_mbm->register_handler(mtype, - [this](meta_blk* mblk, sisl::byte_view buf, size_t size) { - if (mblk) { - std::unique_lock< std::mutex > lg{m_mtx}; - m_cb_blks[mblk->hdr.h.bid.to_integer()] = - std::string{reinterpret_cast< const char* >(buf.bytes()), size}; - } - }, - [this](bool success) { HS_DBG_ASSERT_EQ(success, true); }); + m_mbm->register_handler( + mtype, + [this](meta_blk* mblk, sisl::byte_view buf, size_t size) { + if (mblk) { + std::unique_lock< std::mutex > lg{m_mtx}; + m_cb_blks[mblk->hdr.h.bid.to_integer()] = + std::string{reinterpret_cast< const char* >(buf.bytes()), size}; + } + }, + [this](bool success) { HS_DBG_ASSERT_EQ(success, true); }); } #ifdef _PRERELEASE @@ -717,6 +714,60 @@ TEST_F(VMetaBlkMgrTest, random_load_test) { this->validate(); + this->shutdown(); +} +TEST_F(VMetaBlkMgrTest, get_status_test) { + start_homestore(SISL_OPTIONS["num_devs"].as< uint32_t >(), + SISL_OPTIONS["dev_size_gb"].as< uint64_t >() * 1024 * 1024 * 1024, gp.num_threads); + auto validate_status = [this](std::string type, uint64_t size, bool expected_error=false) { + sisl::status_request status_req; + status_req.obj_name = "MetaBlk_"+type; + status_req.verbose_level = 3; + const auto sobject_mgr = HomeStoreBase::safe_instance()->sobject_mgr(); + const auto status_resp = sobject_mgr->get_status(status_req); + LOGINFO("get_status returned : {}", status_resp.json.dump()); + if(status_resp.json.contains("error")) { + ASSERT_TRUE(expected_error); + } + if(status_resp.json.contains("[0] content")){ + auto encoded_content = status_resp.json["[0] content"]; + const auto decode_content = hs_utils::decodeBase64(encoded_content); + auto val_content = hs_utils::encodeBase64(reinterpret_cast< const unsigned char* >(decode_content.data()), + decode_content.size()); + ASSERT_EQ(encoded_content, val_content); + ASSERT_EQ(!expected_error, decode_content.size() == size); + } + }; + + mtype = "Test_Write"; + reset_counters(); + m_start_time = Clock::now(); + register_client(); + [[maybe_unused]] auto write_result = do_sb_write(false, 500); + validate_status(mtype, 500 ); // check the size written is correct as well as encode/decode + + mtype = "Test_Write2"; + reset_counters(); + register_client(); + write_result = do_sb_write(false, 500); + validate_status(mtype, 500); // register the second type and validate it + + // simulate reboot case that MetaBlkMgr will scan the disk for all the metablks that were written; + this->scan_blks(); + this->recover(); + this->validate(); + MetaBlkMgrSI()->deregister_handler(mtype); // deregister the first type and make sure it is not existed + validate_status("Test_Write", 500); + validate_status("Test_Write2", 500, true); // expected to have error + + mtype = "Test_Write2"; // agian add the first type with different size + reset_counters(); + register_client(); + write_result = do_sb_write(false, 100); + validate_status(mtype, 500, true); // Since the size is 100, the former size must be wrong + validate_status("ERROR_TYPE", 500, true); // An error type also must results in error + + this->shutdown(); } diff --git a/src/homeblks/homeblks_http_server.cpp b/src/homeblks/homeblks_http_server.cpp index d65789db7..6e7e15e75 100644 --- a/src/homeblks/homeblks_http_server.cpp +++ b/src/homeblks/homeblks_http_server.cpp @@ -259,8 +259,6 @@ void HomeBlksHttpServer::dump_disk_metablks(const Pistache::Rest::Request& reque } void HomeBlksHttpServer::get_status(const Pistache::Rest::Request& request, Pistache::Http::ResponseWriter response) { - LOGINFO("Got status request "); - sisl::status_request status_req; std::string failure_resp{""}; int verbose_level{-1}; @@ -274,6 +272,8 @@ void HomeBlksHttpServer::get_status(const Pistache::Rest::Request& request, Pist status_req.json.emplace(iter->first, folly::uriUnescape< std::string >(iter->second)); } + LOGINFO("Got status request {} ", status_req.json.dump()); + auto type(query.get("type")); if (type) { status_req.obj_type = folly::uriUnescape< std::string >(type.value()); } diff --git a/src/homeblks/volume/volume.hpp b/src/homeblks/volume/volume.hpp index 181a897d2..10c2fbbf5 100644 --- a/src/homeblks/volume/volume.hpp +++ b/src/homeblks/volume/volume.hpp @@ -247,6 +247,7 @@ struct vol_sb_hdr { magic, version, num_streams, page_size, size, uuid, vol_name, state); } }; +static_assert(offsetof(struct vol_sb_hdr, vol_name) == VOL_NAME_OFFSET); /* A simple self contained wrapper for completion list, which uses vector pool to avoid additional allocations */ struct vol_completion_req_list { @@ -300,6 +301,7 @@ class VolumeIOWatchDog { class Volume : public std::enable_shared_from_this< Volume > { friend class HomeBlks; + private: vol_params m_params; VolumeMetrics m_metrics; @@ -314,7 +316,7 @@ class Volume : public std::enable_shared_from_this< Volume > { std::atomic< uint64_t > m_err_cnt = 0; sisl::atomic_counter< uint64_t > m_vol_ref_cnt = 0; // volume can not be destroy/shutdown until it is not zero - std::mutex m_sb_lock; // lock for updating vol's sb + std::mutex m_sb_lock; // lock for updating vol's sb sisl::byte_array m_sb_buf; indxmgr_stop_cb m_destroy_done_cb; std::atomic< bool > m_indx_mgr_destroy_started; @@ -396,11 +398,12 @@ class Volume : public std::enable_shared_from_this< Volume > { void destroy_internal(); indx_tbl* create_indx_tbl(); indx_tbl* recover_indx_tbl(btree_super_block& sb, btree_cp_sb& cp_sb); + public: mapping* get_active_indx(); sisl::sobject_ptr sobject() { return m_sobject; } -private: +private: void vol_sb_init(); std::vector< iovec > get_next_iovecs(IoVecTransversal& iovec_transversal, const std::vector< iovec >& data_iovecs, diff --git a/src/homelogstore/log_store.cpp b/src/homelogstore/log_store.cpp index 50a21a6be..d1f1f690a 100644 --- a/src/homelogstore/log_store.cpp +++ b/src/homelogstore/log_store.cpp @@ -346,14 +346,11 @@ nlohmann::json HomeLogStore::dump_log_store(const log_dump_req& dump_req) { json_val["is_inlined"] = static_cast< uint32_t >(record_header.get_inlined()); json_val["store_seq_num"] = static_cast< uint64_t >(record_header.store_seq_num); json_val["store_id"] = static_cast< logstore_id_t >(record_header.store_id); + if (dump_req.verbosity_level == homestore::log_dump_verbosity::CONTENT) { + json_val["content"] = hs_utils::encodeBase64(log_buffer); + } } catch (const std::exception& ex) { THIS_LOGSTORE_LOG(ERROR, "Exception in json dump- {}", ex.what()); } - if (dump_req.verbosity_level == homestore::log_dump_verbosity::CONTENT) { - const uint8_t* const b{log_buffer.bytes()}; - const std::vector< uint8_t > bv(b, b + log_buffer.size()); - auto content = nlohmann::json::binary_t(bv); - json_val["content"] = std::move(content); - } json_records.emplace_back(std::move(json_val)); decltype(idx) end_idx{std::min(max_idx, dump_req.end_seq_num)}; proceed = (cur_idx < end_idx && --batch_size > 0) ? true : false; @@ -410,6 +407,19 @@ sisl::status_response HomeLogStore::get_status(const sisl::status_request& reque response.json["truncation_parallel_to_writes?"] = m_safe_truncation_boundary.active_writes_not_part_of_truncation; response.json["logstore_records"] = m_records.get_status(request.verbose_level); response.json["logstore_sb_first_lsn"] = m_logdev.m_logdev_meta.store_superblk(m_store_id).m_first_seq_num; + + if (request.json.contains("type") && request.json["type"] == "logstore_record") { + log_dump_req dump_req{}; + if (!request.next_cursor.empty()) { dump_req.start_seq_num = std::stoul(request.next_cursor); } + dump_req.end_seq_num = dump_req.start_seq_num + request.batch_size; + homestore::log_dump_verbosity verbose_level = homestore::log_dump_verbosity::HEADER; + if (request.json.contains("log_content")) { + verbose_level = homestore::log_dump_verbosity::CONTENT; + } + dump_req.verbosity_level = verbose_level; + response.json.update(dump_log_store(dump_req)); + } + return response; }