Coverage Report

Created: 2026-10-28 18:29

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/doris/be/src/load/memtable/memtable.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 "load/memtable/memtable.h"
19
20
#include <fmt/format.h>
21
#include <gen_cpp/olap_file.pb.h>
22
#include <pdqsort.h>
23
24
#include <algorithm>
25
#include <limits>
26
#include <string>
27
#include <vector>
28
29
#include "bvar/bvar.h"
30
#include "common/config.h"
31
#include "core/column/column.h"
32
#include "exprs/aggregate/aggregate_function_reader.h"
33
#include "exprs/aggregate/aggregate_function_simple_factory.h"
34
#include "load/memtable/memtable_memory_limiter.h"
35
#include "runtime/descriptors.h"
36
#include "runtime/exec_env.h"
37
#include "runtime/runtime_profile.h"
38
#include "runtime/thread_context.h"
39
#include "storage/olap_define.h"
40
#include "storage/tablet/tablet_schema.h"
41
#include "util/debug_points.h"
42
#include "util/stopwatch.hpp"
43
44
namespace doris {
45
#include "common/compile_check_begin.h"
46
47
bvar::Adder<int64_t> g_memtable_cnt("memtable_cnt");
48
bvar::Adder<uint64_t> g_flush_cuz_memtable_full("flush_cuz_memtable_full");
49
50
using namespace ErrorCode;
51
52
namespace {
53
54
// Share one tracked allocation across a batch. Allocating each row separately also
55
// retains an address record and a stack trace per row when memory diagnostics are enabled.
56
template <typename RowFactory>
57
55
std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) {
58
55
    auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows);
59
17.5k
    for (size_t i = 0; i < num_rows; ++i) {
60
17.4k
        rows[i] = make_row(i);
61
17.4k
    }
62
55
    return rows;
63
55
}
memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable6insertEPKNS_5BlockERKSt6vectorIjNS_18CustomStdAllocatorIjNS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEE3$_1EESt10shared_ptrIA_NS_10RowInBlockEEmOT_
Line
Count
Source
57
45
std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) {
58
45
    auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows);
59
10.3k
    for (size_t i = 0; i < num_rows; ++i) {
60
10.2k
        rows[i] = make_row(i);
61
10.2k
    }
62
45
    return rows;
63
45
}
memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable21_sort_by_cluster_keysEvE3$_1EESt10shared_ptrIA_NS_10RowInBlockEEmOT_
Line
Count
Source
57
5
std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) {
58
5
    auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows);
59
3.08k
    for (size_t i = 0; i < num_rows; ++i) {
60
3.07k
        rows[i] = make_row(i);
61
3.07k
    }
62
5
    return rows;
63
5
}
memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable10_aggregateILb0ELb0EEEvvEUlmE_EESt10shared_ptrIA_NS_10RowInBlockEEmOT_
Line
Count
Source
57
5
std::shared_ptr<RowInBlock[]> make_row_batch(size_t num_rows, RowFactory&& make_row) {
58
5
    auto rows = std::allocate_shared<RowInBlock[]>(CustomStdAllocator<RowInBlock>(), num_rows);
59
4.10k
    for (size_t i = 0; i < num_rows; ++i) {
60
4.09k
        rows[i] = make_row(i);
61
4.09k
    }
62
5
    return rows;
63
5
}
Unexecuted instantiation: memtable.cpp:_ZN5doris12_GLOBAL__N_114make_row_batchIZNS_8MemTable10_aggregateILb0ELb1EEEvvEUlmE_EESt10shared_ptrIA_NS_10RowInBlockEEmOT_
64
65
} // namespace
66
67
MemTable::MemTable(int64_t tablet_id, std::shared_ptr<TabletSchema> tablet_schema,
68
                   const std::vector<SlotDescriptor*>* slot_descs, TupleDescriptor* tuple_desc,
69
                   bool enable_unique_key_mow, PartialUpdateInfo* partial_update_info,
70
                   const std::shared_ptr<ResourceContext>& resource_ctx)
71
41
        : _mem_type(MemType::ACTIVE),
72
41
          _tablet_id(tablet_id),
73
41
          _enable_unique_key_mow(enable_unique_key_mow),
74
41
          _keys_type(tablet_schema->keys_type()),
75
41
          _tablet_schema(tablet_schema),
76
41
          _resource_ctx(resource_ctx),
77
41
          _is_first_insertion(true),
78
41
          _agg_functions(tablet_schema->num_columns()),
79
41
          _offsets_of_aggregate_states(tablet_schema->num_columns()),
80
41
          _total_size_of_aggregate_states(0) {
81
41
    g_memtable_cnt << 1;
82
41
    _mem_tracker = std::make_shared<MemTracker>();
83
41
    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
84
41
            _resource_ctx->memory_context()->mem_tracker()->write_tracker());
85
41
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker);
86
41
    _vec_row_comparator = std::make_shared<RowInBlockComparator>(_tablet_schema);
87
41
    if (partial_update_info != nullptr) {
88
18
        _partial_update_mode = partial_update_info->update_mode();
89
18
        if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
90
0
            if (partial_update_info->is_schema_contains_auto_inc_column &&
91
0
                !partial_update_info->is_input_columns_contains_auto_inc_column) {
92
0
                _is_partial_update_and_auto_inc = true;
93
0
            }
94
0
        }
95
18
    }
96
41
    _init_columns_offset_by_slot_descs(slot_descs, tuple_desc);
97
    // TODO: Support ZOrderComparator in the future
98
41
    _row_in_blocks = std::make_unique<DorisVector<std::shared_ptr<RowInBlock>>>();
99
41
    _load_mem_limit = MemInfo::mem_limit() * config::load_process_max_memory_limit_percent / 100;
