Coverage Report

Created: 2026-08-06 19:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
be/src/io/fs/buffered_reader.cpp
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
#include "io/fs/buffered_reader.h"
19
20
#include <bvar/reducer.h>
21
#include <bvar/window.h>
22
#include <string.h>
23
24
#include <algorithm>
25
#include <chrono>
26
#include <cstdint>
27
#include <memory>
28
29
#include "common/cast_set.h"
30
#include "common/compiler_util.h" // IWYU pragma: keep
31
#include "common/config.h"
32
#include "common/status.h"
33
#include "core/custom_allocator.h"
34
#include "runtime/exec_env.h"
35
#include "runtime/file_scan_profile.h"
36
#include "runtime/runtime_profile.h"
37
#include "runtime/thread_context.h"
38
#include "runtime/workload_group/workload_group.h"
39
#include "runtime/workload_management/io_throttle.h"
40
#include "runtime/workload_management/resource_context.h"
41
#include "util/slice.h"
42
#include "util/threadpool.h"
43
namespace doris {
44
45
namespace io {
46
struct IOContext;
47
48
// add bvar to capture the download bytes per second by buffered reader
49
bvar::Adder<uint64_t> g_bytes_downloaded("buffered_reader", "bytes_downloaded");
50
bvar::PerSecond<bvar::Adder<uint64_t>> g_bytes_downloaded_per_second("buffered_reader",
51
                                                                     "bytes_downloaded_per_second",
52
                                                                     &g_bytes_downloaded, 60);
53
54
Status MergeRangeFileReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
55
1.01M
                                          const IOContext* io_ctx) {
56
1.01M
    _statistics.request_io++;
57
1.01M
    *bytes_read = 0;
58
1.01M
    if (result.size == 0) {
59
0
        return Status::OK();
60
0
    }
61
1.01M
    const int range_index = _search_read_range(offset, offset + result.size);
62
1.01M
    if (range_index < 0) {
63
29
        SCOPED_RAW_TIMER(&_statistics.read_time);
64
29
        Status st = _reader->read_at(offset, result, bytes_read, io_ctx);
65
29
        _statistics.merged_io++;
66
29
        _statistics.request_bytes += *bytes_read;
67
29
        _statistics.merged_bytes += *bytes_read;
68
29
        return st;
69
29
    }
70
1.01M
    if (offset + result.size > _random_access_ranges[range_index].end_offset) {
71
        // return _reader->read_at(offset, result, bytes_read, io_ctx);
72
0
        return Status::IOError("Range in RandomAccessReader should be read sequentially");
73
0
    }
74
75
1.01M
    size_t has_read = 0;
76
1.01M
    RangeCachedData& cached_data = _range_cached_data[range_index];
77
1.01M
    cached_data.has_read = true;
78
1.01M
    if (cached_data.contains(offset)) {
79
        // has cached data in box
80
975k
        _read_in_box(cached_data, offset, result, &has_read);
81
975k
        _statistics.request_bytes += has_read;
82
975k
        if (has_read == result.size) {
83
            // all data is read in cache
84
975k
            *bytes_read = has_read;
85
975k
            return Status::OK();
86
975k
        }
87
975k
    } else if (!cached_data.empty()) {
88
        // the data in range may be skipped or ignored
89
6
        for (int16_t box_index : cached_data.ref_box) {
90
6
            _dec_box_ref(box_index);
91
6
        }
92
6
        cached_data.reset();
93
6
    }
94
95
38.7k
    size_t to_read = result.size - has_read;
96
38.7k
    if (to_read >= SMALL_IO || to_read >= _remaining) {
97
0
        SCOPED_RAW_TIMER(&_statistics.read_time);
98
0
        size_t read_size = 0;
99
0
        RETURN_IF_ERROR(_reader->read_at(offset + has_read, Slice(result.data + has_read, to_read),
100
0
                                         &read_size, io_ctx));
101
0
        *bytes_read = has_read + read_size;
102
0
        _statistics.merged_io++;
103
0
        _statistics.request_bytes += read_size;
104
0
        _statistics.merged_bytes += read_size;
105
0
        return Status::OK();
106
0
    }
107
108
    // merge small IO
109
38.7k
    size_t merge_start = offset + has_read;
110
38.7k
    const size_t merge_end = merge_start + _merged_read_slice_size;
111
    // <slice_size, is_content>
112
38.7k
    std::vector<std::pair<size_t, bool>> merged_slice;
113
38.7k
    size_t content_size = 0;
114
38.7k
    size_t hollow_size = 0;
115
38.7k
    if (merge_start > _random_access_ranges[range_index].end_offset) {
116
0
        return Status::IOError("Fail to merge small IO");
117
0
    }
118
38.7k
    int merge_index = range_index;
119
194k
    while (merge_start < merge_end && merge_index < _random_access_ranges.size()) {
120
158k
        size_t content_max = _remaining - content_size;
121
158k
        if (content_max == 0) {
122
0
            break;
123
0
        }
124
158k
        if (merge_index != range_index && _range_cached_data[merge_index].has_read) {
125
            // don't read or merge twice
126
0
            break;
127
0
        }
128
158k
        if (_random_access_ranges[merge_index].end_offset > merge_end) {
129
115
            size_t add_content = std::min(merge_end - merge_start, content_max);
130
115
            content_size += add_content;
131
115
            merge_start += add_content;
132
115
            merged_slice.emplace_back(add_content, true);
133
115
            break;
134
115
        }
135
158k
        size_t add_content =
136
158k
                std::min(_random_access_ranges[merge_index].end_offset - merge_start, content_max);
137
158k
        content_size += add_content;
138
158k
        merge_start += add_content;
139
158k
        merged_slice.emplace_back(add_content, true);
140
158k
        if (merge_start != _random_access_ranges[merge_index].end_offset) {
141
0
            break;
142
0
        }
143
158k
        if (merge_index < _random_access_ranges.size() - 1 && merge_start < merge_end) {
144
121k
            size_t gap = _random_access_ranges[merge_index + 1].start_offset -
145
121k
                         _random_access_ranges[merge_index].end_offset;
146
121k
            if ((content_size + hollow_size) > SMALL_IO && gap >= SMALL_IO) {
147
                // too large gap
148
40
                break;
149
40
            }
150
121k
            if (gap < merge_end - merge_start && content_size < _remaining &&
151
121k
                !_range_cached_data[merge_index + 1].has_read) {
152
119k
                hollow_size += gap;
153
119k
                merge_start = _random_access_ranges[merge_index + 1].start_offset;
154
119k
                merged_slice.emplace_back(gap, false);
155
119k
            } else {
156
                // there's no enough memory to read hollow data
157
1.86k
                break;
158
1.86k
            }
159
121k
        }
160
156k
        merge_index++;
161
156k
    }
162
38.7k
    content_size = 0;
163
38.7k
    hollow_size = 0;
164
38.7k
    std::vector<std::pair<double, size_t>> ratio_and_size;
165
    // Calculate the read amplified ratio for each merge operation and the size of the merged data.
166
    // Find the largest size of the merged data whose amplified ratio is less than config::max_amplified_read_ratio
167
277k
    for (const std::pair<size_t, bool>& slice : merged_slice) {
168
277k
        if (slice.second) {
169
158k
            content_size += slice.first;
170
158k
            if (slice.first > 0) {
171
158k
                ratio_and_size.emplace_back((double)hollow_size / (double)content_size,
172
158k
                                            content_size + hollow_size);
173
158k
            }
174
158k
        } else {
175
119k
            hollow_size += slice.first;
176
119k
        }
177
277k
    }
178
38.7k
    size_t best_merged_size = 0;
179
196k
    for (int i = 0; i < ratio_and_size.size(); ++i) {
180
158k
        const std::pair<double, size_t>& rs = ratio_and_size[i];
181
158k
        size_t equivalent_size = rs.second / (i + 1);
182
158k
        if (rs.second > best_merged_size) {
183
158k
            if (rs.first <= _max_amplified_ratio ||
184
158k
                (_max_amplified_ratio < 1 && equivalent_size <= _equivalent_io_size)) {
185
158k
                best_merged_size = rs.second;
186
158k
            }
187
158k
        }
188
158k
    }
189
190
38.7k
    if (best_merged_size == to_read) {
191
        // read directly to avoid copy operation
192
11.6k
        SCOPED_RAW_TIMER(&_statistics.read_time);
193
11.6k
        size_t read_size = 0;
194
11.6k
        RETURN_IF_ERROR(_reader->read_at(offset + has_read, Slice(result.data + has_read, to_read),
195
11.6k
                                         &read_size, io_ctx));
196
11.6k
        *bytes_read = has_read + read_size;
197
11.6k
        _statistics.merged_io++;
198
11.6k
        _statistics.request_bytes += read_size;
199
11.6k
        _statistics.merged_bytes += read_size;
200
11.6k
        return Status::OK();
201
11.6k
    }
202
203
27.0k
    merge_start = offset + has_read;
204
27.0k
    size_t merge_read_size = 0;
205
27.0k
    RETURN_IF_ERROR(
206
27.0k
            _fill_box(range_index, merge_start, best_merged_size, &merge_read_size, io_ctx));
207
27.0k
    if (cached_data.start_offset != merge_start) {
208
0
        return Status::IOError("Wrong start offset in merged IO");
209
0
    }
210
211
    // read from cached data
212
27.0k
    size_t box_read_size = 0;
213
27.0k
    _read_in_box(cached_data, merge_start, Slice(result.data + has_read, to_read), &box_read_size);
214
27.0k
    *bytes_read = has_read + box_read_size;
215
27.0k
    _statistics.request_bytes += box_read_size;
216
27.0k
    if (*bytes_read < result.size && box_read_size < merge_read_size) {
217
0
        return Status::IOError("Can't read enough bytes in merged IO");
218
0
    }
219
27.0k
    return Status::OK();
220
27.0k
}
221
222
1.01M
int MergeRangeFileReader::_search_read_range(size_t start_offset, size_t end_offset) {
223
1.01M
    if (_random_access_ranges.empty()) {
224
0
        return -1;
225
0
    }
226
1.01M
    int left = 0, right = cast_set<int>(_random_access_ranges.size()) - 1;
227
2.98M
    do {
228
2.98M
        int mid = left + (right - left) / 2;
229
2.98M
        const PrefetchRange& range = _random_access_ranges[mid];
230
2.98M
        if (range.start_offset <= start_offset && start_offset < range.end_offset) {
231
1.01M
            if (range.start_offset <= end_offset && end_offset <= range.end_offset) {
232
1.01M
                return mid;
233
1.01M
            } else {
234
30
                return -1;
235
30
            }
236
1.96M
        } else if (range.start_offset > start_offset) {
237
764k
            right = mid - 1;
238
1.20M
        } else {
239
1.20M
            left = mid + 1;
240
1.20M
        }
241
2.98M
    } while (left <= right);
242
18.4E
    return -1;
243
1.01M
}
244
245
138k
void MergeRangeFileReader::_clean_cached_data(RangeCachedData& cached_data) {
246
138k
    if (!cached_data.empty()) {
247
0
        for (int i = 0; i < cached_data.ref_box.size(); ++i) {
248
0
            DCHECK_GT(cached_data.box_end_offset[i], cached_data.box_start_offset[i]);
249
0
            int16_t box_index = cached_data.ref_box[i];
250
0
            DCHECK_GT(_box_ref[box_index], 0);
251
0
            _box_ref[box_index]--;
252
0
        }
253
0
    }
254
138k
    cached_data.reset();
255
138k
}
256
257
140k
void MergeRangeFileReader::_dec_box_ref(int16_t box_index) {
258
140k
    if (--_box_ref[box_index] == 0) {
259
25.7k
        _remaining += BOX_SIZE;
260
25.7k
    }
261
140k
    if (box_index == _last_box_ref) {
262
26.4k
        _last_box_ref = -1;
263
26.4k
        _last_box_usage = 0;
264
26.4k
    }
265
140k
}
266
267
void MergeRangeFileReader::_read_in_box(RangeCachedData& cached_data, size_t offset, Slice result,
268
1.00M
                                        size_t* bytes_read) {
269
1.00M
    SCOPED_RAW_TIMER(&_statistics.copy_time);
270
1.00M
    auto handle_in_box = [&](size_t remaining, char* copy_out) {
271
1.00M
        size_t to_handle = remaining;
272
1.00M
        int cleaned_box = 0;
273
2.02M
        for (int i = 0; i < cached_data.ref_box.size() && remaining > 0; ++i) {
274
1.01M
            int16_t box_index = cached_data.ref_box[i];
275
1.01M
            size_t box_to_handle = std::min(remaining, (size_t)(cached_data.box_end_offset[i] -
276
1.01M
                                                                cached_data.box_start_offset[i]));
277
1.01M
            if (copy_out != nullptr) {
278
1.00M
            }
279
1.01M
            if (copy_out != nullptr) {
280
1.00M
                memcpy(copy_out + to_handle - remaining,
281
1.00M
                       _boxes[box_index].data() + cached_data.box_start_offset[i], box_to_handle);
282
1.00M
            }
283
1.01M
            remaining -= box_to_handle;
284
1.01M
            cached_data.box_start_offset[i] += box_to_handle;
285
1.01M
            if (cached_data.box_start_offset[i] == cached_data.box_end_offset[i]) {
286
140k
                cleaned_box++;
287
140k
                _dec_box_ref(box_index);
288
140k
            }
289
1.01M
        }
290
1.00M
        DCHECK_EQ(remaining, 0);
291
1.00M
        if (cleaned_box > 0) {
292
140k
            cached_data.ref_box.erase(cached_data.ref_box.begin(),
293
140k
                                      cached_data.ref_box.begin() + cleaned_box);
294
140k
            cached_data.box_start_offset.erase(cached_data.box_start_offset.begin(),
295
140k
                                               cached_data.box_start_offset.begin() + cleaned_box);
296
140k
            cached_data.box_end_offset.erase(cached_data.box_end_offset.begin(),
297
140k
                                             cached_data.box_end_offset.begin() + cleaned_box);
298
140k
        }
299
1.00M
        cached_data.start_offset += to_handle;
300
1.00M
        if (cached_data.start_offset == cached_data.end_offset) {
301
138k
            _clean_cached_data(cached_data);
302
138k
        }
303
1.00M
    };
304
305
1.00M
    if (offset > cached_data.start_offset) {
306
        // the data in range may be skipped
307
6.74k
        size_t to_skip = offset - cached_data.start_offset;
308
6.74k
        handle_in_box(to_skip, nullptr);
309
6.74k
    }
310
311
1.00M
    size_t to_read = std::min(cached_data.end_offset - cached_data.start_offset, result.size);
312
1.00M
    handle_in_box(to_read, result.data);
313
1.00M
    *bytes_read = to_read;
314
1.00M
}
315
316
Status MergeRangeFileReader::_fill_box(int range_index, size_t start_offset, size_t to_read,
317
27.0k
                                       size_t* bytes_read, const IOContext* io_ctx) {
318
27.0k
    if (!_read_slice) {
319
25.9k
        _read_slice = std::make_unique<OwnedSlice>(_merged_read_slice_size);
320
25.9k
    }
321
322
27.0k
    *bytes_read = 0;
323
27.0k
    {
324
27.0k
        SCOPED_RAW_TIMER(&_statistics.read_time);
325
27.0k
        RETURN_IF_ERROR(_reader->read_at(start_offset, Slice(_read_slice->data(), to_read),
326
27.0k
                                         bytes_read, io_ctx));
327
27.0k
        _statistics.merged_io++;
328
27.0k
        _statistics.merged_bytes += *bytes_read;
329
27.0k
    }
330
331
27.0k
    SCOPED_RAW_TIMER(&_statistics.copy_time);
332
27.0k
    size_t copy_start = start_offset;
333
27.0k
    const size_t copy_end = start_offset + *bytes_read;
334
    // copy data into small boxes
335
    // tuple(box_index, box_start_offset, file_start_offset, file_end_offset)
336
27.0k
    std::vector<std::tuple<int16_t, uint32_t, size_t, size_t>> filled_boxes;
337
338
148k
    auto fill_box = [&](int16_t fill_box_ref, uint32_t box_usage, size_t box_copy_end) {
339
148k
        size_t copy_size = std::min(box_copy_end - copy_start, BOX_SIZE - box_usage);
340
148k
        memcpy(_boxes[fill_box_ref].data() + box_usage,
341
148k
               _read_slice->data() + copy_start - start_offset, copy_size);
342
148k
        filled_boxes.emplace_back(fill_box_ref, box_usage, copy_start, copy_start + copy_size);
343
148k
        copy_start += copy_size;
344
148k
        _last_box_ref = fill_box_ref;
345
148k
        _last_box_usage = box_usage + cast_set<int>(copy_size);
346
148k
        _box_ref[fill_box_ref]++;
347
148k
        if (box_usage == 0) {
348
28.7k
            _remaining -= BOX_SIZE;
349
28.7k
        }
350
148k
    };
351
352
27.0k
    for (int fill_range_index = range_index;
353
173k
         fill_range_index < _random_access_ranges.size() && copy_start < copy_end;
354
146k
         ++fill_range_index) {
355
146k
        RangeCachedData& fill_range_cache = _range_cached_data[fill_range_index];
356
146k
        DCHECK(fill_range_cache.empty());
357
146k
        fill_range_cache.reset();
358
146k
        const PrefetchRange& fill_range = _random_access_ranges[fill_range_index];
359
146k
        if (fill_range.start_offset > copy_start) {
360
            // don't copy hollow data
361
25.2k
            size_t hollow_size = fill_range.start_offset - copy_start;
362
25.2k
            DCHECK_GT(copy_end - copy_start, hollow_size);
363
25.2k
            copy_start += hollow_size;
364
25.2k
        }
365
366
146k
        const size_t range_copy_end = std::min(copy_end, fill_range.end_offset);
367
        // reuse the remaining capacity of last box
368
146k
        if (_last_box_ref >= 0 && _last_box_usage < BOX_SIZE) {
369
119k
            fill_box(_last_box_ref, _last_box_usage, range_copy_end);
370
119k
        }
371
        // reuse the former released box
372
154k
        for (int16_t i = 0; i < _boxes.size() && copy_start < range_copy_end; ++i) {
373
8.23k
            if (_box_ref[i] == 0) {
374
472
                fill_box(i, 0, range_copy_end);
375
472
            }
376
8.23k
        }
377
        // apply for new box to copy data
378
174k
        while (copy_start < range_copy_end && _boxes.size() < NUM_BOX) {
379
28.2k
            _boxes.emplace_back(BOX_SIZE);
380
28.2k
            _box_ref.emplace_back(0);
381
28.2k
            fill_box(cast_set<int16_t>(_boxes.size()) - 1, 0, range_copy_end);
382
28.2k
        }
383
146k
        DCHECK_EQ(copy_start, range_copy_end);
384
385
146k
        if (!filled_boxes.empty()) {
386
146k
            fill_range_cache.start_offset = std::get<2>(filled_boxes[0]);
387
146k
            fill_range_cache.end_offset = std::get<3>(filled_boxes.back());
388
148k
            for (auto& tuple : filled_boxes) {
389
148k
                fill_range_cache.ref_box.emplace_back(std::get<0>(tuple));
390
148k
                fill_range_cache.box_start_offset.emplace_back(std::get<1>(tuple));
391
148k
                fill_range_cache.box_end_offset.emplace_back(
392
148k
                        std::get<1>(tuple) + std::get<3>(tuple) - std::get<2>(tuple));
393
148k
            }
394
146k
            filled_boxes.clear();
395
146k
        }
396
146k
    }
397
27.0k
    return Status::OK();
398
27.0k
}
399
400
// there exists occasions where the buffer is already closed but
401
// some prior tasks are still queued in thread pool, so we have to check whether
402
// the buffer is closed each time the condition variable is notified.
403
355
void PrefetchBuffer::reset_offset(size_t offset) {
404
355
    {
405
355
        std::unique_lock lck {_lock};
406
355
        if (!_prefetched.wait_for(
407
355
                    lck, std::chrono::milliseconds(config::buffered_reader_read_timeout_ms),
408
355
                    [this]() { return _buffer_status != BufferStatus::PENDING; })) {
409
0
            _prefetch_status = Status::TimedOut("time out when reset prefetch buffer");
410
0
            return;
411
0
        }
412
355
        if (UNLIKELY(_buffer_status == BufferStatus::CLOSED)) {
413
0
            _prefetched.notify_all();
414
0
            return;
415
0
        }
416
355
        _buffer_status = BufferStatus::RESET;
417
355
        _offset = offset;
418
355
        _prefetched.notify_all();
419
355
    }
420
355
    if (UNLIKELY(offset >= _file_range.end_offset)) {
421
208
        _len = 0;
422
208
        _exceed = true;
423
208
        return;
424
208
    } else {
425
147
        _exceed = false;
426
147
    }
427
    // Lazy-allocate the backing buffer in the calling (query) thread, which has a
428
    // MemTrackerLimiter attached. The prefetch thread pool threads are "Orphan" threads
429
    // without a tracker, so allocation must not happen there.
430
147
    if (_buf.empty()) {
431
131
        _buf.resize(_size);
432
131
    }
433
147
    _prefetch_status = ExecEnv::GetInstance()->buffered_reader_prefetch_thread_pool()->submit_func(
434
147
            [buffer_ptr = shared_from_this()]() { buffer_ptr->prefetch_buffer(); });
435
147
}
436
437
// only this function would run concurrently in another thread
438
147
void PrefetchBuffer::prefetch_buffer() {
439
147
    {
440
147
        std::unique_lock lck {_lock};
441
147
        if (!_prefetched.wait_for(
442
147
                    lck, std::chrono::milliseconds(config::buffered_reader_read_timeout_ms),
443
147
                    [this]() {
444
147
                        return _buffer_status == BufferStatus::RESET ||
445
147
                               _buffer_status == BufferStatus::CLOSED;
446
147
                    })) {
447
0
            _prefetch_status = Status::TimedOut("time out when invoking prefetch buffer");
448
0
            return;
449
0
        }
450
        // in case buffer is already closed
451
147
        if (UNLIKELY(_buffer_status == BufferStatus::CLOSED)) {
452
0
            _prefetched.notify_all();
453
0
            return;
454
0
        }
455
147
        _buffer_status = BufferStatus::PENDING;
456
147
        _prefetched.notify_all();
457
147
    }
458
459
0
    int read_range_index = search_read_range(_offset);
460
147
    size_t buf_size;
461
147
    if (read_range_index == -1) {
462
147
        buf_size =
463
147
                _file_range.end_offset - _offset > _size ? _size : _file_range.end_offset - _offset;
464
147
    } else {
465
0
        buf_size = merge_small_ranges(_offset, read_range_index);
466
0
    }
467
468
147
    _len = 0;
469
147
    Status s;
470
471
147
    {
472
147
        SCOPED_RAW_TIMER(&_statis.read_time);
473
147
        s = _reader->read_at(_offset, Slice {_buf.data(), buf_size}, &_len, _io_ctx);
474
147
    }
475
147
    if (UNLIKELY(s.ok() && buf_size != _len)) {
476
        // This indicates that the data size returned by S3 object storage is smaller than what we requested,
477
        // which seems to be a violation of the S3 protocol since our request range was valid.
478
        // We currently consider this situation a bug and will treat this task as a failure.
479
0
        s = Status::InternalError("Data size returned by S3 is smaller than requested");
480
0
        LOG(WARNING) << "Data size returned by S3 is smaller than requested" << _reader->path()
481
0
                     << " request bytes " << buf_size << " returned size " << _len;
482
0
    }
483
147
    g_bytes_downloaded << _len;
484
147
    _statis.prefetch_request_io += 1;
485
147
    _statis.prefetch_request_bytes += _len;
486
147
    std::unique_lock lck {_lock};
487
147
    if (!_prefetched.wait_for(lck,
488
147
                              std::chrono::milliseconds(config::buffered_reader_read_timeout_ms),
489
147
                              [this]() { return _buffer_status == BufferStatus::PENDING; })) {
490
0
        _prefetch_status = Status::TimedOut("time out when invoking prefetch buffer");
491
0
        return;
492
0
    }
493
147
    if (!s.ok() && _offset < _reader->size()) {
494
        // We should print the error msg since this buffer might not be accessed by the consumer
495
        // which would result in the status being missed
496
1
        LOG_WARNING("prefetch path {} failed, offset {}, error {}", _reader->path().native(),
497
1
                    _offset, s.to_string());
498
1
        _prefetch_status = std::move(s);
499
1
    }
500
147
    _buffer_status = BufferStatus::PREFETCHED;
501
147
    _prefetched.notify_all();
502
    // eof would come up with len == 0, it would be handled by read_buffer
503
147
}
504
505
147
int PrefetchBuffer::search_read_range(size_t off) const {
506
147
    if (_random_access_ranges == nullptr || _random_access_ranges->empty()) {
507
147
        return -1;
508
147
    }
509
0
    const std::vector<PrefetchRange>& random_access_ranges = *_random_access_ranges;
510
0
    int left = 0, right = cast_set<int>(random_access_ranges.size()) - 1;
511
0
    do {
512
0
        int mid = left + (right - left) / 2;
513
0
        const PrefetchRange& range = random_access_ranges[mid];
514
0
        if (range.start_offset <= off && range.end_offset > off) {
515
0
            return mid;
516
0
        } else if (range.start_offset > off) {
517
0
            right = mid;
518
0
        } else {
519
0
            left = mid + 1;
520
0
        }
521
0
    } while (left < right);
522
0
    if (random_access_ranges[right].start_offset > off) {
523
0
        return right;
524
0
    } else {
525
0
        return -1;
526
0
    }
527
0
}
528
529
0
size_t PrefetchBuffer::merge_small_ranges(size_t off, int range_index) const {
530
0
    if (_random_access_ranges == nullptr || _random_access_ranges->empty()) {
531
0
        return _size;
532
0
    }
533
0
    int64_t remaining = _size;
534
0
    const std::vector<PrefetchRange>& random_access_ranges = *_random_access_ranges;
535
0
    while (remaining > 0 && range_index < random_access_ranges.size()) {
536
0
        const PrefetchRange& range = random_access_ranges[range_index];
537
0
        if (range.start_offset <= off && range.end_offset > off) {
538
0
            remaining -= range.end_offset - off;
539
0
            off = range.end_offset;
540
0
            range_index++;
541
0
        } else if (range.start_offset > off) {
542
            // merge small range
543
0
            size_t hollow = range.start_offset - off;
544
0
            if (hollow < remaining) {
545
0
                remaining -= hollow;
546
0
                off = range.start_offset;
547
0
            } else {
548
0
                break;
549
0
            }
550
0
        } else {
551
0
            DCHECK(false);
552
0
        }
553
0
    }
554
0
    if (remaining < 0 || remaining == _size) {
555
0
        remaining = 0;
556
0
    }
557
0
    return _size - remaining;
558
0
}
559
560
Status PrefetchBuffer::read_buffer(size_t off, const char* out, size_t buf_len,
561
154
                                   size_t* bytes_read) {
562
154
    if (UNLIKELY(off >= _file_range.end_offset)) {
563
        // Reader can read out of [start_offset, end_offset) by synchronous method.
564
0
        return _reader->read_at(off, Slice {out, buf_len}, bytes_read, _io_ctx);
565
0
    }
566
154
    if (_exceed) {
567
0
        reset_offset((off / _size) * _size);
568
0
        return read_buffer(off, out, buf_len, bytes_read);
569
0
    }
570
154
    {
571
154
        std::unique_lock lck {_lock};
572
        // buffer must be prefetched or it's closed
573
154
        if (!_prefetched.wait_for(
574
154
                    lck, std::chrono::milliseconds(config::buffered_reader_read_timeout_ms),
575
246
                    [this]() {
576
246
                        return _buffer_status == BufferStatus::PREFETCHED ||
577
246
                               _buffer_status == BufferStatus::CLOSED;
578
246
                    })) {
579
1
            _prefetch_status = Status::TimedOut("time out when read prefetch buffer");
580
1
            return _prefetch_status;
581
1
        }
582
153
        if (UNLIKELY(BufferStatus::CLOSED == _buffer_status)) {
583
0
            return Status::OK();
584
0
        }
585
153
    }
586
153
    RETURN_IF_ERROR(_prefetch_status);
587
    // there is only parquet would do not sequence read
588
    // it would read the end of the file first
589
153
    if (UNLIKELY(!contains(off))) {
590
0
        reset_offset((off / _size) * _size);
591
0
        return read_buffer(off, out, buf_len, bytes_read);
592
0
    }
593
153
    if (UNLIKELY(0 == _len || _offset + _len < off)) {
594
0
        return Status::OK();
595
0
    }
596
597
153
    {
598
153
        LIMIT_REMOTE_SCAN_IO(bytes_read);
599
        // [0]: maximum len trying to read, [1] maximum length buffer can provide, [2] actual len buffer has
600
153
        size_t read_len = std::min({buf_len, _offset + _size - off, _offset + _len - off});
601
153
        {
602
153
            SCOPED_RAW_TIMER(&_statis.copy_time);
603
153
            memcpy((void*)out, _buf.data() + (off - _offset), read_len);
604
153
        }
605
153
        *bytes_read = read_len;
606
153
        _statis.request_io += 1;
607
153
        _statis.request_bytes += read_len;
608
153
    }
609
153
    if (off + *bytes_read == _offset + _len) {
610
103
        reset_offset(_offset + _whole_buffer_size);
611
103
    }
612
153
    return Status::OK();
613
153
}
614
615
252
void PrefetchBuffer::close() {
616
252
    std::unique_lock lck {_lock};
617
    // in case _reader still tries to write to the buf after we close the buffer
618
252
    if (!_prefetched.wait_for(lck,
619
252
                              std::chrono::milliseconds(config::buffered_reader_read_timeout_ms),
620
255
                              [this]() { return _buffer_status != BufferStatus::PENDING; })) {
621
1
        _prefetch_status = Status::TimedOut("time out when close prefetch buffer");
622
1
        return;
623
1
    }
624
251
    _buffer_status = BufferStatus::CLOSED;
625
251
    _prefetched.notify_all();
626
    // Explicitly release the backing buffer here, in the calling (query) thread which has a
627
    // MemTrackerLimiter. The destructor may run in the thread pool's Orphan thread (when the
628
    // last shared_ptr ref is released after the prefetch lambda completes), so we must not
629
    // rely on ~PODArray() to release memory — that would trigger memory_orphan_check().
630
251
    PODArray<char>().swap(_buf);
631
251
}
632
633
72
void PrefetchBuffer::_collect_profile_before_close() {
634
72
    if (_sync_profile != nullptr) {
635
72
        _sync_profile(*this);
636
72
    }
637
72
}
638
639
// buffered reader
640
PrefetchBufferedReader::PrefetchBufferedReader(RuntimeProfile* profile, io::FileReaderSPtr reader,
641
                                               PrefetchRange file_range,
642
                                               std::shared_ptr<const IOContext> io_ctx,
643
                                               int64_t buffer_size)
644
63
        : _reader(std::move(reader)), _file_range(file_range), _io_ctx_holder(std::move(io_ctx)) {
645
63
    if (_io_ctx_holder == nullptr) {
646
5
        _io_ctx_holder = std::make_shared<IOContext>();
647
5
    }
648
63
    _io_ctx = _io_ctx_holder.get();
649
63
    if (buffer_size == -1L) {
650
63
        buffer_size = config::remote_storage_read_buffer_mb * 1024 * 1024;
651
63
    }
652
63
    _size = _reader->size();
653
63
    _whole_pre_buffer_size = buffer_size;
654
63
    _file_range.end_offset = std::min(_file_range.end_offset, _size);
655
63
    int buffer_num = buffer_size > s_max_pre_buffer_size
656
63
                             ? cast_set<int>(buffer_size) / cast_set<int>(s_max_pre_buffer_size)
657
63
                             : 1;
658
63
    std::function<void(PrefetchBuffer&)> sync_buffer = nullptr;
659
63
    if (profile != nullptr) {
660
58
        const char* prefetch_buffered_reader = "PrefetchBufferedReader";
661
58
        auto* total_time =
662
58
                ADD_CHILD_TIMER(profile, prefetch_buffered_reader,
663
58
                                file_scan_profile::parent_or_root(profile, file_scan_profile::IO));
664
58
        auto copy_time = ADD_CHILD_TIMER(profile, "CopyTime", prefetch_buffered_reader);
665
58
        auto read_time = ADD_CHILD_TIMER(profile, "ReadTime", prefetch_buffered_reader);
666
58
        auto prefetch_request_io =
667
58
                ADD_CHILD_COUNTER(profile, "PreRequestIO", TUnit::UNIT, prefetch_buffered_reader);
668
58
        auto prefetch_request_bytes = ADD_CHILD_COUNTER(profile, "PreRequestBytes", TUnit::BYTES,
669
58
                                                        prefetch_buffered_reader);
670
58
        auto request_io =
671
58
                ADD_CHILD_COUNTER(profile, "RequestIO", TUnit::UNIT, prefetch_buffered_reader);
672
58
        auto request_bytes =
673
58
                ADD_CHILD_COUNTER(profile, "RequestBytes", TUnit::BYTES, prefetch_buffered_reader);
674
72
        sync_buffer = [=](PrefetchBuffer& buf) {
675
72
            COUNTER_UPDATE(total_time, buf._statis.copy_time + buf._statis.read_time);
676
72
            COUNTER_UPDATE(copy_time, buf._statis.copy_time);
677
72
            COUNTER_UPDATE(read_time, buf._statis.read_time);
678
72
            COUNTER_UPDATE(prefetch_request_io, buf._statis.prefetch_request_io);
679
72
            COUNTER_UPDATE(prefetch_request_bytes, buf._statis.prefetch_request_bytes);
680
72
            COUNTER_UPDATE(request_io, buf._statis.request_io);
681
72
            COUNTER_UPDATE(request_bytes, buf._statis.request_bytes);
682
72
        };
683
58
    }
684
    // set the _cur_offset of this reader as same as the inner reader's,
685
    // to make sure the buffer reader will start to read at right position.
686
315
    for (int i = 0; i < buffer_num; i++) {
687
252
        _pre_buffers.emplace_back(std::make_shared<PrefetchBuffer>(
688
252
                _file_range, s_max_pre_buffer_size, _whole_pre_buffer_size, _reader, _io_ctx_holder,
689
252
                sync_buffer));
690
252
    }
691
63
}
692
693
63
PrefetchBufferedReader::~PrefetchBufferedReader() {
694
    /// Better not to call virtual functions in a destructor.
695
63
    static_cast<void>(_close_internal());
696
63
}
697
698
Status PrefetchBufferedReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
699
129
                                            const IOContext* io_ctx) {
700
129
    if (!_initialized) {
701
63
        reset_all_buffer(offset);
702
63
        _initialized = true;
703
63
    }
704
129
    if (UNLIKELY(result.get_size() == 0 || offset >= size())) {
705
7
        *bytes_read = 0;
706
7
        return Status::OK();
707
7
    }
708
122
    size_t nbytes = result.get_size();
709
122
    int actual_bytes_read = 0;
710
275
    while (actual_bytes_read < nbytes && offset < size()) {
711
154
        size_t read_num = 0;
712
154
        auto buffer_pos = get_buffer_pos(offset);
713
154
        RETURN_IF_ERROR(
714
154
                _pre_buffers[buffer_pos]->read_buffer(offset, result.get_data() + actual_bytes_read,
715
154
                                                      nbytes - actual_bytes_read, &read_num));
716
153
        actual_bytes_read += read_num;
717
153
        offset += read_num;
718
153
    }
719
121
    *bytes_read = actual_bytes_read;
720
121
    return Status::OK();
721
122
}
722
723
12
Status PrefetchBufferedReader::close() {
724
12
    return _close_internal();
725
12
}
726
727
75
Status PrefetchBufferedReader::_close_internal() {
728
75
    if (!_closed) {
729
63
        _closed = true;
730
63
        std::for_each(_pre_buffers.begin(), _pre_buffers.end(),
731
252
                      [](std::shared_ptr<PrefetchBuffer>& buffer) { buffer->close(); });
732
63
        return _reader->close();
733
63
    }
734
735
12
    return Status::OK();
736
75
}
737
738
18
void PrefetchBufferedReader::_collect_profile_before_close() {
739
18
    std::for_each(_pre_buffers.begin(), _pre_buffers.end(),
740
72
                  [](std::shared_ptr<PrefetchBuffer>& buffer) {
741
72
                      buffer->collect_profile_before_close();
742
72
                  });
743
18
    if (_reader != nullptr) {
744
18
        _reader->collect_profile_before_close();
745
18
    }
746
18
}
747
748
// InMemoryFileReader
749
44.0k
InMemoryFileReader::InMemoryFileReader(io::FileReaderSPtr reader) : _reader(std::move(reader)) {
750
44.0k
    _size = _reader->size();
751
44.0k
}
752
753
44.1k
InMemoryFileReader::~InMemoryFileReader() {
754
44.1k
    static_cast<void>(_close_internal());
755
44.1k
}
756
757
237
Status InMemoryFileReader::close() {
758
237
    return _close_internal();
759
237
}
760
761
44.3k
Status InMemoryFileReader::_close_internal() {
762
44.3k
    if (!_closed) {
763
44.1k
        _closed = true;
764
44.1k
        return _reader->close();
765
44.1k
    }
766
235
    return Status::OK();
767
44.3k
}
768
769
Status InMemoryFileReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
770
87.3k
                                        const IOContext* io_ctx) {
771
87.3k
    if (_data == nullptr) {
772
39.7k
        _data = std::make_unique_for_overwrite<char[]>(_size);
773
774
39.7k
        size_t file_size = 0;
775
39.7k
        RETURN_IF_ERROR(_reader->read_at(0, Slice(_data.get(), _size), &file_size, io_ctx));
776
39.7k
        DCHECK_EQ(file_size, _size);
777
39.7k
    }
778
87.3k
    if (UNLIKELY(offset > _size)) {
779
0
        return Status::IOError("Out of bounds access");
780
0
    }
781
87.3k
    *bytes_read = std::min(result.size, _size - offset);
782
87.3k
    memcpy(result.data, _data.get() + offset, *bytes_read);
783
87.3k
    return Status::OK();
784
87.3k
}
785
786
23.9k
void InMemoryFileReader::_collect_profile_before_close() {
787
23.9k
    if (_reader != nullptr) {
788
23.9k
        _reader->collect_profile_before_close();
789
23.9k
    }
790
23.9k
}
791
792
// BufferedFileStreamReader
793
BufferedFileStreamReader::BufferedFileStreamReader(io::FileReaderSPtr file, uint64_t offset,
794
                                                   uint64_t length, size_t max_buf_size)
