Coverage Report

Created: 2026-09-03 21:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/load/memtable/memtable.h
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
#pragma once
19
20
#include <stddef.h>
21
#include <stdint.h>
22
23
#include <cstdint>
24
#include <cstring>
25
#include <memory>
26
#include <vector>
27
28
#include "common/status.h"
29
#include "core/arena.h"
30
#include "core/block/block.h"
31
#include "core/custom_allocator.h"
32
#include "exprs/aggregate/aggregate_function.h"
33
#include "runtime/memory/mem_tracker.h"
34
#include "runtime/thread_context.h"
35
#include "storage/partial_update_info.h"
36
#include "storage/rowset/rowset_fwd.h"
37
#include "storage/tablet/tablet_schema.h"
38
39
namespace doris {
40
41
class SlotDescriptor;
42
class TabletSchema;
43
struct TabletAddRowsPayload;
44
class TupleDescriptor;
45
enum KeysType : int;
46
47
// Active: the memtable is currently used by writer to insert into blocks
48
// Write_finished: the memtable finished write blocks and in the queue waiting for flush
49
// FLUSH: the memtable is under flushing, write segment to disk.
50
enum MemType { ACTIVE = 0, WRITE_FINISHED = 1, FLUSH = 2 };
51
52
// row pos in _input_mutable_block
53
struct RowInBlock {
54
    size_t _row_pos;
55
    int64_t _allocated_lsn = 0;
56
    char* _agg_mem = nullptr;
57
    size_t* _agg_state_offset = nullptr;
58
    bool _has_init_agg;
59
60
815k
    RowInBlock(size_t row) : _row_pos(row), _has_init_agg(false) {}
61
    RowInBlock(size_t row, int64_t allocated_lsn)
62
7.69M
            : _row_pos(row), _allocated_lsn(allocated_lsn), _has_init_agg(false) {}
63
64
8.39k
    void init_agg_places(char* agg_mem, size_t* agg_state_offset) {
65
8.39k
        _has_init_agg = true;
66
8.39k
        _agg_mem = agg_mem;
67
8.39k
        _agg_state_offset = agg_state_offset;
68
8.39k
    }
69
70
455k
    char* agg_places(size_t offset) const { return _agg_mem + _agg_state_offset[offset]; }
71
72
8.15M
    inline bool has_init_agg() const { return _has_init_agg; }
73
74
8.39k
    inline void remove_init_agg() { _has_init_agg = false; }
75
};
76
77
class Tie {
78
public:
79
    class Iter {
80
    public:
81
202k
        Iter(Tie& tie) : _tie(tie), _next(tie._begin + 1) {}
82
10.8M
        size_t left() const { return _left; }
83
56.3M
        size_t right() const { return _right; }
84
85
        // return false means no more ranges
86
3.95M
        bool next() {
87
3.95M
            if (_next >= _tie._end) {
88
153k
                return false;
89
153k
            }
90
3.80M
            _next = _find(1, _next);
91
3.80M
            if (_next >= _tie._end) {
92
48.8k
                return false;
93
48.8k
            }
94
3.75M
            _left = _next - 1;
95
3.75M
            _next = _find(0, _next);
96
3.75M
            _right = _next;
97
3.75M
            return true;
98
3.80M
        }
99
100
    private:
101
7.55M
        size_t _find(uint8_t value, size_t start) {
102
7.55M
            if (start >= _tie._end) {
103
0
                return start;
104
0
            }
105
7.55M
            size_t offset = start - _tie._begin;
106
7.55M
            size_t size = _tie._end - start;
107
7.55M
            void* p = std::memchr(_tie._bits.data() + offset, value, size);
108
7.55M
            if (p == nullptr) {
109
83.0k
                return _tie._end;
110
83.0k
            }
111
7.47M
            return static_cast<uint8_t*>(p) - _tie._bits.data() + _tie._begin;
112
7.55M
        }
113
114
    private:
115
        Tie& _tie;
116
        size_t _left;
117
        size_t _right;
118
        size_t _next;
119
    };
120
121
public:
122
55.8k
    Tie(size_t begin, size_t end) : _begin(begin), _end(end) {
123
55.8k
        _bits = std::vector<uint8_t>(_end - _begin, 1);
124
55.8k
    }
125
0
    uint8_t operator[](size_t i) const { return _bits[i - _begin]; }
126
52.6M
    uint8_t& operator[](size_t i) { return _bits[i - _begin]; }
127
202k
    Iter iter() { return Iter(*this); }
128
129
private:
130
    const size_t _begin;
131
    const size_t _end;
132
    std::vector<uint8_t> _bits;
133
};
134
135
class RowInBlockComparator {
136
public:
137
    RowInBlockComparator(std::shared_ptr<TabletSchema> tablet_schema)
138
54.3k
            : _tablet_schema(tablet_schema) {}
139
    // call set_block before operator().
140
    // only first time insert block to create _input_mutable_block,
141
    // so can not Comparator of construct to set pblock
142
56.5k
    void set_block(MutableBlock* pblock) { _pblock = pblock; }
143
    int operator()(const RowInBlock* left, const RowInBlock* right) const;
144
145
private:
146
    std::shared_ptr<TabletSchema> _tablet_schema;
147
    MutableBlock* _pblock = nullptr; //  corresponds to Memtable::_input_mutable_block
148
};
149
150
class MemTableStat {
151
public:
152
54.2k
    MemTableStat& operator+=(const MemTableStat& stat) {
153
54.2k
        raw_rows += stat.raw_rows;
154
54.2k
        merged_rows += stat.merged_rows;
155
54.2k
        sort_ns += stat.sort_ns;
156
54.2k
        agg_ns += stat.agg_ns;
157
54.2k
        put_into_output_ns += stat.put_into_output_ns;
158
54.2k
        duration_ns += stat.duration_ns;
159
54.2k
        sort_times += stat.sort_times;
160
54.2k
        agg_times += stat.agg_times;
161
162
54.2k
        return *this;
163
54.2k
    }
164
165
    std::atomic<int64_t> raw_rows = 0;
166
    std::atomic<int64_t> merged_rows = 0;
167
    int64_t sort_ns = 0;
168
    int64_t agg_ns = 0;
169
    int64_t put_into_output_ns = 0;
170
    int64_t duration_ns = 0;
171
    std::atomic<int64_t> sort_times = 0;
172
    std::atomic<int64_t> agg_times = 0;
173
};
174
175
class MemTable {
176
public:
177
    MemTable(int64_t tablet_id, std::shared_ptr<TabletSchema> tablet_schema,
178
             const std::vector<SlotDescriptor*>* slot_descs, TupleDescriptor* tuple_desc,
179
             bool enable_unique_key_mow, PartialUpdateInfo* partial_update_info,
180
             const std::shared_ptr<ResourceContext>& resource_ctx, bool need_lsn = false);
181
    ~MemTable();
182
183
54.3k
    int64_t tablet_id() const { return _tablet_id; }
184
1.87M
    size_t memory_usage() const { return _mem_tracker->consumption(); }
185
    size_t get_flush_reserve_memory_size() const;
186
    // insert tuple from (row_pos) to (row_pos+num_rows)
187
    Status insert(const Block* block, const TabletAddRowsPayload& rows);
188
189
    void shrink_memtable_by_agg();
190
191
    bool need_flush() const;
192
193
    bool need_agg() const;
194
195
    Status to_block(std::unique_ptr<Block>* res);
196
197
64
    ConstAllocatedLsnVectorSharedPtr allocated_lsns() const { return _output_allocated_lsns; }
198
199
54.3k
    bool empty() const { return _input_mutable_block.rows() == 0; }
200
201
54.2k
    const MemTableStat& stat() { return _stat; }
202
203
108k
    std::shared_ptr<ResourceContext> resource_ctx() { return _resource_ctx; }
204
205
54.4k
    std::shared_ptr<MemTracker> mem_tracker() { return _mem_tracker; }
206
207
54.2k
    void set_flush_success() { _is_flush_success = true; }
208
209
754k
    MemType get_mem_type() { return _mem_type; }
210
211
108k
    void update_mem_type(MemType memtype) { _mem_type = memtype; }
212
213
114k
    int64_t raw_rows() { return _stat.raw_rows.load(); }
214
215
private:
216
    // for vectorized
217
    template <bool has_skip_bitmap_col>
218
    void _aggregate_two_row_in_block(MutableBlock& mutable_block, RowInBlock* new_row,
219
                                     RowInBlock* row_in_skiplist);
220
221
    // Merge allocated LSN sidecar only when MemTable merges two RowInBlock objects.
222
    // Table models that require complex merge semantics, such as AGG tables and unique key
223
    // merge-on-read tables, do not support allocated LSN now and are rejected in insert().
224
    void _merge_allocated_lsn(RowInBlock* src_row, RowInBlock* dst_row);
225
226
    void _append_output_allocated_lsn(RowInBlock* row);
227
228
    void _aggregate_two_row_with_sequence_map(MutableBlock& mutable_block, RowInBlock* new_row,
229
                                              RowInBlock* row_in_skiplist);
230
231
    // Used to wrapped by to_block to do exception handle logic
232
    Status _to_block(std::unique_ptr<Block>* res);
233
234
    int64_t _adaptive_write_buffer_size() const;
235
236
private:
237
    std::atomic<MemType> _mem_type;
238
    int64_t _tablet_id;
239
    bool _enable_unique_key_mow = false;
240
    bool _is_flush_success = false;
241
    UniqueKeyUpdateModePB _partial_update_mode {UniqueKeyUpdateModePB::UPSERT};
242
    const KeysType _keys_type;
243
    std::shared_ptr<TabletSchema> _tablet_schema;
244
245
    std::shared_ptr<RowInBlockComparator> _vec_row_comparator;
246
247
    std::shared_ptr<ResourceContext> _resource_ctx;
248
249
    std::shared_ptr<MemTracker> _mem_tracker;
250
    // Only the rows will be inserted into block can allocate memory from _arena.
251
    // In this way, we can make MemTable::memory_usage() to be more accurate, and eventually
252
    // reduce the number of segment files that are generated by current load
253
    Arena _arena;
254
    int64_t _load_mem_limit = -1;
255
256
    void _init_columns_offset_by_slot_descs(const std::vector<SlotDescriptor*>* slot_descs,
257
                                            const TupleDescriptor* tuple_desc);
258
    std::vector<int> _column_offset;
259
    int32_t _row_lsn_col_pos = -1;
260
261
    // Number of rows inserted to this memtable.
262
    // This is not the rows in this memtable, because rows may be merged
263
    // in unique or aggregate key model.
264
    MemTableStat _stat;
265
266
    //for vectorized
267
    MutableBlock _input_mutable_block;
268
    MutableBlock _output_mutable_block;
269
    AllocatedLsnVectorSharedPtr _output_allocated_lsns = std::make_shared<std::vector<int64_t>>();
270
    bool _need_lsn = false;
271
    size_t _last_sorted_pos = 0;
272
    size_t _last_agg_pos = 0;
273
274
    //return number of same keys
275
    size_t _sort();
276
    Status _sort_by_cluster_keys();
277
    template <typename RowRef, typename RowPosGetter>
278
    size_t _sort_rows(DorisVector<RowRef>& rows, RowPosGetter&& get_row_pos);
279
    template <typename RowRef, typename Comparator>
280
    void _sort_one_column(DorisVector<RowRef>& rows, Tie& tie, Comparator&& cmp);
281
    template <bool is_final>
282
    void _finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos);
283
    void _init_row_for_agg(RowInBlock* row, MutableBlock& mutable_block);
284
    void _clear_row_agg(RowInBlock* row);
285
286
    template <bool is_final, bool has_skip_bitmap_col = false>
287
    void _aggregate();
288
289
    template <bool is_final>
290
    void _aggregate_for_flexible_partial_update_without_seq_col(
291
            MutableBlock& mutable_block,
292
            DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks);
293
294
    template <bool is_final>
295
    void _aggregate_for_flexible_partial_update_with_seq_col(
296
            MutableBlock& mutable_block,
297
            DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks);
298
299
    Status _put_into_output(Block& in_block);
300
    bool _is_first_insertion;
301
302
    void _init_agg_functions(const Block* block);
303
    std::vector<AggregateFunctionPtr> _agg_functions;
304
    std::vector<size_t> _offsets_of_aggregate_states;
305
    size_t _total_size_of_aggregate_states;
306
    // DUP_KEYS only needs a permutation of source row positions for sorting. A uint32_t vector
307
    // avoids one shared_ptr, one RowInBlock, and one allocation/control block per input.
308
    std::unique_ptr<DorisVector<uint32_t>> _duplicate_key_row_positions;
309
    // Optional row-binlog LSNs remain indexed by the original DUP_KEYS row position. Output uses
310
    // the sorted position vector to apply the identical permutation to rows and their LSNs.
311
    std::unique_ptr<DorisVector<int64_t>> _duplicate_key_allocated_lsns;
312
    // Only UNIQUE_KEYS and AGG_KEYS use RowInBlock. Those models still need per-row aggregation
313
    // state and, when enabled, the allocated LSN stored in RowInBlock.
314
    std::unique_ptr<DorisVector<std::shared_ptr<RowInBlock>>> _row_in_blocks;
315
316
    size_t _num_columns;
317
    int32_t _seq_col_idx_in_block {-1};
318
    int32_t _skip_bitmap_col_idx {-1};
319
    int32_t _delete_sign_col_idx {-1};
320
    int32_t _delete_sign_col_unique_id {-1};
321
    int32_t _seq_col_unique_id {-1};
322
323
    bool _is_partial_update_and_auto_inc = false;
324
}; // class MemTable
325
326
} // namespace doris