Coverage Report

Created: 2026-07-30 17:55

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/task/index_builder.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
#include "storage/task/index_builder.h"
19
20
#include <mutex>
21
22
#include "common/logging.h"
23
#include "common/status.h"
24
#include "storage/index/index_file_reader.h"
25
#include "storage/index/index_file_writer.h"
26
#include "storage/index/inverted/inverted_index_desc.h"
27
#include "storage/index/inverted/inverted_index_fs_directory.h"
28
#include "storage/olap_define.h"
29
#include "storage/rowset/beta_rowset.h"
30
#include "storage/rowset/rowset_writer_context.h"
31
#include "storage/segment/segment_loader.h"
32
#include "storage/storage_engine.h"
33
#include "storage/tablet/tablet_schema.h"
34
#include "util/debug_points.h"
35
#include "util/trace.h"
36
37
namespace doris {
38
39
IndexBuilder::IndexBuilder(StorageEngine& engine, TabletSharedPtr tablet,
40
                           const std::vector<TColumn>& columns,
41
                           const std::vector<doris::TOlapTableIndex>& alter_inverted_indexes,
42
                           bool is_drop_op)
43
25
        : _engine(engine),
44
25
          _tablet(std::move(tablet)),
45
25
          _columns(columns),
46
25
          _alter_inverted_indexes(alter_inverted_indexes),
47
25
          _is_drop_op(is_drop_op) {
48
25
    _olap_data_convertor = std::make_unique<OlapBlockDataConvertor>();
49
25
}
50
51
25
IndexBuilder::~IndexBuilder() {
52
25
    _olap_data_convertor.reset();
53
25
    _index_column_writers.clear();
54
25
}
55
56
25
Status IndexBuilder::init() {
57
27
    for (auto inverted_index : _alter_inverted_indexes) {
58
27
        _alter_index_ids.insert(inverted_index.index_id);
59
27
    }
60
25
    return Status::OK();
61
25
}
62
63
21
Status IndexBuilder::update_inverted_index_info() {
64
    // just do link files
65
21
    LOG(INFO) << "begin to update_inverted_index_info, tablet=" << _tablet->tablet_id()
66
21
              << ", is_drop_op=" << _is_drop_op;
67
    // index ids that will not be linked
68
21
    std::set<int64_t> without_index_uids;
69
21
    _output_rowsets.reserve(_input_rowsets.size());
70
21
    _pending_rs_guards.reserve(_input_rowsets.size());
71
22
    for (auto&& input_rowset : _input_rowsets) {
72
22
        bool is_local_rowset = input_rowset->is_local();
73
22
        DBUG_EXECUTE_IF("IndexBuilder::update_inverted_index_info_is_local_rowset",
74
22
                        { is_local_rowset = false; })
75
22
        if (!is_local_rowset) [[unlikely]] {
76
            // DCHECK(false) << _tablet->tablet_id() << ' ' << input_rowset->rowset_id();
77
0
            return Status::InternalError("should be local rowset. tablet_id={} rowset_id={}",
78
0
                                         _tablet->tablet_id(),
79
0
                                         input_rowset->rowset_id().to_string());
80
0
        }
81
82
22
        TabletSchemaSPtr output_rs_tablet_schema = std::make_shared<TabletSchema>();
83
22
        const auto& input_rs_tablet_schema = input_rowset->tablet_schema();
84
22
        output_rs_tablet_schema->copy_from(*input_rs_tablet_schema);
85
22
        int64_t total_index_size = 0;
86
22
        auto* beta_rowset = reinterpret_cast<BetaRowset*>(input_rowset.get());
87
22
        auto size_st = beta_rowset->get_inverted_index_size(&total_index_size);
88
22
        DBUG_EXECUTE_IF("IndexBuilder::update_inverted_index_info_size_st_not_ok", {
89
22
            size_st = Status::Error<ErrorCode::INIT_FAILED>("debug point: get fs failed");
90
22
        })
91
22
        if (!size_st.ok() && !size_st.is<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>() &&
92
22
            !size_st.is<ErrorCode::NOT_FOUND>()) {
93
0
            return size_st;
94
0
        }
95
22
        size_t drop_index_size = 0;
96
97
22
        if (_is_drop_op) {
98
6
            for (const auto& t_inverted_index : _alter_inverted_indexes) {
99
6
                DCHECK_EQ(t_inverted_index.columns.size(), 1);
100
6
                auto column_name = t_inverted_index.columns[0];
101
6
                auto column_idx = output_rs_tablet_schema->field_index(column_name);
102
6
                if (column_idx < 0) {
103
0
                    if (!t_inverted_index.column_unique_ids.empty()) {
104
0
                        auto column_unique_id = t_inverted_index.column_unique_ids[0];
105
0
                        column_idx = output_rs_tablet_schema->field_index(column_unique_id);
106
0
                    }
107
0
                    if (column_idx < 0) {
108
0
                        LOG(WARNING) << "referenced column was missing. "
109
0
                                     << "[column=" << column_name
110
0
                                     << " referenced_column=" << column_idx << "]";
111
0
                        continue;
112
0
                    }
113
0
                }
114
6
                auto column = output_rs_tablet_schema->column(column_idx);
115
116
                // inverted index
117
6
                auto index_metas = output_rs_tablet_schema->inverted_indexs(column);
118
6
                for (const auto& index_meta : index_metas) {
119
                    // Only drop the index that matches the requested index_id,
120
                    // not all indexes on this column
121
6
                    if (index_meta->index_id() != t_inverted_index.index_id) {
122
1
                        continue;
123
1
                    }
124
5
                    if (output_rs_tablet_schema->get_inverted_index_storage_format() ==
125
5
                        InvertedIndexStorageFormatPB::V1) {
126
1
                        const auto& fs = io::global_local_filesystem();
127
128
1
                        for (auto seg : input_rowset->segments()) {
129
1
                            auto seg_path = local_segment_path(
130
1
                                    _tablet->tablet_path(), input_rowset->rowset_id().to_string(),
131
1
                                    seg.id());
132
1
                            auto index_path = InvertedIndexDescriptor::get_index_file_path_v1(
133
1
                                    InvertedIndexDescriptor::get_index_file_path_prefix(seg_path),
134
1
                                    index_meta->index_id(), index_meta->get_index_suffix());
135
1
                            int64_t index_size = 0;
136
1
                            RETURN_IF_ERROR(fs->file_size(index_path, &index_size));
137
1
                            VLOG_DEBUG << "inverted index file:" << index_path
138
0
                                       << " size:" << index_size;
139
1
                            drop_index_size += index_size;
140
1
                        }
141
1
                    }
142
5
                    _dropped_inverted_indexes.push_back(*index_meta);
143
                    // ATTN: DO NOT REMOVE INDEX AFTER OUTPUT_ROWSET_WRITER CREATED.
144
                    // remove dropped index_meta from output rowset tablet schema
145
5
                    output_rs_tablet_schema->remove_index(index_meta->index_id());
146
5
                }
147
148
                // ann index
149
6
                const auto* ann_index = output_rs_tablet_schema->ann_index(column);
150
6
                if (!ann_index) {
151
5
                    continue;
152
5
                }
153
                // Only drop the ann index that matches the requested index_id
154
1
                if (ann_index->index_id() != t_inverted_index.index_id) {
155
0
                    continue;
156
0
                }
157
1
                DCHECK(output_rs_tablet_schema->get_inverted_index_storage_format() !=
158
1
                       InvertedIndexStorageFormatPB::V1);
159
1
                _dropped_inverted_indexes.push_back(*ann_index);
160
                // ATTN: DO NOT REMOVE INDEX AFTER OUTPUT_ROWSET_WRITER CREATED.
161
                // remove dropped index_meta from output rowset tablet schema
162
1
                output_rs_tablet_schema->remove_index(ann_index->index_id());
163
1
            }
164
165
6
            DBUG_EXECUTE_IF("index_builder.update_inverted_index_info.drop_index", {
166
6
                auto indexes_count = DebugPoints::instance()->get_debug_param_or_default<int32_t>(
167
6
                        "index_builder.update_inverted_index_info.drop_index", "indexes_count", 0);
168
6
                if (indexes_count < 0) {
169
6
                    return Status::Error<ErrorCode::INTERNAL_ERROR>(
170
6
                            "indexes count cannot be negative");
171
6
                }
172
6
                auto indexes_size = output_rs_tablet_schema->inverted_indexes().size();
173
6
                if (indexes_count != indexes_size) {
174
6
                    return Status::Error<ErrorCode::INTERNAL_ERROR>(
175
6
                            "indexes count not equal to expected");
176
6
                }
177
6
            })
178
16
        } else {
179
            // base on input rowset's tablet_schema to build
180
            // output rowset's tablet_schema which only add
181
            // the indexes specified in this build index request
182
18
            for (auto t_inverted_index : _alter_inverted_indexes) {
183
18
                TabletIndex index;
184
18
                index.init_from_thrift(t_inverted_index, *input_rs_tablet_schema);
185
18
                auto column_uid = index.col_unique_ids()[0];
186
18
                if (column_uid < 0) {
187
3
                    LOG(WARNING) << "referenced column was missing. "
188
3
                                 << "[column=" << t_inverted_index.columns[0]
189
3
                                 << " referenced_column=" << column_uid << "]";
190
3
                    continue;
191
3
                }
192
15
                const TabletColumn& col = output_rs_tablet_schema->column_by_uid(column_uid);
193
194
                // inverted index
195
15
                auto exist_indexs = output_rs_tablet_schema->inverted_indexs(col);
196
15
                for (const auto& exist_index : exist_indexs) {
197
0
                    if (exist_index->index_id() != index.index_id()) {
198
0
                        if (exist_index->is_same_except_id(&index)) {
199
0
                            LOG(WARNING) << fmt::format(
200
0
                                    "column: {} has a exist inverted index, but the index id not "
201
0
                                    "equal "
202
0
                                    "request's index id, , exist index id: {}, request's index id: "
203
0
                                    "{}, "
204
0
                                    "remove exist index in new output_rs_tablet_schema",
205
0
                                    column_uid, exist_index->index_id(), index.index_id());
206
0
                            without_index_uids.insert(exist_index->index_id());
207
0
                            output_rs_tablet_schema->remove_index(exist_index->index_id());
208
0
                        }
209
0
                    }
210
0
                }
211
212
                // ann index
213
15
                const auto* exist_index = output_rs_tablet_schema->ann_index(col);
214
15
                if (exist_index && exist_index->index_id() != index.index_id()) {
215
0
                    if (exist_index->is_same_except_id(&index)) {
216
0
                        LOG(WARNING) << fmt::format(
217
0
                                "column: {} has a exist ann index, but the index id not "
218
0
                                "equal request's index id, , exist index id: {}, request's index "
219
0
                                "id: {}, remove exist index in new output_rs_tablet_schema",
220
0
                                column_uid, exist_index->index_id(), index.index_id());
221
0
                        without_index_uids.insert(exist_index->index_id());
222
0
                        output_rs_tablet_schema->remove_index(exist_index->index_id());
223
0
                    }
224
0
                }
225
226
15
                output_rs_tablet_schema->append_index(std::move(index));
227
15
            }
228
16
        }
229
        // construct input rowset reader
230
22
        RowsetReaderSharedPtr input_rs_reader;
231
22
        RETURN_IF_ERROR(input_rowset->create_reader(&input_rs_reader));
232
        // construct output rowset writer
233
22
        RowsetWriterContext context;
234
22
        context.version = input_rs_reader->version();
235
22
        context.rowset_state = VISIBLE;
236
22
        context.segments_overlap = input_rowset->rowset_meta()->segments_overlap();
237
22
        context.tablet_schema = output_rs_tablet_schema;
238
22
        context.newest_write_timestamp = input_rs_reader->newest_write_timestamp();
239
22
        auto output_rs_writer = DORIS_TRY(_tablet->create_rowset_writer(context, false));
240
22
        _pending_rs_guards.push_back(_engine.add_pending_rowset(context));
241
242
        // if without_index_uids is not empty, copy _alter_index_ids to it
243
        // else just use _alter_index_ids to avoid copy
244
22
        if (!without_index_uids.empty()) {
245
0
            without_index_uids.insert(_alter_index_ids.begin(), _alter_index_ids.end());
246
0
        }
247
248
        // build output rowset
249
22
        RETURN_IF_ERROR(input_rowset->link_files_to(
250
22
                _tablet->tablet_path(), output_rs_writer->rowset_id(), 0,
251
22
                without_index_uids.empty() ? &_alter_index_ids : &without_index_uids));
252
253
22
        auto input_rowset_meta = input_rowset->rowset_meta();
254
22
        RowsetMetaSharedPtr rowset_meta = std::make_shared<RowsetMeta>();
255
22
        rowset_meta->set_num_rows(input_rowset_meta->num_rows());
256
22
        if (output_rs_tablet_schema->get_inverted_index_storage_format() ==
257
22
            InvertedIndexStorageFormatPB::V1) {
258
4
            if (_is_drop_op) {
259
1
                VLOG_DEBUG << "data_disk_size:" << input_rowset_meta->data_disk_size()
260
0
                           << " total_disk_size:" << input_rowset_meta->total_disk_size()
261
0
                           << " index_disk_size:" << input_rowset_meta->index_disk_size()
262
0
                           << " drop_index_size:" << drop_index_size;
263
1
                rowset_meta->set_total_disk_size(input_rowset_meta->total_disk_size() -
264
1
                                                 drop_index_size);
265
1
                rowset_meta->set_data_disk_size(input_rowset_meta->data_disk_size());
266
1
                rowset_meta->set_index_disk_size(input_rowset_meta->index_disk_size() -
267
1
                                                 drop_index_size);
268
3
            } else {
269
3
                rowset_meta->set_total_disk_size(input_rowset_meta->total_disk_size());
270
3
                rowset_meta->set_data_disk_size(input_rowset_meta->data_disk_size());
271
3
                rowset_meta->set_index_disk_size(input_rowset_meta->index_disk_size());
272
3
            }
273
18
        } else {
274
21
            for (auto seg : input_rowset->segments()) {
275
21
                auto seg_path = DORIS_TRY(seg.path());
276
21
                auto idx_file_reader = std::make_unique<IndexFileReader>(
277
21
                        context.fs(),
278
21
                        std::string {InvertedIndexDescriptor::get_index_file_path_prefix(seg_path)},
279
21
                        output_rs_tablet_schema->get_inverted_index_storage_format(),
280
21
                        InvertedIndexFileInfo(), _tablet->tablet_id());
281
21
                auto st = idx_file_reader->init();
282
21
                DBUG_EXECUTE_IF(
283
21
                        "IndexBuilder::update_inverted_index_info_index_file_reader_init_not_ok", {
284
21
                            st = Status::Error<ErrorCode::INIT_FAILED>(
285
21
                                    "debug point: reader init error");
286
21
                        })
287
21
                if (!st.ok() && !st.is<ErrorCode::INVERTED_INDEX_FILE_NOT_FOUND>()) {
288
0
                    return st;
289
0
                }
290
21
                _index_file_readers.emplace(
291
21
                        std::make_pair(output_rs_writer->rowset_id().to_string(), seg.id()),
292
21
                        std::move(idx_file_reader));
293
21
            }
294
18
            rowset_meta->set_total_disk_size(input_rowset_meta->total_disk_size() -
295
18
                                             total_index_size);
296
18
            rowset_meta->set_data_disk_size(input_rowset_meta->data_disk_size());
297
18
            rowset_meta->set_index_disk_size(input_rowset_meta->index_disk_size() -
298
18
                                             total_index_size);
299
18
        }
300
22
        rowset_meta->set_empty(input_rowset_meta->empty());
301
22
        rowset_meta->set_num_segments(input_rowset_meta->num_segments());
302
22
        rowset_meta->set_segments_overlap(input_rowset_meta->segments_overlap());
303
22
        rowset_meta->set_rowset_state(input_rowset_meta->rowset_state());
304
22
        std::vector<KeyBoundsPB> key_bounds;
305
22
        RETURN_IF_ERROR(input_rowset->get_segments_key_bounds(&key_bounds));
306
22
        rowset_meta->set_segments_key_bounds_truncated(
307
22
                input_rowset_meta->is_segments_key_bounds_truncated());
308
        // preserve aggregated layout via the setter so the aggregated flag is not
309
        // clobbered by set_segments_key_bounds's default reset path.
310
22
        rowset_meta->set_segments_key_bounds(
311
22
                key_bounds, input_rowset_meta->is_segments_key_bounds_aggregated());
312
22
        std::vector<uint32_t> num_segment_rows;
313
22
        input_rowset_meta->get_num_segment_rows(&num_segment_rows);
314
22
        rowset_meta->set_num_segment_rows(num_segment_rows);
315
22
        auto output_rowset = output_rs_writer->manual_build(rowset_meta);
316
22
        if (input_rowset_meta->has_delete_predicate()) {
317
0
            output_rowset->rowset_meta()->set_delete_predicate(
318
0
                    input_rowset_meta->delete_predicate());
319
0
        }
320
22
        _output_rowsets.push_back(output_rowset);
321
22
    }
322
323
21
    return Status::OK();
324
21
}
325
326
Status IndexBuilder::handle_single_rowset(RowsetMetaSharedPtr output_rowset_meta,
327
20
                                          std::vector<segment_v2::SegmentSharedPtr>& segments) {
328
20
    bool is_local_rowset = output_rowset_meta->is_local();
329
20
    DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_is_local_rowset",
330
20
                    { is_local_rowset = false; })
331
20
    if (!is_local_rowset) [[unlikely]] {
332
        // DCHECK(false) << _tablet->tablet_id() << ' ' << output_rowset_meta->rowset_id();
333
0
        return Status::InternalError("should be local rowset. tablet_id={} rowset_id={}",
334
0
                                     _tablet->tablet_id(),
335
0
                                     output_rowset_meta->rowset_id().to_string());
336
0
    }
337
338
20
    if (_is_drop_op) {
339
6
        const auto& output_rs_tablet_schema = output_rowset_meta->tablet_schema();
340
6
        if (output_rs_tablet_schema->get_inverted_index_storage_format() !=
341
6
            InvertedIndexStorageFormatPB::V1) {
342
5
            const auto& fs = output_rowset_meta->fs();
343
344
5
            const auto& output_rowset_schema = output_rowset_meta->tablet_schema();
345
5
            size_t inverted_index_size = 0;
346
5
            for (auto& seg_ptr : segments) {
347
5
                auto idx_file_reader_iter = _index_file_readers.find(
348
5
                        std::make_pair(output_rowset_meta->rowset_id().to_string(), seg_ptr->id()));
349
5
                DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_can_not_find_reader_drop_op",
350
5
                                { idx_file_reader_iter = _index_file_readers.end(); })
351
5
                if (idx_file_reader_iter == _index_file_readers.end()) {
352
0
                    LOG(ERROR) << "idx_file_reader_iter" << output_rowset_meta->rowset_id() << ":"
353
0
                               << seg_ptr->id() << " cannot be found";
354
0
                    continue;
355
0
                }
356
5
                auto dirs = DORIS_TRY(idx_file_reader_iter->second->get_all_directories());
357
358
5
                std::string index_path_prefix {
359
5
                        InvertedIndexDescriptor::get_index_file_path_prefix(local_segment_path(
360
5
                                _tablet->tablet_path(), output_rowset_meta->rowset_id().to_string(),
361
5
                                seg_ptr->id()))};
362
363
5
                std::string index_path =
364
5
                        InvertedIndexDescriptor::get_index_file_path_v2(index_path_prefix);
365
5
                io::FileWriterPtr file_writer;
366
5
                Status st = fs->create_file(index_path, &file_writer);
367
5
                if (!st.ok()) {
368
0
                    LOG(WARNING) << "failed to create writable file. path=" << index_path
369
0
                                 << ", err: " << st;
370
0
                    return st;
371
0
                }
372
5
                auto index_file_writer = std::make_unique<IndexFileWriter>(
373
5
                        fs, std::move(index_path_prefix),
374
5
                        output_rowset_meta->rowset_id().to_string(), seg_ptr->id(),
375
5
                        output_rowset_schema->get_inverted_index_storage_format(),
376
5
                        std::move(file_writer), true /* can_use_ram_dir */, _tablet->tablet_id());
377
5
                RETURN_IF_ERROR(index_file_writer->initialize(dirs));
378
                // create inverted index writer
379
5
                for (auto& index_meta : _dropped_inverted_indexes) {
380
5
                    RETURN_IF_ERROR(index_file_writer->delete_index(&index_meta));
381
5
                }
382
5
                _index_file_writers.emplace(seg_ptr->id(), std::move(index_file_writer));
383
5
            }
384
5
            for (auto&& [seg_id, index_file_writer] : _index_file_writers) {
385
5
                auto st = index_file_writer->begin_close();
386
5
                if (!st.ok()) {
387
0
                    LOG(ERROR) << "close index_file_writer error:" << st;
388
0
                    return st;
389
0
                }
390
5
                inverted_index_size += index_file_writer->get_index_file_total_size();
391
5
            }
392
5
            for (auto&& [seg_id, index_file_writer] : _index_file_writers) {
393
5
                auto st = index_file_writer->finish_close();
394
5
                if (!st.ok()) {
395
0
                    LOG(ERROR) << "wait close index_file_writer error:" << st;
396
0
                    return st;
397
0
                }
398
5
            }
399
5
            _index_file_writers.clear();
400
5
            output_rowset_meta->set_data_disk_size(output_rowset_meta->data_disk_size());
401
5
            output_rowset_meta->set_total_disk_size(output_rowset_meta->total_disk_size() +
402
5
                                                    inverted_index_size);
403
5
            output_rowset_meta->set_index_disk_size(output_rowset_meta->index_disk_size() +
404
5
                                                    inverted_index_size);
405
5
        }
406
6
        LOG(INFO) << "all row nums. source_rows=" << output_rowset_meta->num_rows();
407
6
        return Status::OK();
408
14
    } else {
409
        // create inverted or ann index writer
410
14
        const auto& fs = output_rowset_meta->fs();
411
14
        auto output_rowset_schema = output_rowset_meta->tablet_schema();
412
14
        size_t inverted_index_size = 0;
413
17
        for (auto& seg_ptr : segments) {
414
17
            std::string index_path_prefix {
415
17
                    InvertedIndexDescriptor::get_index_file_path_prefix(local_segment_path(
416
17
                            _tablet->tablet_path(), output_rowset_meta->rowset_id().to_string(),
417
17
                            seg_ptr->id()))};
418
17
            std::vector<ColumnId> return_columns;
419
17
            std::vector<std::pair<int64_t, int64_t>> inverted_index_writer_signs;
420
17
            _olap_data_convertor->reserve(_alter_inverted_indexes.size());
421
422
17
            std::unique_ptr<IndexFileWriter> index_file_writer = nullptr;
423
17
            if (output_rowset_schema->get_inverted_index_storage_format() >=
424
17
                InvertedIndexStorageFormatPB::V2) {
425
14
                auto idx_file_reader_iter = _index_file_readers.find(
426
14
                        std::make_pair(output_rowset_meta->rowset_id().to_string(), seg_ptr->id()));
427
14
                DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_can_not_find_reader",
428
14
                                { idx_file_reader_iter = _index_file_readers.end(); })
429
14
                if (idx_file_reader_iter == _index_file_readers.end()) {
430
0
                    LOG(ERROR) << "idx_file_reader_iter" << output_rowset_meta->rowset_id() << ":"
431
0
                               << seg_ptr->id() << " cannot be found";
432
0
                    continue;
433
0
                }
434
14
                std::string index_path =
435
14
                        InvertedIndexDescriptor::get_index_file_path_v2(index_path_prefix);
436
14
                io::FileWriterPtr file_writer;
437
14
                Status st = fs->create_file(index_path, &file_writer);
438
14
                if (!st.ok()) {
439
0
                    LOG(WARNING) << "failed to create writable file. path=" << index_path
440
0
                                 << ", err: " << st;
441
0
                    return st;
442
0
                }
443
14
                auto dirs = DORIS_TRY(idx_file_reader_iter->second->get_all_directories());
444
14
                index_file_writer = std::make_unique<IndexFileWriter>(
445
14
                        fs, index_path_prefix, output_rowset_meta->rowset_id().to_string(),
446
14
                        seg_ptr->id(), output_rowset_schema->get_inverted_index_storage_format(),
447
14
                        std::move(file_writer), true /* can_use_ram_dir */, _tablet->tablet_id());
448
14
                RETURN_IF_ERROR(index_file_writer->initialize(dirs));
449
14
            } else {
450
3
                index_file_writer = std::make_unique<IndexFileWriter>(
451
3
                        fs, index_path_prefix, output_rowset_meta->rowset_id().to_string(),
452
3
                        seg_ptr->id(), output_rowset_schema->get_inverted_index_storage_format(),
453
3
                        nullptr, true /* can_use_ram_dir */, _tablet->tablet_id());
454
3
            }
455
            // create inverted index writer, or ann index writer
456
21
            for (auto inverted_index : _alter_inverted_indexes) {
457
21
                DCHECK(inverted_index.index_type == TIndexType::INVERTED ||
458
21
                       inverted_index.index_type == TIndexType::ANN);
459
21
                DCHECK_EQ(inverted_index.columns.size(), 1);
460
21
                auto index_id = inverted_index.index_id;
461
21
                auto column_name = inverted_index.columns[0];
462
21
                auto column_idx = output_rowset_schema->field_index(column_name);
463
21
                if (column_idx < 0) {
464
4
                    if (inverted_index.__isset.column_unique_ids &&
465
4
                        !inverted_index.column_unique_ids.empty()) {
466
1
                        column_idx = output_rowset_schema->field_index(
467
1
                                inverted_index.column_unique_ids[0]);
468
1
                    }
469
4
                    if (column_idx < 0) {
470
3
                        LOG(WARNING) << "referenced column was missing. "
471
3
                                     << "[column=" << column_name
472
3
                                     << " referenced_column=" << column_idx << "]";
473
3
                        continue;
474
3
                    }
475
4
                }
476
18
                auto column = output_rowset_schema->column(column_idx);
477
                // variant column is not support for building index
478
18
                auto is_support_inverted_index =
479
18
                        IndexColumnWriter::check_support_inverted_index(column);
480
18
                auto is_support_ann_index = IndexColumnWriter::check_support_ann_index(column);
481
18
                DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_support_inverted_index",
482
18
                                { is_support_inverted_index = false; })
483
18
                if (!is_support_inverted_index && !is_support_ann_index) {
484
0
                    continue;
485
0
                }
486
18
                DCHECK(output_rowset_schema->has_inverted_index_with_index_id(index_id));
487
18
                _olap_data_convertor->add_column_data_convertor(column);
488
18
                return_columns.emplace_back(column_idx);
489
490
18
                if (inverted_index.index_type == TIndexType::INVERTED) {
491
                    // inverted index
492
17
                    auto index_metas = output_rowset_schema->inverted_indexs(column);
493
17
                    for (const auto& index_meta : index_metas) {
494
17
                        if (index_meta->index_id() != index_id) {
495
0
                            continue;
496
0
                        }
497
17
                        std::unique_ptr<segment_v2::IndexColumnWriter> inverted_index_builder;
498
17
                        try {
499
17
                            RETURN_IF_ERROR(segment_v2::IndexColumnWriter::create(
500
17
                                    &column, &inverted_index_builder, index_file_writer.get(),
501
17
                                    index_meta));
502
17
                            DBUG_EXECUTE_IF(
503
17
                                    "IndexBuilder::handle_single_rowset_index_column_writer_create_"
504
17
                                    "error",
505
17
                                    {
506
17
                                        _CLTHROWA(CL_ERR_IO,
507
17
                                                  "debug point: "
508
17
                                                  "handle_single_rowset_index_column_writer_create_"
509
17
                                                  "error");
510
17
                                    })
511
17
                        } catch (const std::exception& e) {
512
0
                            return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
513
0
                                    "CLuceneError occurred: {}", e.what());
514
0
                        }
515
516
17
                        if (inverted_index_builder) {
517
17
                            auto writer_sign = std::make_pair(seg_ptr->id(), index_id);
518
17
                            _index_column_writers.insert(
519
17
                                    std::make_pair(writer_sign, std::move(inverted_index_builder)));
520
17
                            inverted_index_writer_signs.emplace_back(writer_sign);
521
17
                        }
522
17
                    }
523
17
                } else if (inverted_index.index_type == TIndexType::ANN) {
524
                    // ann index
525
1
                    const auto* index_meta = output_rowset_schema->ann_index(column);
526
1
                    if (index_meta && index_meta->index_id() == index_id) {
527
1
                        std::unique_ptr<segment_v2::IndexColumnWriter> index_writer;
528
1
                        try {
529
1
                            RETURN_IF_ERROR(segment_v2::IndexColumnWriter::create(
530
1
                                    &column, &index_writer, index_file_writer.get(), index_meta));
531
1
                            DBUG_EXECUTE_IF(
532
1
                                    "IndexBuilder::handle_single_rowset_index_column_writer_create_"
533
1
                                    "error",
534
1
                                    {
535
1
                                        _CLTHROWA(CL_ERR_IO,
536
1
                                                  "debug point: "
537
1
                                                  "handle_single_rowset_index_column_writer_create_"
538
1
                                                  "error");
539
1
                                    })
540
1
                        } catch (const std::exception& e) {
541
0
                            return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
542
0
                                    "CLuceneError occurred: {}", e.what());
543
0
                        }
544
545
1
                        if (index_writer) {
546
1
                            auto writer_sign = std::make_pair(seg_ptr->id(), index_id);
547
1
                            _index_column_writers.insert(
548
1
                                    std::make_pair(writer_sign, std::move(index_writer)));
549
1
                            inverted_index_writer_signs.emplace_back(writer_sign);
550
1
                        }
551
1
                    }
