From ed0b23f641b49906e87ac037dbb07d323ec364e5 Mon Sep 17 00:00:00 2001 From: Xiaoxi Chen Date: Wed, 3 Jun 2026 18:38:36 +0800 Subject: [PATCH 1/3] Support hex shard_id in dump_shard API (#427) --- src/lib/homestore_backend/hs_http_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/homestore_backend/hs_http_manager.cpp b/src/lib/homestore_backend/hs_http_manager.cpp index 848aa303..363315a7 100644 --- a/src/lib/homestore_backend/hs_http_manager.cpp +++ b/src/lib/homestore_backend/hs_http_manager.cpp @@ -202,7 +202,7 @@ void HttpManager::get_shard(const Pistache::Rest::Request& request, Pistache::Ht response.send(Pistache::Http::Code::Bad_Request, "shard_id is required"); return; } - uint64_t shard_id = std::stoul(shard_str.value()); + uint64_t shard_id = std::stoull(shard_str.value(), nullptr, 0); nlohmann::json j; j["shard_id"] = shard_id; auto chk = ho_.get_shard_v_chunk_id(shard_id); @@ -272,7 +272,7 @@ void HttpManager::dump_shard(const Pistache::Rest::Request& request, Pistache::H response.send(Pistache::Http::Code::Bad_Request, "shard_id is required"); return; } - uint64_t shard_id = std::stoul(shard_str.value()); + uint64_t shard_id = std::stoull(shard_str.value(), nullptr, 0); nlohmann::json j; j["shard_id"] = shard_id; auto chk = ho_.get_shard_v_chunk_id(shard_id); From 8dc6377541c3001601f2d8fd75326dafebd58617 Mon Sep 17 00:00:00 2001 From: yawzhang Date: Fri, 5 Jun 2026 16:00:08 +0800 Subject: [PATCH 2/3] dump chunk state in http apis --- src/lib/homestore_backend/hs_http_manager.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/lib/homestore_backend/hs_http_manager.cpp b/src/lib/homestore_backend/hs_http_manager.cpp index 363315a7..1ba57a2e 100644 --- a/src/lib/homestore_backend/hs_http_manager.cpp +++ b/src/lib/homestore_backend/hs_http_manager.cpp @@ -214,6 +214,9 @@ void HttpManager::get_shard(const Pistache::Rest::Request& request, Pistache::Ht j["v_chunk_id"] = chk.value(); j["p_chunk_id"] = pchk.value(); pg_id_t pg_id = ho_.get_pg_id_from_shard_id(shard_id); + if (auto vchunk = ho_.chunk_selector()->get_pg_vchunk(pg_id, chk.value()); vchunk) { + j["v_chunk_state"] = enum_name(vchunk->m_state); + } auto hs_pg = ho_.get_hs_pg(pg_id); if (!hs_pg) { response.send(Pistache::Http::Code::Internal_Server_Error, "pg not found"); @@ -253,6 +256,11 @@ void HttpManager::dump_chunk(const Pistache::Rest::Request& request, Pistache::H uint16_t v_chunk_id = std::stoul(chunk_str.value()); nlohmann::json j; j["v_chunk_id"] = v_chunk_id; + + if (auto vchunk = ho_.chunk_selector()->get_pg_vchunk(pg_id, v_chunk_id); vchunk) { + j["v_chunk_state"] = enum_name(vchunk->m_state); + } + auto shards = hs_pg->get_chunk_shards(v_chunk_id); for (auto const& s : shards) { nlohmann::json shard_json; @@ -281,6 +289,12 @@ void HttpManager::dump_shard(const Pistache::Rest::Request& request, Pistache::H return; } j["v_chunk_id"] = chk.value(); + + pg_id_t pg_id = ho_.get_pg_id_from_shard_id(shard_id); + if (auto vchunk = ho_.chunk_selector()->get_pg_vchunk(pg_id, chk.value()); vchunk) { + j["v_chunk_state"] = enum_name(vchunk->m_state); + } + auto r = ho_.get_shard_blobs(shard_id); if (!r) { response.send(Pistache::Http::Code::Internal_Server_Error, "failed to get shard blobs"); From 226b630149299870e1a1582bbf97f26bc8e0fe66 Mon Sep 17 00:00:00 2001 From: ywz <649521587@qq.com> Date: Mon, 8 Jun 2026 18:12:14 +0800 Subject: [PATCH 3/3] SDSTOR-22636: support yield leader to a specific candidate (#430) also return more shard details on get_shard/dump_chunk/dump_shard API Co-authored-by: yawzhang --- conanfile.py | 5 +- src/lib/homestore_backend/hs_homeobject.cpp | 5 +- src/lib/homestore_backend/hs_homeobject.hpp | 4 +- src/lib/homestore_backend/hs_http_manager.cpp | 61 +++++++++++++++++-- src/lib/homestore_backend/hs_pg_manager.cpp | 11 +++- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/conanfile.py b/conanfile.py index 1a85e5ac..03f67f78 100644 --- a/conanfile.py +++ b/conanfile.py @@ -10,7 +10,7 @@ class HomeObjectConan(ConanFile): name = "homeobject" - version = "4.1.4" + version = "4.1.5" homepage = "https://github.com/eBay/HomeObject" description = "Blob Store built on HomeStore" @@ -122,7 +122,8 @@ def package(self): keep_path=True) def package_info(self): - self.cpp_info.components["homestore"].requires = ["homestore::homestore", "iomgr::iomgr", "sisl::sisl", "nuraft_mesg::nuraft_mesg"] + self.cpp_info.components["homestore"].requires = ["homestore::homestore", "iomgr::iomgr", "sisl::sisl", + "nuraft_mesg::nuraft_mesg"] self.cpp_info.components["memory"].requires = ["sisl::sisl"] self.cpp_info.components["homeobject"].requires = ["homestore"] diff --git a/src/lib/homestore_backend/hs_homeobject.cpp b/src/lib/homestore_backend/hs_homeobject.cpp index ef84a4c2..3f08ad9c 100644 --- a/src/lib/homestore_backend/hs_homeobject.cpp +++ b/src/lib/homestore_backend/hs_homeobject.cpp @@ -544,8 +544,9 @@ void HSHomeObject::reconcile_pg_leader(int32_t pg_id) { } } -void HSHomeObject::yield_pg_leadership_to_follower(int32_t pg_id) { +void HSHomeObject::yield_pg_leadership_to_follower(int32_t pg_id, std::optional< peer_id_t > candidate) { if (pg_id == -1) { + // candidate is only meaningful for a specific PG, ignore it when iterating all PGs LOGI("PG id not set, start yield leaders for all PGs"); std::shared_lock lock_guard(_pg_lock); std::vector< std::future< void > > futures; @@ -563,7 +564,7 @@ void HSHomeObject::yield_pg_leadership_to_follower(int32_t pg_id) { LOGI("Yielding leader for PG {}", pg_id); auto hs_pg = get_hs_pg(pg_id); if (hs_pg) { - hs_pg->yield_leadership_to_follower(); + hs_pg->yield_leadership_to_follower(candidate); } else { LOGE("PG {} not found", pg_id); } diff --git a/src/lib/homestore_backend/hs_homeobject.hpp b/src/lib/homestore_backend/hs_homeobject.hpp index d4a1d25f..0b67f750 100644 --- a/src/lib/homestore_backend/hs_homeobject.hpp +++ b/src/lib/homestore_backend/hs_homeobject.hpp @@ -403,7 +403,7 @@ class HSHomeObject : public HomeObjectImpl { void reconcile_leader() const; - void yield_leadership_to_follower() const; + void yield_leadership_to_follower(std::optional< peer_id_t > candidate = std::nullopt) const; void trigger_snapshot_creation(int64_t compact_lsn, bool wait_for_commit) const; @@ -1001,7 +1001,7 @@ class HSHomeObject : public HomeObjectImpl { /** * @brief yield leadership to follower with newest progress, only used for test */ - void yield_pg_leadership_to_follower(int32_t pg_id = 1); + void yield_pg_leadership_to_follower(int32_t pg_id = 1, std::optional< peer_id_t > candidate = std::nullopt); /** * @brief Manually trigger a snapshot creation. diff --git a/src/lib/homestore_backend/hs_http_manager.cpp b/src/lib/homestore_backend/hs_http_manager.cpp index 1ba57a2e..c3c0657d 100644 --- a/src/lib/homestore_backend/hs_http_manager.cpp +++ b/src/lib/homestore_backend/hs_http_manager.cpp @@ -112,8 +112,42 @@ void HttpManager::yield_leadership_to_follower(const Pistache::Rest::Request& re Pistache::Http::ResponseWriter response) { const auto pg_id_param = request.query().get("pg_id"); int32_t pg_id = std::stoi(pg_id_param.value_or("-1")); - LOGINFO("Received yield leadership request for pg_id {} to follower", pg_id); - ho_.yield_pg_leadership_to_follower(pg_id); + + const auto candidate_param = request.query().get("candidate"); + if (candidate_param && candidate_param->empty()) { + response.send(Pistache::Http::Code::Bad_Request, "candidate must not be empty"); + return; + } + if (candidate_param && pg_id < 0) { + response.send(Pistache::Http::Code::Bad_Request, "candidate requires pg_id to be specified"); + return; + } + + std::optional< peer_id_t > candidate; + auto candidate_str = candidate_param.value_or("auto"); + if (candidate_param) { + LOGINFO("Checking candidate {} for pg_id {}", candidate_str, pg_id); + try { + candidate = boost::uuids::string_generator()(candidate_str); + } catch (const std::exception&) { + response.send(Pistache::Http::Code::Bad_Request, "Invalid candidate UUID format"); + return; + } + auto hs_pg = ho_.get_hs_pg(static_cast< uint16_t >(pg_id)); + if (!hs_pg) { + response.send(Pistache::Http::Code::Not_Found, "pg not found"); + return; + } + auto const& members = hs_pg->pg_info_.members; + if (!std::any_of(members.begin(), members.end(), [&](const auto& m) { return m.id == *candidate; })) { + response.send(Pistache::Http::Code::Bad_Request, + fmt::format("candidate {} is not a member of pg {}", candidate_str, pg_id)); + return; + } + } + + LOGINFO("Received yield leadership request for pg_id {} to follower, candidate={}", pg_id, candidate_str); + ho_.yield_pg_leadership_to_follower(pg_id, candidate); response.send(Pistache::Http::Code::Ok, "Yield leadership request submitted"); } @@ -227,9 +261,12 @@ void HttpManager::get_shard(const Pistache::Rest::Request& request, Pistache::Ht response.send(Pistache::Http::Code::Internal_Server_Error, "failed to get shard"); return; } - j["created_time"] = r.value().created_time; - j["state"] = r.value().state; - j["lsn"] = r.value().lsn; + const auto& shard_info = r.value(); + j["created_time"] = shard_info.created_time; + j["last_modified_time"] = shard_info.last_modified_time; + j["state"] = shard_info.state; + j["lsn"] = shard_info.lsn; + j["meta"] = std::string(reinterpret_cast< const char* >(shard_info.meta)); auto blobs = ho_.get_shard_blobs(shard_id); if (!blobs) { response.send(Pistache::Http::Code::Internal_Server_Error, "failed to get shard blobs"); @@ -266,8 +303,10 @@ void HttpManager::dump_chunk(const Pistache::Rest::Request& request, Pistache::H nlohmann::json shard_json; shard_json["shard_id"] = s.info.id; shard_json["created_time"] = s.info.created_time; + shard_json["last_modified_time"] = s.info.last_modified_time; shard_json["state"] = s.info.state; shard_json["lsn"] = s.info.lsn; + shard_json["meta"] = std::string(reinterpret_cast< const char* >(s.info.meta)); j["shards"].push_back(shard_json); } j["total_shard_count"] = shards.size(); @@ -295,6 +334,18 @@ void HttpManager::dump_shard(const Pistache::Rest::Request& request, Pistache::H j["v_chunk_state"] = enum_name(vchunk->m_state); } + auto s = ho_.shard_manager()->get_shard(shard_id).get(); + if (!s) { + response.send(Pistache::Http::Code::Internal_Server_Error, "failed to get shard"); + return; + } + const auto& shard_info = s.value(); + j["created_time"] = shard_info.created_time; + j["last_modified_time"] = shard_info.last_modified_time; + j["state"] = shard_info.state; + j["lsn"] = shard_info.lsn; + j["meta"] = std::string(reinterpret_cast< const char* >(shard_info.meta)); + auto r = ho_.get_shard_blobs(shard_id); if (!r) { response.send(Pistache::Http::Code::Internal_Server_Error, "failed to get shard blobs"); diff --git a/src/lib/homestore_backend/hs_pg_manager.cpp b/src/lib/homestore_backend/hs_pg_manager.cpp index e12cbf67..b3435ede 100644 --- a/src/lib/homestore_backend/hs_pg_manager.cpp +++ b/src/lib/homestore_backend/hs_pg_manager.cpp @@ -1054,13 +1054,22 @@ void HSHomeObject::HS_PG::get_peer_info(std::vector< peer_info >& members) const void HSHomeObject::HS_PG::reconcile_leader() const { repl_dev_->reconcile_leader(); } -void HSHomeObject::HS_PG::yield_leadership_to_follower() const { +void HSHomeObject::HS_PG::yield_leadership_to_follower(std::optional< peer_id_t > candidate) const { if (!repl_dev_->is_leader()) { LOGDEBUG("Not a leader, no need to yield leadership"); return; } auto leader_id = repl_dev_->get_leader_id(); + + if (candidate.has_value()) { + LOGI("Trying to yield leadership from {} to specified candidate {}", boost::uuids::to_string(leader_id), + boost::uuids::to_string(candidate.value())); + repl_dev_->yield_leadership(false /*immediate_yield*/, candidate.value()); + return; + } + + // No candidate specified: pick the follower with the highest priority. auto candidate_leader_id = leader_id; int32_t highest_prority = 0; auto const replication_status = repl_dev_->get_replication_status();