Skip to content

Commit 74cbceb

Browse files
committed
[fix](cloud) Stabilize grouped single-rowset compaction
Issue Number: None Related PR: #65907 Problem Summary: Single-rowset grouped compaction produces rowsets whose segments are non-overlapping only within recorded groups. Follow-up compaction, vertical reading, row ID conversion, cloud snapshot conversion, and schema change did not consistently preserve or interpret that layout. Repeated compaction also selected input by logical group count while planning ranges by physical segment count, so it could rewrite a rowset without reducing groups. Preserve and validate grouped metadata across cloud paths, read ordered segments lazily within each group, plan follow-up merges from logical group boundaries, use logical merge-way counts, and skip configurations that cannot make progress. Run the regression case non-concurrently only in cloud mode on the existing cloud cluster, temporarily disable SQL cache and mutable BE settings with guaranteed restoration, and use the configured MetaService endpoint. Reuse ReadSchema in the repeated-compaction unit test so it uses the current storage reader API. None - Test: No tests executed (targeted BE UT build was previously blocked during CMake configuration because OpenMP_C was unavailable; later runs were skipped per request); git diff --check passed - Behavior changed: Yes (grouped rowsets are preserved and read correctly, repeated compaction reduces logical groups, and the cloud regression case is skipped outside cloud mode) - Does this need documentation: No
1 parent 3568feb commit 74cbceb

17 files changed

Lines changed: 1056 additions & 134 deletions

be/src/cloud/cloud_cumulative_compaction.cpp

Lines changed: 87 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
#include "cloud/cloud_cumulative_compaction.h"
1919

20+
#include <fmt/format.h>
21+
#include <fmt/ranges.h>
2022
#include <gen_cpp/cloud.pb.h>
2123

