Coverage Report

Created: 2026-10-15 20:44

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