This is an automated email from the ASF dual-hosted git repository.

gavinchou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 6a268dcaa08 [fix](cloud) Recycle empty rowsets without resource id 
(#64630)
6a268dcaa08 is described below

commit 6a268dcaa084c624b3faf49b79ddd15fe5ba5b8c
Author: Xin Liao <[email protected]>
AuthorDate: Thu Jun 18 20:05:58 2026 +0800

    [fix](cloud) Recycle empty rowsets without resource id (#64630)
    
    When recycling a dropped tablet, empty rowsets may have `num_segments ==
    0` and an empty `resource_id`. The protobuf field can be explicitly set
    to an empty string, so `has_resource_id()` returns true. The recycler
    then attempts to look up an accessor for the empty ID, fails the recycle
    round, and repeatedly leaves the dropped tablet/index metadata behind.
    
    This PR skips object deletion for every rowset with zero segments
    because it has no segment objects to remove. Rowsets with segments still
    require a non-empty resource ID.
---
 cloud/src/recycler/recycler.cpp | 14 +++++++-------
 cloud/test/recycler_test.cpp    | 31 +++++++++++++++++++++++++++++++
 2 files changed, 38 insertions(+), 7 deletions(-)

diff --git a/cloud/src/recycler/recycler.cpp b/cloud/src/recycler/recycler.cpp
index f2cea7904d8..bd1b9bff878 100644
--- a/cloud/src/recycler/recycler.cpp
+++ b/cloud/src/recycler/recycler.cpp
@@ -4358,24 +4358,24 @@ int InstanceRecycler::recycle_tablet(int64_t tablet_id, 
RecyclerMetricsContext&
     
TEST_SYNC_POINT_CALLBACK("InstanceRecycler::recycle_tablet.create_rowset_meta", 
&resp);
 
     for (const auto& rs_meta : resp.rowset_meta()) {
-        // The rowset has no resource id and segments when it was generated by 
compaction
-        // with multiple hole rowsets or it's version is [0-1], so we can skip 
it.
-        if (!rs_meta.has_resource_id() && rs_meta.num_segments() == 0) {
-            LOG_INFO("rowset meta does not have a resource id and no segments, 
skip this rowset")
+        // Empty rowsets have no segment objects to delete, so they do not 
need a resource id.
+        if (rs_meta.num_segments() <= 0) {
+            LOG_INFO("rowset meta has no segments, skip this rowset")
                     .tag("rs_meta", rs_meta.ShortDebugString())
                     .tag("instance_id", instance_id_)
                     .tag("tablet_id", tablet_id);
             recycle_rowsets_number += 1;
             continue;
         }
-        if (!rs_meta.has_resource_id()) {
-            LOG_WARNING("rowset meta does not have a resource id, impossible!")
+        if (!rs_meta.has_resource_id() || rs_meta.resource_id().empty()) {
+            LOG_WARNING("rowset meta has a missing or empty resource id, 
impossible!")
                     .tag("rs_meta", rs_meta.ShortDebugString())
                     .tag("instance_id", instance_id_)
                     .tag("tablet_id", tablet_id);
             return -1;
         }
-        DCHECK(rs_meta.has_resource_id()) << "rs_meta" << 
rs_meta.ShortDebugString();
+        DCHECK(rs_meta.has_resource_id() && !rs_meta.resource_id().empty())
+                << "rs_meta" << rs_meta.ShortDebugString();
         auto it = accessor_map_.find(rs_meta.resource_id());
         // possible if the accessor is not initilized correctly
         if (it == accessor_map_.end()) [[unlikely]] {
diff --git a/cloud/test/recycler_test.cpp b/cloud/test/recycler_test.cpp
index 1bfbcc62a72..adb3f362c2a 100644
--- a/cloud/test/recycler_test.cpp
+++ b/cloud/test/recycler_test.cpp
@@ -7265,6 +7265,37 @@ TEST(RecyclerTest, recycle_tablet_without_resource_id) {
     
EXPECT_EQ(recycler.accessor_map_.at("success_vault")->exists("data/0/test.csv"),
 0);
 }
 
+TEST(RecyclerTest, recycle_tablet_with_empty_resource_id_and_no_segments) {
+    auto* sp = SyncPoint::get_instance();
+    DORIS_CLOUD_DEFER {
+        sp->clear_all_call_backs();
+        sp->clear_trace();
+        sp->disable_processing();
+    };
+
+    auto txn_kv = std::make_shared<MemTxnKv>();
+    EXPECT_EQ(txn_kv->init(), 0);
+    InstanceInfoPB instance;
+    instance.set_instance_id("test_instance");
+
+    sp->set_call_back("InstanceRecycler::recycle_tablet.create_rowset_meta", 
[](auto&& args) {
+        auto* resp = try_any_cast<GetRowsetResponse*>(args[0]);
+        auto* rs = resp->add_rowset_meta();
+        rs->set_num_segments(0);
+        rs->set_resource_id("");
+        EXPECT_TRUE(rs->has_resource_id());
+        EXPECT_TRUE(rs->resource_id().empty());
+    });
+    sp->enable_processing();
+
+    InstanceRecycler recycler(txn_kv, instance, thread_group,
+                              std::make_shared<TxnLazyCommitter>(txn_kv));
+    EXPECT_EQ(recycler.init(), 0);
+
+    RecyclerMetricsContext ctx;
+    EXPECT_EQ(recycler.recycle_tablet(0, ctx), 0);
+}
+
 TEST(RecyclerTest, recycle_tablet_with_wrong_resource_id) {
     auto* sp = SyncPoint::get_instance();
     DORIS_CLOUD_DEFER {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to