Coverage Report

Created: 2026-11-02 23:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
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