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]