100
41
}
101
102
void MemTable::_init_columns_offset_by_slot_descs(const std::vector<SlotDescriptor*>* slot_descs,
103
41
                                                  const TupleDescriptor* tuple_desc) {
104
155
    for (auto slot_desc : *slot_descs) {
105
155
        const auto& slots = tuple_desc->slots();
106
745
        for (int j = 0; j < slots.size(); ++j) {
107
745
            if (slot_desc->id() == slots[j]->id()) {
108
155
                _column_offset.emplace_back(j);
109
155
                break;
110
155
            }
111
745
        }
112
155
    }
113
41
    if (_is_partial_update_and_auto_inc) {
114
0
        _column_offset.emplace_back(_column_offset.size());
115
0
    }
116
41
    _num_columns = _column_offset.size();
117
41
}
118
119
23
void MemTable::_init_agg_functions(const Block* block) {
120
23
    if (_num_columns > _column_offset.size()) [[unlikely]] {
121
0
        throw doris::Exception(doris::ErrorCode::INTERNAL_ERROR,
122
0
                               "num_columns {} is greater than block columns {}", _num_columns,
123
0
                               _column_offset.size());
124
0
    }
125
64
    for (auto cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
126
41
        AggregateFunctionPtr function;
127
41
        if (_keys_type == KeysType::UNIQUE_KEYS && _enable_unique_key_mow) {
128
            // In such table, non-key column's aggregation type is NONE, so we need to construct
129
            // the aggregate function manually.
130
14
            if (_skip_bitmap_col_idx != cid) {
131
14
                function = AggregateFunctionSimpleFactory::instance().get(
132
14
                        "replace_load", {block->get_data_type(cid)}, block->get_data_type(cid),
133
14
                        block->get_data_type(cid)->is_nullable(),
134
14
                        BeExecVersionManager::get_newest_version());
135
14
            } else {
136
0
                function = AggregateFunctionSimpleFactory::instance().get(
137
0
                        "bitmap_intersect", {block->get_data_type(cid)}, block->get_data_type(cid),
138
0
                        false, BeExecVersionManager::get_newest_version());
139
0
            }
140
27
        } else {
141
27
            function = _tablet_schema->column(cid).get_aggregate_function(
142
27
                    AGG_LOAD_SUFFIX, _tablet_schema->column(cid).get_be_exec_version(),
143
27
                    block->get_data_type(cid));
144
27
            if (function == nullptr) {
145
0
                LOG(WARNING) << "column get aggregate function failed, column="
146
0
                             << _tablet_schema->column(cid).name();
147
0
            }
148
27
        }
149
150
41
        DCHECK(function != nullptr);
151
41
        _agg_functions[cid] = function;
152
41
    }
153
154
64
    for (auto cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
155
41
        _offsets_of_aggregate_states[cid] = _total_size_of_aggregate_states;
156
41
        _total_size_of_aggregate_states += _agg_functions[cid]->size_of_data();
157
158
        // If not the last aggregate_state, we need pad it so that next aggregate_state will be aligned.
159
41
        if (cid + 1 < _num_columns) {
160
24
            size_t alignment_of_next_state = _agg_functions[cid + 1]->align_of_data();
161
162
            /// Extend total_size to next alignment requirement
163
            /// Add padding by rounding up 'total_size_of_aggregate_states' to be a multiplier of alignment_of_next_state.
164
24
            _total_size_of_aggregate_states =
165
24
                    (_total_size_of_aggregate_states + alignment_of_next_state - 1) /
166
24
                    alignment_of_next_state * alignment_of_next_state;
167
24
        }
168
41
    }
169
23
}
170
171
41
MemTable::~MemTable() {
172
41
    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
173
41
            _resource_ctx->memory_context()->mem_tracker()->write_tracker());
174
41
    {
175
41
        SCOPED_CONSUME_MEM_TRACKER(_mem_tracker);
176
41
        g_memtable_cnt << -1;
177
41
        if (_keys_type != KeysType::DUP_KEYS) {
178
4.16k
            for (auto it = _row_in_blocks->begin(); it != _row_in_blocks->end(); it++) {
179
4.13k
                if (!(*it)->has_init_agg()) {
180
4.12k
                    continue;
181
4.12k
                }
182
                // We should release agg_places here, because they are not released when a
183
                // load is canceled.
184
13
                for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
185
7
                    auto function = _agg_functions[i];
186
7
                    DCHECK(function != nullptr);
187
7
                    function->destroy((*it)->agg_places(i));
188
7
                }
189
6
            }
190
34
        }
191
192
41
        _arena.clear(true);
193
41
        _vec_row_comparator.reset();
194
41
        _row_in_blocks.reset();
195
41
        _agg_functions.clear();
196
41
        _input_mutable_block.clear();
197
41
        _output_mutable_block.clear();
198
41
    }
199
41
    if (_is_flush_success) {
200
        // If the memtable is flush success, then its memtracker's consumption should be 0
201
14
        if (_mem_tracker->consumption() != 0 && config::crash_in_memory_tracker_inaccurate) {
202
0
            LOG(FATAL) << "memtable flush success but cosumption is not 0, it is "
203
0
                       << _mem_tracker->consumption();
204
0
        }
205
14
    }
206
41
}
207
208
18.4k
int RowInBlockComparator::operator()(const RowInBlock* left, const RowInBlock* right) const {
209
18.4k
    return _pblock->compare_at(left->_row_pos, right->_row_pos, _tablet_schema->num_key_columns(),
210
18.4k
                               *_pblock, -1);
211
18.4k
}
212
213
47
Status MemTable::insert(const Block* input_block, const DorisVector<uint32_t>& row_idxs) {
214
47
    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
215
47
            _resource_ctx->memory_context()->mem_tracker()->write_tracker());
216
47
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker);
217
218
47
    if (_is_first_insertion) {
219
26
        _is_first_insertion = false;
220
26
        auto clone_block = input_block->clone_without_columns(&_column_offset);
221
26
        _input_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block));
222
26
        _vec_row_comparator->set_block(&_input_mutable_block);
223
26
        clone_block = input_block->clone_without_columns(&_column_offset);
224
26
        _output_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block));
225
26
        if (_tablet_schema->has_sequence_col()) {
226
7
            if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
227
                // for unique key fixed partial update, sequence column index in block
228
                // may be different with the index in `_tablet_schema`
229
0
                for (int32_t i = 0; i < clone_block.columns(); i++) {
230
0
                    if (clone_block.get_by_position(i).name == SEQUENCE_COL) {
231
0
                        _seq_col_idx_in_block = i;
232
0
                        break;
233
0
                    }
234
0
                }
235
7
            } else {
236
7
                _seq_col_idx_in_block = _tablet_schema->sequence_col_idx();
237
7
            }
238
7
        }
239
26
        if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS &&
240
26
            _tablet_schema->has_skip_bitmap_col()) {
241
            // init of _skip_bitmap_col_idx and _delete_sign_col_idx must be before _init_agg_functions()
242
0
            _skip_bitmap_col_idx = _tablet_schema->skip_bitmap_col_idx();
243
0
            _delete_sign_col_idx = _tablet_schema->delete_sign_idx();
244
0
            _delete_sign_col_unique_id = _tablet_schema->column(_delete_sign_col_idx).unique_id();
245
0
            if (_seq_col_idx_in_block != -1) {
246
0
                _seq_col_unique_id = _tablet_schema->column(_seq_col_idx_in_block).unique_id();
247
0
            }
248
0
        }
249
26
        if (_keys_type != KeysType::DUP_KEYS) {
250
            // there may be additional intermediate columns in input_block
251
            // we only need columns indicated by column offset in the output
252
23
            RETURN_IF_CATCH_EXCEPTION(_init_agg_functions(&clone_block));
253
23
        }
254
26
    }
255
256
47
    auto num_rows = row_idxs.size();
257
47
    size_t cursor_in_mutableblock = _input_mutable_block.rows();
258
47
    RETURN_IF_ERROR(_input_mutable_block.add_rows(input_block, row_idxs.data(),
259
47
                                                  row_idxs.data() + num_rows, &_column_offset));
260
47
    DBUG_EXECUTE_IF("MemTable.insert.row_batch_allocation", DBUG_RUN_CALLBACK(this));
261
47
    auto row_batch = make_row_batch(
262
10.2k
            num_rows, [&](size_t i) { return RowInBlock(cursor_in_mutableblock + i); });
263
10.3k
    for (size_t i = 0; i < num_rows; ++i) {
264
10.2k
        _row_in_blocks->emplace_back(row_batch, &row_batch[i]);
265
10.2k
    }
266
267
47
    _stat.raw_rows += num_rows;