552
1
                }
553
18
            }
554
555
            // DO NOT forget index_file_writer for the segment, otherwise, original inverted index will be deleted.
556
17
            _index_file_writers.emplace(seg_ptr->id(), std::move(index_file_writer));
557
17
            if (return_columns.empty()) {
558
                // no columns to read
559
3
                continue;
560
3
            }
561
            // create iterator for each segment
562
14
            StorageReadOptions read_options;
563
14
            OlapReaderStatistics stats;
564
14
            read_options.stats = &stats;
565
14
            read_options.tablet_schema = output_rowset_schema;
566
14
            std::shared_ptr<Schema> schema =
567
14
                    std::make_shared<Schema>(output_rowset_schema->columns(), return_columns);
568
14
            std::unique_ptr<RowwiseIterator> iter;
569
14
            auto res = seg_ptr->new_iterator(schema, read_options, &iter);
570
14
            DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_create_iterator_error", {
571
14
                res = Status::Error<ErrorCode::INTERNAL_ERROR>(
572
14
                        "debug point: handle_single_rowset_create_iterator_error");
573
14
            })
574
14
            if (!res.ok()) {
575
0
                LOG(WARNING) << "failed to create iterator[" << seg_ptr->id()
576
0
                             << "]: " << res.to_string();
577
0
                return Status::Error<ErrorCode::ROWSET_READER_INIT>(res.to_string());
578
0
            }
