Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/table/block_fetcher_sync_and_async.h
Line
Count
Source
1
//  Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
2
//  This source code is licensed under both the GPLv2 (found in the
3
//  COPYING file in the root directory) and Apache 2.0 License
4
//  (found in the LICENSE.Apache file in the root directory).
5
//
6
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
7
// Use of this source code is governed by a BSD-style license that can be
8
// found in the LICENSE file. See the AUTHORS file for names of contributors.
9
10
#include "util/coro_utils.h"
11
12
#if defined(WITHOUT_COROUTINES) || \
13
    (defined(USE_COROUTINES) && defined(WITH_COROUTINES))
14
15
namespace ROCKSDB_NAMESPACE {
16
17
// Read a block from the file and verify its checksum. Upon return, io_status_
18
// will be updated with the status of the read, and slice_ will be
19
// updated with a pointer to the data.
20
290k
DEFINE_SYNC_AND_ASYNC(void, BlockFetcher::ReadBlock)(bool retry) {
21
290k
  FSReadRequest read_req;
22
290k
  IOOptions opts;
23
290k
  IODebugContext dbg;
24
290k
  io_status_ = file_->PrepareIOOptions(read_options_, opts, &dbg);
25
290k
  opts.verify_and_reconstruct_read = retry;
26
290k
  read_req.status.PermitUncheckedError();
27
  // Actual file read
28
290k
  if (io_status_.ok()) {
29
290k
    if (file_->use_direct_io()) {
30
0
      AlignedBuffer::Allocator direct_io_allocator;
31
0
      if (block_buffer_provider_.has_value() && !maybe_compressed_) {
32
0
        direct_io_allocator = MakeReadScopedAlignedBufferAllocator(
33
0
            block_buffer_provider_, &read_scoped_buf_lease_);
34
0
      }
35
0
      AlignedBufferAllocationContext direct_io_context{
36
0
          &direct_io_buffer_,
37
0
          direct_io_allocator ? &direct_io_allocator : nullptr};
38
0
      PERF_TIMER_GUARD(block_read_time);
39
0
#if defined(WITHOUT_COROUTINES)
40
0
      PERF_CPU_TIMER_GUARD(
41
0
          block_read_cpu_time,
42
0
          ioptions_.env ? ioptions_.env->GetSystemClock().get() : nullptr);
43
0
#endif
44
0
      read_req.offset = handle_.offset();
45
0
      read_req.len = block_size_with_trailer_;
46
0
      read_req.scratch = nullptr;
47
0
      CO_AWAIT(file_->Read, opts, &read_req, &direct_io_context, &dbg);
48
0
      io_status_ = std::move(read_req.status);
49
0
      slice_ = read_req.result;
50
0
      PERF_COUNTER_ADD(block_read_count, 1);
51
0
      used_buf_ = const_cast<char*>(slice_.data());
52
290k
    } else if (use_fs_scratch_) {
53
0
      PERF_TIMER_GUARD(block_read_time);
54
0
#if defined(WITHOUT_COROUTINES)
55
0
      PERF_CPU_TIMER_GUARD(
56
0
          block_read_cpu_time,
57
0
          ioptions_.env ? ioptions_.env->GetSystemClock().get() : nullptr);
58
0
#endif
59
0
      read_req.offset = handle_.offset();
60
0
      read_req.len = block_size_with_trailer_;
61
0
      read_req.scratch = nullptr;
62
0
      AlignedBuffer direct_io_buffer;
63
0
      AlignedBufferAllocationContext direct_io_context{&direct_io_buffer};
64
0
      io_status_ = CO_AWAIT(file_->MultiRead, opts, &read_req, /*num_reqs=*/1,
65
0
                            &direct_io_context, &dbg);
66
0
      PERF_COUNTER_ADD(block_read_count, 1);
67
68
0
      slice_ = Slice(read_req.result.data(), read_req.result.size());
69
0
      used_buf_ = const_cast<char*>(slice_.data());
70
290k
    } else {
71
      // It allocates/assign used_buf_
72
290k
      PrepareBufferForBlockFromFile();
73
290k
      if (!io_status_.ok()) {
74
0
        CO_RETURN;
75
0
      }
76
77
290k
      PERF_TIMER_GUARD(block_read_time);
78
290k
#if defined(WITHOUT_COROUTINES)
79
290k
      PERF_CPU_TIMER_GUARD(
80
290k
          block_read_cpu_time,
81
290k
          ioptions_.env ? ioptions_.env->GetSystemClock().get() : nullptr);
82
290k
#endif
83
84
290k
      read_req.offset = handle_.offset();
85
290k
      read_req.len = block_size_with_trailer_;
86
290k
      read_req.scratch = used_buf_;
87
290k
      CO_AWAIT(file_->Read, opts, &read_req,
88
290k
               /*direct_io_buffer=*/nullptr, &dbg);
89
290k
      io_status_ = std::move(read_req.status);
90
290k
      slice_ = read_req.result;
91
290k
      PERF_COUNTER_ADD(block_read_count, 1);
92
#ifndef NDEBUG
93
      if (slice_.data() == &stack_buf_[0]) {
94
        num_stack_buf_memcpy_++;
95
      } else if (slice_.data() == heap_buf_.get()) {
96
        num_heap_buf_memcpy_++;
97
      } else if (slice_.data() == compressed_buf_.get()) {
98
        num_compressed_buf_memcpy_++;
99
      }
100
#endif
101
290k
    }
102
290k
  }
103
104
  // TODO: introduce dedicated perf counter for range tombstones
105
290k
  switch (block_type_) {
106
0
    case BlockType::kFilter:
107
0
    case BlockType::kFilterPartitionIndex:
108
0
      PERF_COUNTER_ADD(filter_block_read_count, 1);
109
0
      break;
110
111
0
    case BlockType::kCompressionDictionary:
112
0
      PERF_COUNTER_ADD(compression_dict_block_read_count, 1);
113
0
      break;
114
115
87.2k
    case BlockType::kIndex:
116
87.2k
    case BlockType::kUserDefinedIndex:
117
87.2k
      PERF_COUNTER_ADD(index_block_read_count, 1);
118
87.2k
      break;
119
120
    // Nothing to do here as we don't have counters for the other types.
121
202k
    default:
122
202k
      break;
123
290k
  }
124
125
289k
  PERF_COUNTER_ADD(block_read_byte, block_size_with_trailer_);
126
289k
  RecordBlockReadBytePerfCounter(block_type_, block_size_with_trailer_);
127
289k
  IGNORE_STATUS_IF_ERROR(io_status_);
128
289k
  if (io_status_.ok()) {
129
288k
    if (use_fs_scratch_ && !read_req.status.ok()) {
130
0
      io_status_ = read_req.status;
131
288k
    } else if (slice_.size() != block_size_with_trailer_) {
132
0
      io_status_ = IOStatus::Corruption(
133
0
          "truncated block read from " + file_->file_name() + " offset " +
134
0
          std::to_string(handle_.offset()) + ", expected " +
135
0
          std::to_string(block_size_with_trailer_) + " bytes, got " +
136
0
          std::to_string(slice_.size()));
137
0
    }
138
288k
  }
139
140
289k
  if (io_status_.ok()) {
141
289k
    ProcessTrailerIfPresent();
142
289k
  }
143
144
289k
  if (retry) {
145
0
    RecordTick(ioptions_.stats, FILE_READ_CORRUPTION_RETRY_COUNT);
146
0
  }
147
289k
  if (io_status_.ok()) {
148
288k
    InsertCompressedBlockToPersistentCacheIfNeeded();
149
288k
    fs_buf_ = std::move(read_req.fs_scratch);
150
288k
    if (retry) {
151
0
      RecordTick(ioptions_.stats, FILE_READ_CORRUPTION_RETRY_SUCCESS_COUNT);
152
0
    }
153
288k
  } else {
154
885
    ReleaseFileSystemProvidedBuffer(&read_req);
155
885
    direct_io_buffer_.Release();
156
885
    compressed_buf_.reset();
157
885
    heap_buf_.reset();
158
885
    used_buf_ = nullptr;
159
885
  }
160
289k
}
161
162
289k
DEFINE_SYNC_AND_ASYNC(IOStatus, BlockFetcher::ReadBlockContents)() {
163
289k
  if (TryGetUncompressBlockFromPersistentCache()) {
164
0
    compression_type() = kNoCompression;
165
#ifndef NDEBUG
166
    contents_->has_trailer = footer_.GetBlockTrailerSize() > 0;
167
#endif  // NDEBUG
168
0
    CO_RETURN IOStatus::OK();
169
0
  }
170
289k
  if (TryGetFromPrefetchBuffer()) {
171
0
    if (io_status_.IsCorruption() && retry_corrupt_read_) {
172
0
      CO_AWAIT(ReadBlock, /*retry=*/true);
173
0
    }
174
0
    if (!io_status_.ok()) {
175
0
      assert(!fs_buf_);
176
0
      CO_RETURN io_status_;
177
0
    }
178
290k
  } else if (!TryGetSerializedBlockFromPersistentCache()) {
179
290k
    CO_AWAIT(ReadBlock, /*retry =*/false);
180
    // If the file system supports retry after corruption, then try to
181
    // re-read the block and see if it succeeds.
182
290k
    if (io_status_.IsCorruption() && retry_corrupt_read_) {
183
0
      assert(!fs_buf_);
184
0
      CO_AWAIT(ReadBlock, /*retry=*/true);
185
0
    }
186
290k
    if (!io_status_.ok()) {
187
0
      assert(!fs_buf_);
188
0
      CO_RETURN io_status_;
189
0
    }
190
290k
  }
191
192
289k
  if (do_uncompress_ && compression_type() != kNoCompression) {
193
0
    PERF_TIMER_GUARD(block_decompress_time);
194
    // Process the compressed block without trailer
195
0
    slice_.size_ = block_size_;
196
0
    decomp_args_.compressed_data = slice_;
197
0
    io_status_ = status_to_io_status(DecompressSerializedBlock(
198
0
        decomp_args_, *decompressor_, contents_, ioptions_, memory_allocator_,
199
0
        block_buffer_provider_));
200
#ifndef NDEBUG
201
    num_heap_buf_memcpy_++;
202
#endif
203
289k
  } else {
204
289k
    GetBlockContents();
205
289k
    slice_ = Slice();
206
289k
  }
207
208
289k
  InsertUncompressedBlockToPersistentCacheIfNeeded();
209
210
289k
  CO_RETURN io_status_;
211
289k
}
212
213
}  // namespace ROCKSDB_NAMESPACE
214
215
#endif  // WITHOUT_COROUTINES || (USE_COROUTINES && WITH_COROUTINES)