2224
#include <random>
@@ -47,9 +49,13 @@ using namespace ErrorCode;
4749
namespace cloud {
4850

4951
bool is_single_rowset_compaction_candidate(const RowsetSharedPtr& rowset) {
50-
return !rowset->rowset_meta()->has_delete_predicate() &&
51-
rowset->rowset_meta()->is_segments_overlapping() &&
52-
rowset->num_segments() >= config::cloud_single_rowset_compaction_min_segments;
52+
const auto& rowset_meta = rowset->rowset_meta();
53+
const int64_t overlap_unit_count =
54+
rowset_meta->segments_overlap() == NONOVERLAPPING_WITHIN_GROUP
55+
? static_cast<int64_t>(rowset_meta->segment_group_sizes().size())
56+
: rowset->num_segments();
57+
return !rowset_meta->has_delete_predicate() && rowset_meta->is_segments_overlapping() &&
58+
overlap_unit_count >= config::cloud_single_rowset_compaction_min_segments;
5359
}
5460

5561
bool should_use_single_rowset_grouped_compaction(const std::vector<RowsetSharedPtr>& input_rowsets,
@@ -61,6 +67,51 @@ bool should_use_single_rowset_grouped_compaction(const std::vector<RowsetSharedP
6167
is_single_rowset_compaction_candidate(input_rowsets.front());
6268
}
6369

70+
std::vector<SegmentGroupMergeRange> build_segment_group_merge_ranges(const RowsetMeta& rowset_meta,
71+
int64_t segment_group_size) {
72+
DORIS_CHECK_GT(segment_group_size, 1);
73+
DORIS_CHECK_GT(rowset_meta.num_segments(), 0);
74+
75+
std::vector<SegmentGroupMergeRange> ranges;
76+
if (rowset_meta.segments_overlap() == NONOVERLAPPING_WITHIN_GROUP) {
77+
const auto& input_segment_group_sizes = rowset_meta.segment_group_sizes();
78+
const int64_t input_group_count = cast_set<int64_t>(input_segment_group_sizes.size());
79+
DORIS_CHECK_GT(input_group_count, 0);
80+
ranges.reserve(cast_set<size_t>((input_group_count + segment_group_size - 1) /
81+
segment_group_size));
82+
83+
int64_t segment_end = 0;
84+
for (int64_t group_start = 0; group_start < input_group_count;
85+
group_start += segment_group_size) {
86+
const int64_t group_end = std::min(group_start + segment_group_size, input_group_count);
87+
const int64_t segment_start = segment_end;
88+
for (int64_t group_index = group_start; group_index < group_end; ++group_index) {
89+
const int32_t input_group_size =
90+
input_segment_group_sizes.Get(cast_set<int>(group_index));
91+
DORIS_CHECK_GT(input_group_size, 0);
92+
segment_end += input_group_size;
93+
}
94+
95+
ranges.push_back({.segment_start = segment_start,
96+
.segment_end = segment_end,
97+
.merge_way_num = group_end - group_start});
98+
}
99+
DORIS_CHECK_EQ(segment_end, rowset_meta.num_segments());
100+
} else {
101+
ranges.reserve(cast_set<size_t>((rowset_meta.num_segments() + segment_group_size - 1) /
102+
segment_group_size));
103+
for (int64_t segment_start = 0; segment_start < rowset_meta.num_segments();
104+
segment_start += segment_group_size) {
105+
const int64_t segment_end =
106+
std::min(segment_start + segment_group_size, rowset_meta.num_segments());
107+
ranges.push_back({.segment_start = segment_start,
108+
.segment_end = segment_end,
109+
.merge_way_num = segment_end - segment_start});
110+
}
111+
}
112+
return ranges;
113+
}
114+
64115
} // namespace cloud
65116

66117
bvar::Adder<uint64_t> cumu_output_size("cumu_compaction", "output_size");
@@ -273,6 +324,18 @@ Status CloudCumulativeCompaction::execute_compact() {
273324
return st;
274325
}
275326

327+
bool CloudCumulativeCompaction::should_calculate_new_cumulative_point(
328+
int64_t input_cumulative_point) const {
329+
if (!_single_rowset_compaction_segment_group_size.has_value()) {
330+
return true;
331+
}
332+
333+
DORIS_CHECK_EQ(_input_rowsets.size(), 1);
334+
DORIS_CHECK(_output_rowset != nullptr);
335+
return _input_rowsets.front()->start_version() == input_cumulative_point &&
336+
_output_rowset->rowset_meta()->segments_overlap() == NONOVERLAPPING;
337+
}
338+
276339
Status CloudCumulativeCompaction::modify_rowsets() {
277340
// calculate new cumulative point
278341
int64_t input_cumulative_point;
@@ -290,7 +353,7 @@ Status CloudCumulativeCompaction::modify_rowsets() {
290353
}
291354
auto compaction_policy = cloud_tablet()->tablet_meta()->compaction_policy();
292355
int64_t new_cumulative_point = input_cumulative_point;
293-
if (!_single_rowset_compaction_segment_group_size.has_value()) {
356+
if (should_calculate_new_cumulative_point(input_cumulative_point)) {
294357
if (!_enable_parallel_cumu_compaction && input_tablet_state == TABLET_NOTREADY &&
295358
_output_rowset->start_version() > input_cumulative_point) {
296359
// Historical rowsets are absent from a schema-change target until conversion finishes.
@@ -672,14 +735,15 @@ Status CloudCumulativeCompaction::pick_rowsets_to_compact() {
672735
config::cumulative_compaction_min_deltas, &_input_rowsets,
673736
&_last_delete_version, &compaction_score);
674737

675-
if (config::enable_cloud_single_rowset_compaction) {
738+
const int64_t segment_group_size =
739+
config::cloud_single_rowset_compaction_segment_group_size;
740+
if (config::enable_cloud_single_rowset_compaction && segment_group_size > 1) {
676741
for (const auto& rowset : _input_rowsets) {
677742
if (cloud::should_use_single_rowset_grouped_compaction(
678743
{rowset}, *cloud_tablet()->tablet_schema(), compaction_policy)) {
679744
auto grouped_input_rowset = rowset;
680745
_input_rowsets = {std::move(grouped_input_rowset)};
681-
_single_rowset_compaction_segment_group_size =
682-
config::cloud_single_rowset_compaction_segment_group_size;
746+
_single_rowset_compaction_segment_group_size = segment_group_size;
683747
return Status::OK();
684748
}
685749
}
@@ -754,11 +818,7 @@ Status CloudCumulativeCompaction::prepare_merge_input_rowsets(MergeInputRowsetsR
754818
}
755819

756820
const int64_t segment_group_size = *_single_rowset_compaction_segment_group_size;
757-
if (segment_group_size <= 0) {
758-
return Status::InvalidArgument(
759-
"cloud_single_rowset_compaction_segment_group_size must be positive, value={}",
760-
segment_group_size);
761-
}
821+
DORIS_CHECK_GT(segment_group_size, 1);
762822
result->is_segment_grouped = true;
763823
result->segment_group_size = segment_group_size;
764824
return Status::OK();
@@ -773,12 +833,10 @@ Status CloudCumulativeCompaction::do_merge_input_rowsets(
773833

774834
const int64_t segment_group_size = result->segment_group_size;
775835
const auto& input_rowset = _input_rowsets.front();
776-
const int64_t segment_group_count =
777-
(input_rowset->num_segments() + segment_group_size - 1) / segment_group_size;
778-
for (int64_t segment_start = 0; segment_start < input_rowset->num_segments();
779-
segment_start += segment_group_size) {
780-
const int64_t segment_end =
781-
std::min(segment_start + segment_group_size, input_rowset->num_segments());
836+
const auto segment_ranges = cloud::build_segment_group_merge_ranges(
837+
*input_rowset->rowset_meta(), segment_group_size);
838+
for (size_t range_index = 0; range_index < segment_ranges.size(); ++range_index) {
839+
const auto& range = segment_ranges[range_index];
782840
const int32_t output_segment_start = _output_rs_writer->get_allocated_segment_id();
783841

784842
RowsetReaderSharedPtr rs_reader;
@@ -788,10 +846,10 @@ Status CloudCumulativeCompaction::do_merge_input_rowsets(
788846

789847
Merger::Statistics group_stats;
790848
group_stats.rowid_conversion = _stats.rowid_conversion;
791-
RETURN_IF_ERROR(execute_merge(group_readers, segment_end - segment_start, &group_stats,
792-
std::make_pair(segment_start, segment_end),
793-
{.total_ranges = segment_group_count,
794-
.range_index = segment_start / segment_group_size}));
849+
RETURN_IF_ERROR(execute_merge(group_readers, range.merge_way_num, &group_stats,
850+
std::make_pair(range.segment_start, range.segment_end),
851+
{.total_ranges = cast_set<int64_t>(segment_ranges.size()),
852+
.range_index = cast_set<int64_t>(range_index)}));
795853

796854
_stats.output_rows += group_stats.output_rows;
797855
_stats.merged_rows += group_stats.merged_rows;
@@ -805,7 +863,7 @@ Status CloudCumulativeCompaction::do_merge_input_rowsets(
805863
const int32_t output_segment_end = _output_rs_writer->get_allocated_segment_id();
806864
const int32_t output_group_size = output_segment_end - output_segment_start;
807865
if (output_group_size > 0) {
808-
++result->output_segment_group_count;
866+
result->output_segment_group_sizes.push_back(output_group_size);
809867
}
810868
}
811869
return Status::OK();
@@ -816,8 +874,9 @@ void CloudCumulativeCompaction::update_output_rowset_after_build(
816874
if (!result.is_segment_grouped) {
817875
return;
818876
}
819-
if (result.output_segment_group_count > 1) {
820-
_output_rowset->rowset_meta()->set_segments_overlap(OVERLAPPING);
877+
if (result.output_segment_group_sizes.size() > 1) {
878+
_output_rowset->rowset_meta()->set_segments_overlap(NONOVERLAPPING_WITHIN_GROUP);
879+
_output_rowset->rowset_meta()->set_segment_group_sizes(result.output_segment_group_sizes);
821880
}
822881

823882
const auto& input_rowset = _input_rowsets.front();
@@ -827,7 +886,9 @@ void CloudCumulativeCompaction::update_output_rowset_after_build(
827886
.tag("input_segments", input_rowset->num_segments())
828887
.tag("segment_group_size", result.segment_group_size)
829888
.tag("output_segments", _output_rowset->num_segments())
830-
.tag("output_groups", result.output_segment_group_count);
889+
.tag("output_groups", result.output_segment_group_sizes.size())
890+
.tag("output_segment_group_sizes",
891+
fmt::format("[{}]", fmt::join(result.output_segment_group_sizes, ", ")));
831892
}
832893

833894
void CloudCumulativeCompaction::update_cumulative_point(int64_t input_cumulative_point,

be/src/cloud/cloud_cumulative_compaction.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,25 @@
3131

3232
namespace doris {
3333

34+
class RowsetMeta;
35+
3436
namespace cloud {
3537

38+
struct SegmentGroupMergeRange {
39+
int64_t segment_start;
40+
int64_t segment_end;
41+
int64_t merge_way_num;
42+
};
43+
3644
bool is_single_rowset_compaction_candidate(const RowsetSharedPtr& rowset);
3745

3846
bool should_use_single_rowset_grouped_compaction(const std::vector<RowsetSharedPtr>& input_rowsets,
3947
const TabletSchema& tablet_schema,
4048
std::string_view compaction_policy);
4149

50+
std::vector<SegmentGroupMergeRange> build_segment_group_merge_ranges(const RowsetMeta& rowset_meta,
51+
int64_t segment_group_size);
52+
4253
} // namespace cloud
4354

4455
class CloudCumulativeCompaction : public CloudCompactionMixin {
@@ -73,6 +84,8 @@ class CloudCumulativeCompaction : public CloudCompactionMixin {
7384

7485
void update_output_rowset_after_build(const MergeInputRowsetsResult& result) override;
7586

87+
bool should_calculate_new_cumulative_point(int64_t input_cumulative_point) const;
88+
7689
std::string_view compaction_name() const override { return "CloudCumulativeCompaction"; }
7790

7891
protected:

be/src/cloud/cloud_schema_change_job.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,13 @@ Status CloudSchemaChangeJob::_convert_historical_rowsets(const SchemaChangeParam
389389
context.txn_expiration = _expiration;
390390
context.version = rs_reader->version();
391391
context.rowset_state = VISIBLE;
392-
context.segments_overlap = rs_reader->rowset()->rowset_meta()->segments_overlap();
392+
const auto input_segments_overlap = rs_reader->rowset()->rowset_meta()->segments_overlap();
393+
// Cloud schema change rewrites remote rowsets, so the input group layout is no longer
394+
// applicable. Fall back to the conservative overlap state without assuming that the
395+
// rewritten segments are globally ordered.
396+
context.segments_overlap = input_segments_overlap == NONOVERLAPPING_WITHIN_GROUP
397+
? OVERLAPPING
398+
: input_segments_overlap;
393399
context.tablet_schema = _new_tablet->tablet_schema();
394400
context.newest_write_timestamp = rs_reader->newest_write_timestamp();
395401
context.storage_resource = _cloud_storage_engine.get_storage_resource(sc_params.vault_id);

be/src/cloud/cloud_snapshot_mgr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ Status CloudSnapshotMgr::_create_rowset_meta(
272272
new_rowset_meta_pb->set_num_segments(source_meta_pb.num_segments());
273273
new_rowset_meta_pb->mutable_segment_ids()->CopyFrom(source_meta_pb.segment_ids());
274274
new_rowset_meta_pb->set_rowset_state(source_meta_pb.rowset_state());
275+
new_rowset_meta_pb->mutable_segment_group_sizes()->CopyFrom(
276+
source_meta_pb.segment_group_sizes());
275277

276278
new_rowset_meta_pb->clear_segments_key_bounds();
277279
for (const auto& key_bound : source_meta_pb.segments_key_bounds()) {

be/src/cloud/pb_convert.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ void doris_rowset_meta_to_cloud(RowsetMetaCloudPB* out, const RowsetMetaPB& in)
8080
}
8181
out->set_txn_expiration(in.txn_expiration());
8282
out->set_segments_overlap_pb(in.segments_overlap_pb());
83+
out->mutable_segment_group_sizes()->CopyFrom(in.segment_group_sizes());
8384
if (in.has_segments_key_bounds_truncated()) {
8485
out->set_segments_key_bounds_truncated(in.segments_key_bounds_truncated());
8586
}
@@ -179,6 +180,7 @@ void doris_rowset_meta_to_cloud(RowsetMetaCloudPB* out, RowsetMetaPB&& in) {
179180
}
180181
out->set_txn_expiration(in.txn_expiration());
181182
out->set_segments_overlap_pb(in.segments_overlap_pb());
183+
out->mutable_segment_group_sizes()->Swap(in.mutable_segment_group_sizes());
182184
if (in.has_segments_key_bounds_truncated()) {
183185
out->set_segments_key_bounds_truncated(in.segments_key_bounds_truncated());
184186
}
@@ -290,6 +292,7 @@ void cloud_rowset_meta_to_doris(RowsetMetaPB* out, const RowsetMetaCloudPB& in)
290292
}
291293
out->set_txn_expiration(in.txn_expiration());
292294
out->set_segments_overlap_pb(in.segments_overlap_pb());
295+
out->mutable_segment_group_sizes()->CopyFrom(in.segment_group_sizes());
293296
if (in.has_segments_key_bounds_truncated()) {
294297
out->set_segments_key_bounds_truncated(in.segments_key_bounds_truncated());
295298
}
@@ -389,6 +392,7 @@ void cloud_rowset_meta_to_doris(RowsetMetaPB* out, RowsetMetaCloudPB&& in) {
389392
}
390393
out->set_txn_expiration(in.txn_expiration());
391394
out->set_segments_overlap_pb(in.segments_overlap_pb());
395+
out->mutable_segment_group_sizes()->Swap(in.mutable_segment_group_sizes());
392396
if (in.has_segments_key_bounds_truncated()) {
393397
out->set_segments_key_bounds_truncated(in.segments_key_bounds_truncated());
394398
}

be/src/storage/compaction/compaction.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ class Compaction {
106106
struct MergeInputRowsetsResult {
107107
bool is_segment_grouped = false;
108108
int64_t segment_group_size = 0;
109-
int64_t output_segment_group_count = 0;
109+
std::vector<int32_t> output_segment_group_sizes;
110110
};
111111

112112
Status merge_input_rowsets();

0 commit comments

Comments
 (0)