be/src/storage/segment/column_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 <cstdint> |
22 | | #include <map> |
23 | | #include <memory> |
24 | | #include <roaring/roaring.hh> |
25 | | #include <vector> |
26 | | |
27 | | #include "common/status.h" |
28 | | #include "io/fs/file_range_coalescer.h" |
29 | | #include "storage/segment/common.h" |
30 | | |
31 | | namespace doris::io { |
32 | | struct ReadAheadStatistics; |
33 | | } |
34 | | |
35 | | namespace doris::segment_v2 { |
36 | | |
37 | | struct ColumnReadAheadOptions { |
38 | | /// Compressed data-page bytes in each read-ahead window; the final page may overshoot it. |
39 | | size_t window_bytes {1}; |
40 | | |
41 | | Status validate() const; |
42 | 6 | bool operator==(const ColumnReadAheadOptions&) const = default; |
43 | | }; |
44 | | |
45 | | struct ColumnReadAheadPage { |
46 | | /// Stable position in the column's ordered data-page list. |
47 | | int32_t page_index {0}; |
48 | | /// Inclusive row-ordinal bounds represented by this data page. |
49 | | ordinal_t first_ordinal {0}; |
50 | | ordinal_t last_ordinal {0}; |
51 | | /// Compressed page bytes in the segment file. |
52 | | io::FileRange range; |
53 | | |
54 | 110 | bool operator==(const ColumnReadAheadPage&) const = default; |
55 | | }; |
56 | | |
57 | | class ColumnReadAhead; |
58 | | class SegmentReadAhead; |
59 | | |
60 | | struct ColumnReadAheadPlan { |
61 | | ColumnReadAhead* column {nullptr}; |
62 | | /// Pages newly entering the window and eligible for cache probe and range planning. |
63 | | std::vector<ColumnReadAheadPage> new_pages; |
64 | | /// Window entries that the scan has passed; the coordinator drops any remaining registration. |
65 | | std::vector<ColumnReadAheadPage> released_pages; |
66 | | /// Per-call elapsed times, collected even when no pages enter or leave the window. |
67 | | int64_t window_discard_ns {0}; |
68 | | /// Exact-rowid planning, including extensions triggered by this call. |
69 | | int64_t current_batch_plan_ns {0}; |
70 | | int64_t window_extend_ns {0}; |
71 | | |
72 | | /// Reuse this plan for another call, retaining the page vectors' allocated capacity. |
73 | | void reset(ColumnReadAhead* owner); |
74 | | /// Accumulate per-call timings even for an empty plan; statistics may be absent. |
75 | | void update_statistics(io::ReadAheadStatistics* statistics) const; |
76 | 43 | bool empty() const { return new_pages.empty() && released_pages.empty(); } |
77 | | }; |
78 | | |
79 | | enum class ColumnReadAheadRole : uint8_t { |
80 | | /// The column has no earlier filtering dependency and uses the larger look-ahead window. |
81 | | EAGER, |
82 | | /// The column depends on earlier filtering and uses the smaller look-ahead window. |
83 | | LAZY, |
84 | | }; |
85 | | |
86 | | struct ColumnReadAheadContext { |
87 | | ColumnReadAheadOptions eager_options; |
88 | | ColumnReadAheadOptions lazy_options; |
89 | | SegmentReadAhead* segment {nullptr}; |
90 | | |
91 | | const ColumnReadAheadOptions& options(ColumnReadAheadRole role) const; |
92 | | }; |
93 | | |
94 | | struct ColumnReadAheadRequest { |
95 | | /// Sorted row IDs needed by the current batch; all their pages are included in the plan. |
96 | | const rowid_t* current_rowids {nullptr}; |
97 | | size_t current_rowid_count {0}; |
98 | | /// Eligible rows used to build the candidate-page sequence. |
99 | | const roaring::Roaring* scan_rowids {nullptr}; |
100 | | const ColumnReadAheadContext* context {nullptr}; |
101 | | ColumnReadAheadRole role {ColumnReadAheadRole::EAGER}; |
102 | | bool reverse {false}; |
103 | | /// Initialize a scan once; later data-page reads advance its window without batch row IDs. |
104 | | bool page_driven {false}; |
105 | | |
106 | | const ColumnReadAheadOptions& options() const; |
107 | | void sanity_check() const; |
108 | | }; |
109 | | |
110 | | /// Maintains the byte window of one physical column. The class only decides which compressed |
111 | | /// data pages belong to the window; it does not inspect caches, coalesce ranges, or perform IO. |
112 | | class ColumnReadAhead { |
113 | | public: |
114 | | ColumnReadAhead(const ColumnReadAhead&) = delete; |
115 | | ColumnReadAhead& operator=(const ColumnReadAhead&) = delete; |
116 | | |
117 | | /// `pages` must be non-empty and ordered by disjoint row-ordinal spans, with page_index equal |
118 | | /// to each page's vector position. |
119 | | static Status create(std::vector<ColumnReadAheadPage> pages, ColumnReadAheadOptions options, |
120 | | bool reverse, std::unique_ptr<ColumnReadAhead>* output); |
121 | | |
122 | | /// Adds every page touched by the current batch and discards predictions passed by the scan. |
123 | | /// Reaching a window's trigger page appends the next byte-sized window. A read beyond the |
124 | | /// planned coverage starts a new window at that page, including on the first call. |
125 | | void plan(const rowid_t* current_rowids, size_t count, const roaring::Roaring& scan_rowids, |
126 | | ColumnReadAheadPlan* output); |
127 | | |
128 | | /// Build the scan's candidate pages once and plan its first byte-sized window. |
129 | | void start(const roaring::Roaring& scan_rowids, ColumnReadAheadPlan* output); |
130 | | /// Advance from a physical page index already resolved by the column reader. This also |
131 | | /// retires skipped predictions and restarts the window after a large seek. |
132 | | void advance(int32_t page_index, ColumnReadAheadPlan* output); |
133 | | |
134 | | /// Stop counting a planned page toward the byte window. The completed entry remains in the |
135 | | /// window until the scan passes it, so repeated row IDs in the same page do not submit it |
136 | | /// again. |
137 | | void complete(int32_t page_index); |
138 | | |
139 | 541 | size_t pending_bytes() const { return _pending_bytes; } |
140 | 6 | bool reverse() const { return _reverse; } |
141 | 6 | const ColumnReadAheadOptions& options() const { return _options; } |
142 | | bool pending(int32_t page_index) const; |
143 | | |
144 | | private: |
145 | | struct WindowEntry { |
146 | | bool pending {true}; |
147 | | }; |
148 | | |
149 | | ColumnReadAhead(std::vector<ColumnReadAheadPage> pages, ColumnReadAheadOptions options, |
150 | | bool reverse); |
151 | | |
152 | | const ColumnReadAheadPage& _page_for_ordinal(rowid_t ordinal) const; |
153 | | void _select_candidate_pages(const roaring::Roaring& scan_rowids); |
154 | | void _plan_page(int32_t page_index, ColumnReadAheadPlan* output); |
155 | | /// Add one page only if the window has not already seen it. |
156 | | void _add_page(const ColumnReadAheadPage& page, ColumnReadAheadPlan* output); |
157 | | /// Remove window entries strictly behind the given page in the configured scan direction. |
158 | | void _discard_passed_pages(int32_t page_index, ColumnReadAheadPlan* output); |
159 | | /// Add one window from _next_page_index using the candidate-page sequence. Its second |
160 | | /// selected page triggers the next window; a one-page window uses its only page. |
161 | | void _extend_window(ColumnReadAheadPlan* output); |
162 | | void _complete(const ColumnReadAheadPage& page, WindowEntry* entry); |
163 | | |
164 | | const std::vector<ColumnReadAheadPage> _pages; |
165 | | const ColumnReadAheadOptions _options; |
166 | | const bool _reverse; |
167 | | /// Physical page indexes in file order. Scan mode builds this once; exact-rowid requests |
168 | | /// replace it with their current selection. No scan bitmap is retained by the window. |
169 | | std::vector<int32_t> _candidate_pages; |
170 | | std::map<int32_t, WindowEntry> _window; |
171 | | /// Pending-page accounting only; completing a page does not advance the scan position. |
172 | | size_t _pending_bytes {0}; |
173 | | /// First page beyond the latest planned window, in scan order; may be outside _pages at EOF. |
174 | | int64_t _next_page_index; |
175 | | /// Scan position that triggers the next window; -1 means there is no trigger. |
176 | | int32_t _next_trigger_page_index {-1}; |
177 | | /// Reverse scanners visit batches backwards but read each batch forwards. Keep predictions |
178 | | /// above this low page until a new lower page is reached by the next batch. |
179 | | int32_t _reverse_low_page_index; |
180 | | }; |
181 | | |
182 | | } // namespace doris::segment_v2 |