Coverage Report

Created: 2026-08-06 12:21

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 <functional>
26
#include <memory>
27
#include <vector>
28
29
#include "common/status.h"
30
#include "core/arena.h"
31
#include "core/block/block.h"
32
#include "core/custom_allocator.h"
33
#include "exprs/aggregate/aggregate_function.h"
34
#include "runtime/memory/mem_tracker.h"
35
#include "runtime/thread_context.h"
36
#include "storage/partial_update_info.h"
37
#include "storage/tablet/tablet_schema.h"
38
39
namespace doris {
40
41
class Schema;
42
class SlotDescriptor;
43
class TabletSchema;
44
struct TabletAddRowsPayload;
45
class TupleDescriptor;
46
enum KeysType : int;
47
48
// Active: the memtable is currently used by writer to insert into blocks
49
// Write_finished: the memtable finished write blocks and in the queue waiting for flush
50
// FLUSH: the memtable is under flushing, write segment to disk.
51
enum MemType { ACTIVE = 0, WRITE_FINISHED = 1, FLUSH = 2 };
52
53
// row pos in _input_mutable_block
54
struct RowInBlock {
55
    size_t _row_pos;
56
    int64_t _row_binlog_lsn = 0;
57
    char* _agg_mem = nullptr;
58
    size_t* _agg_state_offset = nullptr;
59
    bool _has_init_agg;
60
61
782k
    RowInBlock(size_t row) : _row_pos(row), _has_init_agg(false) {}
62
    RowInBlock(size_t row, int64_t row_binlog_lsn)
63
35.9M
            : _row_pos(row), _row_binlog_lsn(row_binlog_lsn), _has_init_agg(false) {}
64
65
7.39k
    void init_agg_places(char* agg_mem, size_t* agg_state_offset) {
66
7.39k
        _has_init_agg = true;
67
7.39k
        _agg_mem = agg_mem;
68
7.39k
        _agg_state_offset = agg_state_offset;
69
7.39k
    }
70
71
389k
    char* agg_places(size_t offset) const { return _agg_mem + _agg_state_offset[offset]; }
72
73
8.17M
    inline bool has_init_agg() const { return _has_init_agg; }
74
75
7.39k
    inline void remove_init_agg() { _has_init_agg = false; }
76
};
77
78
class RowInBlockComparator {
79
public:
80
    RowInBlockComparator(std::shared_ptr<TabletSchema> tablet_schema)
81
86.5k
            : _tablet_schema(tablet_schema) {}
82
    // call set_block before operator().
83
    // only first time insert block to create _input_mutable_block,
84
    // so can not Comparator of construct to set pblock
85
175k
    void set_block(MutableBlock* pblock) { _pblock = pblock; }
86
    int operator()(const RowInBlock* left, const RowInBlock* right) const;
87
88
private:
89
    std::shared_ptr<TabletSchema> _tablet_schema;
90
    MutableBlock* _pblock = nullptr; //  corresponds to Memtable::_input_mutable_block
91
};
92
93
class MemTableStat {
94
public:
95
74.0k
    MemTableStat& operator+=(const MemTableStat& stat) {
96
74.0k
        raw_rows += stat.raw_rows;
97
74.0k
        merged_rows += stat.merged_rows;
98
74.0k
        sort_ns += stat.sort_ns;
99
74.0k
        agg_ns += stat.agg_ns;
100
74.0k
        put_into_output_ns += stat.put_into_output_ns;
101
74.0k
        duration_ns += stat.duration_ns;
102
74.0k
        sort_times += stat.sort_times;
103
74.0k
        agg_times += stat.agg_times;
104
105
74.0k
        return *this;
106
74.0k
    }
107
108
    std::atomic<int64_t> raw_rows = 0;
109
    std::atomic<int64_t> merged_rows = 0;
110
    int64_t sort_ns = 0;
111
    int64_t agg_ns = 0;
112
    int64_t put_into_output_ns = 0;
113
    int64_t duration_ns = 0;
114
    std::atomic<int64_t> sort_times = 0;
115
    std::atomic<int64_t> agg_times = 0;
116
};
117
118
class MemTable {
119
public:
120
    MemTable(int64_t tablet_id, std::shared_ptr<TabletSchema> tablet_schema,
121
             const std::vector<SlotDescriptor*>* slot_descs, TupleDescriptor* tuple_desc,
122
             bool enable_unique_key_mow, PartialUpdateInfo* partial_update_info,
123
             const std::shared_ptr<ResourceContext>& resource_ctx,
124
             bool need_row_binlog_lsn = false);
125
    ~MemTable();
126
127
74.1k
    int64_t tablet_id() const { return _tablet_id; }
128
2.03M
    size_t memory_usage() const { return _mem_tracker->consumption(); }
129
    size_t get_flush_reserve_memory_size() const;
130
    // insert tuple from (row_pos) to (row_pos+num_rows)
131
    Status insert(const Block* block, const TabletAddRowsPayload& rows);
132
133
    void shrink_memtable_by_agg();
134
135
    bool need_flush() const;
136
137
    bool need_agg() const;
138
139
    Status to_block(std::unique_ptr<Block>* res);
140
141
3
    const DorisVector<int64_t>& row_binlog_lsns() const { return _output_row_binlog_lsns; }
142
143
86.7k
    bool empty() const { return _input_mutable_block.rows() == 0; }
144
145
74.0k
    const MemTableStat& stat() { return _stat; }
146
147
148k
    std::shared_ptr<ResourceContext> resource_ctx() { return _resource_ctx; }
148
149
74.1k
    std::shared_ptr<MemTracker> mem_tracker() { return _mem_tracker; }
150
151
73.9k
    void set_flush_success() { _is_flush_success = true; }
152
153
614k
    MemType get_mem_type() { return _mem_type; }
154
155
160k
    void update_mem_type(MemType memtype) { _mem_type = memtype; }
156
157
135k
    int64_t raw_rows() { return _stat.raw_rows.load(); }
158
159
private:
160
    // for vectorized
161
    template <bool has_skip_bitmap_col>
162
    void _aggregate_two_row_in_block(MutableBlock& mutable_block, RowInBlock* new_row,
163
                                     RowInBlock* row_in_skiplist);
164
165
    // Merge row-binlog LSN sidecar only when MemTable merges two RowInBlock objects.
166
    // Table models that require complex merge semantics, such as AGG tables and unique key
167
    // merge-on-read tables, do not support row-binlog LSN now and are rejected in insert().
168
    void _merge_row_binlog_lsn(RowInBlock* src_row, RowInBlock* dst_row);
169
170
    void _append_output_row_binlog_lsn(RowInBlock* row);
171
172
    void _aggregate_two_row_with_sequence_map(MutableBlock& mutable_block, RowInBlock* new_row,
173
                                              RowInBlock* row_in_skiplist);
174
175
    // Used to wrapped by to_block to do exception handle logic
176
    Status _to_block(std::unique_ptr<Block>* res);
177
178
    int64_t _adaptive_write_buffer_size() const;
179
180
private:
181
    std::atomic<MemType> _mem_type;
182
    int64_t _tablet_id;
183
    bool _enable_unique_key_mow = false;
184
    bool _is_flush_success = false;
185
    UniqueKeyUpdateModePB _partial_update_mode {UniqueKeyUpdateModePB::UPSERT};
186
    const KeysType _keys_type;
187
    std::shared_ptr<TabletSchema> _tablet_schema;
188
189
    std::shared_ptr<RowInBlockComparator> _vec_row_comparator;
190
191
    std::shared_ptr<ResourceContext> _resource_ctx;
192
193
    std::shared_ptr<MemTracker> _mem_tracker;
194
    // Only the rows will be inserted into block can allocate memory from _arena.
195
    // In this way, we can make MemTable::memory_usage() to be more accurate, and eventually
196
    // reduce the number of segment files that are generated by current load
197
    Arena _arena;
198
    int64_t _load_mem_limit = -1;
199
200
    void _init_columns_offset_by_slot_descs(const std::vector<SlotDescriptor*>* slot_descs,
201
                                            const TupleDescriptor* tuple_desc);
202
    std::vector<int> _column_offset;
203
204
    // Number of rows inserted to this memtable.
205
    // This is not the rows in this memtable, because rows may be merged
206
    // in unique or aggregate key model.
207
    MemTableStat _stat;
208
209
    //for vectorized
210
    MutableBlock _input_mutable_block;
211
    MutableBlock _output_mutable_block;
212
    DorisVector<int64_t> _output_row_binlog_lsns;
213
    bool _need_row_binlog_lsn = false;
214
    size_t _last_sorted_pos = 0;
215
    size_t _last_agg_pos = 0;
216
217
    // Sorts `perm` (row positions into `block`) by `key_col_idx`, reusing the
218
    // vectorized ColumnSorter the query engine uses, then stabilises rows with
219
    // an equal key on ascending row position.
220
    // Returns the number of rows that share their key with a neighbour.
221
    static size_t _sort_permutation_by_key_columns(MutableBlock& block,
222
                                                   const std::vector<int>& key_col_idx,
223
                                                   IColumn::Permutation& perm);
224
    //return number of same keys
225
    size_t _sort();
226
    Status _sort_by_cluster_keys();
227
    template <bool is_final>
228
    void _finalize_one_row(RowInBlock* row, MutableBlock& mutable_block, int row_pos);
229
    void _init_row_for_agg(RowInBlock* row, MutableBlock& mutable_block);
230
    void _clear_row_agg(RowInBlock* row);
231
232
    template <bool is_final, bool has_skip_bitmap_col = false>
233
    void _aggregate();
234
235
    template <bool is_final>
236
    void _aggregate_for_flexible_partial_update_without_seq_col(
237
            MutableBlock& mutable_block,
238
            DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks);
239
240
    template <bool is_final>
241
    void _aggregate_for_flexible_partial_update_with_seq_col(
242
            MutableBlock& mutable_block,
243
            DorisVector<std::shared_ptr<RowInBlock>>& temp_row_in_blocks);
244
245
    Status _put_into_output(Block& in_block);
246
    bool _is_first_insertion;
247
248
    void _init_agg_functions(const Block* block);
249
    std::vector<AggregateFunctionPtr> _agg_functions;
250
    std::vector<size_t> _offsets_of_aggregate_states;
251
    size_t _total_size_of_aggregate_states;
252
    std::unique_ptr<DorisVector<std::shared_ptr<RowInBlock>>> _row_in_blocks;
253
254
    size_t _num_columns;
255
    int32_t _seq_col_idx_in_block {-1};
256
    int32_t _skip_bitmap_col_idx {-1};
257
    int32_t _delete_sign_col_idx {-1};
258
    int32_t _delete_sign_col_unique_id {-1};
259
    int32_t _seq_col_unique_id {-1};
260
261
    bool _is_partial_update_and_auto_inc = false;
262
}; // class MemTable
263
264
} // namespace doris