579
580
14
            auto block = Block::create_unique(output_rowset_schema->create_block(return_columns));
581
28
            while (true) {
582
28
                auto status = iter->next_batch(block.get());
583
28
                DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_iterator_next_batch_error", {
584
28
                    status = Status::Error<ErrorCode::SCHEMA_CHANGE_INFO_INVALID>(
585
28
                            "next_batch fault injection");
586
28
                });
587
28
                if (!status.ok()) {
588
14
                    if (status.is<ErrorCode::END_OF_FILE>()) {
589
14
                        break;
590
14
                    }
591
14
                    LOG(WARNING)
592
0
                            << "failed to read next block when schema change for inverted index."
593
0
                            << ", err=" << status.to_string();
594
0
                    return status;
595
14
                }
596
597
                // write inverted index data, or ann index data
598
14
                status = _write_inverted_index_data(output_rowset_schema, iter->data_id(),
599
14
                                                    block.get());
600
14
                DBUG_EXECUTE_IF(
601
14
                        "IndexBuilder::handle_single_rowset_write_inverted_index_data_error", {
602
14
                            status = Status::Error<ErrorCode::INTERNAL_ERROR>(
603
14
                                    "debug point: "
604
14
                                    "handle_single_rowset_write_inverted_index_data_error");
605
14
                        })