795
171k
        : _file(file),
796
171k
          _file_start_offset(offset),
797
171k
          _file_end_offset(offset + length),
798
171k
          _max_buf_size(max_buf_size) {}
799
800
Status BufferedFileStreamReader::read_bytes(const uint8_t** buf, uint64_t offset,
801
1.96M
                                            const size_t bytes_to_read, const IOContext* io_ctx) {
802
1.96M
    if (offset < _file_start_offset || offset >= _file_end_offset ||
803
1.96M
        offset + bytes_to_read > _file_end_offset) {
804
0
        return Status::IOError(
805
0
                "Out-of-bounds Access: offset={}, bytes_to_read={}, file_start={}, "
806
0
                "file_end={}",
807
0
                offset, bytes_to_read, _file_start_offset, _file_end_offset);
808
0
    }
809
1.96M
    int64_t end_offset = offset + bytes_to_read;
810
1.96M
    if (_buf_start_offset <= offset && _buf_end_offset >= end_offset) {
811
947k
        *buf = _buf.get() + offset - _buf_start_offset;
812
947k
        return Status::OK();
813
947k
    }
814
1.02M
    size_t buf_size = std::max(_max_buf_size, bytes_to_read);
815
1.02M
    if (_buf_size < buf_size) {
816
179k
        auto new_buf = make_unique_buffer<uint8_t>(buf_size);
817
180k
        if (offset >= _buf_start_offset && offset < _buf_end_offset) {
818
21.3k
            memcpy(new_buf.get(), _buf.get() + offset - _buf_start_offset,
819
21.3k
                   _buf_end_offset - offset);
820
21.3k
        }
821
179k
        _buf = std::move(new_buf);
822
179k
        _buf_size = buf_size;
823
842k
    } else if (offset > _buf_start_offset && offset < _buf_end_offset) {
824
799k
        memmove(_buf.get(), _buf.get() + offset - _buf_start_offset, _buf_end_offset - offset);
825
799k
    }
826
1.02M
    if (offset < _buf_start_offset || offset >= _buf_end_offset) {
827
201k
        _buf_end_offset = offset;
828
201k
    }
829
1.02M
    _buf_start_offset = offset;
830
1.02M
    int64_t buf_remaining = _buf_end_offset - _buf_start_offset;
831
1.02M
    int64_t to_read = std::min(_buf_size - buf_remaining, _file_end_offset - _buf_end_offset);
832
1.02M
    int64_t has_read = 0;
833
2.04M
    while (has_read < to_read) {
834
1.02M
        size_t loop_read = 0;
835
1.02M
        Slice result(_buf.get() + buf_remaining + has_read, to_read - has_read);
836
1.02M
        RETURN_IF_ERROR(_file->read_at(_buf_end_offset + has_read, result, &loop_read, io_ctx));
837
1.02M
        if (loop_read == 0) {
838
0
            break;
839
0
        }
840
1.02M
        has_read += loop_read;
841
1.02M
    }
842
1.02M
    if (has_read != to_read) {
843
0
        return Status::Corruption("Try to read {} bytes, but received {} bytes", to_read, has_read);
844
0
    }
845
1.02M
    _buf_end_offset += to_read;
846
1.02M
    *buf = _buf.get();
847
1.02M
    return Status::OK();
848
1.02M
}
849
850
Status BufferedFileStreamReader::read_bytes(Slice& slice, uint64_t offset,
851
683k
                                            const IOContext* io_ctx) {
852
683k
    return read_bytes((const uint8_t**)&slice.data, offset, slice.size, io_ctx);
853
683k
}
854
855
Result<io::FileReaderSPtr> DelegateReader::create_file_reader(
856
        RuntimeProfile* profile, const FileSystemProperties& system_properties,
857
        const FileDescription& file_description, const io::FileReaderOptions& reader_options,
858
2.67k
        AccessMode access_mode, const IOContext* io_ctx, const PrefetchRange file_range) {
859
2.67k
    std::shared_ptr<const IOContext> io_ctx_holder;
860
2.67k
    if (io_ctx != nullptr) {
861
        // Old API: best-effort safety by copying the IOContext onto the heap.
862
2.63k
        io_ctx_holder = std::make_shared<IOContext>(*io_ctx);
863
2.63k
    }
864
2.67k
    return create_file_reader(profile, system_properties, file_description, reader_options,
865
2.67k
                              access_mode, std::move(io_ctx_holder), file_range);
866
2.67k
}
867
868
Result<io::FileReaderSPtr> DelegateReader::create_file_reader(
869
        RuntimeProfile* profile, const FileSystemProperties& system_properties,
870
        const FileDescription& file_description, const io::FileReaderOptions& reader_options,
871
        AccessMode access_mode, std::shared_ptr<const IOContext> io_ctx,
872
129k
        const PrefetchRange file_range) {
873
129k
    if (io_ctx == nullptr) {
874
436
        io_ctx = std::make_shared<IOContext>();
875
436
    }
876
129k
    return FileFactory::create_file_reader(system_properties, file_description, reader_options,
877
129k
                                           profile)
878
131k
            .transform([&](auto&& reader) -> io::FileReaderSPtr {
879
131k
                if (reader->size() < config::in_memory_file_size &&
880
131k
                    typeid_cast<io::S3FileReader*>(reader.get())) {
881
44.0k
                    return std::make_shared<InMemoryFileReader>(std::move(reader));
882
44.0k
                }
883
884
87.5k
                if (access_mode == AccessMode::SEQUENTIAL) {
885
543
                    bool is_thread_safe = false;
886
543
                    if (typeid_cast<io::S3FileReader*>(reader.get())) {
887
58
                        is_thread_safe = true;
888
485
                    } else if (auto* cached_reader =
889
485
                                       typeid_cast<io::CachedRemoteFileReader*>(reader.get());
890
485
                               cached_reader &&
891
485
                               typeid_cast<io::S3FileReader*>(cached_reader->get_remote_reader())) {
892
0
                        is_thread_safe = true;
893
0
                    }
894
543
                    if (is_thread_safe) {
895
                        // PrefetchBufferedReader needs thread-safe reader to prefetch data concurrently.
896
58
                        return std::make_shared<io::PrefetchBufferedReader>(
897
58
                                profile, std::move(reader), file_range, io_ctx);
898
58
                    }
899
543
                }
900
901
87.4k
                return reader;
902
87.5k
            });
903
129k
}
904
905
Status LinearProbeRangeFinder::get_range_for(int64_t desired_offset,
906
6
                                             io::PrefetchRange& result_range) {
907
9
    while (index < _ranges.size()) {
908
9
        io::PrefetchRange& range = _ranges[index];
909
9
        if (range.end_offset > desired_offset) {
910
6
            if (range.start_offset > desired_offset) [[unlikely]] {
911
0
                return Status::InvalidArgument("Invalid desiredOffset");
912
0
            }
913
6
            result_range = range;
914
6
            return Status::OK();
915
6
        }
916
3
        ++index;
917
3
    }
918
0
    return Status::InvalidArgument("Invalid desiredOffset");
919
6
}
920
921
RangeCacheFileReader::RangeCacheFileReader(RuntimeProfile* profile, io::FileReaderSPtr inner_reader,
922
                                           std::shared_ptr<RangeFinder> range_finder)
