be/src/storage/segment/segment_read_ahead.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 <cstddef> |
21 | | #include <functional> |
22 | | #include <map> |
23 | | #include <memory> |
24 | | #include <mutex> |
25 | | #include <span> |
26 | | #include <vector> |
27 | | |
28 | | #include "io/cache/file_range_planner.h" |
29 | | #include "io/fs/file_range_read_scheduler.h" |
30 | | #include "io/fs/file_reader.h" |
31 | | #include "storage/segment/column_read_ahead.h" |
32 | | |
33 | | namespace doris { |
34 | | |
35 | | class ExecEnv; |
36 | | class StorageReadOptions; |
37 | | |
38 | | namespace segment_v2 { |
39 | | |
40 | | class ColumnIterator; |
41 | | |
42 | | /// Invoked with the whole physical range at most once, when any registered page successfully |
43 | | /// consumes bytes from it. |
44 | | using ReadAheadRangeConsumer = std::function<void(const io::FileRangeRead&)>; |
45 | | /// Creates submission-scoped state immediately before asynchronous range tasks are admitted. |
46 | | using ReadAheadRangeConsumerFactory = std::function<ReadAheadRangeConsumer()>; |
47 | | /// Returns true when the exact compressed data-page range is already in StoragePageCache. |
48 | | using ReadAheadPageCacheProbe = std::function<bool(const io::FileRange& page_range)>; |
49 | | |
50 | | struct SegmentReadAheadOptions { |
51 | | io::FileRangePlanOptions range_plan; |
52 | | ReadAheadPageCacheProbe page_cache_probe; |
53 | | /// Capture per-submission writeback state immediately before asynchronous IO starts. The |
54 | | /// returned consumer is retained only by ranges accepted in that submission. |
55 | | ReadAheadRangeConsumerFactory range_consumer_factory; |
56 | | }; |
57 | | |
58 | | struct SegmentReadAheadResult { |
59 | | /// Data pages newly requested by all column windows before the page-cache probe. |
60 | | size_t new_pages {0}; |
61 | | /// Newly requested pages already available from StoragePageCache. |
62 | | size_t page_cache_hits {0}; |
63 | | /// Physical range handles returned for an accepted scheduler batch. |
64 | | size_t submitted_ranges {0}; |
65 | | /// Sum of that batch's range sizes, including coalescing and block-fill bytes. |
66 | | size_t submitted_bytes {0}; |
67 | | /// Batch-level scheduler rejection; NONE also covers plans that required no source reads. |
68 | | io::FileRangeReadRejectReason reject_reason {io::FileRangeReadRejectReason::NONE}; |
69 | | /// Planning or batch-submission status. Per-range execution failures surface on page reads. |
70 | | Status status; |
71 | | |
72 | 20 | bool accepted() const { |
73 | 20 | return reject_reason == io::FileRangeReadRejectReason::NONE && status.ok(); |
74 | 20 | } |
75 | | }; |
76 | | |
77 | | /// FileReader view used by PageIO. Exact data-page reads reuse an asynchronous range buffer; |
78 | | /// unrelated reads and failed prefetches transparently use the original reader. |
79 | | class SegmentReadAheadFileReader final : public io::FileReader { |
80 | | public: |
81 | 0 | Status close() override { return _inner->close(); } |
82 | 35 | const io::Path& path() const override { return _inner->path(); } |
83 | 33 | size_t size() const override { return _inner->size(); } |
84 | 0 | bool closed() const override { return _inner->closed(); } |
85 | 0 | int64_t mtime() const override { return _inner->mtime(); } |
86 | 0 | const std::string& get_data_dir_path() override { return _inner->get_data_dir_path(); } |
87 | | |
88 | 11 | const io::FileReaderSPtr& inner_reader() const { return _inner; } |
89 | | |
90 | | /// Try an exact data page, waiting for its range when necessary. A true return means the page |
91 | | /// was registered; a failed status leaves the caller to use the original reader. PageIO uses |
92 | | /// this entry to distinguish buffer reuse from synchronous data-page fallback. |
93 | | bool try_read_page(size_t offset, Slice output, Status* status); |
94 | | |
95 | | protected: |
96 | | Status read_at_impl(size_t offset, Slice result, size_t* bytes_read, |
97 | | const io::IOContext* io_ctx) override; |
98 | | Status read_at_iobuf_impl(size_t offset, size_t bytes_req, butil::IOBuf* out, |
99 | | size_t* bytes_read, const io::IOContext* io_ctx) override; |
100 | 0 | void _collect_profile_at_runtime() override { _inner->collect_profile_at_runtime(); } |
101 | 0 | void _collect_profile_before_close() override { _inner->collect_profile_before_close(); } |
102 | | |
103 | | private: |
104 | | friend class SegmentReadAhead; |
105 | | |
106 | | struct PageKey { |
107 | | size_t offset {0}; |
108 | | size_t size {0}; |
109 | | |
110 | 221 | bool operator<(const PageKey& other) const { |
111 | 221 | return offset < other.offset || (offset == other.offset && size < other.size); |
112 | 221 | } |
113 | | }; |
114 | | |
115 | | struct PageOwner { |
116 | | ColumnReadAhead* column {nullptr}; |
117 | | int32_t page_index {0}; |
118 | | |
119 | 15 | bool operator==(const PageOwner&) const = default; |
120 | | }; |
121 | | |
122 | | struct BufferedRange { |
123 | | std::shared_ptr<io::FileRangeRead> read; |
124 | | ReadAheadRangeConsumer consumer; |
125 | | /// Guards the range-level consumer when several registered pages share this buffer. |
126 | | bool consumed {false}; |
127 | | }; |
128 | | |
129 | | struct PageSlot { |
130 | | std::vector<PageOwner> owners; |
131 | | std::shared_ptr<BufferedRange> range; |
132 | | size_t buffer_offset {0}; |
133 | | }; |
134 | | |
135 | | SegmentReadAheadFileReader(io::FileReaderSPtr inner, |
136 | | std::shared_ptr<io::ReadAheadStatistics> statistics); |
137 | | /// Associate one exact page read with its location in a scheduled physical range. |
138 | | void _register_page(ColumnReadAhead* column, const ColumnReadAheadPage& page, |
139 | | std::shared_ptr<BufferedRange> range, size_t buffer_offset); |
140 | | /// Attach a column's independently advanced window to an already buffered physical page. |
141 | | bool _reuse_page(ColumnReadAhead* column, const ColumnReadAheadPage& page); |
142 | | /// Drop one column's prediction after its scan has passed the page. |
143 | | void _release_page(ColumnReadAhead* column, const ColumnReadAheadPage& page); |
144 | | /// Remove a page from every owning column and invoke the range consumer once after a successful |
145 | | /// read. Callbacks run without `_mutex`. |
146 | | void _finish_page(const PageKey& key, bool consumed); |
147 | | |
148 | | const io::FileReaderSPtr _inner; |
149 | | const std::shared_ptr<io::ReadAheadStatistics> _statistics; |
150 | | std::mutex _mutex; |
151 | | std::map<PageKey, PageSlot> _pages; |
152 | | }; |
153 | | |
154 | | /// Coordinates the newly added pages of all physical columns in one segment submission. It does |
155 | | /// not decide column roles or window sizes and does not own the BE-level asynchronous reader. |
156 | | class SegmentReadAhead final { |
157 | | public: |
158 | | SegmentReadAhead(io::FileReaderSPtr source_reader, io::FileRangeReadScheduler* scheduler, |
159 | | std::shared_ptr<io::FileRangeReadContext> context, |
160 | | io::FileRangeReadIOContext io_context, SegmentReadAheadOptions options, |
161 | | ColumnReadAheadOptions eager_options = {}, |
162 | | ColumnReadAheadOptions lazy_options = {}); |
163 | | |
164 | | /// Read the dynamic query read-ahead switch and cloud-mode requirement. |
165 | | static bool enabled(); |
166 | | |
167 | | /// Create query read-ahead state when enabled for a query reader. On an inapplicable request, |
168 | | /// return OK with `output` cleared so callers continue with the original reader. |
169 | | static Status create_for_query(io::FileReaderSPtr source_reader, ExecEnv* exec_env, |
170 | | std::shared_ptr<io::FileRangeReadContext> context, |
171 | | const StorageReadOptions& read_options, |
172 | | std::unique_ptr<SegmentReadAhead>* output); |
173 | | |
174 | | SegmentReadAhead(const SegmentReadAhead&) = delete; |
175 | | SegmentReadAhead& operator=(const SegmentReadAhead&) = delete; |
176 | | |
177 | | /// Apply released pages first, probe new pages, then coalesce and submit all misses together. |
178 | | /// Planning or admission failure completes those pages and leaves PageIO to read them through |
179 | | /// the original reader. |
180 | | SegmentReadAheadResult apply_plans(std::vector<ColumnReadAheadPlan> plans); |
181 | | |
182 | | /// Plans all data pages touched by one exact row-id batch before any column starts decoding. |
183 | | SegmentReadAheadResult prefetch_by_rowids(const rowid_t* rowids, size_t count, |
184 | | std::span<ColumnIterator* const> columns); |
185 | | |
186 | 20 | const std::shared_ptr<SegmentReadAheadFileReader>& file_reader() const { return _reader; } |
187 | | |
188 | 0 | const ColumnReadAheadContext& column_context() const { return _column_context; } |
189 | | |
190 | | private: |
191 | | const io::FileReaderSPtr _source_reader; |
192 | | io::FileRangeReadScheduler* const _scheduler; |
193 | | const std::shared_ptr<io::FileRangeReadContext> _context; |
194 | | io::FileRangeReadIOContext _io_context; |
195 | | const SegmentReadAheadOptions _options; |
196 | | const std::shared_ptr<io::ReadAheadStatistics> _statistics; |
197 | | const std::shared_ptr<SegmentReadAheadFileReader> _reader; |
198 | | const ColumnReadAheadContext _column_context; |
199 | | }; |
200 | | |
201 | | ReadAheadPageCacheProbe make_storage_page_cache_probe(const io::FileReaderSPtr& file_reader); |
202 | | |
203 | | } // namespace segment_v2 |
204 | | } // namespace doris |