Coverage Report

Created: 2026-09-15 16:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/tablet_info.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 <butil/fast_rand.h>
21
#include <gen_cpp/Descriptors_types.h>
22
#include <gen_cpp/Types_types.h>
23
#include <gen_cpp/descriptors.pb.h>
24
#include <gen_cpp/olap_file.pb.h>
25
26
#include <cstdint>
27
#include <functional>
28
#include <iterator>
29
#include <map>
30
#include <memory>
31
#include <optional>
32
#include <string>
33
#include <tuple>
34
#include <unordered_map>
35
#include <utility>
36
#include <vector>
37
38
#include "cloud/config.h"
39
#include "common/cast_set.h"
40
#include "common/logging.h"
41
#include "common/object_pool.h"
42
#include "common/status.h"
43
#include "core/block/block.h"
44
#include "core/block/column_with_type_and_name.h"
45
#include "core/column/column.h"
46
#include "exprs/vexpr.h"
47
#include "exprs/vexpr_fwd.h"
48
#include "runtime/descriptors.h"
49
#include "util/raw_value.h"
50
51
namespace doris {
52
class MemTracker;
53
class SlotDescriptor;
54
class TExprNode;
55
class TabletColumn;
56
class TabletIndex;
57
class TupleDescriptor;
58
59
struct RowBinlogColumnUidMapping {
60
    int32_t source_uid;
61
    int32_t current_uid;
62
    std::optional<int32_t> before_uid;
63
};
64
65
struct OlapTableIndexSchema {
66
    int64_t index_id;
67
    int64_t row_binlog_id = 0;
68
    bool row_binlog_need_historical_value = false;
69
    std::vector<RowBinlogColumnUidMapping> row_binlog_column_mappings;
70
    std::vector<SlotDescriptor*> slots;
71
    int32_t schema_hash;
72
    std::vector<TabletColumn*> columns;
73
    std::vector<TabletIndex*> indexes;
74
    VExprContextSPtr where_clause;
75
76
    void to_protobuf(POlapTableIndexSchema* pindex) const;
77
};
78
79
class OlapTableSchemaParam {
80
public:
81
134k
    OlapTableSchemaParam() = default;
82
134k
    ~OlapTableSchemaParam() noexcept = default;
83
84
    Status init(const TOlapTableSchemaParam& tschema);
85
    Status init(const POlapTableSchemaParam& pschema);
86
87
479k
    int64_t db_id() const { return _db_id; }
88
590k
    int64_t table_id() const { return _table_id; }
89
251k
    int64_t version() const { return _version; }
90
91
126k
    TupleDescriptor* tuple_desc() const { return _tuple_desc; }
92
1.01M
    const std::vector<OlapTableIndexSchema*>& indexes() const { return _indexes; }
93
    const OlapTableIndexSchema* row_binlog_index_schema(int64_t index_id) const;
94
0
    const std::vector<OlapTableIndexSchema*>& row_binlog_index_schemas() const {
95
0
        return _row_binlog_index_schemas;
96
0
    }
97
98
    void to_protobuf(POlapTableSchemaParam* pschema) const;
99
100
    // NOTE: this function is not thread-safe.
101
47.7k
    POlapTableSchemaParam* to_protobuf() const {
102
47.7k
        if (_proto_schema == nullptr) {
103
46.6k
            _proto_schema = _obj_pool.add(new POlapTableSchemaParam());
104
46.6k
            to_protobuf(_proto_schema);
105
46.6k
        }
106
47.7k
        return _proto_schema;
107
47.7k
    }
108
109
179k
    UniqueKeyUpdateModePB unique_key_update_mode() const { return _unique_key_update_mode; }
110
111
280k
    bool is_partial_update() const {
112
280k
        return _unique_key_update_mode != UniqueKeyUpdateModePB::UPSERT;
113
280k
    }
114
46.2k
    bool is_fixed_partial_update() const {
115
46.2k
        return _unique_key_update_mode == UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS;
116
46.2k
    }
117
0
    bool is_flexible_partial_update() const {
118
0
        return _unique_key_update_mode == UniqueKeyUpdateModePB::UPDATE_FLEXIBLE_COLUMNS;
119
0
    }
120
121
179k
    const std::set<std::string>& partial_update_input_columns() const {
122
179k
        return _partial_update_input_columns;
123
179k
    }
124
179k
    PartialUpdateNewRowPolicyPB partial_update_new_key_policy() const {
125
179k
        return _partial_update_new_row_policy;
126
179k
    }
127
188k
    std::string auto_increment_coulumn() const { return _auto_increment_column; }
128
46.5k
    int32_t auto_increment_column_unique_id() const { return _auto_increment_column_unique_id; }
129
46.9k
    void set_timestamp_ms(int64_t timestamp_ms) { _timestamp_ms = timestamp_ms; }
130
179k
    int64_t timestamp_ms() const { return _timestamp_ms; }
131
46.9k
    void set_nano_seconds(int32_t nano_seconds) { _nano_seconds = nano_seconds; }
132
179k
    int32_t nano_seconds() const { return _nano_seconds; }
133
46.7k
    void set_timezone(std::string timezone) { _timezone = timezone; }
134
179k
    std::string timezone() const { return _timezone; }
135
227k
    bool is_strict_mode() const { return _is_strict_mode; }
136
179k
    int32_t sequence_map_col_uid() const { return _sequence_map_col_uid; }
137
    std::string debug_string() const;
138
139
    Status init_unique_key_update_mode(const TOlapTableSchemaParam& tschema);
140
141
private:
142
    int64_t _db_id;
143
    int64_t _table_id;
144
    int64_t _version;
145
146
    TupleDescriptor* _tuple_desc = nullptr;
147
    mutable POlapTableSchemaParam* _proto_schema = nullptr;
148
    std::vector<OlapTableIndexSchema*> _indexes;
149
    std::vector<OlapTableIndexSchema*> _row_binlog_index_schemas;
150
    mutable ObjectPool _obj_pool;
151
    UniqueKeyUpdateModePB _unique_key_update_mode {UniqueKeyUpdateModePB::UPSERT};
152
    PartialUpdateNewRowPolicyPB _partial_update_new_row_policy {
153
            PartialUpdateNewRowPolicyPB::APPEND};
154
    std::set<std::string> _partial_update_input_columns;
155
    bool _is_strict_mode = false;
156
    std::string _auto_increment_column;
157
    int32_t _auto_increment_column_unique_id;
158
    int64_t _timestamp_ms = 0;
159
    int32_t _nano_seconds {0};
160
    std::string _timezone;
161
    int32_t _sequence_map_col_uid {-1};
162
};
163
164
using OlapTableIndexTablets = TOlapTableIndexTablets;
165
// struct TOlapTableIndexTablets {
166
//     1: required i64 index_id
167
//     2: required list<i64> tablets
168
//     3: optional i64 bucket_be_id
169
//     4: optional list<i32> local_bucket_seqs
170
// }
171
172
using BlockRow = std::pair<Block*, int32_t>;
173
using BlockRowWithIndicator = std::tuple<Block*, int32_t, bool>; // [block, row, is_transformed]
174
175
struct VOlapTablePartition {
176
    int64_t id = 0;
177
    BlockRow start_key;
178
    BlockRow end_key;
179
    std::vector<BlockRow> in_keys;
180
    int64_t num_buckets = 0;
181
    std::vector<OlapTableIndexTablets> indexes;
182
    bool is_mutable;
183
    // -1 indicates partition with hash distribution
184
    int64_t load_tablet_idx = -1;
185
    // Fallback FE-selected bucket owner BE for adaptive random bucket mode. New FE versions send
186
    // this per index in OlapTableIndexTablets.
187
    int64_t bucket_be_id = -1;
188
    // Fallback bucket indices (0-based) used by FIND_TABLET_RANDOM_BUCKET rotation. New FE versions
189
    // send this per index in OlapTableIndexTablets.
190
    std::vector<int32_t> local_bucket_seqs;
191
    int total_replica_num = 0;
192
    int load_required_replica_num = 0;
193
    // tablet_id -> set of backend_ids that have version gaps
194
    std::unordered_map<int64_t, std::unordered_set<int64_t>> tablet_version_gap_backends;
195
196
    VOlapTablePartition(Block* partition_block)
197
            // the default value of partition bound is -1.
198
60.7k
            : start_key {partition_block, -1}, end_key {partition_block, -1} {}
199
};
200
201
// this is only used by tablet_sink. so we can assume it's inited by its' descriptor.
202
class VOlapTablePartKeyComparator {
203
public:
204
    VOlapTablePartKeyComparator(const std::vector<uint16_t>& slot_locs,
205
                                const std::vector<uint16_t>& params_locs)
206
38.1M
            : _slot_locs(slot_locs), _param_locs(params_locs) {}
207
208
    // return true if lhs < rhs
209
    // 'row' is -1 mean maximal boundary
210
    bool operator()(const BlockRowWithIndicator& lhs, const BlockRowWithIndicator& rhs) const;
211
212
private:
213
    const std::vector<uint16_t>& _slot_locs;
214
    const std::vector<uint16_t>& _param_locs;
215
};
216
217
// store an olap table's tablet information
218
class VOlapTablePartitionParam {
219
public:
220
    VOlapTablePartitionParam(std::shared_ptr<OlapTableSchemaParam>& schema,
221
                             const TOlapTablePartitionParam& param);
222
223
    ~VOlapTablePartitionParam();
224
225
    Status init();
226
227
210
    int64_t db_id() const { return _t_param.db_id; }
228
210
    int64_t table_id() const { return _t_param.table_id; }
229
0
    int64_t version() const { return _t_param.version; }
230
231
    // return true if we found this block_row in partition
232
    ALWAYS_INLINE bool find_partition(Block* block, int row,
233
38.3M
                                      VOlapTablePartition*& partition) const {
234
38.3M
        auto it = _is_in_partition ? _partitions_map->find(std::tuple {block, row, true})
235
38.3M
                                   : _partitions_map->upper_bound(std::tuple {block, row, true});
236
18.4E
        VLOG_TRACE << "find row " << row << " of\n"
237
18.4E
                   << block->dump_data() << "in:\n"
238
18.4E
                   << _partition_block.dump_data() << "result line row: " << std::get<1>(it->first);
239
240
        // for list partition it might result in default partition
241
38.3M
        if (_is_in_partition) {
242
227k
            partition = (it != _partitions_map->end()) ? it->second : _default_partition;
243
227k
            it = _partitions_map->end();
244
227k
        }
245
38.3M
        if (it != _partitions_map->end() &&
246
38.3M
            _part_contains(it->second, std::tuple {block, row, true})) {
247
38.1M
            partition = it->second;
248
38.1M
        }
249
38.3M
        return (partition != nullptr);
250
38.3M
    }
251
252
    ALWAYS_INLINE void find_tablets(
253
            Block* block, const std::vector<uint32_t>& indexes,
254
            const std::vector<VOlapTablePartition*>& partitions,
255
            std::vector<uint32_t>& tablet_indexes /*result*/,
256
            /*TODO: check if flat hash map will be better*/
257
35.2k
            std::map<VOlapTablePartition*, int64_t>* partition_tablets_buffer = nullptr) const {
258
35.2k
        std::function<uint32_t(Block*, uint32_t, const VOlapTablePartition&)> compute_function;
259
35.2k
        if (!_distributed_slot_locs.empty()) {
260
            //TODO: refactor by saving the hash values. then we can calculate in columnwise.
261
35.2k
            compute_function = [this](Block* block, uint32_t row,
262
33.8M
                                      const VOlapTablePartition& partition) -> uint32_t {
263
33.8M
                uint32_t hash_val = 0;
264
33.9M
                for (unsigned short _distributed_slot_loc : _distributed_slot_locs) {
265
33.9M
                    auto* slot_desc = _slots[_distributed_slot_loc];
266
33.9M
                    auto& column = block->get_by_position(_distributed_slot_loc).column;
267
33.9M
                    auto val = column->get_data_at(row);
268
33.9M
                    if (val.data != nullptr) {
269
23.8M
                        hash_val = RawValue::zlib_crc32(val.data, val.size,
270
23.8M
                                                        slot_desc->type()->get_primitive_type(),
271
23.8M
                                                        hash_val);
272
23.8M
                    } else {
273
10.0M
                        hash_val = HashUtil::zlib_crc_hash_null(hash_val);
274
10.0M
                    }
275
33.9M
                }
276
33.8M
                return cast_set<uint32_t>(hash_val % partition.num_buckets);
277
33.8M
            };
278
35.2k
        } else { // random distribution
279
11
            compute_function = [](Block* block, uint32_t row,
280
18
                                  const VOlapTablePartition& partition) -> uint32_t {
281
18
                if (partition.load_tablet_idx == -1) {
282
                    // for compatible with old version, just do random
283
0
                    return cast_set<uint32_t>(butil::fast_rand() % partition.num_buckets);
284
0
                }
285
18
                return cast_set<uint32_t>(partition.load_tablet_idx % partition.num_buckets);
286
18
            };
287
11
        }
288
289
35.2k
        if (partition_tablets_buffer == nullptr) {
290
33.8M
            for (auto index : indexes) {
291
33.8M
                tablet_indexes[index] = compute_function(block, index, *partitions[index]);
292
33.8M
            }
293
35.2k
        } else { // use buffer
294
106
            for (auto index : indexes) {
295
106
                auto* partition = partitions[index];
296
106
                if (auto it = partition_tablets_buffer->find(partition);
297
106
                    it != partition_tablets_buffer->end()) {
298
88
                    tablet_indexes[index] = cast_set<uint32_t>(it->second); // tablet
299
88
                } else {
300
                    // compute and save in buffer
301
18
                    (*partition_tablets_buffer)[partition] = tablet_indexes[index] =
302
18
                            compute_function(block, index, *partitions[index]);
303
18
                }
304
106
            }
305
11
        }
306
35.2k
    }
307
308
132k
    const std::vector<VOlapTablePartition*>& get_partitions() const { return _partitions; }
309
310
    // it's same with auto now because we only support transformed partition in auto partition. may expand in future
311
40.4k
    bool is_projection_partition() const { return _is_auto_partition; }
312
134k
    bool is_auto_partition() const { return _is_auto_partition; }
313
314
40.4k
    bool is_auto_detect_overwrite() const { return _is_auto_detect_overwrite; }
315
34
    int64_t get_overwrite_group_id() const { return _overwrite_group_id; }
316
317
193
    std::shared_ptr<TNetworkAddress> get_master_address() const { return _master_address; }
318
319
341
    std::vector<uint16_t> get_partition_keys() const { return _partition_slot_locs; }
320
321
    Status add_partitions(const std::vector<TOlapTablePartition>& partitions);
322
    // no need to del/reinsert partition keys, but change the link. reset the _partitions items
323
    Status replace_partitions(std::vector<int64_t>& old_partition_ids,
324
                              const std::vector<TOlapTablePartition>& new_partitions);
325
326
40.8k
    VExprContextSPtrs get_part_func_ctx() { return _part_func_ctx; }
327
40.8k
    VExprSPtrs get_partition_function() { return _partition_function; }
328
329
    // which will affect _partition_block
330
    Status generate_partition_from(const TOlapTablePartition& t_part,
331
                                   VOlapTablePartition*& part_result);
332
333
515
    void set_transformed_slots(const std::vector<uint16_t>& new_slots) {
334
515
        _transformed_slot_locs = new_slots;
335
515
    }
336
337
private:
338
    Status _create_partition_keys(const std::vector<TExprNode>& t_exprs, BlockRow* part_key);
339
340
    // check if this partition contain this key
341
    bool _part_contains(VOlapTablePartition* part, BlockRowWithIndicator key) const;
342
343
    // this partition only valid in this schema
344
    std::shared_ptr<OlapTableSchemaParam> _schema;
345
    TOlapTablePartitionParam _t_param;
346
347
    const std::vector<SlotDescriptor*>& _slots;
348
    std::vector<uint16_t> _partition_slot_locs;
349
    std::vector<uint16_t> _transformed_slot_locs;
350
    std::vector<uint16_t> _distributed_slot_locs;
351
352
    ObjectPool _obj_pool;
353
    Block _partition_block;
354
    std::unique_ptr<MemTracker> _mem_tracker;
355
    std::vector<VOlapTablePartition*> _partitions;
356
    // For all partition value rows saved in this map, indicator is false. whenever we use a value to find in it, the param is true.
357
    // so that we can distinguish which column index to use (origin slots or transformed slots).
358
    // For range partition we ONLY SAVE RIGHT ENDS. when we find a part's RIGHT by a value, check if part's left cover it then.
359
    std::unique_ptr<
360
            std::map<BlockRowWithIndicator, VOlapTablePartition*, VOlapTablePartKeyComparator>>
361
            _partitions_map;
362
363
    bool _is_in_partition = false;
364
    size_t _mem_usage = 0;
365
    // only works when using list partition, the resource is owned by _partitions
366
    VOlapTablePartition* _default_partition = nullptr;
367
368
    bool _is_auto_partition = false;
369
    VExprContextSPtrs _part_func_ctx = {nullptr};
370
    VExprSPtrs _partition_function = {nullptr};
371
    TPartitionType::type _part_type; // support list or range
372
    // "insert overwrite partition(*)", detect which partitions by BE
373
    bool _is_auto_detect_overwrite = false;
374
    int64_t _overwrite_group_id = 0;
375
    std::shared_ptr<TNetworkAddress> _master_address = nullptr;
376
};
377
378
// indicate where's the tablet and all its replications (node-wise)
379
using TabletLocation = TTabletLocation;
380
// struct TTabletLocation {
381
//     1: required i64 tablet_id
382
//     2: required list<i64> node_ids
383
// }
384
385
class OlapTableLocationParam {
386
public:
387
46.7k
    OlapTableLocationParam(const TOlapTableLocationParam& t_param) : _t_param(t_param) {
388
336k
        for (auto& location : _t_param.tablets) {
389
336k
            _tablets.emplace(location.tablet_id, &location);
390
336k
            _update_base_to_binlog_tablet(location);
391
336k
        }
392
46.7k
    }
393
394
0
    int64_t db_id() const { return _t_param.db_id; }
395
0
    int64_t table_id() const { return _t_param.table_id; }
396
0
    int64_t version() const { return _t_param.version; }
397
398
338k
    TabletLocation* find_tablet(int64_t tablet_id) const {
399
338k
        auto it = _tablets.find(tablet_id);
400
341k
        if (it != std::end(_tablets)) {
401
341k
            return it->second;
402
341k
        }
403
18.4E
        return nullptr;
404
338k
    }
405
406
    // used by base tablet on node_id to mark whether to write binlog, 0 means no binlog.
407
340k
    int64_t get_binlog_tablet_id(int64_t base_tablet_id, int64_t node_id) const {
408
340k
        auto it = _base_to_binlog_tablet.find({base_tablet_id, node_id});
409
340k
        if (it != _base_to_binlog_tablet.end()) {
410
2.24k
            return it->second;
411
2.24k
        }
412
338k
        if (config::is_cloud_mode()) {
413
338k
            auto cloud_it = _base_to_binlog_tablet.lower_bound({base_tablet_id, INT64_MIN});
414
338k
            if (cloud_it != _base_to_binlog_tablet.end() &&
415
338k
                cloud_it->first.first == base_tablet_id) {
416
0
                return cloud_it->second;
417
0
            }
418
338k
        }
419
338k
        return 0;
420
338k
    }
421
422
193
    void add_locations(std::vector<TTabletLocation>& locations) {
423
6.68k
        for (auto& location : locations) {
424
6.68k
            if (_tablets.find(location.tablet_id) == _tablets.end()) {
425
6.68k
                _tablets[location.tablet_id] = &location;
426
6.68k
            }
427
6.68k
            _update_base_to_binlog_tablet(location);
428
6.68k
        }
429
193
    }
430
431
private:
432
343k
    void _update_base_to_binlog_tablet(const TTabletLocation& location) {
433
343k
        if (!location.__isset.base_tablet_id) {
434
341k
            return;
435
341k
        }
436
        // the binlog tablet is written by its base tablet on the nodes that own both.
437
2.24k
        for (int64_t node_id : location.node_ids) {
438
2.24k
            _base_to_binlog_tablet.emplace(std::make_pair(location.base_tablet_id, node_id),
439
2.24k
                                           location.tablet_id);
440
2.24k
        }
441
2.02k
    }
442
443
    TOlapTableLocationParam _t_param;
444
    // [tablet_id, tablet]. tablet has id, also.
445
    std::unordered_map<int64_t, TabletLocation*> _tablets;
446
    // [(base_tablet_id, node_id), row_binlog_tablet_id]
447
    std::map<std::pair<int64_t, int64_t>, int64_t> _base_to_binlog_tablet;
448
};
449
450
struct NodeInfo {
451
    int64_t id;
452
    int64_t option;
453
    std::string host;
454
    int32_t brpc_port;
455
456
46.8k
    NodeInfo() = default;
457
458
    NodeInfo(const TNodeInfo& tnode)
459
45.6k
            : id(tnode.id),
460
45.6k
              option(tnode.option),
461
45.6k
              host(tnode.host),
462
45.6k
              brpc_port(tnode.async_internal_port) {}
463
};
464
465
class DorisNodesInfo {
466
public:
467
    DorisNodesInfo() = default;
468
46.3k
    DorisNodesInfo(const TPaloNodesInfo& t_nodes) {
469
46.8k
        for (const auto& node : t_nodes.nodes) {
470
46.8k
            _nodes.emplace(node.id, node);
471
46.8k
        }
472
46.3k
    }
473
0
    void setNodes(const TPaloNodesInfo& t_nodes) {
474
0
        _nodes.clear();
475
0
        for (const auto& node : t_nodes.nodes) {
476
0
            _nodes.emplace(node.id, node);
477
0
        }
478
0
    }
479
47.6k
    const NodeInfo* find_node(int64_t id) const {
480
47.6k
        auto it = _nodes.find(id);
481
48.1k
        if (it != std::end(_nodes)) {
482
48.1k
            return &it->second;
483
48.1k
        }
484
18.4E
        return nullptr;
485
47.6k
    }
486
487
193
    void add_nodes(const std::vector<TNodeInfo>& t_nodes) {
488
193
        for (const auto& node : t_nodes) {
489
0
            const auto* node_info = find_node(node.id);
490
0
            if (node_info == nullptr) {
491
0
                _nodes.emplace(node.id, node);
492
0
            }
493
0
        }
494
193
    }
495
496
0
    const std::unordered_map<int64_t, NodeInfo>& nodes_info() { return _nodes; }
497
498
private:
499
    std::unordered_map<int64_t, NodeInfo> _nodes;
500
};
501
502
} // namespace doris