268
47
    return Status::OK();
269
47
}
270
271
template <bool has_skip_bitmap_col>
272
void MemTable::_aggregate_two_row_in_block(MutableBlock& mutable_block, RowInBlock* src_row,
273
5.12k
                                           RowInBlock* dst_row) {
274
    // for flexible partial update, the caller must guarantees that either src_row and dst_row
275
    // both specify the sequence column, or src_row and dst_row both don't specify the
276
    // sequence column
277
5.12k
    if (_tablet_schema->has_sequence_col() && _seq_col_idx_in_block >= 0) {
278
3
        DCHECK_LT(_seq_col_idx_in_block, mutable_block.columns());
279
3
        auto col_ptr = mutable_block.mutable_columns()[_seq_col_idx_in_block].get();
280
3
        auto res = col_ptr->compare_at(dst_row->_row_pos, src_row->_row_pos, *col_ptr, -1);
281
        // dst sequence column larger than src, don't need to update
282
3
        if (res > 0) {
283
3
            return;
284
3
        }
285
        // need to update the row pos in dst row to the src row pos when has
286
        // sequence column
287
0
        dst_row->_row_pos = src_row->_row_pos;
288
0
    }
289
    // dst is non-sequence row, or dst sequence is smaller
290
5.12k
    if constexpr (!has_skip_bitmap_col) {
291
5.12k
        DCHECK(_skip_bitmap_col_idx == -1);
292
10.2k
        for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
293
5.12k
            auto* col_ptr = mutable_block.mutable_columns()[cid].get();
294
5.12k
            _agg_functions[cid]->add(dst_row->agg_places(cid),
295
5.12k
                                     const_cast<const doris::IColumn**>(&col_ptr),
296
5.12k
                                     src_row->_row_pos, _arena);
297
5.12k
        }
298
5.12k
    } else {
299
0
        DCHECK(_skip_bitmap_col_idx != -1);
300
0
        DCHECK_LT(_skip_bitmap_col_idx, mutable_block.columns());
301
0
        const BitmapValue& skip_bitmap =
302
0
                assert_cast<ColumnBitmap*, TypeCheckOnRelease::DISABLE>(
303
0
                        mutable_block.mutable_columns()[_skip_bitmap_col_idx].get())
304
0
                        ->get_data()[src_row->_row_pos];
305
0
        for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
306
0
            const auto& col = _tablet_schema->column(cid);
307
0
            if (cid != _skip_bitmap_col_idx && skip_bitmap.contains(col.unique_id())) {
308
0
                continue;
309
0
            }
310
0
            auto* col_ptr = mutable_block.mutable_columns()[cid].get();
311
0
            _agg_functions[cid]->add(dst_row->agg_places(cid),
312
0
                                     const_cast<const doris::IColumn**>(&col_ptr),
313
0
                                     src_row->_row_pos, _arena);
314
0
        }
315
0
    }
316
5.12k
}
_ZN5doris8MemTable27_aggregate_two_row_in_blockILb0EEEvRNS_12MutableBlockEPNS_10RowInBlockES5_
Line
Count
Source
273
5.12k
                                           RowInBlock* dst_row) {
274
    // for flexible partial update, the caller must guarantees that either src_row and dst_row
275
    // both specify the sequence column, or src_row and dst_row both don't specify the
276
    // sequence column
277
5.12k
    if (_tablet_schema->has_sequence_col() && _seq_col_idx_in_block >= 0) {
278
3
        DCHECK_LT(_seq_col_idx_in_block, mutable_block.columns());
279
3
        auto col_ptr = mutable_block.mutable_columns()[_seq_col_idx_in_block].get();
280
3
        auto res = col_ptr->compare_at(dst_row->_row_pos, src_row->_row_pos, *col_ptr, -1);
281
        // dst sequence column larger than src, don't need to update
282
3
        if (res > 0) {
283
3
            return;
284
3
        }
285
        // need to update the row pos in dst row to the src row pos when has
286
        // sequence column
287
0
        dst_row->_row_pos = src_row->_row_pos;
288
0
    }
289
    // dst is non-sequence row, or dst sequence is smaller
290
5.12k
    if constexpr (!has_skip_bitmap_col) {
291
5.12k
        DCHECK(_skip_bitmap_col_idx == -1);
292
10.2k
        for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
293
5.12k
            auto* col_ptr = mutable_block.mutable_columns()[cid].get();
294
5.12k
            _agg_functions[cid]->add(dst_row->agg_places(cid),
295
5.12k
                                     const_cast<const doris::IColumn**>(&col_ptr),
296
5.12k
                                     src_row->_row_pos, _arena);
297
5.12k
        }
298
    } else {
299
        DCHECK(_skip_bitmap_col_idx != -1);
300
        DCHECK_LT(_skip_bitmap_col_idx, mutable_block.columns());
301
        const BitmapValue& skip_bitmap =
302
                assert_cast<ColumnBitmap*, TypeCheckOnRelease::DISABLE>(
303
                        mutable_block.mutable_columns()[_skip_bitmap_col_idx].get())
304
                        ->get_data()[src_row->_row_pos];
305
        for (size_t cid = _tablet_schema->num_key_columns(); cid < _num_columns; ++cid) {
306
            const auto& col = _tablet_schema->column(cid);
307
            if (cid != _skip_bitmap_col_idx && skip_bitmap.contains(col.unique_id())) {
308
                continue;
309
            }
310
            auto* col_ptr = mutable_block.mutable_columns()[cid].get();
311
            _agg_functions[cid]->add(dst_row->agg_places(cid),
312
                                     const_cast<const doris::IColumn**>(&col_ptr),
313
                                     src_row->_row_pos, _arena);
314
        }
315
    }
