Coverage Report

Created: 2026-11-02 16:57

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