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