316
5.12k
}
Unexecuted instantiation: _ZN5doris8MemTable27_aggregate_two_row_in_blockILb1EEEvRNS_12MutableBlockEPNS_10RowInBlockES5_
317
11
Status MemTable::_put_into_output(Block& in_block) {
318
11
    SCOPED_RAW_TIMER(&_stat.put_into_output_ns);
319
11
    DorisVector<uint32_t> row_pos_vec;
320
11
    DCHECK(in_block.rows() <= std::numeric_limits<int>::max());
321
11
    row_pos_vec.reserve(in_block.rows());
322
1.04k
    for (int i = 0; i < _row_in_blocks->size(); i++) {
323
1.03k
        row_pos_vec.emplace_back((*_row_in_blocks)[i]->_row_pos);
324
1.03k
    }
325
11
    return _output_mutable_block.add_rows(&in_block, row_pos_vec.data(),
326
11
                                          row_pos_vec.data() + in_block.rows());
327
11
}
328
329
23
size_t MemTable::_sort() {
330
23
    SCOPED_RAW_TIMER(&_stat.sort_ns);
331
23
    _stat.sort_times++;
332
23
    size_t same_keys_num = 0;
333
    // sort new rows
334
23
    Tie tie = Tie(_last_sorted_pos, _row_in_blocks->size());
335
72
    for (size_t i = 0; i < _tablet_schema->num_key_columns(); i++) {
336
48.9k
        auto cmp = [&](RowInBlock* lhs, RowInBlock* rhs) -> int {
337
48.9k
            return _input_mutable_block.compare_one_column(lhs->_row_pos, rhs->_row_pos, i, -1);
338
48.9k
        };
339
49
        _sort_one_column(*_row_in_blocks, tie, cmp);
340
49
    }
341
23
    bool is_dup = (_keys_type == KeysType::DUP_KEYS);
342
    // sort extra round by _row_pos to make the sort stable
343
23
    auto iter = tie.iter();
344
1.05k
    while (iter.next()) {
345
1.03k
        pdqsort(std::next(_row_in_blocks->begin(), iter.left()),
346
1.03k
                std::next(_row_in_blocks->begin(), iter.right()),
347
1.03k
                [&is_dup](const std::shared_ptr<RowInBlock>& lhs,
348
1.03k
                          const std::shared_ptr<RowInBlock>& rhs) -> bool {
349
1.03k
                    return is_dup ? lhs->_row_pos > rhs->_row_pos : lhs->_row_pos < rhs->_row_pos;
350
1.03k
                });
351
1.03k
        same_keys_num += iter.right() - iter.left();
352
1.03k
    }
353
    // merge new rows and old rows
354
23
    _vec_row_comparator->set_block(&_input_mutable_block);
355
23
    auto cmp_func = [this, is_dup, &same_keys_num](const std::shared_ptr<RowInBlock>& l,
356
8.18k
                                                   const std::shared_ptr<RowInBlock>& r) -> bool {
357
8.18k
        auto value = (*(this->_vec_row_comparator))(l.get(), r.get());
358
8.18k
        if (value == 0) {
359
4.09k
            same_keys_num++;
360
4.09k
            return is_dup ? l->_row_pos > r->_row_pos : l->_row_pos < r->_row_pos;
361
4.09k
        } else {
362
4.09k
            return value < 0;
363
4.09k
        }
364
8.18k
    };
365
23
    auto new_row_it = std::next(_row_in_blocks->begin(), _last_sorted_pos);
366
23
    std::inplace_merge(_row_in_blocks->begin(), new_row_it, _row_in_blocks->end(), cmp_func);
367
23
    _last_sorted_pos = _row_in_blocks->size();
368
23
    return same_keys_num;
369
23
}
370
371
5
Status MemTable::_sort_by_cluster_keys() {
372
5
    SCOPED_RAW_TIMER(&_stat.sort_ns);
373
5
    _stat.sort_times++;
374
    // sort all rows
375
5
    Block in_block = _output_mutable_block.to_block();
376
5
    auto clone_block = in_block.clone_without_columns();
377
5
    MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block));
378
5
    _output_mutable_block = MutableBlock::build_mutable_block(std::move(clone_block));
379
380
5
    DorisVector<std::shared_ptr<RowInBlock>> row_in_blocks;
381
5
    row_in_blocks.reserve(mutable_block.rows());
382
3.07k
    auto rows = make_row_batch(mutable_block.rows(), [&](size_t i) { return RowInBlock(i); });
383
3.08k
    for (size_t i = 0; i < mutable_block.rows(); ++i) {
384
3.07k
        row_in_blocks.emplace_back(rows, &rows[i]);
385
3.07k
    }
386
5
    Tie tie = Tie(0, mutable_block.rows());
387
388
6
    for (auto cid : _tablet_schema->cluster_key_uids()) {
389
6
        auto index = _tablet_schema->field_index(cid);
390
6
        if (index == -1) {
391
1
            return Status::InternalError("could not find cluster key column with unique_id=" +
392
1
                                         std::to_string(cid) + " in tablet schema");
393
1
        }
394
8.26k
        auto cmp = [&](const RowInBlock* lhs, const RowInBlock* rhs) -> int {
395
8.26k
            return mutable_block.compare_one_column(lhs->_row_pos, rhs->_row_pos, index, -1);
396
8.26k
        };
397
5
        _sort_one_column(row_in_blocks, tie, cmp);
398
5
    }
399
400
    // sort extra round by _row_pos to make the sort stable
401
4
    auto iter = tie.iter();
402
4
    while (iter.next()) {
403
0
        pdqsort(std::next(row_in_blocks.begin(), iter.left()),
404
0
                std::next(row_in_blocks.begin(), iter.right()),
405
0
                [](const std::shared_ptr<RowInBlock>& lhs, const std::shared_ptr<RowInBlock>& rhs)
406
0
                        -> bool { return lhs->_row_pos < rhs->_row_pos; });
407
0
    }
408
409
4
    in_block = mutable_block.to_block();
410
4
    SCOPED_RAW_TIMER(&_stat.put_into_output_ns);
411
4
    DorisVector<uint32_t> row_pos_vec;
412
4
    DCHECK(in_block.rows() <= std::numeric_limits<int>::max());
413
4
    row_pos_vec.reserve(in_block.rows());
414
2.05k
    for (int i = 0; i < row_in_blocks.size(); i++) {
415
2.05k
        row_pos_vec.emplace_back(row_in_blocks[i]->_row_pos);
416
2.05k
    }
417
4
    std::vector<int> column_offset;
418
18
    for (int i = 0; i < _column_offset.size(); ++i) {
419
14
        column_offset.emplace_back(i);
420
14
    }
421
4
    return _output_mutable_block.add_rows(&in_block, row_pos_vec.data(),
422
4
                                          row_pos_vec.data() + in_block.rows(), &column_offset);
423
5
}
424
425
void MemTable::_sort_one_column(DorisVector<std::shared_ptr<RowInBlock>>& row_in_blocks, Tie& tie,
426
54
                                std::function<int(RowInBlock*, RowInBlock*)> cmp) {
427
54
    auto iter = tie.iter();
428
1.10k
    while (iter.next()) {
429
1.04k
        pdqsort(std::next(row_in_blocks.begin(), static_cast<int>(iter.left())),
430
1.04k
                std::next(row_in_blocks.begin(), static_cast<int>(iter.right())),
431
47.9k
                [&cmp](auto lhs, auto rhs) -> bool { return cmp(lhs.get(), rhs.get()) < 0; });
432
1.04k
        tie[iter.left()] = 0;
433
10.2k
        for (auto i = iter.left() + 1; i < iter.right(); i++) {
434
9.23k
            tie[i] = (cmp(row_in_blocks[i - 1].get(), row_in_blocks[i].get()) == 0);
435
9.23k
        }
436
1.04k
    }
437
54
}
438
439
template <bool is_final>
440
5.13k
void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) {
441
    // move key columns
442
15.3k
    for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) {
443
10.2k
        _output_mutable_block.get_column_by_position(i)->insert_from(
444
10.2k
                *mutable_block.get_column_by_position(i), row->_row_pos);
445
10.2k
    }
446
5.13k
    if (row->has_init_agg()) {
447
        // get value columns from agg_places
448
10.2k
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
449
5.13k
            auto function = _agg_functions[i];
450
5.13k
            auto* agg_place = row->agg_places(i);
451
5.13k
            auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
452
5.13k
            function->insert_result_into(agg_place, *col_ptr);
453
454
5.13k
            if constexpr (is_final) {
455
1.03k
                function->destroy(agg_place);
456
4.10k
            } else {
457
4.10k
                function->reset(agg_place);
458
4.10k
            }
459
5.13k
        }