606
14
                if (!status.ok()) {
607
0
                    return Status::Error<ErrorCode::SCHEMA_CHANGE_INFO_INVALID>(
608
0
                            "failed to write block.");
609
0
                }
610
14
                block->clear_column_data();
611
14
            }
612
613
            // finish write inverted index, flush data to compound file
614
18
            for (auto& writer_sign : inverted_index_writer_signs) {
615
18
                try {
616
18
                    if (_index_column_writers[writer_sign]) {
617
18
                        RETURN_IF_ERROR(_index_column_writers[writer_sign]->finish());
618
18
                    }
619
18
                    DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_index_build_finish_error", {
620
18
                        _CLTHROWA(CL_ERR_IO,
621
18
                                  "debug point: handle_single_rowset_index_build_finish_error");
622
18
                    })
623
18
                } catch (const std::exception& e) {
624
0
                    return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
625
0
                            "CLuceneError occurred: {}", e.what());
626
0
                }
627
18
            }
628
629
14
            _olap_data_convertor->reset();
630
14
        }
631
17
        for (auto&& [seg_id, index_file_writer] : _index_file_writers) {
632
17
            auto st = index_file_writer->begin_close();
633
17
            DBUG_EXECUTE_IF("IndexBuilder::handle_single_rowset_file_writer_close_error", {
634
17
                st = Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
635
17
                        "debug point: handle_single_rowset_file_writer_close_error");
636
17
            })
