Coverage Report

Created: 2026-10-16 14:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/storage/segment/segment.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/macros.h>
21
#include <gen_cpp/olap_file.pb.h>
22
#include <gen_cpp/segment_v2.pb.h>
23
#include <glog/logging.h>
24
25
#include <atomic>
26
#include <cstdint>
27
#include <functional>
28
#include <map>
29
#include <memory> // for unique_ptr
30
#include <string>
31
#include <unordered_map>
32
33
#include "agent/be_exec_version_manager.h"
34
#include "common/be_mock_util.h"
35
#include "common/status.h" // Status
36
#include "core/column/column.h"
37
#include "core/data_type/data_type.h"
38
#include "core/field.h"
39
#include "io/cache/file_cache_common.h" // io::UInt128Wrapper returned by value
40
#include "io/fs/file_reader.h"
41
#include "io/fs/file_reader_writer_fwd.h"
42
#include "io/fs/file_system.h"
43
#include "io/io_common.h"
44
#include "runtime/descriptors.h"
45
#include "storage/cache/page_cache.h"
46
#include "storage/olap_common.h"
47
#include "storage/schema.h"
48
#include "storage/segment/page_handle.h"
49
#include "storage/tablet/tablet_schema.h"
50
#include "util/once.h"
51
#include "util/slice.h"
52
namespace doris {
53
class IDataType;
54
55
class ShortKeyIndexDecoder;
56
class ReadSchema;
57
class StorageReadOptions;
58
class PrimaryKeyIndexReader;
59
class RowwiseIterator;
60
struct RowLocation;
61
62
namespace segment_v2 {
63
64
class Segment;
65
class InvertedIndexIterator;
66
class IndexFileReader;
67
class IndexIterator;
68
class ColumnReader;
69
class ColumnIterator;
70
class ColumnReaderCache;
71
class ColumnMetaAccessor;
72
class VariantColumnReader;
73
74
using SegmentSharedPtr = std::shared_ptr<Segment>;
75
76
struct SparseColumnCache;
77
using SparseColumnCacheSPtr = std::shared_ptr<SparseColumnCache>;
78
79
// key is column path, value is the sparse column cache
80
// now column path is only SPARSE_COLUMN_PATH, in the future, we can add more sparse column paths
81
using PathToSparseColumnCache = std::unordered_map<std::string, SparseColumnCacheSPtr>;
82
using PathToSparseColumnCacheUPtr = std::unique_ptr<PathToSparseColumnCache>;
83
84
struct BinaryColumnCache;
85
using BinaryColumnCacheSPtr = std::shared_ptr<BinaryColumnCache>;
86
using PathToBinaryColumnCache = std::unordered_map<std::string, BinaryColumnCacheSPtr>;
87
using PathToBinaryColumnCacheUPtr = std::unique_ptr<PathToBinaryColumnCache>;
88
89
// A Segment is used to represent a segment in memory format. When segment is
90
// generated, it won't be modified, so this struct aimed to help read operation.
91
// It will prepare all ColumnReader to create ColumnIterator as needed.
92
// And user can create a RowwiseIterator through new_iterator function.
93
//
94
// NOTE: This segment is used to a specified TabletSchema, when TabletSchema
95
// is changed, this segment can not be used any more. For example, after a schema
96
// change finished, client should disable all cached Segment for old TabletSchema.
97
class Segment : public std::enable_shared_from_this<Segment>, public MetadataAdder<Segment> {
98
public:
99
    static Status open(io::FileSystemSPtr fs, const std::string& path, int64_t tablet_id,
100
                       uint32_t segment_id, RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
101
                       const io::FileReaderOptions& reader_options,
102
                       std::shared_ptr<Segment>* output, InvertedIndexFileInfo idx_file_info = {},
103
                       OlapReaderStatistics* stats = nullptr,
104
                       const io::IOContext* io_ctx = nullptr);
105
106
    static io::UInt128Wrapper file_cache_key(std::string_view rowset_id, uint32_t seg_id);
107
0
    io::UInt128Wrapper file_cache_key() const {
108
0
        return file_cache_key(_rowset_id.to_string(), _segment_id);
109
0
    }
110
111
    ~Segment() override;
112
113
    int64_t get_metadata_size() const override;
114
    void update_metadata_size();
115
116
    Status new_iterator(ReadSchemaSPtr schema, const StorageReadOptions& read_options,
117
                        std::unique_ptr<RowwiseIterator>* iter);
118
119
    static Status get_default_value_field(const TabletColumn& tablet_column, Field* field);
120
    static Status new_constant_iterator(const TabletColumn& tablet_column,
121
                                        std::unique_ptr<ColumnIterator>* iter);
122
123
    // Reader resolution is intentionally split by the capability required by each caller:
124
    // 1. _get_column_reader_for_read() resolves logical row values and may synthesize a
125
    //    ConstantColumnReader for an ALTER-added column or a read-time system value.
126
    // 2. get_variant_root_reader() requires physical VARIANT metadata and therefore never returns
127
    //    a ConstantColumnReader.
128
    // 3. get_column_reader_for_pruning() resolves the reader whose metadata exactly describes the
129
    //    predicate column, including a physical VARIANT leaf when one exists.
130
    // Keeping these contracts separate prevents callers from treating a logical constant as a
131
    // VariantColumnReader, or from using a VARIANT root's metadata to prune on one of its paths.
132
133
    // Resolve the physical VARIANT root that owns path metadata. A nullable/defaulted root absent
134
    // from an old segment returns NOT_FOUND; a present root returns a VariantColumnReader. For
135
    // example, metadata collection for an ALTER-added `v` skips old segments on NOT_FOUND, while
136
    // value reads through new_column_iterator() still produce v's NULL/default rows. An extracted
137
    // path resolves through parent_unique_id; a root whose parent uid is -1 uses its own uid.
138
    // A root uid absent from the segment schema returns NOT_FOUND regardless of the caller's
139
    // column type or nullability. On success, column_reader is non-null.
140
    Status get_variant_root_reader(const TabletColumn& col, const StorageReadOptions& read_options,
141
                                   std::shared_ptr<VariantColumnReader>* column_reader);
142
143
    // Resolve the reader whose metadata can be used for pruning or index lookup. An ALTER-added
144
    // scalar/default column may return a ConstantColumnReader so its constant zonemap can prune the
145
    // segment. A VARIANT path returns its physical leaf reader instead of the root reader. If, for
146
    // example, `v.user.id` exists only in the sparse binary column, no physical leaf zonemap exists;
147
    // this returns NOT_FOUND and the caller must conservatively keep the segment. This missing
148
    // VARIANT leaf is the only expected NOT_FOUND result; an ordinary missing column is resolved to
149
    // a ConstantColumnReader or reported as an error. On success, column_reader is always non-null.
150
    Status get_column_reader_for_pruning(const TabletColumn& col,
151
                                         const StorageReadOptions& read_options,
152
                                         std::shared_ptr<ColumnReader>* column_reader);
153
154
9.44M
    uint32_t id() const { return _segment_id; }
155
156
1.09M
    RowsetId rowset_id() const { return _rowset_id; }
157
158
13.1M
    MOCK_FUNCTION uint32_t num_rows() const { return _num_rows; }
159
160
    // if variant_sparse_column_cache is nullptr, means the sparse column cache is not used
161
    Status new_column_iterator(const TabletColumn& tablet_column,
162
                               std::unique_ptr<ColumnIterator>* iter, const StorageReadOptions* opt,
163
                               const std::unordered_map<int32_t, PathToBinaryColumnCacheUPtr>*
164
                                       variant_sparse_column_cache = nullptr);
165
166
    Status new_index_iterator(const TabletColumn& tablet_column, const TabletIndex* index_meta,
167
                              const StorageReadOptions& read_options,
168
                              std::unique_ptr<IndexIterator>* iter);
169
170
772k
    const ShortKeyIndexDecoder* get_short_key_index() const {
171
772k
        DCHECK(_load_index_once.has_called() && _load_index_once.stored_result().ok());
172
772k
        return _sk_index_decoder.get();
173
772k
    }
174
175
3.97M
    const PrimaryKeyIndexReader* get_primary_key_index() const {
176
3.97M
        DCHECK(_load_index_once.has_called() && _load_index_once.stored_result().ok());
177
3.97M
        return _pk_index_reader.get();
178
3.97M
    }
179
180
    Status lookup_row_key(const Slice& key, const TabletSchema* latest_schema, bool with_seq_col,
181
                          bool with_rowid, RowLocation* row_location, OlapReaderStatistics* stats,
182
                          std::string* encoded_seq_value = nullptr,
183
                          const io::IOContext* io_ctx = nullptr);
184
185
    Status read_key_by_rowid(uint32_t row_id, std::string* key);
186
187
    // row_ids must be strictly increasing.
188
    // `read_column` is what `slot` resolves to, or its variant parent column for a subpath slot.
189
    Status seek_and_read_by_rowid(const TabletColumn& read_column, SlotDescriptor* slot,
190
                                  const std::vector<uint32_t>& row_ids, MutableColumnPtr& result,
191
                                  StorageReadOptions& storage_read_options,
192
                                  std::unique_ptr<ColumnIterator>& iterator_hint);
193
194
    Status load_index(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
195
196
    Status load_pk_index_and_bf(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
197
198
33
    void update_healthy_status(Status new_status) { _healthy_status.update(new_status); }
199
    // The segment is loaded into SegmentCache and then will load indices, if there are something wrong
200
    // during loading indices, should remove it from SegmentCache. If not, it will always report error during
201
    // query. So we add a healthy status API, the caller should check the healhty status before using the segment.
202
    Status healthy_status();
203
204
1.97M
    std::string min_key() {
205
1.97M
        DCHECK(_tablet_schema->keys_type() == UNIQUE_KEYS && _pk_index_meta != nullptr);
206
1.97M
        return _pk_index_meta->min_key();
207
1.97M
    }
208
1.78M
    std::string max_key() {
209
1.78M
        DCHECK(_tablet_schema->keys_type() == UNIQUE_KEYS && _pk_index_meta != nullptr);
210
1.78M
        return _pk_index_meta->max_key();
211
1.78M
    }
212
213
23.1k
    io::FileReaderSPtr file_reader() { return _file_reader; }
214
215
    // Including the column reader memory.
216
    // another method `get_metadata_size` not include the column reader, only the segment object itself.
217
1.69M
    int64_t meta_mem_usage() const { return _meta_mem_usage; }
218
219
    // PK pages are tracked by PKIndexPageCache, but their pinned bytes must also
220
    // count toward SegmentCache eviction. Do not add these bytes to metadata tracking.
221
1.70k
    size_t cache_charge() const {
222
1.70k
        return _meta_mem_usage + _pk_index_cache_bytes.load(std::memory_order_relaxed);
223
1.70k
    }
224
225
    // Install before publishing this segment in SegmentCache; immutable afterwards.
226
1.68k
    void set_cache_charge_callback(std::function<void(size_t)> callback) {
227
1.68k
        DCHECK(!_cache_charge_callback);
228
1.68k
        _cache_charge_callback = std::move(callback);
229
1.68k
    }
230
231
    // Variant paths infer their storage type from the physical root reader. If an ALTER-added root
232
    // is absent from an old segment, use the path's declared schema type. Other columns use
233
    // read_type directly.
234
    std::shared_ptr<const IDataType> get_data_type_of(const TabletColumn& read_column,
235
                                                      const DataTypePtr& read_type,
236
                                                      const StorageReadOptions& read_options);
237
238
    // If column in segment is the same type in schema, then it is safe to apply predicate.
239
    // `ordinal` is a read-schema ordinal (the coordinate ColumnPredicate::column_id() carries).
240
    bool can_apply_predicate_safely(
241
            int ordinal, const ReadSchema& schema,
242
            const std::map<std::string, DataTypePtr>& target_cast_type_for_variants,
243
2.50M
            const StorageReadOptions& read_options) {
244
2.50M
        const TabletColumn* col = schema.column(ordinal);
245
18.4E
        DCHECK(col != nullptr) << "Column not found in schema for ordinal=" << ordinal;
246
2.50M
        DataTypePtr storage_column_type =
247
2.50M
                get_data_type_of(*col, schema.data_type(ordinal), read_options);
248
2.54M
        if (storage_column_type == nullptr || col->type() != FieldType::OLAP_FIELD_TYPE_VARIANT ||
249
2.53M
            !target_cast_type_for_variants.contains(col->name())) {
250
            // Default column iterator or not variant column
251
2.53M
            return true;
252
2.53M
        }
253
18.4E
        if (storage_column_type->equals(*target_cast_type_for_variants.at(col->name()))) {
254
830
            return true;
255
18.4E
        } else {
256
18.4E
            return false;
257
18.4E
        }
258
18.4E
    }
259
260
    const TabletSchemaSPtr& tablet_schema() const { return _tablet_schema; }
261
262
    Status traverse_column_meta_pbs(const std::function<void(const ColumnMetaPB&)>& visitor);
263
264
    // Returns the cached raw_data_bytes for the given column unique id, or 0 if not found.
265
    // Data is populated during _create_column_meta (under call_once), so thread-safe after init.
266
16.7M
    uint64_t column_raw_data_bytes(int32_t column_uid) const {
267
16.7M
        auto it = _column_uid_to_raw_bytes.find(column_uid);
268
18.4E
        return it != _column_uid_to_raw_bytes.end() ? it->second : 0;
269
16.7M
    }
270
271
    static StoragePageCache::CacheKey get_segment_footer_cache_key(
272
            const io::FileReaderSPtr& file_reader);
273
274
private:
275
    DISALLOW_COPY_AND_ASSIGN(Segment);
276
    Segment(uint32_t segment_id, RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
277
            InvertedIndexFileInfo idx_file_info = InvertedIndexFileInfo());
278
    static Status _open(io::FileSystemSPtr fs, const std::string& path, uint32_t segment_id,
279
                        RowsetId rowset_id, TabletSchemaSPtr tablet_schema,
280
                        const io::FileReaderOptions& reader_options,
281
                        std::shared_ptr<Segment>* output, InvertedIndexFileInfo idx_file_info,
282
                        OlapReaderStatistics* stats = nullptr,
283
                        const io::IOContext* io_ctx = nullptr);
284
    // open segment file and read the minimum amount of necessary information (footer)
285
    Status _open(OlapReaderStatistics* stats, const io::IOContext* io_ctx = nullptr);
286
    Status _parse_footer(std::shared_ptr<SegmentFooterPB>& footer,
287
                         OlapReaderStatistics* stats = nullptr,
288
                         const io::IOContext* io_ctx = nullptr);
289
    Status _create_column_meta(const SegmentFooterPB& footer, OlapReaderStatistics* stats = nullptr,
290
                               const io::IOContext* io_ctx = nullptr);
291
    Status _load_pk_bloom_filter(OlapReaderStatistics* stats,
292
                                 const io::IOContext* io_ctx = nullptr);
293
    Status _write_error_file(size_t file_size, size_t offset, size_t bytes_read, char* data,
294
                             io::IOContext& io_ctx);
295
296
    Status _open_index_file_reader();
297
298
    Status _create_column_meta_once(OlapReaderStatistics* stats,
299
                                    const io::IOContext* io_ctx = nullptr);
300
301
    // Resolve the reader used to produce a column's logical row values. A physical column returns
302
    // its cached reader, while a read-time system value or a schema default returns a
303
    // request-scoped ConstantColumnReader. For example, a nullable VARIANT root added after an old
304
    // segment was written returns ConstantColumnReader(NULL, VARIANT), not VariantColumnReader.
305
    // This is private because the returned reader guarantees value materialization only; it does
306
    // not guarantee physical capabilities such as VARIANT metadata, leaf zonemaps, or indexes.
307
    // Value consumers enter through new_column_iterator(), while capability-specific callers use
308
    // get_variant_root_reader() or get_column_reader_for_pruning(). A persisted column uses its own
309
    // uid; only a BE-generated path whose uid is -1 resolves through its parent. On success,
310
    // column_reader is always non-null.
311
    Status _get_column_reader_for_read(const TabletColumn& col,
312
                                       const StorageReadOptions& read_options,
313
                                       std::shared_ptr<ColumnReader>* column_reader);
314
315
    virtual Status _get_segment_footer(std::shared_ptr<SegmentFooterPB>&,
316
                                       OlapReaderStatistics* stats,
317
                                       const io::IOContext* io_ctx = nullptr);
318
319
    StoragePageCache::CacheKey get_segment_footer_cache_key() const;
320
321
    friend class SegmentIterator;
322
    friend class ColumnReaderCache;
323
    friend class MockSegment;
324
325
    io::FileSystemSPtr _fs;
326
    io::FileReaderSPtr _file_reader;
327
    // Relative path passed to `open`, used to derive the inverted index path (see
328
    // _open_index_file_reader).
329
    std::string _seg_path;
330
    uint32_t _segment_id;
331
    uint32_t _num_rows;
332
    AtomicStatus _healthy_status;
333
334
    // 1. Tracking memory use by segment meta data such as footer or index page.
335
    // 2. Tracking memory use by segment column reader
336
    // The memory consumed by querying is tracked in segment iterator.
337
    int64_t _meta_mem_usage;
338
    int64_t _tracked_meta_mem_usage = 0;
339
    std::atomic<size_t> _pk_index_cache_bytes {0};
340
    std::function<void(size_t)> _cache_charge_callback;
341
342
    RowsetId _rowset_id;
343
    TabletSchemaSPtr _tablet_schema;
344
345
    std::unique_ptr<PrimaryKeyIndexMetaPB> _pk_index_meta;
346
    PagePointerPB _sk_index_page;
347
348
    // Limited cache for column readers
349
    std::unique_ptr<ColumnReaderCache> _column_reader_cache;
350
351
    // Centralized accessor for column metadata layout and uid->column_ordinal mapping.
352
    std::unique_ptr<ColumnMetaAccessor> _column_meta_accessor;
353
354
    // Init from ColumnMetaPB in SegmentFooterPB
355
    // map column unique id ---> it's inner data type
356
    std::map<int32_t, std::shared_ptr<const IDataType>> _file_column_types;
357
358
    // used to guarantee that short key index will be loaded at most once in a thread-safe way
359
    DorisCallOnce<Status> _load_index_once;
360
    // used to guarantee that primary key bloom filter will be loaded at most once in a thread-safe way
361
    DorisCallOnce<Status> _load_pk_bf_once;
362
363
    DorisCallOnce<Status> _create_column_meta_once_call;
364
365
    std::weak_ptr<SegmentFooterPB> _footer_pb;
366
367
    // Cached raw_data_bytes per column unique id, populated once in _create_column_meta().
368
    std::unordered_map<int32_t, uint64_t> _column_uid_to_raw_bytes;
369
370
    // used to hold short key index page in memory
371
    PageHandle _sk_index_handle;
372
    // short key index decoder
373
    // all content is in memory
374
    std::unique_ptr<ShortKeyIndexDecoder> _sk_index_decoder;
375
    // primary key index reader
376
    std::unique_ptr<PrimaryKeyIndexReader> _pk_index_reader;
377
    std::mutex _open_lock;
378
    // inverted index file reader
379
    std::shared_ptr<IndexFileReader> _index_file_reader;
380
    DorisCallOnce<Status> _index_file_reader_open;
381
382
    InvertedIndexFileInfo _idx_file_info;
383
    int64_t _tablet_id = -1;
384
385
    int _be_exec_version = BeExecVersionManager::get_newest_version();
386
};
387
388
} // namespace segment_v2
389
} // namespace doris