460
461
5.12k
        if constexpr (is_final) {
462
1.02k
            row->remove_init_agg();
463
4.10k
        } else {
464
8.20k
            for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
465
4.10k
                auto function = _agg_functions[i];
466
4.10k
                auto* agg_place = row->agg_places(i);
467
4.10k
                auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
468
4.10k
                function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos,
469
4.10k
                              _arena);
470
4.10k
            }
471
4.10k
        }
472
5.12k
    } else {
473
        // move columns for rows do not need agg
474
12
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
475
9
            _output_mutable_block.get_column_by_position(i)->insert_from(
476
9
                    *mutable_block.get_column_by_position(i), row->_row_pos);
477
9
        }
478
3
    }
479
5.13k
    if constexpr (!is_final) {
480
4.10k
        row->_row_pos = row_pos;
481
4.10k
    }
482
5.13k
}
_ZN5doris8MemTable17_finalize_one_rowILb0EEEvPNS_10RowInBlockERNS_12MutableBlockEi
Line
Count
Source
440
4.10k
void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) {
441
    // move key columns
442
12.2k
    for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) {
443
8.19k
        _output_mutable_block.get_column_by_position(i)->insert_from(
444
8.19k
                *mutable_block.get_column_by_position(i), row->_row_pos);
445
8.19k
    }
446
4.10k
    if (row->has_init_agg()) {
447
        // get value columns from agg_places
448
8.20k
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
449
4.10k
            auto function = _agg_functions[i];
450
4.10k
            auto* agg_place = row->agg_places(i);
451
4.10k
            auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
452
4.10k
            function->insert_result_into(agg_place, *col_ptr);
453
454
            if constexpr (is_final) {
455
                function->destroy(agg_place);
456
4.10k
            } else {
457
4.10k
                function->reset(agg_place);
458
4.10k
            }
459
4.10k
        }
460
461
        if constexpr (is_final) {
462
            row->remove_init_agg();
463
4.10k
        } else {
464
8.20k
            for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
465
4.10k
                auto function = _agg_functions[i];
466
4.10k
                auto* agg_place = row->agg_places(i);
467
4.10k
                auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
468
4.10k
                function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos,
469
4.10k
                              _arena);
470
4.10k
            }
471
4.10k
        }
472
4.10k
    } else {
473
        // move columns for rows do not need agg
474
0
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
475
0
            _output_mutable_block.get_column_by_position(i)->insert_from(
476
0
                    *mutable_block.get_column_by_position(i), row->_row_pos);
477
0
        }
478
0
    }
479
4.10k
    if constexpr (!is_final) {
480
4.10k
        row->_row_pos = row_pos;
481
4.10k
    }
482
4.10k
}
_ZN5doris8MemTable17_finalize_one_rowILb1EEEvPNS_10RowInBlockERNS_12MutableBlockEi
Line
Count
Source
440
1.03k
void MemTable::_finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos) {
441
    // move key columns
442
3.09k
    for (size_t i = 0; i < _tablet_schema->num_key_columns(); ++i) {
443
2.06k
        _output_mutable_block.get_column_by_position(i)->insert_from(
444
2.06k
                *mutable_block.get_column_by_position(i), row->_row_pos);
445
2.06k
    }
446
1.03k
    if (row->has_init_agg()) {
447
        // get value columns from agg_places
448
2.06k
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
449
1.03k
            auto function = _agg_functions[i];
450
1.03k
            auto* agg_place = row->agg_places(i);
451
1.03k
            auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
452
1.03k
            function->insert_result_into(agg_place, *col_ptr);
453
454
1.03k
            if constexpr (is_final) {
455
1.03k
                function->destroy(agg_place);
456
            } else {
457
                function->reset(agg_place);
458
            }
459
1.03k
        }
460
461
1.02k
        if constexpr (is_final) {
462
1.02k
            row->remove_init_agg();
463
        } else {
464
            for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
465
                auto function = _agg_functions[i];
466
                auto* agg_place = row->agg_places(i);
467
                auto* col_ptr = _output_mutable_block.get_column_by_position(i).get();
468
                function->add(agg_place, const_cast<const doris::IColumn**>(&col_ptr), row_pos,
469
                              _arena);
470
            }
471
        }
472
1.02k
    } else {
473
        // move columns for rows do not need agg
474
12
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
475
9
            _output_mutable_block.get_column_by_position(i)->insert_from(
476
9
                    *mutable_block.get_column_by_position(i), row->_row_pos);
477
9
        }
478
3
    }
479
    if constexpr (!is_final) {
480
        row->_row_pos = row_pos;
481
    }
482
1.03k
}
483
484
1.03k
void MemTable::_init_row_for_agg(RowInBlock* row, MutableBlock& mutable_block) {
485
1.03k
    auto* agg_mem = _arena.aligned_alloc(_total_size_of_aggregate_states, 16);
486
1.03k
    auto first_value_column = _tablet_schema->num_key_columns();
487
1.03k
    auto cid = first_value_column;
488
1.03k
    try {
489
2.07k
        for (; cid < _num_columns; ++cid) {
490
1.04k
            _agg_functions[cid]->create(agg_mem + _offsets_of_aggregate_states[cid]);
491
1.04k
        }
492
1.03k
    } catch (...) {
493
        // A failed constructor leaves only the completed prefix available for destruction.
494
2
        while (cid > first_value_column) {
495
1
            --cid;
496
1
            _agg_functions[cid]->destroy(agg_mem + _offsets_of_aggregate_states[cid]);
497
1
        }
498
1
        throw;
499
1
    }
500
    // add() can allocate. Publish the row only after all states can be safely destroyed.
501
1.03k
    row->init_agg_places(agg_mem, _offsets_of_aggregate_states.data());
502
2.07k
    for (cid = first_value_column; cid < _num_columns; ++cid) {
503
1.03k
        auto* col_ptr = mutable_block.mutable_columns()[cid].get();
504
1.03k
        auto* data = row->agg_places(cid);
505
1.03k
        _agg_functions[cid]->add(data, const_cast<const doris::IColumn**>(&col_ptr), row->_row_pos,
506
1.03k
                                 _arena);
507
1.03k
    }
508
1.03k
}
509
5.12k
void MemTable::_clear_row_agg(RowInBlock* row) {
510
5.12k
    if (row->has_init_agg()) {
511
0
        for (size_t i = _tablet_schema->num_key_columns(); i < _num_columns; ++i) {
512
0
            auto function = _agg_functions[i];
513
0
            auto* agg_place = row->agg_places(i);
514
0
            function->destroy(agg_place);
515
0
        }
516
0
        row->remove_init_agg();
517
0
    }
518
5.12k
}
519
// only in `to_block` the `is_final` flag will be true, in other cases, it will be false
520
template <bool is_final, bool has_skip_bitmap_col>
521
13
void MemTable::_aggregate() {
522
13
    SCOPED_RAW_TIMER(&_stat.agg_ns);
523
13
    _stat.agg_times++;
524
13
    Block in_block = _input_mutable_block.to_block();
525
13
    std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0);
526
13
    MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block));
527
13
    _vec_row_comparator->set_block(&mutable_block);