637
17
            if (!st.ok()) {
638
0
                LOG(ERROR) << "close index_file_writer error:" << st;
639
0
                return st;
640
0
            }
641
17
            inverted_index_size += index_file_writer->get_index_file_total_size();
642
17
        }
643
17
        for (auto&& [seg_id, index_file_writer] : _index_file_writers) {
644
17
            auto st = index_file_writer->finish_close();
645
17
            if (!st.ok()) {
646
0
                LOG(ERROR) << "wait close index_file_writer error:" << st;
647
0
                return st;
648
0
            }
649
17
        }
650
14
        _index_column_writers.clear();
651
14
        _index_file_writers.clear();
652
14
        output_rowset_meta->set_data_disk_size(output_rowset_meta->data_disk_size());
653
14
        output_rowset_meta->set_total_disk_size(output_rowset_meta->total_disk_size() +
654
14
                                                inverted_index_size);
655
14
        output_rowset_meta->set_index_disk_size(output_rowset_meta->index_disk_size() +
656
14
                                                inverted_index_size);
657
14
        LOG(INFO) << "all row nums. source_rows=" << output_rowset_meta->num_rows();
658
14
    }
659
660
14
    return Status::OK();
661
20
}
662
663
Status IndexBuilder::_write_inverted_index_data(TabletSchemaSPtr tablet_schema, int64_t segment_idx,
664
14
                                                Block* block) {
665
14
    VLOG_DEBUG << "begin to write inverted/ann index";
666
    // converter block data
667
14
    _olap_data_convertor->set_source_content(block, 0, block->rows());
668
32
    for (auto i = 0; i < _alter_inverted_indexes.size(); ++i) {
669
18
        auto inverted_index = _alter_inverted_indexes[i];
670
18
        auto index_id = inverted_index.index_id;
671
18
        auto column_name = inverted_index.columns[0];
672
18
        auto column_idx = tablet_schema->field_index(column_name);
673
18
        DBUG_EXECUTE_IF("IndexBuilder::_write_inverted_index_data_column_idx_is_negative",
674
18
                        { column_idx = -1; })
675
18
        if (column_idx < 0) {
676
1
            if (!inverted_index.column_unique_ids.empty()) {
677
1
                auto column_unique_id = inverted_index.column_unique_ids[0];
678
1
                column_idx = tablet_schema->field_index(column_unique_id);
679
1
            }
680
1
            if (column_idx < 0) {
681
0
                LOG(WARNING) << "referenced column was missing. "
682
0
                             << "[column=" << column_name << " referenced_column=" << column_idx
683
0
                             << "]";
684
0
                continue;
685
0
            }
686
1
        }
687
18
        const auto& column = tablet_schema->column(column_idx);
688
18
        auto writer_sign = std::make_pair(segment_idx, index_id);
689
18
        auto converted_result = _olap_data_convertor->convert_column_data(i);
690
18
        DBUG_EXECUTE_IF("IndexBuilder::_write_inverted_index_data_convert_column_data_error", {
691
18
            converted_result.first = Status::Error<ErrorCode::INTERNAL_ERROR>(
692
18
                    "debug point: _write_inverted_index_data_convert_column_data_error");
693
18
        })
694
18
        if (converted_result.first != Status::OK()) {
695
0
            LOG(WARNING) << "failed to convert block, errcode: " << converted_result.first;
696
0
            return converted_result.first;
697
0
        }
698
18
        const auto* ptr = (const uint8_t*)converted_result.second->get_data();
699
18
        const auto* null_map = converted_result.second->get_nullmap();
700
18
        if (null_map) {
701
0
            RETURN_IF_ERROR(_add_nullable(column_name, writer_sign, &column, null_map, &ptr,
702
0
                                          block->rows()));
703
18
        } else {
704
18
            RETURN_IF_ERROR(_add_data(column_name, writer_sign, &column, &ptr, block->rows()));
705
18
        }
706
18
    }
707
14
    _olap_data_convertor->clear_source_content();
708
709
14
    return Status::OK();
710
14
}
711
712
Status IndexBuilder::_add_nullable(const std::string& column_name,
713
                                   const std::pair<int64_t, int64_t>& index_writer_sign,
714
                                   const TabletColumn* column, const uint8_t* null_map,
715
0
                                   const uint8_t** ptr, size_t num_rows) {
716
    // TODO: need to process null data for inverted index
717
0
    if (column->type() == FieldType::OLAP_FIELD_TYPE_ARRAY) {
718
0
        DCHECK(column->get_subtype_count() == 1);
719
        // [size, offset_ptr, item_data_ptr, item_nullmap_ptr]
720
0
        const auto* data_ptr = reinterpret_cast<const uint64_t*>(*ptr);
721
        // total number length
722
0
        auto offset_data = *(data_ptr + 1);
723
0
        const auto* offsets_ptr = (const uint8_t*)offset_data;
724
0
        try {
725
0
            auto data = *(data_ptr + 2);
726
0
            auto nested_null_map = *(data_ptr + 3);
727
0
            RETURN_IF_ERROR(_index_column_writers[index_writer_sign]->add_array_values(
728
0
                    field_type_size(column->get_sub_column(0).type()),
729
0
                    reinterpret_cast<const void*>(data),
730
0
                    reinterpret_cast<const uint8_t*>(nested_null_map), offsets_ptr, num_rows));
731
0
            DBUG_EXECUTE_IF("IndexBuilder::_add_nullable_add_array_values_error", {
732
0
                _CLTHROWA(CL_ERR_IO, "debug point: _add_nullable_add_array_values_error");
733
0
            })
734
0
            RETURN_IF_ERROR(
735
0
                    _index_column_writers[index_writer_sign]->add_array_nulls(null_map, num_rows));
736
0
        } catch (const std::exception& e) {
737
0
            return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>(
738
0
                    "CLuceneError occurred: {}", e.what());
739
0
        }
740
741
0
        return Status::OK();
742
0
    }
743
0
    size_t offset = 0;
744
0
    auto next_run_step = [&]() {
745
0
        size_t step = 1;
746
0
        for (auto i = offset + 1; i < num_rows; ++i) {
747
0
            if (null_map[offset] == null_map[i]) {
748
0
                step++;
749
0
            } else {
750
0
                break;
751
0
            }
752
0
        }
753
0
        return step;
754
0
    };
755
0
    try {
756
0
        do {
757
0
            auto step = next_run_step();
758
0
            if (null_map[offset]) {
759
0
                RETURN_IF_ERROR(_index_column_writers[index_writer_sign]->add_nulls(
760
0
                        static_cast<uint32_t>(step)));
761
0
            } else {
762
0
                RETURN_IF_ERROR(_index_column_writers[index_writer_sign]->add_values(column_name,
763
0
                                                                                     *ptr, step));
764
0
            }
765
0
            *ptr += field_type_size(column->type()) * step;
766
0
            offset += step;
767
0
            DBUG_EXECUTE_IF("IndexBuilder::_add_nullable_throw_exception",
768
0
                            { _CLTHROWA(CL_ERR_IO, "debug point: _add_nullable_throw_exception"); })
769
0
        } while (offset < num_rows);
770
0
    } catch (const std::exception& e) {
771
0
        return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>("CLuceneError occurred: {}",
772
0
                                                                      e.what());
773
0
    }
774
775
0
    return Status::OK();
776
0
}
777
778
Status IndexBuilder::_add_data(const std::string& column_name,
779
                               const std::pair<int64_t, int64_t>& index_writer_sign,
780
18
                               const TabletColumn* column, const uint8_t** ptr, size_t num_rows) {
781
18
    try {
782
18
        if (column->type() == FieldType::OLAP_FIELD_TYPE_ARRAY) {
783
2
            DCHECK(column->get_subtype_count() == 1);
784
            // [size, offset_ptr, item_data_ptr, item_nullmap_ptr]
785
2
            const auto* data_ptr = reinterpret_cast<const uint64_t*>(*ptr);
786
            // total number length
787
2
            auto element_cnt = size_t((unsigned long)(*data_ptr));
788
2
            auto offset_data = *(data_ptr + 1);
789
2
            const auto* offsets_ptr = (const uint8_t*)offset_data;
790
2
            if (element_cnt > 0) {
791
2
                auto data = *(data_ptr + 2);
792
2
                auto nested_null_map = *(data_ptr + 3);
793
2
                RETURN_IF_ERROR(_index_column_writers[index_writer_sign]->add_array_values(
794
2
                        field_type_size(column->get_sub_column(0).type()),
795
2
                        reinterpret_cast<const void*>(data),
796
2
                        reinterpret_cast<const uint8_t*>(nested_null_map), offsets_ptr, num_rows));
797
2
            }
798
16
        } else {
799
16
            RETURN_IF_ERROR(_index_column_writers[index_writer_sign]->add_values(column_name, *ptr,
800
16
                                                                                 num_rows));
801
16
        }
802
18
        DBUG_EXECUTE_IF("IndexBuilder::_add_data_throw_exception",
803
18
                        { _CLTHROWA(CL_ERR_IO, "debug point: _add_data_throw_exception"); })
804
18
    } catch (const std::exception& e) {
805
0
        return Status::Error<ErrorCode::INVERTED_INDEX_CLUCENE_ERROR>("CLuceneError occurred: {}",
806
0
                                                                      e.what());
807
0
    }
808
809
18
    return Status::OK();
810
18
}
811
812
20
Status IndexBuilder::handle_inverted_index_data() {
813
20
    LOG(INFO) << "begin to handle_inverted_index_data";
814
20
    DCHECK(_input_rowsets.size() == _output_rowsets.size());
815
21
    for (auto& _output_rowset : _output_rowsets) {
816
21
        SegmentCacheHandle segment_cache_handle;
817
21
        RETURN_IF_ERROR(SegmentLoader::instance()->load_segments(
818
21
                std::static_pointer_cast<BetaRowset>(_output_rowset), &segment_cache_handle));
819
21
        auto output_rowset_meta = _output_rowset->rowset_meta();
820
21
        auto& segments = segment_cache_handle.get_segments();
821
21
        RETURN_IF_ERROR(handle_single_rowset(output_rowset_meta, segments));
822
21
    }
823
19
    return Status::OK();
824
20
}
825
826
24
Status IndexBuilder::do_build_inverted_index() {
827
24
    LOG(INFO) << "begin to do_build_inverted_index, tablet=" << _tablet->tablet_id()
828
24
              << ", is_drop_op=" << _is_drop_op;
829
24
    DBUG_EXECUTE_IF("IndexBuilder::do_build_inverted_index_alter_inverted_indexes_empty",
830
24
                    { _alter_inverted_indexes.clear(); })
831
24
    if (_alter_inverted_indexes.empty()) {
832
0
        return Status::OK();
833
0
    }
834
835
24
    static constexpr long TRY_LOCK_TIMEOUT = 30;
836
24
    std::unique_lock schema_change_lock(_tablet->get_schema_change_lock(), std::defer_lock);
837
24
    bool owns_lock = schema_change_lock.try_lock_for(std::chrono::seconds(TRY_LOCK_TIMEOUT));
838
839
24
    if (!owns_lock) {
840
0
        return Status::ObtainLockFailed(
841
0
                "try schema_change_lock failed. There might be schema change or cooldown running "
842
0
                "on "
843
0
                "tablet={} ",
844
0
                _tablet->tablet_id());
845
0
    }
846
    // Check executing serially with compaction task.
847
24
    std::unique_lock<std::mutex> base_compaction_lock(_tablet->get_base_compaction_lock(),
848
24
                                                      std::try_to_lock);
849
24
    if (!base_compaction_lock.owns_lock()) {
850
0
        return Status::ObtainLockFailed("try base_compaction_lock failed. tablet={} ",
851
0
                                        _tablet->tablet_id());
852
0
    }
853
24
    std::unique_lock<std::mutex> cumu_compaction_lock(_tablet->get_cumulative_compaction_lock(),
854
24
                                                      std::try_to_lock);
855
24
    if (!cumu_compaction_lock.owns_lock()) {
856
0
        return Status::ObtainLockFailed("try cumu_compaction_lock failed. tablet={}",
857
0
                                        _tablet->tablet_id());
858
0
    }
859
860
24
    std::unique_lock<std::mutex> cold_compaction_lock(_tablet->get_cold_compaction_lock(),
861
24
                                                      std::try_to_lock);
862
24
    if (!cold_compaction_lock.owns_lock()) {
863
0
        return Status::ObtainLockFailed("try cold_compaction_lock failed. tablet={}",
864
0
                                        _tablet->tablet_id());
865
0
    }
866
867
24
    std::unique_lock<std::mutex> build_inverted_index_lock(_tablet->get_build_inverted_index_lock(),
868
24
                                                           std::try_to_lock);
869
24
    if (!build_inverted_index_lock.owns_lock()) {
870
0
        return Status::ObtainLockFailed("failed to obtain build inverted index lock. tablet={}",
871
0
                                        _tablet->tablet_id());
872
0
    }
873
874
24
    std::shared_lock migration_rlock(_tablet->get_migration_lock(), std::try_to_lock);
875
24
    if (!migration_rlock.owns_lock()) {
876
0
        return Status::ObtainLockFailed("got migration_rlock failed. tablet={}",
877
0
                                        _tablet->tablet_id());
878
0
    }
879
880
24
    DCHECK(!_alter_index_ids.empty());
881
24
    _input_rowsets =
882
24
            _tablet->pick_candidate_rowsets_to_build_inverted_index(_alter_index_ids, _is_drop_op);
883
24
    if (_input_rowsets.empty()) {
884
1
        LOG(INFO) << "_input_rowsets is empty";
885
1
        return Status::OK();
886
1
    }
887
888
23
    auto st = update_inverted_index_info();
889
23
    if (!st.ok()) {
890
3
        LOG(WARNING) << "failed to update_inverted_index_info. "
891
3
                     << "tablet=" << _tablet->tablet_id() << ", error=" << st;
892
3
        gc_output_rowset();
893
3
        return st;
894
3
    }
895
896
    // create inverted index file for output rowset
897
20
    st = handle_inverted_index_data();
898
20
    if (!st.ok()) {
899
1
        LOG(WARNING) << "failed to handle_inverted_index_data. "
900
1
                     << "tablet=" << _tablet->tablet_id() << ", error=" << st;
901
1
        gc_output_rowset();
902
1
        return st;
903
1
    }
904
905
    // modify rowsets in memory
906
19
    st = modify_rowsets();
907
19
    DBUG_EXECUTE_IF("IndexBuilder::do_build_inverted_index_modify_rowsets_status_error", {
908
19
        st = Status::Error<ErrorCode::DELETE_VERSION_ERROR>(
909
19
                "debug point: do_build_inverted_index_modify_rowsets_status_error");
910
19
    })
911
19
    if (!st.ok()) {
912
0
        LOG(WARNING) << "failed to modify rowsets in memory. "
913
0
                     << "tablet=" << _tablet->tablet_id() << ", error=" << st;
914
0
        gc_output_rowset();
915
0
        return st;
916
0
    }
917
19
    return Status::OK();
918
19
}
919
920
19
Status IndexBuilder::modify_rowsets(const Merger::Statistics* stats) {
921
19
    DCHECK(std::ranges::all_of(
922
19
            _output_rowsets.begin(), _output_rowsets.end(), [&engine = _engine](auto&& rs) {
923
19
                if (engine.check_rowset_id_in_unused_rowsets(rs->rowset_id())) {
924
19
                    LOG(ERROR) << "output rowset: " << rs->rowset_id() << " in unused rowsets";
925
19
                    return false;
926
19
                }
927
19
                return true;
928
19
            }));
929
930
19
    if (_tablet->keys_type() == KeysType::UNIQUE_KEYS &&
931
19
        _tablet->enable_unique_key_merge_on_write()) {
932
1
        std::lock_guard<std::mutex> rowset_update_wlock(_tablet->get_rowset_update_lock());
933
1
        std::lock_guard meta_wlock(_tablet->get_header_lock());
934
1
        SCOPED_SIMPLE_TRACE_IF_TIMEOUT(TRACE_TABLET_LOCK_THRESHOLD);
935
1
        DeleteBitmapPtr delete_bitmap = std::make_shared<DeleteBitmap>(_tablet->tablet_id());
936
2
        for (auto i = 0; i < _input_rowsets.size(); ++i) {
937
1
            RowsetId input_rowset_id = _input_rowsets[i]->rowset_id();
938
1
            RowsetId output_rowset_id = _output_rowsets[i]->rowset_id();
939
1
            for (const auto& [k, v] : _tablet->tablet_meta()->delete_bitmap().delete_bitmap) {
940
0
                RowsetId rs_id = std::get<0>(k);
941
0
                if (rs_id == input_rowset_id) {
942
0
                    DeleteBitmap::BitmapKey output_rs_key = {output_rowset_id, std::get<1>(k),
943
0
                                                             std::get<2>(k)};
944
0
                    auto res = delete_bitmap->set(output_rs_key, v);
945
0
                    DCHECK(res > 0) << "delete_bitmap set failed, res=" << res;
946
0
                }
947
0
            }
948
1
        }
949
1
        _tablet->tablet_meta()->delete_bitmap().merge(*delete_bitmap);
950
951
        // modify_rowsets will remove the delete_bitmap for input rowsets,
952
        // should call it after merge delete_bitmap
953
1
        RETURN_IF_ERROR(_tablet->modify_rowsets(_output_rowsets, _input_rowsets, true));
954
18
    } else {
955
18
        std::lock_guard wrlock(_tablet->get_header_lock());
956
18
        RETURN_IF_ERROR(_tablet->modify_rowsets(_output_rowsets, _input_rowsets, true));
957
18
    }
958
959
#ifndef BE_TEST
960
    {
961
        std::shared_lock rlock(_tablet->get_header_lock());
962
        _tablet->save_meta();
963
    }
964
#endif
965
19
    return Status::OK();
966
19
}
967
968
4
void IndexBuilder::gc_output_rowset() {
969
4
    for (auto&& output_rowset : _output_rowsets) {
970
2
        auto is_local_rowset = output_rowset->is_local();
971
2
        DBUG_EXECUTE_IF("IndexBuilder::gc_output_rowset_is_local_rowset",
972
2
                        { is_local_rowset = false; })
973
2
        if (!is_local_rowset) {
974
0
            _tablet->record_unused_remote_rowset(output_rowset->rowset_id(),
975
0
                                                 output_rowset->rowset_meta()->resource_id(),
976
0
                                                 output_rowset->num_segments());
977
0
            return;
978
0
        }
979
2
        _engine.add_unused_rowset(output_rowset);
980
2
    }
981
4
}
982
983
} // namespace doris