be/src/io/cache/cached_remote_file_reader.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 <shared_mutex> |
24 | | #include <unordered_set> |
25 | | #include <utility> |
26 | | #include <vector> |
27 | | |
28 | | #include "common/status.h" |
29 | | #include "io/cache/block_file_cache.h" |
30 | | #include "io/cache/file_block.h" |
31 | | #include "io/cache/file_cache_common.h" |
32 | | #include "io/fs/file_reader.h" |
33 | | #include "io/fs/file_reader_writer_fwd.h" |
34 | | #include "io/fs/path.h" |
35 | | #include "util/slice.h" |
36 | | |
37 | | namespace doris::io { |
38 | | struct AsyncCacheWriteEpoch; |
39 | | struct IOContext; |
40 | | struct FileCacheStatistics; |
41 | | struct PeerFetchResult; |
42 | | class PartialBlockWritebackManager; |
43 | | class RangeCacheWriteback; |
44 | | |
45 | | } // namespace doris::io |
46 | | |
47 | | namespace doris { |
48 | | struct PeerCandidate; |
49 | | } |
50 | | |
51 | | namespace doris::io { |
52 | | struct SourceReadBreakdown { |
53 | | int64_t local_bytes = 0; |
54 | | int64_t remote_bytes = 0; |
55 | | int64_t peer_bytes = 0; |
56 | | }; |
57 | | using PeerFetchedBlockSet = std::unordered_set<const FileBlock*>; |
58 | | |
59 | | class CachedRemoteFileReader final : public FileReader, |
60 | | public std::enable_shared_from_this<CachedRemoteFileReader> { |
61 | | public: |
62 | | /// Construct a cached reader on top of a remote reader. |
63 | | /// @param[in] remote_file_reader Underlying reader used for remote/peer fallback reads. |
64 | | /// @param[in] opts File reader options used to initialize cache identity and policy. |
65 | | /// @return None. |
66 | | CachedRemoteFileReader(FileReaderSPtr remote_file_reader, const FileReaderOptions& opts); |
67 | | |
68 | | /// Destroy the cached reader and release direct cache-file ownership tracked by this reader. |
69 | | /// @return None. |
70 | | ~CachedRemoteFileReader() override; |
71 | | |
72 | | /// Close the underlying remote reader. |
73 | | /// @return OK on success; otherwise the close error from the underlying reader. |
74 | | Status close() override; |
75 | | |
76 | | /// Get the path of the underlying file. |
77 | | /// @return Reference to the remote reader path. |
78 | 7.27k | const Path& path() const override { return _remote_file_reader->path(); } |
79 | | |
80 | | /// Get the logical size of the underlying file. |
81 | | /// @return File size in bytes. |
82 | 2.15M | size_t size() const override { return _remote_file_reader->size(); } |
83 | | |
84 | | /// Check whether the underlying reader has been closed. |
85 | | /// @return true if the underlying reader is closed; otherwise false. |
86 | 718k | bool closed() const override { return _remote_file_reader->closed(); } |
87 | | |
88 | | /// Expose the wrapped remote reader. |
89 | | /// @return Raw pointer to the underlying reader owned by this object. |
90 | 1.00k | FileReader* get_remote_reader() { return _remote_file_reader.get(); } |
91 | | |
92 | | /// Build the immutable cache metadata used to write back consumed query read-ahead ranges. |
93 | | /// The returned object reads directly from the inner reader while retaining this wrapper, |
94 | | /// keeping its destructor from closing the source before background hole fills finish. |
95 | | /// A no-write request returns nullptr. |
96 | | std::shared_ptr<RangeCacheWriteback> make_range_cache_writeback( |
97 | | const IOContext& io_context, PartialBlockWritebackManager* partial_block_manager) const; |
98 | | |
99 | | /// Align a read range to file-cache block boundaries. |
100 | | /// @param[in] offset Requested read offset in bytes. |
101 | | /// @param[in] size Requested read size in bytes. |
102 | | /// @param[in] length Total file length in bytes. |
103 | | /// @return Pair of aligned start offset and aligned size. |
104 | | static std::pair<size_t, size_t> s_align_size(size_t offset, size_t size, size_t length); |
105 | | |
106 | 2 | int64_t mtime() const override { return _remote_file_reader->mtime(); } |
107 | | |
108 | | // Asynchronously prefetch a range of file cache blocks. |
109 | | // This method triggers read file cache in dryrun mode to warm up the cache |
110 | | // without actually reading the data into user buffers. |
111 | | // |
112 | | // Parameters: |
113 | | // offset: Starting offset in the file |
114 | | // size: Number of bytes to prefetch |
115 | | // io_ctx: IO context (can be nullptr, will create a dryrun context internally) |
116 | | // |
117 | | // Note: This is a best-effort operation. Errors are logged but not returned. |
118 | | void prefetch_range(size_t offset, size_t size, const IOContext* io_ctx = nullptr); |
119 | | |
120 | | protected: |
121 | | /// Read bytes from cache when possible and fall back to peer/S3 when needed. |
122 | | /// @param[in] offset Start offset in the file. |
123 | | /// @param[out] result Destination buffer for the requested bytes. |
124 | | /// @param[out] bytes_read Number of bytes copied into result. |
125 | | /// @param[in] io_ctx IO context carrying dry-run, warmup and statistics options. |
126 | | /// @return OK on success; otherwise an error from cache lookup or remote read. |
127 | | Status read_at_impl(size_t offset, Slice result, size_t* bytes_read, |
128 | | const IOContext* io_ctx) override; |
129 | | |
130 | | private: |
131 | | struct AsyncReadBlock; |
132 | | struct AsyncReadPlan; |
133 | | |
134 | | enum class FileCacheReadType { |
135 | | DATA, |
136 | | INVERTED_INDEX, |
137 | | SEGMENT_FOOTER_INDEX, |
138 | | }; |
139 | | |
140 | | /// Initialize cache metadata for Doris-table files. |
141 | | /// @return None. |
142 | | void _init_doris_table_cache(); |
143 | | |
144 | | /// Initialize cache metadata for external-table files. |
145 | | /// @param[in] opts Reader options used to choose cache key and cache base path. |
146 | | /// @return None. |
147 | | void _init_external_table_cache(const FileReaderOptions& opts); |
148 | | |
149 | | /// Check whether this reader can read cache files directly without get_or_set. |
150 | | /// @return true when direct cache-file reads are enabled for Doris-table files. |
151 | | bool _can_read_cache_file_directly() const; |
152 | | |
153 | | /// Decide whether remote cache miss reads should try peer cache first. |
154 | | /// @param[in] io_ctx IO context for warmup and request-mode checks. |
155 | | /// @return true if peer read is enabled for this request; otherwise false. |
156 | | bool _should_read_from_peer(const IOContext* io_ctx) const; |
157 | | |
158 | | /// Resolve the write policy for the current read instead of freezing a global setting in the |
159 | | /// reader constructor. Explicit cache-population reads always remain synchronous. |
160 | | /// @param[in] io_ctx Per-read flags and an optional write-mode override. |
161 | | /// @return The effective no-write, synchronous, or asynchronous cache-write mode. |
162 | | CacheWriteMode _resolve_cache_write_mode(const IOContext* io_ctx) const; |
163 | | |
164 | | /// Serve a normal read while moving cache-miss writes off the query thread. The method uses |
165 | | /// the first and last remotely covered blocks as one range, matching the synchronous path's |
166 | | /// preference for a single remote operation over fine-grained hole processing. |
167 | | /// @param[in] offset Original user read offset. |
168 | | /// @param[out] result Destination buffer for the complete user request. |
169 | | /// @param[in] bytes_req Requested user bytes. |
170 | | /// @param[in] already_read Prefix bytes already filled by the direct-cache path. |
171 | | /// @param[out] bytes_read Total completed user bytes. |
172 | | /// @param[in,out] stats Per-read cache statistics. |
173 | | /// @param[in,out] source_read_breakdown Local/remote byte attribution for query profiles. |
174 | | /// @param[in] io_ctx Context passed to cache lookup and remote IO. |
175 | | /// @return OK on success; otherwise the remote-read error. |
176 | | Status _read_async_write_path(size_t offset, Slice result, size_t bytes_req, |
177 | | size_t already_read, size_t* bytes_read, ReadStatistics& stats, |
178 | | SourceReadBreakdown& source_read_breakdown, |
179 | | const IOContext* io_ctx); |
180 | | |
181 | | /// Build ordered, gap-free cache-block coverage for the unread suffix. Block offsets are |
182 | | /// cache-block aligned; only the physical EOF block may be short. Vector indexes remain |
183 | | /// identical across inflight lookup, cache probe, materialization, remote slicing, and task |
184 | | /// submission. A fully inflight-covered request skips BlockFileCache::probe; otherwise one |
185 | | /// whole-range probe returns exactly one entry per logical block. |
186 | | /// @param[in] remaining_offset First user byte not filled by the direct-cache path. |
187 | | /// @param[in] remaining_size Number of unread user bytes. |
188 | | /// @param[in] write_epoch Epoch captured before any lookup or remote IO. |
189 | | /// @param[in] io_ctx Context used to build the cache admission/probe context. |
190 | | /// @param[in,out] stats Lookup and probe counters updated during planning. |
191 | | /// @return Plan that owns any retained probe blocks and first-to-last remote range. |
192 | | AsyncReadPlan _build_async_read_plan(size_t remaining_offset, size_t remaining_size, |
193 | | AsyncCacheWriteEpoch write_epoch, const IOContext* io_ctx, |
194 | | ReadStatistics& stats); |
195 | | |
196 | | /// Copy one block already available from an inflight buffer or downloaded cache file. Cache |
197 | | /// state is revalidated before IO; a race is reported to the caller as a simple full-range |
198 | | /// remote fallback. |
199 | | /// @param[in] plan Plan owning the probed blocks and user boundaries. |
200 | | /// @param[in] block_index Index of the aligned block and its matching probe result. |
201 | | /// @param[in] user_offset Original user request offset used to locate the destination slice. |
202 | | /// @param[out] result Destination buffer for the complete user request. |
203 | | /// @param[in] cache_context Context used only when a successful local read touches LRU. |
204 | | /// @param[in,out] stats Local-read timing counters. |
205 | | /// @param[in,out] materialized_bytes User bytes copied from cache or inflight memory. |
206 | | /// @param[in,out] need_self_heal Set when a cache file disappears during a local read. |
207 | | /// @return true when the block was copied; false when the caller should use remote fallback. |
208 | | bool _materialize_async_block(const AsyncReadPlan& plan, size_t block_index, size_t user_offset, |
209 | | Slice result, const CacheContext& cache_context, |
210 | | ReadStatistics& stats, size_t* materialized_bytes, |
211 | | bool* need_self_heal); |
212 | | |
213 | | /// Copy only blocks before the first and after the last REMOTE block. When no REMOTE block |
214 | | /// exists, copy the entire request. DOWNLOADING blocks outside the remote span keep the |
215 | | /// existing wait behavior; blocks inside the span are covered by the same remote read. |
216 | | /// @param[in] plan Classified block list and remote boundaries. |
217 | | /// @param[in] user_offset Original user request offset. |
218 | | /// @param[out] result Destination buffer for side data. |
219 | | /// @param[in] cache_context Context used for successful local-cache touches. |
220 | | /// @param[in,out] stats Local-read statistics. |
221 | | /// @param[in,out] source_read_breakdown Local bytes copied from the covered sides. |
222 | | /// @param[in,out] indirect_read_bytes User bytes copied from the covered sides. |
223 | | /// @param[in,out] need_self_heal Whether a missing local cache file requires async cleanup. |
224 | | /// @return true when all selected cache/inflight blocks were copied; false on a race/read error. |
225 | | bool _materialize_async_cached_sides(const AsyncReadPlan& plan, size_t user_offset, |
226 | | Slice result, const CacheContext& cache_context, |
227 | | ReadStatistics& stats, |
228 | | SourceReadBreakdown& source_read_breakdown, |
229 | | size_t* indirect_read_bytes, bool* need_self_heal); |
230 | | |
231 | | /// Read the planned middle span once and copy only its overlap with the unread user range. |
232 | | /// @param[in] plan Source plan containing user boundaries. |
233 | | /// @param[in] user_offset Original user request offset. |
234 | | /// @param[out] result Destination buffer for the complete user request. |
235 | | /// @param[in] need_self_heal Whether stale cache metadata should be removed before remote IO. |
236 | | /// @param[in] io_ctx Context passed to remote storage. |
237 | | /// @param[in,out] stats Remote-read timing and source flags. |
238 | | /// @param[in,out] source_read_breakdown Remote user bytes copied from this span. |
239 | | /// @param[in,out] indirect_read_bytes User bytes copied by the indirect path. |
240 | | /// @param[out] remote_buffer Full aligned middle-span payload retained for async tasks. |
241 | | /// @return OK on success; otherwise the remote-read error. |
242 | | Status _read_async_remote_range(const AsyncReadPlan& plan, size_t user_offset, Slice result, |
243 | | bool need_self_heal, const IOContext* io_ctx, |
244 | | ReadStatistics& stats, |
245 | | SourceReadBreakdown& source_read_breakdown, |
246 | | size_t* indirect_read_bytes, |
247 | | std::unique_ptr<char[]>* remote_buffer); |
248 | | |
249 | | /// Copy each real cache-miss block from the remote span into one fixed cache-block-sized |
250 | | /// tracked buffer and enqueue a per-block write task. `write_size` records the valid prefix for |
251 | | /// a short physical EOF block. A final insert-if-absent prevents duplicate ownership after IO. |
252 | | /// @param[in] plan Classified blocks, remote boundaries, and the epoch captured before IO. |
253 | | /// @param[in] remote_buffer Full payload for the plan's first-to-last remote span. |
254 | | /// @param[in] io_ctx Context converted to the worker's admission context. |
255 | | /// @param[in,out] stats Submission, rejection, allocation, and dedup counters. |
256 | | /// @return None. |
257 | | void _submit_async_write_tasks(const AsyncReadPlan& plan, |
258 | | const std::unique_ptr<char[]>& remote_buffer, |
259 | | const IOContext* io_ctx, ReadStatistics& stats); |
260 | | |
261 | | /// Register a downloaded block in the direct-read map owned by this reader. |
262 | | /// @param[in] file_block Downloaded cache block to insert. |
263 | | /// @return None. |
264 | | void _insert_file_reader(FileBlockSPtr file_block); |
265 | | |
266 | | /// Try to satisfy the request by reading already downloaded cache files directly. |
267 | | /// @param[in] offset Requested file offset. |
268 | | /// @param[out] result Destination buffer for the request. |
269 | | /// @param[in] bytes_req Requested byte count. |
270 | | /// @param[in] is_dryrun True if local cache IO should be skipped. |
271 | | /// @param[in,out] stats Read statistics updated during the attempt. |
272 | | /// @param[in,out] already_read Bytes already filled into result by direct cache reads. |
273 | | /// @param[out] bytes_read Total bytes read when the whole request is satisfied directly. |
274 | | /// @return true if the whole request is completed by direct cache-file reads; otherwise false. |
275 | | bool _try_read_from_cached_files_directly(size_t offset, Slice result, size_t bytes_req, |
276 | | bool is_dryrun, ReadStatistics& stats, |
277 | | SourceReadBreakdown& source_read_breakdown, |
278 | | size_t& already_read, size_t* bytes_read); |
279 | | |
280 | | /// Collect blocks that still need remote data and update cache-hit statistics. |
281 | | /// @param[in] holder Cache blocks covering the aligned request range. |
282 | | /// @param[in,out] stats Read statistics updated according to block states. |
283 | | /// @return Blocks that should be fetched from peer/S3 by the current reader. |
284 | | std::vector<FileBlockSPtr> _collect_remote_read_blocks(const FileBlocksHolder& holder, |
285 | | ReadStatistics& stats); |
286 | | |
287 | | /// Fetch missing blocks from peer/S3, write them into cache, and copy the overlap to result. |
288 | | /// @param[in] empty_blocks Blocks selected for remote fetch. |
289 | | /// @param[in] offset Original request offset. |
290 | | /// @param[in] bytes_req Original request size. |
291 | | /// @param[in] already_read Bytes already produced before this step. |
292 | | /// @param[out] result Destination buffer for the original request. |
293 | | /// @param[in] is_dryrun True if cache-file writes and local buffer copies should be skipped. |
294 | | /// @param[in,out] stats Read statistics updated for remote and local cache work. |
295 | | /// @param[in] io_ctx IO context passed to peer/S3 reads. |
296 | | /// @param[in,out] indirect_read_bytes Bytes copied into result through the indirect path. |
297 | | /// @param[out] empty_start Left boundary of the fetched contiguous empty range. |
298 | | /// @param[out] empty_end Right boundary of the fetched contiguous empty range. |
299 | | /// @param[out] peer_fetched_blocks Exact blocks fetched by peer in sparse mode; empty for S3. |
300 | | /// @return OK on success; otherwise an error from peer/S3 read. |
301 | | Status _read_remote_blocks_into_cache(const std::vector<FileBlockSPtr>& empty_blocks, |
302 | | size_t offset, size_t bytes_req, size_t already_read, |
303 | | Slice result, bool is_dryrun, ReadStatistics& stats, |
304 | | SourceReadBreakdown& source_read_breakdown, |
305 | | const IOContext* io_ctx, size_t& indirect_read_bytes, |
306 | | size_t& empty_start, size_t& empty_end, |
307 | | PeerFetchedBlockSet& peer_fetched_blocks); |
308 | | |
309 | | /// Read cached blocks that were not covered by the remote-fetch range, with remote fallback. |
310 | | /// @param[in] holder Cache blocks covering the aligned request range. |
311 | | /// @param[in] offset Original request offset. |
312 | | /// @param[in] bytes_req Original request size. |
313 | | /// @param[out] result Destination buffer for the original request. |
314 | | /// @param[in] is_dryrun True if local cache IO should be skipped. |
315 | | /// @param[in] empty_start Left boundary of the range already handled by remote fetch. |
316 | | /// @param[in] empty_end Right boundary of the range already handled by remote fetch. |
317 | | /// @param[in] peer_fetched_blocks Exact blocks already filled by peer; empty for S3 path. |
318 | | /// @param[in,out] stats Read statistics updated for wait, cache, and remote fallback paths. |
319 | | /// @param[in,out] indirect_read_bytes Bytes copied into result through this indirect stage. |
320 | | /// @param[out] bytes_read Total bytes covered for the original request after this stage. |
321 | | /// @return OK on success; otherwise an error from cache read or remote fallback read. |
322 | | Status _read_remaining_blocks_from_cache(const FileBlocksHolder& holder, size_t offset, |
323 | | size_t bytes_req, Slice result, bool is_dryrun, |
324 | | size_t empty_start, size_t empty_end, |
325 | | const PeerFetchedBlockSet& peer_fetched_blocks, |
326 | | ReadStatistics& stats, |
327 | | SourceReadBreakdown& source_read_breakdown, |
328 | | size_t& indirect_read_bytes, size_t* bytes_read, |
329 | | const IOContext* io_ctx); |
330 | | |
331 | | /// Read through the block-cache metadata path when direct cache-file reads are insufficient. |
332 | | /// @param[in] offset Original request offset. |
333 | | /// @param[out] result Destination buffer for the original request. |
334 | | /// @param[in] bytes_req Original request size. |
335 | | /// @param[in] already_read Bytes already produced by direct cache-file reads. |
336 | | /// @param[in] is_dryrun True if local cache IO should be skipped. |
337 | | /// @param[out] bytes_read Total bytes read for the request. |
338 | | /// @param[in,out] stats Read statistics updated across the indirect path. |
339 | | /// @param[in] io_ctx IO context passed to cache lookup and remote read. |
340 | | /// @return OK on success; otherwise an error from cache lookup or remote read. |
341 | | Status _read_from_indirect_cache(size_t offset, Slice result, size_t bytes_req, |
342 | | size_t already_read, bool is_dryrun, size_t* bytes_read, |
343 | | ReadStatistics& stats, |
344 | | SourceReadBreakdown& source_read_breakdown, |
345 | | const IOContext* io_ctx); |
346 | | |
347 | | /// Try to read the complete request from indexed async-write buffers. Return false without |
348 | | /// modifying `result` when any covered cache block has no in-flight entry. |
349 | | bool _try_read_from_inflight_buffers(size_t offset, Slice result, size_t bytes_req, |
350 | | bool is_dryrun, ReadStatistics& stats); |
351 | | |
352 | | /// Opt-in snapshot of cache coverage immediately before an exact remote read. |
353 | | /// Probes never populate/touch cache blocks, and their result does not alter the IO decision. |
354 | | void _trace_remote_miss(size_t offset, size_t size, const IOContext* io_ctx); |
355 | | |
356 | | /// Read in-flight buffers or downloaded cache blocks only when one source fully covers the |
357 | | /// request; otherwise read remote data directly without writing file cache. |
358 | | /// @param[in] offset Original request offset. |
359 | | /// @param[out] result Destination buffer for the original request. |
360 | | /// @param[in] bytes_req Original request size. |
361 | | /// @param[in] is_dryrun True if local cache IO should be skipped. |
362 | | /// @param[out] bytes_read Total bytes read for the request. |
363 | | /// @param[in,out] stats Read statistics updated for local or remote work. |
364 | | /// @param[in,out] source_read_breakdown Source bytes used by profile metrics. |
365 | | /// @param[in] io_ctx IO context passed to cache lookup and remote read. |
366 | | /// @return OK on success; otherwise an error from cache lookup, cache read, or remote read. |
367 | | Status _read_remote_only_on_cache_miss(size_t offset, Slice result, size_t bytes_req, |
368 | | bool is_dryrun, size_t* bytes_read, |
369 | | ReadStatistics& stats, |
370 | | SourceReadBreakdown& source_read_breakdown, |
371 | | const IOContext* io_ctx); |
372 | | |
373 | | /// Fall back to S3: clear peer_result, allocate buffer, and read from remote storage. |
374 | | /// @param[in] empty_start Start offset of the contiguous remote-read range. |
375 | | /// @param[in] span_size Size of the remote-read range in bytes. |
376 | | /// @param[in,out] buffer Span buffer that receives S3 data. |
377 | | /// @param[in,out] peer_result Cleared when non-null. |
378 | | /// @param[in,out] stats Read statistics updated for S3 execution. |
379 | | /// @param[in] io_ctx IO context passed to the remote reader. |
380 | | /// @return OK on success; otherwise the S3 read error. |
381 | | Status _execute_s3_fallback(size_t empty_start, size_t span_size, |
382 | | std::unique_ptr<char[]>& buffer, PeerFetchResult* peer_result, |
383 | | ReadStatistics& stats, const IOContext* io_ctx); |
384 | | |
385 | | /// Sequential peer-then-S3 fallback: try the best peer candidate, update affinity on |
386 | | /// success/failure, and fall back to S3 if peer fails. |
387 | | /// @param[in] empty_blocks Blocks whose data is missing from local cache. |
388 | | /// @param[in] empty_start Start offset of the contiguous remote-read range. |
389 | | /// @param[in] span_size Size of the remote-read range in bytes. |
390 | | /// @param[in,out] buffer Span buffer that receives S3 data on S3 fallback. |
391 | | /// @param[in,out] peer_result Peer payloads populated on peer success. |
392 | | /// @param[in,out] stats Read statistics updated for peer/S3 execution. |
393 | | /// @param[in] io_ctx IO context passed to the remote reader. |
394 | | /// @param[in] candidates Peer candidates sorted by affinity. |
395 | | /// @param[in] tablet_id Tablet ID for affinity tracking. |
396 | | /// @return OK on success; otherwise the S3 read error. |
397 | | Status _execute_sequential_peer_read(const std::vector<FileBlockSPtr>& empty_blocks, |
398 | | size_t empty_start, size_t span_size, |
399 | | std::unique_ptr<char[]>& buffer, |
400 | | PeerFetchResult* peer_result, ReadStatistics& stats, |
401 | | const IOContext* io_ctx, |
402 | | const std::vector<doris::PeerCandidate>& candidates, |
403 | | int64_t tablet_id); |
404 | | |
405 | | /// Execute a remote fetch for the contiguous empty range, trying peer first when enabled. |
406 | | /// @param[in] empty_blocks Blocks whose data is missing from local cache. |
407 | | /// @param[in] empty_start Start offset of the contiguous remote-read range for S3 fallback. |
408 | | /// @param[in] span_size Size of the enclosing contiguous remote-read range for S3 fallback. |
409 | | /// @param[in,out] buffer Temporary span buffer receiving S3 data. |
410 | | /// @param[in,out] peer_result Segmented peer payloads when the peer path succeeds. |
411 | | /// @param[in,out] stats Read statistics updated for peer/S3 execution. |
412 | | /// @param[in] io_ctx IO context passed to the remote reader. |
413 | | /// @return OK on success; otherwise the peer/S3 read error. |
414 | | Status _execute_remote_read(const std::vector<FileBlockSPtr>& empty_blocks, size_t empty_start, |
415 | | size_t span_size, std::unique_ptr<char[]>& buffer, |
416 | | PeerFetchResult* peer_result, ReadStatistics& stats, |
417 | | const IOContext* io_ctx); |
418 | | |
419 | | /// Execute a winner race between peer read and S3 read for cross compute group scenarios. |
420 | | /// Launches both peer and S3 reads concurrently in bthreads and returns the first successful |
421 | | /// result. Uses bthread::Mutex and bthread::ConditionVariable for synchronization. |
422 | | /// @param[in] empty_blocks Blocks whose data is missing from local cache. |
423 | | /// @param[in] empty_start Start offset of the contiguous remote-read range. |
424 | | /// @param[in] span_size Size of the contiguous range for the S3 fallback read. |
425 | | /// @param[in,out] buffer Temporary span buffer that receives S3 data on S3 win. |
426 | | /// @param[out] peer_result Peer fetch payloads populated when peer wins. |
427 | | /// @param[in,out] stats Read statistics updated for the winning path. |
428 | | /// @param[in] io_ctx IO context passed to both peer and S3 reads. |
429 | | /// @param[in] candidates All peer candidates for the tablet. |
430 | | /// @return OK on success with buffer or peer_result populated; otherwise an error. |
431 | | Status _execute_winner_race(const std::vector<FileBlockSPtr>& empty_blocks, size_t empty_start, |
432 | | size_t span_size, std::unique_ptr<char[]>& buffer, |
433 | | PeerFetchResult* peer_result, ReadStatistics& stats, |
434 | | const IOContext* io_ctx, |
435 | | const std::vector<doris::PeerCandidate>& candidates, |
436 | | int64_t tablet_id); |
437 | | |
438 | | /// Merge per-read statistics into the external file-cache statistics accumulator. |
439 | | /// @param[in] stats Statistics produced by the current read. |
440 | | /// @param[in,out] state Destination statistics accumulator; ignored when null. |
441 | | /// @param[in] read_type Logical file-cache read type used for fine-grained profile counters. |
442 | | /// @return None. |
443 | | void _update_stats(const ReadStatistics& stats, |
444 | | const SourceReadBreakdown& source_read_breakdown, FileCacheStatistics* state, |
445 | | FileCacheReadType read_type) const; |
446 | | |
447 | | bool _is_doris_table = false; |
448 | | CacheAlignMode _cache_align_mode {CacheAlignMode::ALIGN_TO_BLOCK}; |
449 | | CacheWriteMode _cache_write_mode {CacheWriteMode::DEFAULT}; |
450 | | int64_t _tablet_id = -1; |
451 | | std::string _storage_resource_id; |
452 | | FileReaderSPtr _remote_file_reader; |
453 | | UInt128Wrapper _cache_hash; |
454 | | BlockFileCache* _cache = nullptr; |
455 | | std::shared_mutex _mtx; |
456 | | // Downloaded blocks this reader may read directly, keyed by offset. The references are weak on |
457 | | // purpose: the cache decides when a block may go by counting strong references, and a reader |
458 | | // that lives as long as its segment must not keep a block busy. Several readers of the same |
459 | | // file can know about the same block at once. |
460 | | std::map<size_t, std::weak_ptr<FileBlock>> _cache_file_readers; |
461 | | }; |
462 | | |
463 | | } // namespace doris::io |