923
2.29k
        : _profile(profile),
924
2.29k
          _inner_reader(std::move(inner_reader)),
925
2.29k
          _range_finder(std::move(range_finder)) {
926
2.29k
    _size = _inner_reader->size();
927
2.29k
    uint64_t max_cache_size =
928
2.29k
            std::max((uint64_t)4096, (uint64_t)_range_finder->get_max_range_size());
929
2.29k
    _cache = OwnedSlice(max_cache_size);
930
931
2.30k
    if (_profile != nullptr) {
932
2.30k
        const char* random_profile = "RangeCacheFileReader";
933
2.30k
        _total_time = ADD_CHILD_TIMER_WITH_LEVEL(
934
2.30k
                _profile, random_profile,
935
2.30k
                file_scan_profile::parent_or_root(_profile, file_scan_profile::IO), 1);
936
2.30k
        _request_io =
937
2.30k
                ADD_CHILD_COUNTER_WITH_LEVEL(_profile, "RequestIO", TUnit::UNIT, random_profile, 1);
938
2.30k
        _request_bytes = ADD_CHILD_COUNTER_WITH_LEVEL(_profile, "RequestBytes", TUnit::BYTES,
939
2.30k
                                                      random_profile, 1);
940
2.30k
        _request_time = ADD_CHILD_TIMER_WITH_LEVEL(_profile, "RequestTime", random_profile, 1);
941
2.30k
        _read_to_cache_time =
942
2.30k
                ADD_CHILD_TIMER_WITH_LEVEL(_profile, "ReadToCacheTime", random_profile, 1);
943
2.30k
        _cache_refresh_count = ADD_CHILD_COUNTER_WITH_LEVEL(_profile, "CacheRefreshCount",
944
2.30k
                                                            TUnit::UNIT, random_profile, 1);
945
2.30k
        _read_to_cache_bytes = ADD_CHILD_COUNTER_WITH_LEVEL(_profile, "ReadToCacheBytes",
946
2.30k
                                                            TUnit::BYTES, random_profile, 1);
947
2.30k
    }
948
2.29k
}
949
950
Status RangeCacheFileReader::read_at_impl(size_t offset, Slice result, size_t* bytes_read,
951
6
                                          const IOContext* io_ctx) {
952
6
    auto request_size = result.size;
953
954
6
    _cache_statistics.request_io++;
955
6
    _cache_statistics.request_bytes += request_size;
956
6
    SCOPED_RAW_TIMER(&_cache_statistics.request_time);
957
958
6
    PrefetchRange range;
959
6
    if (_range_finder->get_range_for(offset, range)) [[likely]] {
960
6
        if (_current_start_offset != range.start_offset) { // need read new range to cache.
961
6
            auto range_size = range.end_offset - range.start_offset;
962
963
6
            _cache_statistics.cache_refresh_count++;
964
6
            _cache_statistics.read_to_cache_bytes += range_size;
965
6
            SCOPED_RAW_TIMER(&_cache_statistics.read_to_cache_time);
966
967
6
            Slice cache_slice = {_cache.data(), range_size};
968
6
            RETURN_IF_ERROR(
969
6
                    _inner_reader->read_at(range.start_offset, cache_slice, bytes_read, io_ctx));
970
971
6
            if (*bytes_read != range_size) [[unlikely]] {
972
0
                return Status::InternalError(
973
0
                        "RangeCacheFileReader use inner reader read bytes {} not eq expect size {}",
974
0
                        *bytes_read, range_size);
975
0
            }
976
977
6
            _current_start_offset = range.start_offset;
978
6
        }
979
980
6
        int64_t buffer_offset = offset - _current_start_offset;
981
6
        memcpy(result.data, _cache.data() + buffer_offset, request_size);
982
6
        *bytes_read = request_size;
983
984
6
        return Status::OK();
985
6
    } else {
986
0
        return Status::InternalError("RangeCacheFileReader read  not in Ranges. Offset = {}",
987
0
                                     offset);
988
        //                RETURN_IF_ERROR(_inner_reader->read_at(offset, result , bytes_read, io_ctx));
989
        //                return Status::OK();
990
        // think return error is ok,otherwise it will cover up the error.
991
0
    }
992
6
}
993
994
2.31k
void RangeCacheFileReader::_collect_profile_before_close() {
995
2.31k
    if (_profile != nullptr) {
996
2.30k
        COUNTER_UPDATE(_total_time, _cache_statistics.request_time);
997
2.30k
        COUNTER_UPDATE(_request_io, _cache_statistics.request_io);
998
2.30k
        COUNTER_UPDATE(_request_bytes, _cache_statistics.request_bytes);
999
2.30k
        COUNTER_UPDATE(_request_time, _cache_statistics.request_time);
1000
2.30k
        COUNTER_UPDATE(_read_to_cache_time, _cache_statistics.read_to_cache_time);
1001
2.30k
        COUNTER_UPDATE(_cache_refresh_count, _cache_statistics.cache_refresh_count);
1002
2.30k
        COUNTER_UPDATE(_read_to_cache_bytes, _cache_statistics.read_to_cache_bytes);
1003
2.30k
        if (_inner_reader != nullptr) {
1004
2.30k
            _inner_reader->collect_profile_before_close();
1005
2.30k
        }
1006
2.30k
    }
1007
2.31k
}
1008
1009
} // namespace io
1010
1011
} // namespace doris