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 a1e076e8d69 [fix](compaction) Avoid repeatedly compacting large 
cumulative rowset (#64954)
a1e076e8d69 is described below

commit a1e076e8d6969feafd8411f7b19e22664f43c543
Author: Jamie <[email protected]>
AuthorDate: Tue Jul 14 19:47:35 2026 +0800

    [fix](compaction) Avoid repeatedly compacting large cumulative rowset 
(#64954)
    
    ## Proposed changes
    
    - Remove the promotion-size early return in size-based cumulative
    compaction rowset selection so level-size trimming can exclude an
    oversized leading rowset.
    - Keep the level-size trimming loop from trimming a single candidate
    rowset to an empty input set.
    - Apply the same behavior to both cloud and storage cumulative
    compaction policies.
    - Add regression coverage for the large-head/small-tail case and the
    single-rowset guard in both paths.
---
 .../cloud/cloud_cumulative_compaction_policy.cpp   |  17 +-
 .../compaction/cumulative_compaction_policy.cpp    |  16 +-
 .../cloud_cumulative_compaction_policy_test.cpp    | 118 ++++++++++-
 .../cumulative_compaction_policy_test.cpp          | 230 ++++++++++++++++++++-
 4 files changed, 363 insertions(+), 18 deletions(-)

diff --git a/be/src/cloud/cloud_cumulative_compaction_policy.cpp 
b/be/src/cloud/cloud_cumulative_compaction_policy.cpp
index 834b9b682ea..ca422be9b24 100644
--- a/be/src/cloud/cloud_cumulative_compaction_policy.cpp
+++ b/be/src/cloud/cloud_cumulative_compaction_policy.cpp
@@ -18,6 +18,7 @@
 #include "cloud/cloud_cumulative_compaction_policy.h"
 
 #include <algorithm>
+#include <iterator>
 #include <list>
 #include <ostream>
 #include <string>
@@ -172,10 +173,6 @@ int64_t 
CloudSizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
         input_rowsets->push_back(rowset);
     }
 
-    if (total_size >= promotion_size) {
-        return transient_size;
-    }
-
     // if there is delete version, do compaction directly
     if (last_delete_version->first != -1) {
         if (input_rowsets->size() == 1) {
@@ -212,6 +209,9 @@ int64_t 
CloudSizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
 
     auto rs_begin = input_rowsets->begin();
     size_t new_compaction_score = *compaction_score;
+    const bool can_handle_exhausted_input =
+            (config::prioritize_query_perf_in_compaction && 
tablet->keys_type() != DUP_KEYS) ||
+            *compaction_score >= static_cast<size_t>(max_compaction_score);
     while (rs_begin != input_rowsets->end()) {
         auto& rs_meta = (*rs_begin)->rowset_meta();
         int64_t current_level = _level_size(rs_meta->total_disk_size());
@@ -221,9 +221,16 @@ int64_t 
CloudSizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
         if (current_level <= remain_level) {
             break;
         }
+
+        auto next = std::next(rs_begin);
+        // Keep the last suffix rowset for the singleton checks unless the 
exhausted-input
+        // fallback below can select a useful input.
+        if (next == input_rowsets->end() && !can_handle_exhausted_input) {
+            break;
+        }
         total_size -= rs_meta->total_disk_size();
         new_compaction_score -= rs_meta->get_compaction_score();
-        ++rs_begin;
+        rs_begin = next;
     }
     if (rs_begin == input_rowsets->end()) { // No suitable level size found in 
`input_rowsets`
         if (config::prioritize_query_perf_in_compaction && tablet->keys_type() 
!= DUP_KEYS) {
diff --git a/be/src/storage/compaction/cumulative_compaction_policy.cpp 
b/be/src/storage/compaction/cumulative_compaction_policy.cpp
index 4ab59709590..a120d941e6f 100644
--- a/be/src/storage/compaction/cumulative_compaction_policy.cpp
+++ b/be/src/storage/compaction/cumulative_compaction_policy.cpp
@@ -18,6 +18,7 @@
 #include "storage/compaction/cumulative_compaction_policy.h"
 
 #include <algorithm>
+#include <iterator>
 #include <list>
 #include <ostream>
 #include <string>
@@ -318,10 +319,6 @@ int 
SizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
     
DBUG_EXECUTE_IF("SizeBaseCumulativeCompactionPolicy.pick_input_rowsets.return_input_rowsets",
                     { return transient_size; })
 
-    if (total_size >= promotion_size) {
-        return transient_size;
-    }
-
     // if there is delete version, do compaction directly
     if (last_delete_version->first != -1) {
         if (input_rowsets->size() == 1) {
@@ -338,6 +335,8 @@ int SizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
 
     auto rs_begin = input_rowsets->begin();
     size_t new_compaction_score = *compaction_score;
+    const bool can_handle_exhausted_input =
+            *compaction_score >= static_cast<size_t>(max_compaction_score);
     while (rs_begin != input_rowsets->end()) {
         auto& rs_meta = (*rs_begin)->rowset_meta();
         int64_t current_level = _level_size(rs_meta->total_disk_size());
@@ -347,9 +346,16 @@ int 
SizeBasedCumulativeCompactionPolicy::pick_input_rowsets(
         if (current_level <= remain_level) {
             break;
         }
+
+        auto next = std::next(rs_begin);
+        // Keep the last suffix rowset for the singleton checks unless the 
exhausted-input
+        // fallback below can select a useful input.
+        if (next == input_rowsets->end() && !can_handle_exhausted_input) {
+            break;
+        }
         total_size -= rs_meta->total_disk_size();
         new_compaction_score -= rs_meta->get_compaction_score();
-        ++rs_begin;
+        rs_begin = next;
     }
     if (rs_begin == input_rowsets->end() && *compaction_score >= 
max_compaction_score) {
         // No suitable level size found in `input_rowsets` but score of 
`input_rowsets` exceed max compaction score,
diff --git a/be/test/cloud/cloud_cumulative_compaction_policy_test.cpp 
b/be/test/cloud/cloud_cumulative_compaction_policy_test.cpp
index 29358eaaee0..08799c900d2 100644
--- a/be/test/cloud/cloud_cumulative_compaction_policy_test.cpp
+++ b/be/test/cloud/cloud_cumulative_compaction_policy_test.cpp
@@ -36,6 +36,9 @@
 
 namespace doris {
 
+static constexpr int64_t kMiB = 1024L * 1024;
+static constexpr int64_t kGiB = 1024L * kMiB;
+
 class TestCloudSizeBasedCumulativeCompactionPolicy : public testing::Test {
 public:
     TestCloudSizeBasedCumulativeCompactionPolicy()
@@ -118,7 +121,7 @@ private:
 };
 
 static RowsetSharedPtr create_rowset(Version version, int num_segments, bool 
overlapping,
-                                     int data_size) {
+                                     int64_t data_size) {
     auto rs_meta = std::make_shared<RowsetMeta>();
     rs_meta->set_rowset_type(BETA_ROWSET); // important
     rs_meta->_rowset_meta_pb.set_start_version(version.first);
@@ -134,6 +137,14 @@ static RowsetSharedPtr create_rowset(Version version, int 
num_segments, bool ove
     return rowset;
 }
 
+static int64_t total_disk_size(const std::vector<RowsetSharedPtr>& rowsets) {
+    int64_t total_size = 0;
+    for (const auto& rowset : rowsets) {
+        total_size += rowset->total_disk_size();
+    }
+    return total_size;
+}
+
 TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy, new_cumulative_point) {
     std::vector<RowsetMetaSharedPtr> rs_metas;
     init_rs_meta_small_base(&rs_metas);
@@ -151,6 +162,111 @@ TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy, 
new_cumulative_point) {
     EXPECT_EQ(policy.new_cumulative_point(&_tablet, output_rowset, version, 
2), 6);
 }
 
+TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_large_head_not_repeated_when_output_below_promotion) 
{
+    CloudTablet _tablet(_engine, _tablet_meta);
+    _tablet._base_size = 20L * kGiB;
+
+    std::vector<RowsetSharedPtr> candidate_rowsets;
+    auto large_head = create_rowset(Version(2, 2), 1, false, 1023L * kMiB);
+    candidate_rowsets.push_back(large_head);
+    for (int i = 0; i < 20; i++) {
+        candidate_rowsets.push_back(create_rowset(Version(i + 3, i + 3), 1, 
true, kMiB));
+    }
+    ASSERT_GT(total_disk_size(candidate_rowsets), kGiB);
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    CloudSizeBasedCumulativeCompactionPolicy policy;
+    policy.pick_input_rowsets(&_tablet, candidate_rowsets, 100, 5, 
&input_rowsets,
+                              &last_delete_version, &compaction_score, true);
+
+    EXPECT_EQ(20, input_rowsets.size());
+    EXPECT_EQ(20, compaction_score);
+    EXPECT_EQ(3, input_rowsets.front()->start_version());
+    EXPECT_EQ(22, input_rowsets.back()->end_version());
+    EXPECT_LT(total_disk_size(input_rowsets), kGiB);
+
+    auto output_rowset = create_rowset(Version(3, 22), 1, false, 20L * kMiB);
+    EXPECT_EQ(2, policy.new_cumulative_point(&_tablet, output_rowset, 
last_delete_version, 2));
+
+    std::vector<RowsetSharedPtr> next_candidate_rowsets {large_head, 
output_rowset};
+    input_rowsets.clear();
+    compaction_score = 0;
+    policy.pick_input_rowsets(&_tablet, next_candidate_rowsets, 100, 5, 
&input_rowsets,
+                              &last_delete_version, &compaction_score, true);
+
+    EXPECT_TRUE(input_rowsets.empty());
+    EXPECT_EQ(0, compaction_score);
+}
+
+TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_large_head_single_overlapping_tail_selected) {
+    CloudTablet _tablet(_engine, _tablet_meta);
+    _tablet._base_size = 20L * kGiB;
+
+    std::vector<RowsetSharedPtr> candidate_rowsets {
+            create_rowset(Version(2, 2), 1, false, 900L * kMiB),
+            create_rowset(Version(3, 3), 5, true, 128L * kMiB)};
+    ASSERT_GT(total_disk_size(candidate_rowsets), kGiB);
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    CloudSizeBasedCumulativeCompactionPolicy policy;
+    policy.pick_input_rowsets(&_tablet, candidate_rowsets, 100, 5, 
&input_rowsets,
+                              &last_delete_version, &compaction_score, true);
+
+    ASSERT_EQ(1, input_rowsets.size());
+    EXPECT_EQ(5, compaction_score);
+    EXPECT_EQ(3, input_rowsets.front()->start_version());
+    EXPECT_EQ(128L * kMiB, input_rowsets.front()->total_disk_size());
+}
+
+TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_single_overlapping_rowset_not_trimmed_empty) {
+    CloudTablet _tablet(_engine, _tablet_meta);
+    _tablet._base_size = 20L * kGiB;
+
+    std::vector<RowsetSharedPtr> candidate_rowsets {
+            create_rowset(Version(2, 2), 3, true, 2L * kGiB)};
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    CloudSizeBasedCumulativeCompactionPolicy policy;
+    policy.pick_input_rowsets(&_tablet, candidate_rowsets, 100, 5, 
&input_rowsets,
+                              &last_delete_version, &compaction_score, true);
+
+    EXPECT_EQ(1, input_rowsets.size());
+    EXPECT_EQ(3, compaction_score);
+    EXPECT_EQ(2, input_rowsets.front()->start_version());
+}
+
+TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_single_non_overlapping_rowset_still_skipped) {
+    CloudTablet _tablet(_engine, _tablet_meta);
+    _tablet._base_size = 20L * kGiB;
+
+    std::vector<RowsetSharedPtr> candidate_rowsets {
+            create_rowset(Version(2, 2), 1, false, 2L * kGiB)};
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    CloudSizeBasedCumulativeCompactionPolicy policy;
+    policy.pick_input_rowsets(&_tablet, candidate_rowsets, 100, 5, 
&input_rowsets,
+                              &last_delete_version, &compaction_score, true);
+
+    EXPECT_TRUE(input_rowsets.empty());
+    EXPECT_EQ(0, compaction_score);
+}
+
 // Test case: Empty rowset compaction with skip_trim
 TEST_F(TestCloudSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_empty_rowset_compaction) {
     // Save original config values
diff --git a/be/test/storage/compaction/cumulative_compaction_policy_test.cpp 
b/be/test/storage/compaction/cumulative_compaction_policy_test.cpp
index b0573ead66b..58b18128b86 100644
--- a/be/test/storage/compaction/cumulative_compaction_policy_test.cpp
+++ b/be/test/storage/compaction/cumulative_compaction_policy_test.cpp
@@ -26,6 +26,7 @@
 #include "json2pb/json_to_pb.h"
 #include "storage/compaction/cumulative_compaction.h"
 #include "storage/olap_common.h"
+#include "storage/rowset/rowset_factory.h"
 #include "storage/rowset/rowset_meta.h"
 #include "storage/storage_engine.h"
 #include "storage/tablet/tablet.h"
@@ -34,6 +35,9 @@
 
 namespace doris {
 
+static constexpr int64_t kMiB = 1024L * 1024;
+static constexpr int64_t kGiB = 1024L * kMiB;
+
 class TestSizeBasedCumulativeCompactionPolicy : public testing::Test {
 public:
     TestSizeBasedCumulativeCompactionPolicy() : _engine(StorageEngine({})) {}
@@ -1085,6 +1089,218 @@ TEST_F(TestSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_trim_after_pr
     EXPECT_EQ(100, input_rowsets.size());
 }
 
+TEST_F(TestSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_large_head_not_repeated_when_output_below_promotion) 
{
+    std::vector<RowsetMetaSharedPtr> rs_metas;
+
+    RowsetMetaSharedPtr base_rs(new RowsetMeta());
+    init_rs_meta(base_rs, 0, 1);
+    base_rs->set_total_disk_size(20L * kGiB);
+    base_rs->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(base_rs);
+
+    RowsetMetaSharedPtr large_head(new RowsetMeta());
+    init_rs_meta(large_head, 2, 2);
+    large_head->set_total_disk_size(1023L * kMiB);
+    large_head->set_num_segments(1);
+    large_head->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(large_head);
+
+    for (int i = 0; i < 20; i++) {
+        RowsetMetaSharedPtr ptr(new RowsetMeta());
+        init_rs_meta(ptr, i + 3, i + 3);
+        ptr->set_total_disk_size(kMiB);
+        ptr->set_num_segments(1);
+        ptr->set_segments_overlap(OVERLAPPING);
+        rs_metas.push_back(ptr);
+    }
+
+    for (auto& rowset : rs_metas) {
+        static_cast<void>(_tablet_meta->add_rs_meta(rowset));
+    }
+
+    TabletSharedPtr _tablet(
+            new Tablet(_engine, _tablet_meta, nullptr, 
CUMULATIVE_SIZE_BASED_POLICY));
+    static_cast<void>(_tablet->init());
+    _tablet->calculate_cumulative_point();
+    ASSERT_EQ(2, _tablet->cumulative_layer_point());
+    ASSERT_EQ(kGiB, _tablet->cumulative_promotion_size());
+
+    auto candidate_rowsets = 
_tablet->pick_candidate_rowsets_to_cumulative_compaction();
+    ASSERT_EQ(21, candidate_rowsets.size());
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    _tablet->_cumulative_compaction_policy->pick_input_rowsets(
+            _tablet.get(), candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
+            &compaction_score, config::enable_delete_when_cumu_compaction);
+
+    EXPECT_EQ(20, input_rowsets.size());
+    EXPECT_EQ(20, compaction_score);
+    EXPECT_EQ(3, input_rowsets.front()->start_version());
+    EXPECT_EQ(22, input_rowsets.back()->end_version());
+
+    RowsetMetaSharedPtr output_meta(new RowsetMeta());
+    init_rs_meta(output_meta, 3, 22);
+    output_meta->set_total_disk_size(20L * kMiB);
+    output_meta->set_num_segments(1);
+    output_meta->set_segments_overlap(NONOVERLAPPING);
+    RowsetSharedPtr output_rowset;
+    ASSERT_TRUE(RowsetFactory::create_rowset(nullptr, "", output_meta, 
&output_rowset).ok());
+
+    _tablet->_cumulative_compaction_policy->update_cumulative_point(
+            _tablet.get(), input_rowsets, output_rowset, last_delete_version);
+    EXPECT_EQ(2, _tablet->cumulative_layer_point());
+
+    std::vector<RowsetSharedPtr> next_candidate_rowsets 
{candidate_rowsets.front(), output_rowset};
+    input_rowsets.clear();
+    compaction_score = 0;
+    _tablet->_cumulative_compaction_policy->pick_input_rowsets(
+            _tablet.get(), next_candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
+            &compaction_score, config::enable_delete_when_cumu_compaction);
+
+    EXPECT_TRUE(input_rowsets.empty());
+    EXPECT_EQ(0, compaction_score);
+}
+
+TEST_F(TestSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_large_head_single_overlapping_tail_selected) {
+    std::vector<RowsetMetaSharedPtr> rs_metas;
+
+    RowsetMetaSharedPtr base_rs(new RowsetMeta());
+    init_rs_meta(base_rs, 0, 1);
+    base_rs->set_total_disk_size(20L * kGiB);
+    base_rs->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(base_rs);
+
+    RowsetMetaSharedPtr large_head(new RowsetMeta());
+    init_rs_meta(large_head, 2, 2);
+    large_head->set_total_disk_size(900L * kMiB);
+    large_head->set_num_segments(1);
+    large_head->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(large_head);
+
+    RowsetMetaSharedPtr tail(new RowsetMeta());
+    init_rs_meta(tail, 3, 3);
+    tail->set_total_disk_size(128L * kMiB);
+    tail->set_num_segments(5);
+    tail->set_segments_overlap(OVERLAPPING);
+    rs_metas.push_back(tail);
+
+    for (auto& rowset : rs_metas) {
+        static_cast<void>(_tablet_meta->add_rs_meta(rowset));
+    }
+
+    TabletSharedPtr _tablet(
+            new Tablet(_engine, _tablet_meta, nullptr, 
CUMULATIVE_SIZE_BASED_POLICY));
+    static_cast<void>(_tablet->init());
+    _tablet->calculate_cumulative_point();
+    ASSERT_EQ(2, _tablet->cumulative_layer_point());
+    ASSERT_EQ(kGiB, _tablet->cumulative_promotion_size());
+
+    auto candidate_rowsets = 
_tablet->pick_candidate_rowsets_to_cumulative_compaction();
+    ASSERT_EQ(2, candidate_rowsets.size());
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    _tablet->_cumulative_compaction_policy->pick_input_rowsets(
+            _tablet.get(), candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
+            &compaction_score, config::enable_delete_when_cumu_compaction);
+
+    ASSERT_EQ(1, input_rowsets.size());
+    EXPECT_EQ(5, compaction_score);
+    EXPECT_EQ(3, input_rowsets.front()->start_version());
+    EXPECT_EQ(128L * kMiB, input_rowsets.front()->total_disk_size());
+}
+
+TEST_F(TestSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_single_overlapping_rowset_not_trimmed_empty) {
+    std::vector<RowsetMetaSharedPtr> rs_metas;
+
+    RowsetMetaSharedPtr base_rs(new RowsetMeta());
+    init_rs_meta(base_rs, 0, 1);
+    base_rs->set_total_disk_size(20L * kGiB);
+    base_rs->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(base_rs);
+
+    RowsetMetaSharedPtr ptr(new RowsetMeta());
+    init_rs_meta(ptr, 2, 2);
+    ptr->set_total_disk_size(2L * kGiB);
+    ptr->set_num_segments(3);
+    ptr->set_segments_overlap(OVERLAPPING);
+    rs_metas.push_back(ptr);
+
+    for (auto& rowset : rs_metas) {
+        static_cast<void>(_tablet_meta->add_rs_meta(rowset));
+    }
+
+    TabletSharedPtr _tablet(
+            new Tablet(_engine, _tablet_meta, nullptr, 
CUMULATIVE_SIZE_BASED_POLICY));
+    static_cast<void>(_tablet->init());
+    _tablet->calculate_cumulative_point();
+
+    auto candidate_rowsets = 
_tablet->pick_candidate_rowsets_to_cumulative_compaction();
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    _tablet->_cumulative_compaction_policy->pick_input_rowsets(
+            _tablet.get(), candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
+            &compaction_score, config::enable_delete_when_cumu_compaction);
+
+    EXPECT_EQ(1, input_rowsets.size());
+    EXPECT_EQ(3, compaction_score);
+    EXPECT_EQ(2, input_rowsets.front()->start_version());
+}
+
+TEST_F(TestSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_single_non_overlapping_rowset_still_skipped) {
+    std::vector<RowsetMetaSharedPtr> rs_metas;
+
+    RowsetMetaSharedPtr base_rs(new RowsetMeta());
+    init_rs_meta(base_rs, 0, 1);
+    base_rs->set_total_disk_size(20L * kGiB);
+    base_rs->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(base_rs);
+
+    RowsetMetaSharedPtr ptr(new RowsetMeta());
+    init_rs_meta(ptr, 2, 2);
+    ptr->set_total_disk_size(2L * kGiB);
+    ptr->set_num_segments(1);
+    ptr->set_segments_overlap(NONOVERLAPPING);
+    rs_metas.push_back(ptr);
+
+    for (auto& rowset : rs_metas) {
+        static_cast<void>(_tablet_meta->add_rs_meta(rowset));
+    }
+
+    TabletSharedPtr _tablet(
+            new Tablet(_engine, _tablet_meta, nullptr, 
CUMULATIVE_SIZE_BASED_POLICY));
+    static_cast<void>(_tablet->init());
+    _tablet->calculate_cumulative_point();
+
+    std::vector<RowsetSharedPtr> candidate_rowsets;
+    RowsetSharedPtr rowset;
+    ASSERT_TRUE(RowsetFactory::create_rowset(nullptr, "", ptr, &rowset).ok());
+    candidate_rowsets.push_back(rowset);
+
+    std::vector<RowsetSharedPtr> input_rowsets;
+    Version last_delete_version {-1, -1};
+    size_t compaction_score = 0;
+
+    _tablet->_cumulative_compaction_policy->pick_input_rowsets(
+            _tablet.get(), candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
+            &compaction_score, config::enable_delete_when_cumu_compaction);
+
+    EXPECT_TRUE(input_rowsets.empty());
+    EXPECT_EQ(0, compaction_score);
+}
+
 // Test case: Trim with varying scores (high score rowsets at tail)
 TEST_F(TestSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_trim_high_score_tail) {
     std::vector<RowsetMetaSharedPtr> rs_metas;
@@ -1765,8 +1981,9 @@ TEST_F(TestSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_single_non_ov
     EXPECT_EQ(0, compaction_score);
 }
 
-// Test case: Fallback when all removed by level_size but score < max
-TEST_F(TestSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_fallback_score_below_max) {
+// Test case: Keep the final overlapping suffix when exhausted-input fallback 
is unavailable
+TEST_F(TestSizeBasedCumulativeCompactionPolicy,
+       pick_input_rowsets_keep_final_overlapping_below_max) {
     std::vector<RowsetMetaSharedPtr> rs_metas;
 
     // Base rowset: 20GB
@@ -1807,15 +2024,14 @@ TEST_F(TestSizeBasedCumulativeCompactionPolicy, 
pick_input_rowsets_fallback_scor
     Version last_delete_version {-1, -1};
     size_t compaction_score = 0;
 
-    // All rowsets removed by level_size, score=50 < max=100
-    // Does not trigger fallback, goes to normal flow with empty result
+    // The original score is below max, so the loop must stop before removing 
the final rowset.
     _tablet->_cumulative_compaction_policy->pick_input_rowsets(
             _tablet.get(), candidate_rowsets, 100, 5, &input_rowsets, 
&last_delete_version,
             &compaction_score, config::enable_delete_when_cumu_compaction);
 
-    // After level_size removes all, result is empty (no fallback since score 
< max)
-    EXPECT_EQ(0, input_rowsets.size());
-    EXPECT_EQ(0, compaction_score);
+    ASSERT_EQ(1, input_rowsets.size());
+    EXPECT_EQ(20, compaction_score);
+    EXPECT_EQ(3, input_rowsets.front()->start_version());
 }
 
 // Test case: level_size removes large head, then trim after


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

Reply via email to