528
13
    DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks;
529
13
    temp_row_in_blocks.reserve(_last_sorted_pos);
530
    //only init agg if needed
531
532
13
    if constexpr (!has_skip_bitmap_col) {
533
13
        RowInBlock* prev_row = nullptr;
534
13
        int row_pos = -1;
535
10.2k
        for (const auto& cur_row_ptr : *_row_in_blocks) {
536
10.2k
            RowInBlock* cur_row = cur_row_ptr.get();
537
10.2k
            if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) {
538
5.13k
                if (!prev_row->has_init_agg()) {
539
1.03k
                    _init_row_for_agg(prev_row, mutable_block);
540
1.03k
                }
541
5.13k
                _stat.merged_rows++;
542
5.13k
                _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row);
543
                // Clean up aggregation state of the merged row to avoid memory leak
544
5.13k
                _clear_row_agg(cur_row);
545
5.13k
            } else {
546
5.13k
                prev_row = cur_row;
547
5.13k
                if (!temp_row_in_blocks.empty()) {
548
                    // The rows from the previous batch of _row_in_blocks have been merged into
549
                    // temp_row_in_blocks, now finalize into the mutable block.
550
5.12k
                    _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block,
551
5.12k
                                                row_pos);
552
5.12k
                }
553
5.13k
                temp_row_in_blocks.push_back(cur_row_ptr);
554
5.13k
                row_pos++;
555
5.13k
            }
556
10.2k
        }
557
13
        if (!temp_row_in_blocks.empty()) {
558
            // finalize the last low
559
11
            _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos);
560
11
        }
561
13
    } else {
562
0
        DCHECK(_delete_sign_col_idx != -1);
563
0
        if (_seq_col_idx_in_block == -1) {
564
0
            _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block,
565
0
                                                                             temp_row_in_blocks);
566
0
        } else {
567
0
            _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block,
568
0
                                                                          temp_row_in_blocks);
569
0
        }
570
0
    }
571
13
    if constexpr (!is_final) {
572
        // if is not final, we collect the agg results to input_block and then continue to insert
573
9
        _input_mutable_block.swap(_output_mutable_block);
574
        //TODO(weixang):opt here.
575
9
        _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block));
576
9
        _output_mutable_block.clear_column_data();
577
        // Repack surviving rows so they do not retain batches of merged-away rows.
578
        // Aggregation states belong to the memtable; preserve their pointers and flags.
579
9
        DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this));
580
9
        auto rows = make_row_batch(temp_row_in_blocks.size(),
581
4.09k
                                   [&](size_t i) { return *temp_row_in_blocks[i]; });
_ZZN5doris8MemTable10_aggregateILb0ELb0EEEvvENKUlmE_clEm
Line
Count
Source
581
4.09k
                                   [&](size_t i) { return *temp_row_in_blocks[i]; });
Unexecuted instantiation: _ZZN5doris8MemTable10_aggregateILb0ELb1EEEvvENKUlmE_clEm
582
4.10k
        for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) {
583
4.09k
            temp_row_in_blocks[i] = {rows, &rows[i]};
584
4.09k
        }
585
9
        *_row_in_blocks = temp_row_in_blocks;
586
9
        _last_sorted_pos = _row_in_blocks->size();
587
9
    }
588
13
}
_ZN5doris8MemTable10_aggregateILb0ELb0EEEvv
Line
Count
Source
521
9
void MemTable::_aggregate() {
522
9
    SCOPED_RAW_TIMER(&_stat.agg_ns);
523
9
    _stat.agg_times++;
524
9
    Block in_block = _input_mutable_block.to_block();
525
9
    std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0);
526
9
    MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block));
527
9
    _vec_row_comparator->set_block(&mutable_block);
528
9
    DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks;
529
9
    temp_row_in_blocks.reserve(_last_sorted_pos);
530
    //only init agg if needed
531
532
9
    if constexpr (!has_skip_bitmap_col) {
533
9
        RowInBlock* prev_row = nullptr;
534
9
        int row_pos = -1;
535
8.20k
        for (const auto& cur_row_ptr : *_row_in_blocks) {
536
8.20k
            RowInBlock* cur_row = cur_row_ptr.get();
537
8.20k
            if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) {
538
4.10k
                if (!prev_row->has_init_agg()) {
539
1.03k
                    _init_row_for_agg(prev_row, mutable_block);
540
1.03k
                }
541
4.10k
                _stat.merged_rows++;
542
4.10k
                _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row);
543
                // Clean up aggregation state of the merged row to avoid memory leak
544
4.10k
                _clear_row_agg(cur_row);
545
4.10k
            } else {
546
4.10k
                prev_row = cur_row;
547
4.10k
                if (!temp_row_in_blocks.empty()) {
548
                    // The rows from the previous batch of _row_in_blocks have been merged into
549
                    // temp_row_in_blocks, now finalize into the mutable block.
550
4.09k
                    _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block,
551
4.09k
                                                row_pos);
552
4.09k
                }
553
4.10k
                temp_row_in_blocks.push_back(cur_row_ptr);
554
4.10k
                row_pos++;
555
4.10k
            }
556
8.20k
        }
557
9
        if (!temp_row_in_blocks.empty()) {
558
            // finalize the last low
559
7
            _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos);
560
7
        }
561
    } else {
562
        DCHECK(_delete_sign_col_idx != -1);
563
        if (_seq_col_idx_in_block == -1) {
564
            _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block,
565
                                                                             temp_row_in_blocks);
566
        } else {
567
            _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block,
568
                                                                          temp_row_in_blocks);
569
        }
570
    }
571
9
    if constexpr (!is_final) {
572
        // if is not final, we collect the agg results to input_block and then continue to insert
573
9
        _input_mutable_block.swap(_output_mutable_block);
574
        //TODO(weixang):opt here.
575
9
        _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block));
576
9
        _output_mutable_block.clear_column_data();
577
        // Repack surviving rows so they do not retain batches of merged-away rows.
578
        // Aggregation states belong to the memtable; preserve their pointers and flags.
579
9
        DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this));
580
9
        auto rows = make_row_batch(temp_row_in_blocks.size(),
581
9
                                   [&](size_t i) { return *temp_row_in_blocks[i]; });
582
4.10k
        for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) {
583
4.09k
            temp_row_in_blocks[i] = {rows, &rows[i]};
584
4.09k
        }
585
9
        *_row_in_blocks = temp_row_in_blocks;
586
9
        _last_sorted_pos = _row_in_blocks->size();
587
9
    }
