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