588
9
}
Unexecuted instantiation: _ZN5doris8MemTable10_aggregateILb0ELb1EEEvv
_ZN5doris8MemTable10_aggregateILb1ELb0EEEvv
Line
Count
Source
521
4
void MemTable::_aggregate() {
522
4
    SCOPED_RAW_TIMER(&_stat.agg_ns);
523
4
    _stat.agg_times++;
524
4
    Block in_block = _input_mutable_block.to_block();
525
4
    std::unique_ptr<Block> empty_input_block = in_block.create_same_struct_block(0);
526
4
    MutableBlock mutable_block = MutableBlock::build_mutable_block(std::move(in_block));
527
4
    _vec_row_comparator->set_block(&mutable_block);
528
4
    DorisVector<std::shared_ptr<RowInBlock>> temp_row_in_blocks;
529
4
    temp_row_in_blocks.reserve(_last_sorted_pos);
530
    //only init agg if needed
531
532
4
    if constexpr (!has_skip_bitmap_col) {
533
4
        RowInBlock* prev_row = nullptr;
534
4
        int row_pos = -1;
535
2.05k
        for (const auto& cur_row_ptr : *_row_in_blocks) {
536
2.05k
            RowInBlock* cur_row = cur_row_ptr.get();
537
2.05k
            if (!temp_row_in_blocks.empty() && (*_vec_row_comparator)(prev_row, cur_row) == 0) {
538
1.02k
                if (!prev_row->has_init_agg()) {
539
3
                    _init_row_for_agg(prev_row, mutable_block);
540
3
                }
541
1.02k
                _stat.merged_rows++;
542
1.02k
                _aggregate_two_row_in_block<has_skip_bitmap_col>(mutable_block, cur_row, prev_row);
543
                // Clean up aggregation state of the merged row to avoid memory leak
544
1.02k
                _clear_row_agg(cur_row);
545
1.03k
            } else {
546
1.03k
                prev_row = cur_row;
547
1.03k
                if (!temp_row_in_blocks.empty()) {
548
                    // The rows from the previous batch of _row_in_blocks have been merged into
549
                    // temp_row_in_blocks, now finalize into the mutable block.
550
1.02k
                    _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block,
551
1.02k
                                                row_pos);
552
1.02k
                }
553
1.03k
                temp_row_in_blocks.push_back(cur_row_ptr);
554
1.03k
                row_pos++;
555
1.03k
            }
556
2.05k
        }
557
4
        if (!temp_row_in_blocks.empty()) {
558
            // finalize the last low
559
4
            _finalize_one_row<is_final>(temp_row_in_blocks.back().get(), mutable_block, row_pos);
560
4
        }
561
    } else {
562
        DCHECK(_delete_sign_col_idx != -1);
563
        if (_seq_col_idx_in_block == -1) {
564
            _aggregate_for_flexible_partial_update_without_seq_col<is_final>(mutable_block,
565
                                                                             temp_row_in_blocks);
566
        } else {
567
            _aggregate_for_flexible_partial_update_with_seq_col<is_final>(mutable_block,
568
                                                                          temp_row_in_blocks);
569
        }
570
    }
571
    if constexpr (!is_final) {
572
        // if is not final, we collect the agg results to input_block and then continue to insert
573
        _input_mutable_block.swap(_output_mutable_block);
574
        //TODO(weixang):opt here.
575
        _output_mutable_block = MutableBlock::build_mutable_block(std::move(*empty_input_block));
576
        _output_mutable_block.clear_column_data();
577
        // Repack surviving rows so they do not retain batches of merged-away rows.
578
        // Aggregation states belong to the memtable; preserve their pointers and flags.
579
        DBUG_EXECUTE_IF("MemTable.aggregate.row_batch_allocation", DBUG_RUN_CALLBACK(this));
580
        auto rows = make_row_batch(temp_row_in_blocks.size(),
581
                                   [&](size_t i) { return *temp_row_in_blocks[i]; });
582
        for (size_t i = 0; i < temp_row_in_blocks.size(); ++i) {
583
            temp_row_in_blocks[i] = {rows, &rows[i]};
584
        }
585
        *_row_in_blocks = temp_row_in_blocks;
586
        _last_sorted_pos = _row_in_blocks->size();
587
    }
588
4
}
Unexecuted instantiation: _ZN5doris8MemTable10_aggregateILb1ELb1EEEvv
589
590
template <bool is_final>
591
void MemTable::_aggregate_for_flexible_partial_update_without_seq_col(
592
0
        MutableBlock& mutable_block, DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks) {
593
0
    std::shared_ptr<RowInBlock> prev_row {nullptr};
594
0
    int row_pos = -1;
595
0
    auto& skip_bitmaps =
596
0
            assert_cast<ColumnBitmap*>(mutable_block.mutable_columns()[_skip_bitmap_col_idx].get())
597
0
                    ->get_data();
598
0
    auto& delete_signs =
599
0
            assert_cast<ColumnInt8*>(mutable_block.mutable_columns()[_delete_sign_col_idx].get())
600
0
                    ->get_data();
601
0
    std::shared_ptr<RowInBlock> row_with_delete_sign {nullptr};
602
0
    std::shared_ptr<RowInBlock> row_without_delete_sign {nullptr};
603
604
0
    auto finalize_rows = [&]() {
605
0
        if (row_with_delete_sign != nullptr) {
606
0
            temp_row_in_blocks.push_back(row_with_delete_sign);
607
0
            _finalize_one_row<is_final>(row_with_delete_sign.get(), mutable_block, ++row_pos);
608
0
            row_with_delete_sign = nullptr;
609
0
        }
610
0
        if (row_without_delete_sign != nullptr) {
611
0
            temp_row_in_blocks.push_back(row_without_delete_sign);
612
0
            _finalize_one_row<is_final>(row_without_delete_sign.get(), mutable_block, ++row_pos);
613
0
            row_without_delete_sign = nullptr;
614
0
        }
615
        // _arena.clear();
616
0
    };
Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlvE_clEv
Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlvE_clEv
617
618
0
    auto add_row = [&](std::shared_ptr<RowInBlock> row, bool with_delete_sign) {
619
0
        if (with_delete_sign) {
620
0
            row_with_delete_sign = std::move(row);
621
0
        } else {
622
0
            row_without_delete_sign = std::move(row);
623
0
        }
624
0
    };
Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlS7_bE_clES7_b
Unexecuted instantiation: _ZZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEEENKUlS7_bE_clES7_b
625
0
    for (const auto& cur_row_ptr : *_row_in_blocks) {
626
0
        RowInBlock* cur_row = cur_row_ptr.get();
627
0
        const BitmapValue& skip_bitmap = skip_bitmaps[cur_row->_row_pos];
628
0
        bool cur_row_has_delete_sign = (!skip_bitmap.contains(_delete_sign_col_unique_id) &&
629
0
                                        delete_signs[cur_row->_row_pos] != 0);
630
0
        prev_row =
631
0
                (row_with_delete_sign == nullptr) ? row_without_delete_sign : row_with_delete_sign;
632
        // compare keys, the keys of row_with_delete_sign and row_without_delete_sign is the same,
633
        // choose any of them if it's valid
634
0
        if (prev_row != nullptr && (*_vec_row_comparator)(prev_row.get(), cur_row) == 0) {
635
0
            if (cur_row_has_delete_sign) {
636
0
                if (row_without_delete_sign != nullptr) {
637
                    // if there exits row without delete sign, remove it first
638
0
                    _clear_row_agg(row_without_delete_sign.get());
639
0
                    _stat.merged_rows++;
640
0
                    row_without_delete_sign = nullptr;
641
0
                }
642
                // and then unconditionally replace the previous row
643
0
                prev_row = row_with_delete_sign;
644
0
            } else {
645
0
                prev_row = row_without_delete_sign;
646
0
            }
647
648
0
            if (prev_row == nullptr) {
649
0
                add_row(cur_row_ptr, cur_row_has_delete_sign);
650
0
            } else {
651
0
                if (!prev_row->has_init_agg()) {
652
0
                    _init_row_for_agg(prev_row.get(), mutable_block);
653
0
                }
654
0
                _stat.merged_rows++;
655
0
                _aggregate_two_row_in_block<true>(mutable_block, cur_row, prev_row.get());
656
0
            }
657
0
        } else {
658
0
            finalize_rows();
659
0
            add_row(cur_row_ptr, cur_row_has_delete_sign);
660
0
        }
661
0
    }
662
    // finalize the last lows
663
0
    finalize_rows();
664
0
}
Unexecuted instantiation: _ZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE
Unexecuted instantiation: _ZN5doris8MemTable54_aggregate_for_flexible_partial_update_without_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE
665
666
template <bool is_final>
667
void MemTable::_aggregate_for_flexible_partial_update_with_seq_col(
668
0
        MutableBlock& mutable_block, DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks) {
669
    // For flexible partial update, when table has sequence column, we don't do any aggregation
670
    // in memtable. These duplicate rows will be aggregated in VerticalSegmentWriter
671
0
    int row_pos = -1;
672
0
    for (const auto& row_ptr : *_row_in_blocks) {
673
0
        RowInBlock* row = row_ptr.get();
674
0
        temp_row_in_blocks.push_back(row_ptr);
675
0
        _finalize_one_row<is_final>(row, mutable_block, ++row_pos);
676
0
    }
677
0
}
Unexecuted instantiation: _ZN5doris8MemTable51_aggregate_for_flexible_partial_update_with_seq_colILb0EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE
Unexecuted instantiation: _ZN5doris8MemTable51_aggregate_for_flexible_partial_update_with_seq_colILb1EEEvRNS_12MutableBlockERSt6vectorISt10shared_ptrINS_10RowInBlockEENS_18CustomStdAllocatorIS7_NS_9AllocatorILb0ELb0ELb0ENS_22DefaultMemoryAllocatorELb1EEEEEE
678
679
9
void MemTable::shrink_memtable_by_agg() {
680
9
    SCOPED_SWITCH_THREAD_MEM_TRACKER_LIMITER(
681
9
            _resource_ctx->memory_context()->mem_tracker()->write_tracker());
682
9
    SCOPED_CONSUME_MEM_TRACKER(_mem_tracker);
683
9
    if (_keys_type == KeysType::DUP_KEYS) {
684
0
        return;
685
0
    }
686
9
    size_t same_keys_num = _sort();
687
9
    if (same_keys_num != 0) {
688
9
        (_skip_bitmap_col_idx == -1) ? _aggregate<false, false>() : _aggregate<false, true>();
689
9
    }
690
9
    _last_agg_pos = memory_usage();
691
9
}
692
693
31
bool MemTable::need_flush() const {
694
31
    DBUG_EXECUTE_IF("MemTable.need_flush", { return true; });
695
31
    auto max_size = _adaptive_write_buffer_size();
696
31
    if (_partial_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS) {
697
0
        auto update_columns_size = _num_columns;
698
0
        auto min_buffer_size = config::min_write_buffer_size_for_partial_update;
699
0
        max_size = max_size * update_columns_size / _tablet_schema->num_columns();
700
0
        max_size = max_size > min_buffer_size ? max_size : min_buffer_size;
701
0
    }
702
703
31
    if (memory_usage() >= max_size) {
704
1
        g_flush_cuz_memtable_full << 1;
705
1
        return true;
706
1
    }
707
30
    return false;
708
31
}
709
710
31
int64_t MemTable::_adaptive_write_buffer_size() const {
711
31
    if (!config::enable_adaptive_write_buffer_size) [[unlikely]] {
712
8
        return config::write_buffer_size;
713
8
    }
714
23
    auto* memtable_limiter = ExecEnv::GetInstance()->memtable_memory_limiter();
715
23
    int64_t memtable_mem =
716
23
            (memtable_limiter != nullptr && memtable_limiter->mem_tracker() != nullptr)
717
23
                    ? memtable_limiter->mem_tracker()->consumption()
718
23
                    : 0;
719
23
    int64_t factor = 4;
720
    // Memory usage intervals:
721
    // (80 %, 100 %] → 1× buffer
722
    // (50 %, 80 %]  → 2× buffer
723
    // [0 %, 50 %]   → 4× buffer
724
23
    if (memtable_mem > (_load_mem_limit * 4) / 5) { // > 80 %
725
0
        factor = 1;
726
23
    } else if (memtable_mem > _load_mem_limit / 2) { // > 50 %
727
0
        factor = 2;
728
0
    }
729
23
    return config::write_buffer_size * factor;
730
31
}
731
732
33
bool MemTable::need_agg() const {
733
33
    if (_keys_type == KeysType::AGG_KEYS) {
734
15
        auto max_size = _last_agg_pos + config::write_buffer_size_for_agg;
735
15
        return memory_usage() >= max_size;
736
15
    }
737
18
    return false;
738
33
}
739
740
0
size_t MemTable::get_flush_reserve_memory_size() const {
741
0
    if (_keys_type == KeysType::DUP_KEYS && _tablet_schema->num_key_columns() == 0) {
742
0
        return 0; // no need to reserve
743
0
    }
744
0
    return static_cast<size_t>(static_cast<double>(_input_mutable_block.allocated_bytes()) * 1.2);
745
0
}
746
747
14
Status MemTable::_to_block(std::unique_ptr<Block>* res) {
748
14
    size_t same_keys_num = _sort();
749
14
    if (_keys_type == KeysType::DUP_KEYS || same_keys_num == 0) {
750
10
        if (_keys_type == KeysType::DUP_KEYS && _tablet_schema->num_key_columns() == 0) {
751
0
            _output_mutable_block.swap(_input_mutable_block);
752
10
        } else {
753
10
            Block in_block = _input_mutable_block.to_block();
754
10
            RETURN_IF_ERROR(_put_into_output(in_block));
755
10
        }
756
10
    } else {
757
4
        (_skip_bitmap_col_idx == -1) ? _aggregate<true, false>() : _aggregate<true, true>();
758
4
    }
759
14
    if (_keys_type == KeysType::UNIQUE_KEYS && _enable_unique_key_mow &&
760
14
        !_tablet_schema->cluster_key_uids().empty()) {
761
3
        if (_partial_update_mode != UniqueKeyUpdateModePB::UPSERT) {
762
0
            return Status::InternalError(
763
0
                    "Partial update for mow with cluster keys is not supported");
764
0
        }
765
3
        RETURN_IF_ERROR(_sort_by_cluster_keys());
766
3
    }
767
14
    _input_mutable_block.clear();
768
14
    *res = Block::create_unique(_output_mutable_block.to_block());
769
14
    return Status::OK();
770
14
}
771
772
14
Status MemTable::to_block(std::unique_ptr<Block>* res) {
773
14
    RETURN_IF_ERROR_OR_CATCH_EXCEPTION(_to_block(res));
774
14
    return Status::OK();
775
14
}
776
777
#include "common/compile_check_end.h"
778
} // namespace doris