Coverage Report

Created: 2026-11-12 08:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/version_set.cc
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 "db/version_set.h"
11
12
#include <algorithm>
13
#include <array>
14
#include <cinttypes>
15
#include <cstdio>
16
#include <list>
17
#include <map>
18
#include <set>
19
#include <stdexcept>
20
#include <string>
21
#include <type_traits>
22
#include <unordered_map>
23
#include <utility>
24
#include <vector>
25
26
#include "db/blob/blob_fetcher.h"
27
#include "db/blob/blob_file_cache.h"
28
#include "db/blob/blob_file_reader.h"
29
#include "db/blob/blob_log_format.h"
30
#include "db/blob/blob_source.h"
31
#include "db/compaction/compaction.h"
32
#include "db/compaction/file_pri.h"
33
#include "db/dbformat.h"
34
#include "db/internal_stats.h"
35
#include "db/log_reader.h"
36
#include "db/log_writer.h"
37
#include "db/manifest_ops.h"
38
#include "db/memtable.h"
39
#include "db/merge_context.h"
40
#include "db/merge_helper.h"
41
#include "db/pinned_iterators_manager.h"
42
#include "db/table_cache.h"
43
#include "db/version_builder.h"
44
#include "db/version_edit.h"
45
#include "db/version_edit_handler.h"
46
#include "db/wide/wide_columns_helper.h"
47
#include "file/file_util.h"
48
#include "file/filename.h"
49
#include "file/line_file_reader.h"
50
#include "file/random_access_file_reader.h"
51
#include "file/read_write_util.h"
52
#include "file/writable_file_writer.h"
53
#include "logging/logging.h"
54
#include "monitoring/file_read_sample.h"
55
#include "monitoring/perf_context_imp.h"
56
#include "monitoring/persistent_stats_history.h"
57
#include "options/options_helper.h"
58
#include "options/options_parser.h"
59
#include "rocksdb/env.h"
60
#include "rocksdb/merge_operator.h"
61
#include "rocksdb/write_buffer_manager.h"
62
#include "table/compaction_merging_iterator.h"
63
#include "table/format.h"
64
#include "table/get_context.h"
65
#include "table/internal_iterator.h"
66
#include "table/merging_iterator.h"
67
#include "table/meta_blocks.h"
68
#include "table/multiget_context.h"
69
#include "table/plain/plain_table_factory.h"
70
#include "table/table_reader.h"
71
#include "table/unique_id_impl.h"
72
#include "test_util/sync_point.h"
73
#include "util/cast_util.h"
74
#include "util/coding.h"
75
#include "util/coro_utils.h"
76
#include "util/fastrange.h"
77
#include "util/hash.h"
78
#include "util/stop_watch.h"
79
#include "util/string_util.h"
80
#include "util/user_comparator_wrapper.h"
81
82
#if USE_COROUTINES
83
#include "folly/coro/BlockingWait.h"
84
#include "folly/coro/Collect.h"
85
#endif  // USE_COROUTINES
86
87
namespace ROCKSDB_NAMESPACE {
88
89
0
void OptionsFileManifestState::ApplyCommit(uint64_t number) {
90
0
  committed_options_file_number = number;
91
0
  has_committed_options_file_number = true;
92
0
}
93
94
0
void OptionsFileManifestState::ApplyColumnFamilyEdit(const VersionEdit& edit) {
95
0
  if (edit.IsColumnFamilyAdd()) {
96
0
    column_family_names[edit.GetColumnFamily()] = edit.GetColumnFamilyName();
97
0
  } else if (edit.IsColumnFamilyDrop()) {
98
0
    column_family_names.erase(edit.GetColumnFamily());
99
0
  }
100
0
}
101
102
namespace {
103
104
using ScanOptionsMap = std::unordered_map<size_t, MultiScanArgs>;
105
106
// Find File in LevelFilesBrief data structure
107
// Within an index range defined by left and right
108
int FindFileInRange(const InternalKeyComparator& icmp,
109
                    const LevelFilesBrief& file_level, const Slice& key,
110
3.04k
                    uint32_t left, uint32_t right) {
111
4.64k
  auto cmp = [&](const FdWithKeyRange& f, const Slice& k) -> bool {
112
4.64k
    return icmp.InternalKeyComparator::Compare(f.largest_key, k) < 0;
113
4.64k
  };
114
3.04k
  const auto& b = file_level.files;
115
3.04k
  return static_cast<int>(std::lower_bound(b + left, b + right, key, cmp) - b);
116
3.04k
}
117
118
InternalKey MultiScanInternalKey(const Slice& user_key,
119
0
                                 const Comparator* user_comparator) {
120
0
  const size_t timestamp_size = user_comparator->timestamp_size();
121
0
  if (timestamp_size == 0) {
122
0
    return InternalKey(user_key, kMaxSequenceNumber, kValueTypeForSeek);
123
0
  }
124
125
0
  std::string key_with_ts;
126
0
  AppendKeyWithMaxTimestamp(&key_with_ts, user_key, timestamp_size);
127
0
  return InternalKey(key_with_ts, kMaxSequenceNumber, kValueTypeForSeek);
128
0
}
129
130
bool MultiScanRangeOverlapsFile(const UserComparatorWrapper& user_comparator,
131
                                const ScanOptions& scan_opts,
132
0
                                const FdWithKeyRange& file) {
133
0
  assert(scan_opts.range.start.has_value());
134
0
  assert(scan_opts.range.limit.has_value());
135
136
0
  if (user_comparator.CompareWithoutTimestamp(
137
0
          scan_opts.range.limit.value(), /*a_has_ts=*/false,
138
0
          ExtractUserKey(file.smallest_key), /*b_has_ts=*/true) <= 0) {
139
0
    return false;
140
0
  }
141
142
0
  if (user_comparator.CompareWithoutTimestamp(
143
0
          scan_opts.range.start.value(), /*a_has_ts=*/false,
144
0
          ExtractUserKey(file.largest_key), /*b_has_ts=*/true) > 0) {
145
0
    return false;
146
0
  }
147
148
0
  return true;
149
0
}
150
151
void AddMultiScanFileIndex(std::vector<size_t>* file_indexes,
152
0
                           std::vector<char>* selected, size_t file_index) {
153
0
  if ((*selected)[file_index]) {
154
0
    return;
155
0
  }
156
0
  (*selected)[file_index] = true;
157
0
  file_indexes->push_back(file_index);
158
0
}
159
160
template <typename OnOverlappingFile>
161
bool NotifyMultiScanOverlappingFile(OnOverlappingFile& on_overlapping_file,
162
                                    size_t file_index,
163
0
                                    const ScanOptions& scan_range) {
164
  if constexpr (std::is_same_v<std::invoke_result_t<OnOverlappingFile, size_t,
165
                                                    const ScanOptions&>,
166
0
                               bool>) {
167
0
    return on_overlapping_file(file_index, scan_range);
168
0
  } else {
169
0
    on_overlapping_file(file_index, scan_range);
170
0
    return true;
171
0
  }
172
0
}
Unexecuted instantiation: version_set.cc:bool rocksdb::(anonymous namespace)::NotifyMultiScanOverlappingFile<rocksdb::(anonymous namespace)::LevelIterator::Prepare(rocksdb::MultiScanArgs const*)::{lambda(unsigned long, rocksdb::ScanOptions const&)#1}>(rocksdb::(anonymous namespace)::LevelIterator::Prepare(rocksdb::MultiScanArgs const*)::{lambda(unsigned long, rocksdb::ScanOptions const&)#1}&, unsigned long, rocksdb::ScanOptions const&)
Unexecuted instantiation: version_set.cc:bool rocksdb::(anonymous namespace)::NotifyMultiScanOverlappingFile<rocksdb::(anonymous namespace)::GetMultiScanOverlappingFiles(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, unsigned long)::$_0>(rocksdb::(anonymous namespace)::GetMultiScanOverlappingFiles(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, unsigned long)::$_0&, unsigned long, rocksdb::ScanOptions const&)
173
174
template <typename OnOverlappingFile>
175
void ForEachMultiScanOverlappingFile(const InternalKeyComparator& icmp,
176
                                     const UserComparatorWrapper& ucmp,
177
                                     const LevelFilesBrief& file_level,
178
                                     int level, const MultiScanArgs& scan_opts,
179
0
                                     OnOverlappingFile on_overlapping_file) {
180
0
  assert(scan_opts.HasBoundedScanRanges());
181
0
  if (file_level.num_files == 0) {
182
0
    return;
183
0
  }
184
185
0
  if (level == 0) {
186
0
    for (size_t i = 0; i < file_level.num_files; ++i) {
187
0
      for (const ScanOptions& scan_range : scan_opts.GetScanRanges()) {
188
0
        if (MultiScanRangeOverlapsFile(ucmp, scan_range, file_level.files[i])) {
189
0
          if (!NotifyMultiScanOverlappingFile(on_overlapping_file, i,
190
0
                                              scan_range)) {
191
0
            return;
192
0
          }
193
0
        }
194
195
0
        if (ucmp.CompareWithoutTimestamp(
196
0
                scan_range.range.start.value(), /*a_has_ts=*/false,
197
0
                ExtractUserKey(file_level.files[i].largest_key),
198
0
                /*b_has_ts=*/true) > 0) {
199
0
          break;
200
0
        }
201
0
      }
202
0
    }
203
0
    return;
204
0
  }
205
206
0
  for (const ScanOptions& scan_range : scan_opts.GetScanRanges()) {
207
0
    const InternalKey start_key = MultiScanInternalKey(
208
0
        scan_range.range.start.value(), ucmp.user_comparator());
209
0
    size_t first_file = FindFile(icmp, file_level, start_key.Encode());
210
0
    if (first_file >= file_level.num_files) {
211
0
      continue;
212
0
    }
213
214
0
    const InternalKey limit_key = MultiScanInternalKey(
215
0
        scan_range.range.limit.value(), ucmp.user_comparator());
216
0
    size_t last_file = FindFile(icmp, file_level, limit_key.Encode());
217
0
    if (last_file >= file_level.num_files) {
218
0
      last_file = file_level.num_files - 1;
219
0
    }
220
221
0
    for (size_t i = first_file; i <= last_file; ++i) {
222
0
      if (MultiScanRangeOverlapsFile(ucmp, scan_range, file_level.files[i])) {
223
0
        if (!NotifyMultiScanOverlappingFile(on_overlapping_file, i,
224
0
                                            scan_range)) {
225
0
          return;
226
0
        }
227
0
      }
228
0
    }
229
0
  }
230
0
}
Unexecuted instantiation: version_set.cc:void rocksdb::(anonymous namespace)::ForEachMultiScanOverlappingFile<rocksdb::(anonymous namespace)::LevelIterator::Prepare(rocksdb::MultiScanArgs const*)::{lambda(unsigned long, rocksdb::ScanOptions const&)#1}>(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, rocksdb::(anonymous namespace)::LevelIterator::Prepare(rocksdb::MultiScanArgs const*)::{lambda(unsigned long, rocksdb::ScanOptions const&)#1})
Unexecuted instantiation: version_set.cc:void rocksdb::(anonymous namespace)::ForEachMultiScanOverlappingFile<rocksdb::(anonymous namespace)::GetMultiScanOverlappingFiles(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, unsigned long)::$_0>(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, rocksdb::(anonymous namespace)::GetMultiScanOverlappingFiles(rocksdb::InternalKeyComparator const&, rocksdb::UserComparatorWrapper const&, rocksdb::LevelFilesBrief const&, int, rocksdb::MultiScanArgs const&, unsigned long)::$_0)
231
232
std::vector<size_t> GetMultiScanOverlappingFiles(
233
    const InternalKeyComparator& icmp, const UserComparatorWrapper& ucmp,
234
    const LevelFilesBrief& file_level, int level,
235
    const MultiScanArgs& scan_opts,
236
0
    size_t max_file_indexes = std::numeric_limits<size_t>::max()) {
237
0
  std::vector<size_t> file_indexes;
238
0
  if (!scan_opts.HasBoundedScanRanges() || file_level.num_files == 0) {
239
0
    return file_indexes;
240
0
  }
241
0
  file_indexes.reserve(
242
0
      std::min(static_cast<size_t>(file_level.num_files), max_file_indexes));
243
244
0
  std::vector<char> selected(file_level.num_files, false);
245
0
  ForEachMultiScanOverlappingFile(
246
0
      icmp, ucmp, file_level, level, scan_opts,
247
0
      [&](size_t file_index, const ScanOptions& /*scan_range*/) {
248
0
        if (file_indexes.size() >= max_file_indexes) {
249
0
          return false;
250
0
        }
251
0
        AddMultiScanFileIndex(&file_indexes, &selected, file_index);
252
0
        return file_indexes.size() < max_file_indexes;
253
0
      });
254
255
0
  return file_indexes;
256
0
}
257
258
void AddTableIteratorForLevel(
259
    ColumnFamilyData* cfd, const ReadOptions& read_options,
260
    const FileOptions& soptions, const MutableCFOptions& mutable_cf_options,
261
    MergeIteratorBuilder* merge_iter_builder, Arena* arena,
262
    const LevelFilesBrief& file_level, int level, size_t file_index,
263
    bool skip_filters, size_t max_file_size_for_l0_meta_pin,
264
10.3k
    bool allow_unprepared_value, SequenceNumber read_seq) {
265
10.3k
  std::unique_ptr<TruncatedRangeDelIterator> tombstone_iter = nullptr;
266
10.3k
  const auto& file = file_level.files[file_index];
267
10.3k
  auto table_iter = cfd->table_cache()->NewIterator(
268
10.3k
      read_options, soptions, cfd->internal_comparator(), *file.file_metadata,
269
10.3k
      /*range_del_agg=*/nullptr, mutable_cf_options, nullptr,
270
10.3k
      cfd->internal_stats()->GetFileReadHist(level),
271
10.3k
      TableReaderCaller::kUserIterator, arena, skip_filters, level,
272
10.3k
      max_file_size_for_l0_meta_pin,
273
10.3k
      /*smallest_compaction_key=*/nullptr,
274
10.3k
      /*largest_compaction_key=*/nullptr, allow_unprepared_value,
275
10.3k
      /*seek_target=*/nullptr, /*prefix_filtered_table=*/nullptr, &read_seq,
276
10.3k
      &tombstone_iter,
277
10.3k
      /*maybe_pin_table_handle=*/true);
278
#ifndef NDEBUG
279
  TEST_SYNC_POINT_CALLBACK("Version::AddIteratorsForLevel:AddedFile",
280
                           file.file_metadata);
281
  std::pair<bool, bool> iterator_type(true /* is_block_based_table_iterator */,
282
                                      false /* is_level_iterator */);
283
  TEST_SYNC_POINT_CALLBACK("Version::AddIteratorsForLevel:IteratorType",
284
                           &iterator_type);
285
#endif
286
10.3k
  if (read_options.ignore_range_deletions) {
287
0
    merge_iter_builder->AddIterator(table_iter);
288
10.3k
  } else {
289
10.3k
    merge_iter_builder->AddPointAndTombstoneIterator(table_iter,
290
10.3k
                                                     std::move(tombstone_iter));
291
10.3k
  }
292
10.3k
}
293
294
Status OverlapWithIterator(const Comparator* ucmp,
295
                           const Slice& smallest_user_key,
296
                           const Slice& largest_user_key,
297
3.12k
                           InternalIterator* iter, bool* overlap) {
298
3.12k
  InternalKey range_start(smallest_user_key, kMaxSequenceNumber,
299
3.12k
                          kValueTypeForSeek);
300
3.12k
  iter->Seek(range_start.Encode());
301
3.12k
  if (!iter->status().ok()) {
302
0
    return iter->status();
303
0
  }
304
305
3.12k
  *overlap = false;
306
3.12k
  if (iter->Valid()) {
307
3.10k
    ParsedInternalKey seek_result;
308
3.10k
    Status s = ParseInternalKey(iter->key(), &seek_result,
309
3.10k
                                false /* log_err_key */);  // TODO
310
3.10k
    if (!s.ok()) {
311
0
      return s;
312
0
    }
313
314
3.10k
    if (ucmp->CompareWithoutTimestamp(seek_result.user_key, largest_user_key) <=
315
3.10k
        0) {
316
3.01k
      *overlap = true;
317
3.01k
    }
318
3.10k
  }
319
320
3.12k
  return iter->status();
321
3.12k
}
322
323
// The status for a blob index whose blob file is not in `vstorage`.
324
Status UnknownBlobFileStatus(const VersionStorageInfo& vstorage,
325
0
                             uint64_t blob_file_number) {
326
0
  if (vstorage.IsDroppedBlobFile(blob_file_number)) {
327
0
    return Status::Corruption("Blob file dropped by best-efforts BlobDB repair",
328
0
                              std::to_string(blob_file_number));
329
0
  }
330
0
  return Status::Corruption("Invalid blob file number");
331
0
}
332
333
}  // namespace
334
335
// Class to help choose the next file to search for the particular key.
336
// Searches and returns files level by level.
337
// We can search level-by-level since entries never hop across
338
// levels. Therefore we are guaranteed that if we find data
339
// in a smaller level, later levels are irrelevant (unless we
340
// are MergeInProgress).
341
class FilePicker {
342
 public:
343
  FilePicker(const Slice& user_key, const Slice& ikey,
344
             autovector<LevelFilesBrief>* file_levels, unsigned int num_levels,
345
             FileIndexer* file_indexer, const Comparator* user_comparator,
346
             const InternalKeyComparator* internal_comparator)
347
2.11k
      : num_levels_(num_levels),
348
2.11k
        curr_level_(static_cast<unsigned int>(-1)),
349
2.11k
        returned_file_level_(static_cast<unsigned int>(-1)),
350
2.11k
        hit_file_level_(static_cast<unsigned int>(-1)),
351
2.11k
        search_left_bound_(0),
352
2.11k
        search_right_bound_(FileIndexer::kLevelMaxIndex),
353
2.11k
        level_files_brief_(file_levels),
354
2.11k
        is_hit_file_last_in_level_(false),
355
2.11k
        curr_file_level_(nullptr),
356
2.11k
        user_key_(user_key),
357
2.11k
        ikey_(ikey),
358
2.11k
        file_indexer_(file_indexer),
359
2.11k
        user_comparator_(user_comparator),
360
2.11k
        internal_comparator_(internal_comparator) {
361
    // Setup member variables to search first level.
362
2.11k
    search_ended_ = !PrepareNextLevel();
363
2.11k
    if (!search_ended_) {
364
      // Prefetch Level 0 table data to avoid cache miss if possible.
365
2.47k
      for (unsigned int i = 0; i < (*level_files_brief_)[0].num_files; ++i) {
366
1.46k
        auto* r = (*level_files_brief_)[0].files[i].fd.pinned_reader.Get();
367
1.46k
        if (r) {
368
1.46k
          r->Prepare(ikey);
369
1.46k
        }
370
1.46k
      }
371
1.01k
    }
372
2.11k
  }
373
374
0
  int GetCurrentLevel() const { return curr_level_; }
375
376
2.48k
  FdWithKeyRange* GetNextFile() {
377
2.90k
    while (!search_ended_) {  // Loops over different levels.
378
1.70k
      while (curr_index_in_curr_level_ < curr_file_level_->num_files) {
379
        // Loops over all files in current level.
380
1.34k
        FdWithKeyRange* f = &curr_file_level_->files[curr_index_in_curr_level_];
381
1.34k
        hit_file_level_ = curr_level_;
382
1.34k
        is_hit_file_last_in_level_ =
383
1.34k
            curr_index_in_curr_level_ == curr_file_level_->num_files - 1;
384
1.34k
        int cmp_largest = -1;
385
386
        // Do key range filtering of files or/and fractional cascading if:
387
        // (1) not all the files are in level 0, or
388
        // (2) there are more than 3 current level files
389
        // If there are only 3 or less current level files in the system, we
390
        // skip the key range filtering. In this case, more likely, the system
391
        // is highly tuned to minimize number of tables queried by each query,
392
        // so it is unlikely that key range filtering is more efficient than
393
        // querying the files.
394
1.34k
        if (num_levels_ > 1 || curr_file_level_->num_files > 3) {
395
          // Check if key is within a file's range. If search left bound and
396
          // right bound point to the same find, we are sure key falls in
397
          // range.
398
774
          assert(curr_level_ == 0 ||
399
774
                 curr_index_in_curr_level_ == start_index_in_curr_level_ ||
400
774
                 user_comparator_->CompareWithoutTimestamp(
401
774
                     user_key_, ExtractUserKey(f->smallest_key)) <= 0);
402
403
774
          int cmp_smallest = user_comparator_->CompareWithoutTimestamp(
404
774
              user_key_, ExtractUserKey(f->smallest_key));
405
774
          if (cmp_smallest >= 0) {
406
535
            cmp_largest = user_comparator_->CompareWithoutTimestamp(
407
535
                user_key_, ExtractUserKey(f->largest_key));
408
535
          }
409
410
          // Setup file search bound for the next level based on the
411
          // comparison results
412
774
          if (curr_level_ > 0) {
413
382
            file_indexer_->GetNextLevelIndex(
414
382
                curr_level_, curr_index_in_curr_level_, cmp_smallest,
415
382
                cmp_largest, &search_left_bound_, &search_right_bound_);
416
382
          }
417
          // Key falls out of current file's range
418
774
          if (cmp_smallest < 0 || cmp_largest > 0) {
419
251
            if (curr_level_ == 0) {
420
199
              ++curr_index_in_curr_level_;
421
199
              continue;
422
199
            } else {
423
              // Search next level.
424
52
              break;
425
52
            }
426
251
          }
427
774
        }
428
429
1.09k
        returned_file_level_ = curr_level_;
430
1.09k
        if (curr_level_ > 0 && cmp_largest < 0) {
431
          // No more files to search in this level.
432
138
          search_ended_ = !PrepareNextLevel();
433
955
        } else {
434
955
          ++curr_index_in_curr_level_;
435
955
        }
436
1.09k
        return f;
437
1.34k
      }
438
      // Start searching next level.
439
412
      search_ended_ = !PrepareNextLevel();
440
412
    }
441
    // Search ended.
442
1.39k
    return nullptr;
443
2.48k
  }
444
445
  // getter for current file level
446
  // for GET_HIT_L0, GET_HIT_L1 & GET_HIT_L2_AND_UP counts
447
3.97k
  unsigned int GetHitFileLevel() { return hit_file_level_; }
448
449
  // Returns true if the most recent "hit file" (i.e., one returned by
450
  // GetNextFile()) is at the last index in its level.
451
1.09k
  bool IsHitFileLastInLevel() { return is_hit_file_last_in_level_; }
452
453
 private:
454
  unsigned int num_levels_;
455
  unsigned int curr_level_;
456
  unsigned int returned_file_level_;
457
  unsigned int hit_file_level_;
458
  int32_t search_left_bound_;
459
  int32_t search_right_bound_;
460
  autovector<LevelFilesBrief>* level_files_brief_;
461
  bool search_ended_;
462
  bool is_hit_file_last_in_level_;
463
  LevelFilesBrief* curr_file_level_;
464
  unsigned int curr_index_in_curr_level_;
465
  unsigned int start_index_in_curr_level_;
466
  Slice user_key_;
467
  Slice ikey_;
468
  FileIndexer* file_indexer_;
469
  const Comparator* user_comparator_;
470
  const InternalKeyComparator* internal_comparator_;
471
472
  // Setup local variables to search next level.
473
  // Returns false if there are no more levels to search.
474
2.66k
  bool PrepareNextLevel() {
475
2.66k
    curr_level_++;
476
4.88k
    while (curr_level_ < num_levels_) {
477
3.35k
      curr_file_level_ = &(*level_files_brief_)[curr_level_];
478
3.35k
      if (curr_file_level_->num_files == 0) {
479
        // When current level is empty, the search bound generated from upper
480
        // level must be [0, -1] or [0, FileIndexer::kLevelMaxIndex] if it is
481
        // also empty.
482
2.20k
        assert(search_left_bound_ == 0);
483
2.20k
        assert(search_right_bound_ == -1 ||
484
2.20k
               search_right_bound_ == FileIndexer::kLevelMaxIndex);
485
        // Since current level is empty, it will need to search all files in
486
        // the next level
487
2.20k
        search_left_bound_ = 0;
488
2.20k
        search_right_bound_ = FileIndexer::kLevelMaxIndex;
489
2.20k
        curr_level_++;
490
2.20k
        continue;
491
2.20k
      }
492
493
      // Some files may overlap each other. We find
494
      // all files that overlap user_key and process them in order from
495
      // newest to oldest. In the context of merge-operator, this can occur at
496
      // any level. Otherwise, it only occurs at Level-0 (since Put/Deletes
497
      // are always compacted into a single entry).
498
1.14k
      int32_t start_index;
499
1.14k
      if (curr_level_ == 0) {
500
        // On Level-0, we read through all files to check for overlap.
501
756
        start_index = 0;
502
756
      } else {
503
        // On Level-n (n>=1), files are sorted. Binary search to find the
504
        // earliest file whose largest key >= ikey. Search left bound and
505
        // right bound are used to narrow the range.
506
389
        if (search_left_bound_ <= search_right_bound_) {
507
389
          if (search_right_bound_ == FileIndexer::kLevelMaxIndex) {
508
389
            search_right_bound_ =
509
389
                static_cast<int32_t>(curr_file_level_->num_files) - 1;
510
389
          }
511
          // `search_right_bound_` is an inclusive upper-bound, but since it was
512
          // determined based on user key, it is still possible the lookup key
513
          // falls to the right of `search_right_bound_`'s corresponding file.
514
          // So, pass a limit one higher, which allows us to detect this case.
515
389
          start_index =
516
389
              FindFileInRange(*internal_comparator_, *curr_file_level_, ikey_,
517
389
                              static_cast<uint32_t>(search_left_bound_),
518
389
                              static_cast<uint32_t>(search_right_bound_) + 1);
519
389
          if (start_index == search_right_bound_ + 1) {
520
            // `ikey_` comes after `search_right_bound_`. The lookup key does
521
            // not exist on this level, so let's skip this level and do a full
522
            // binary search on the next level.
523
7
            search_left_bound_ = 0;
524
7
            search_right_bound_ = FileIndexer::kLevelMaxIndex;
525
7
            curr_level_++;
526
7
            continue;
527
7
          }
528
389
        } else {
529
          // search_left_bound > search_right_bound, key does not exist in
530
          // this level. Since no comparison is done in this level, it will
531
          // need to search all files in the next level.
532
0
          search_left_bound_ = 0;
533
0
          search_right_bound_ = FileIndexer::kLevelMaxIndex;
534
0
          curr_level_++;
535
0
          continue;
536
0
        }
537
389
      }
538
1.13k
      start_index_in_curr_level_ = start_index;
539
1.13k
      curr_index_in_curr_level_ = start_index;
540
541
1.13k
      return true;
542
1.14k
    }
543
    // curr_level_ = num_levels_. So, no more levels to search.
544
1.53k
    return false;
545
2.66k
  }
546
};
547
548
class FilePickerMultiGet {
549
 private:
550
  struct FilePickerContext;
551
552
 public:
553
  FilePickerMultiGet(MultiGetRange* range,
554
                     autovector<LevelFilesBrief>* file_levels,
555
                     unsigned int num_levels, FileIndexer* file_indexer,
556
                     const Comparator* user_comparator,
557
                     const InternalKeyComparator* internal_comparator)
558
0
      : num_levels_(num_levels),
559
0
        curr_level_(static_cast<unsigned int>(-1)),
560
0
        returned_file_level_(static_cast<unsigned int>(-1)),
561
0
        hit_file_level_(static_cast<unsigned int>(-1)),
562
0
        range_(*range, range->begin(), range->end()),
563
0
        maybe_repeat_key_(false),
564
0
        current_level_range_(*range, range->begin(), range->end()),
565
0
        current_file_range_(*range, range->begin(), range->end()),
566
0
        batch_iter_(range->begin()),
567
0
        batch_iter_prev_(range->begin()),
568
0
        upper_key_(range->begin()),
569
0
        level_files_brief_(file_levels),
570
0
        is_hit_file_last_in_level_(false),
571
0
        curr_file_level_(nullptr),
572
0
        file_indexer_(file_indexer),
573
0
        user_comparator_(user_comparator),
574
0
        internal_comparator_(internal_comparator),
575
0
        hit_file_(nullptr) {
576
0
    for (auto iter = range_.begin(); iter != range_.end(); ++iter) {
577
0
      fp_ctx_array_[iter.index()] =
578
0
          FilePickerContext(0, FileIndexer::kLevelMaxIndex);
579
0
    }
580
581
    // Setup member variables to search first level.
582
0
    search_ended_ = !PrepareNextLevel();
583
0
    if (!search_ended_) {
584
      // REVISIT
585
      // Prefetch Level 0 table data to avoid cache miss if possible.
586
      // As of now, only PlainTableReader and CuckooTableReader do any
587
      // prefetching. This may not be necessary anymore once we implement
588
      // batching in those table readers
589
0
      for (unsigned int i = 0; i < (*level_files_brief_)[0].num_files; ++i) {
590
0
        auto* r = (*level_files_brief_)[0].files[i].fd.pinned_reader.Get();
591
0
        if (r) {
592
0
          for (auto iter = range_.begin(); iter != range_.end(); ++iter) {
593
0
            r->Prepare(iter->ikey);
594
0
          }
595
0
        }
596
0
      }
597
0
    }
598
0
  }
599
600
  FilePickerMultiGet(MultiGetRange* range, const FilePickerMultiGet& other)
601
      : num_levels_(other.num_levels_),
602
        curr_level_(other.curr_level_),
603
        returned_file_level_(other.returned_file_level_),
604
        hit_file_level_(other.hit_file_level_),
605
        fp_ctx_array_(other.fp_ctx_array_),
606
        range_(*range, range->begin(), range->end()),
607
        maybe_repeat_key_(false),
608
        current_level_range_(*range, range->begin(), range->end()),
609
        current_file_range_(*range, range->begin(), range->end()),
610
        batch_iter_(range->begin()),
611
        batch_iter_prev_(range->begin()),
612
        upper_key_(range->begin()),
613
        level_files_brief_(other.level_files_brief_),
614
        is_hit_file_last_in_level_(false),
615
        curr_file_level_(other.curr_file_level_),
616
        file_indexer_(other.file_indexer_),
617
        user_comparator_(other.user_comparator_),
618
        internal_comparator_(other.internal_comparator_),
619
0
        hit_file_(nullptr) {
620
0
    PrepareNextLevelForSearch();
621
0
  }
622
623
0
  int GetCurrentLevel() const { return curr_level_; }
624
625
0
  void PrepareNextLevelForSearch() { search_ended_ = !PrepareNextLevel(); }
626
627
0
  FdWithKeyRange* GetNextFileInLevel() {
628
0
    if (batch_iter_ == current_level_range_.end() || search_ended_) {
629
0
      hit_file_ = nullptr;
630
0
      return nullptr;
631
0
    } else {
632
0
      if (maybe_repeat_key_) {
633
0
        maybe_repeat_key_ = false;
634
        // Check if we found the final value for the last key in the
635
        // previous lookup range. If we did, then there's no need to look
636
        // any further for that key, so advance batch_iter_. Else, keep
637
        // batch_iter_ positioned on that key so we look it up again in
638
        // the next file
639
        // For L0, always advance the key because we will look in the next
640
        // file regardless for all keys not found yet
641
0
        if (current_level_range_.CheckKeyDone(batch_iter_) ||
642
0
            curr_level_ == 0) {
643
0
          batch_iter_ = upper_key_;
644
0
        }
645
0
      }
646
      // batch_iter_prev_ will become the start key for the next file
647
      // lookup
648
0
      batch_iter_prev_ = batch_iter_;
649
0
    }
650
651
0
    MultiGetRange next_file_range(current_level_range_, batch_iter_prev_,
652
0
                                  current_level_range_.end());
653
0
    size_t curr_file_index =
654
0
        (batch_iter_ != current_level_range_.end())
655
0
            ? fp_ctx_array_[batch_iter_.index()].curr_index_in_curr_level
656
0
            : curr_file_level_->num_files;
657
0
    FdWithKeyRange* f;
658
0
    bool is_last_key_in_file;
659
0
    if (!GetNextFileInLevelWithKeys(&next_file_range, &curr_file_index, &f,
660
0
                                    &is_last_key_in_file)) {
661
0
      hit_file_ = nullptr;
662
0
      return nullptr;
663
0
    } else {
664
0
      if (is_last_key_in_file) {
665
        // Since cmp_largest is 0, batch_iter_ still points to the last key
666
        // that falls in this file, instead of the next one. Increment
667
        // the file index for all keys between batch_iter_ and upper_key_
668
0
        auto tmp_iter = batch_iter_;
669
0
        while (tmp_iter != upper_key_) {
670
0
          ++(fp_ctx_array_[tmp_iter.index()].curr_index_in_curr_level);
671
0
          ++tmp_iter;
672
0
        }
673
0
        maybe_repeat_key_ = true;
674
0
      }
675
      // Set the range for this file
676
0
      current_file_range_ =
677
0
          MultiGetRange(next_file_range, batch_iter_prev_, upper_key_);
678
0
      returned_file_level_ = curr_level_;
679
0
      hit_file_level_ = curr_level_;
680
0
      is_hit_file_last_in_level_ =
681
0
          curr_file_index == curr_file_level_->num_files - 1;
682
0
      hit_file_ = f;
683
0
      return f;
684
0
    }
685
0
  }
686
687
  // getter for current file level
688
  // for GET_HIT_L0, GET_HIT_L1 & GET_HIT_L2_AND_UP counts
689
0
  unsigned int GetHitFileLevel() { return hit_file_level_; }
690
691
0
  FdWithKeyRange* GetHitFile() { return hit_file_; }
692
693
  // Returns true if the most recent "hit file" (i.e., one returned by
694
  // GetNextFile()) is at the last index in its level.
695
0
  bool IsHitFileLastInLevel() { return is_hit_file_last_in_level_; }
696
697
0
  bool KeyMaySpanNextFile() { return maybe_repeat_key_; }
698
699
0
  bool IsSearchEnded() { return search_ended_; }
700
701
0
  const MultiGetRange& CurrentFileRange() { return current_file_range_; }
702
703
0
  bool RemainingOverlapInLevel() {
704
0
    return !current_level_range_.Suffix(current_file_range_).empty();
705
0
  }
706
707
0
  MultiGetRange& GetRange() { return range_; }
708
709
0
  void ReplaceRange(const MultiGetRange& other) {
710
0
    assert(hit_file_ == nullptr);
711
0
    range_ = other;
712
0
    current_level_range_ = other;
713
0
  }
714
715
  FilePickerMultiGet(FilePickerMultiGet&& other)
716
      : num_levels_(other.num_levels_),
717
        curr_level_(other.curr_level_),
718
        returned_file_level_(other.returned_file_level_),
719
        hit_file_level_(other.hit_file_level_),
720
        fp_ctx_array_(std::move(other.fp_ctx_array_)),
721
        range_(std::move(other.range_)),
722
        maybe_repeat_key_(other.maybe_repeat_key_),
723
        current_level_range_(std::move(other.current_level_range_)),
724
        current_file_range_(std::move(other.current_file_range_)),
725
        batch_iter_(other.batch_iter_, &current_level_range_),
726
        batch_iter_prev_(other.batch_iter_prev_, &current_level_range_),
727
        upper_key_(other.upper_key_, &current_level_range_),
728
        level_files_brief_(other.level_files_brief_),
729
        search_ended_(other.search_ended_),
730
        is_hit_file_last_in_level_(other.is_hit_file_last_in_level_),
731
        curr_file_level_(other.curr_file_level_),
732
        file_indexer_(other.file_indexer_),
733
        user_comparator_(other.user_comparator_),
734
        internal_comparator_(other.internal_comparator_),
735
0
        hit_file_(other.hit_file_) {}
736
  FilePickerMultiGet& operator=(FilePickerMultiGet&&) = delete;
737
  ~FilePickerMultiGet() = default;
738
739
 private:
740
  unsigned int num_levels_;
741
  unsigned int curr_level_;
742
  unsigned int returned_file_level_;
743
  unsigned int hit_file_level_;
744
745
  struct FilePickerContext {
746
    int32_t search_left_bound;
747
    int32_t search_right_bound;
748
    unsigned int curr_index_in_curr_level;
749
    unsigned int start_index_in_curr_level;
750
751
    FilePickerContext(int32_t left, int32_t right)
752
0
        : search_left_bound(left),
753
0
          search_right_bound(right),
754
0
          curr_index_in_curr_level(0),
755
0
          start_index_in_curr_level(0) {}
756
757
    FilePickerContext() = default;
758
  };
759
  std::array<FilePickerContext, MultiGetContext::MAX_BATCH_SIZE> fp_ctx_array_;
760
  MultiGetRange range_;
761
  bool maybe_repeat_key_;
762
  MultiGetRange current_level_range_;
763
  MultiGetRange current_file_range_;
764
  // Iterator to iterate through the keys in a MultiGet batch, that gets reset
765
  // at the beginning of each level. Each call to GetNextFile() will position
766
  // batch_iter_ at or right after the last key that was found in the returned
767
  // SST file
768
  MultiGetRange::Iterator batch_iter_;
769
  // An iterator that records the previous position of batch_iter_, i.e last
770
  // key found in the previous SST file, in order to serve as the start of
771
  // the batch key range for the next SST file
772
  MultiGetRange::Iterator batch_iter_prev_;
773
  MultiGetRange::Iterator upper_key_;
774
  autovector<LevelFilesBrief>* level_files_brief_;
775
  bool search_ended_;
776
  bool is_hit_file_last_in_level_;
777
  LevelFilesBrief* curr_file_level_;
778
  FileIndexer* file_indexer_;
779
  const Comparator* user_comparator_;
780
  const InternalKeyComparator* internal_comparator_;
781
  FdWithKeyRange* hit_file_;
782
783
  // Iterates through files in the current level until it finds a file that
784
  // contains at least one key from the MultiGet batch
785
  bool GetNextFileInLevelWithKeys(MultiGetRange* next_file_range,
786
                                  size_t* file_index, FdWithKeyRange** fd,
787
0
                                  bool* is_last_key_in_file) {
788
0
    size_t curr_file_index = *file_index;
789
0
    FdWithKeyRange* f = nullptr;
790
0
    bool file_hit = false;
791
0
    int cmp_largest = -1;
792
0
    int cmp_smallest = -1;
793
0
    if (curr_file_index >= curr_file_level_->num_files) {
794
      // In the unlikely case the next key is a duplicate of the current key,
795
      // and the current key is the last in the level and the internal key
796
      // was not found, we need to skip lookup for the remaining keys and
797
      // reset the search bounds
798
0
      if (batch_iter_ != current_level_range_.end()) {
799
#ifndef NDEBUG
800
        if (curr_level_ < num_levels_ + 1) {
801
          if ((*level_files_brief_)[curr_level_].num_files == 0) {
802
            struct FilePickerContext& fp_ctx =
803
                fp_ctx_array_[batch_iter_.index()];
804
805
            assert(fp_ctx.search_left_bound == 0);
806
            assert(fp_ctx.search_right_bound == -1 ||
807
                   fp_ctx.search_right_bound == FileIndexer::kLevelMaxIndex);
808
          }
809
        }
810
#endif  // NDBEUG
811
812
0
        ++batch_iter_;
813
0
        for (; batch_iter_ != current_level_range_.end(); ++batch_iter_) {
814
0
          struct FilePickerContext& fp_ctx = fp_ctx_array_[batch_iter_.index()];
815
0
          fp_ctx.search_left_bound = 0;
816
0
          fp_ctx.search_right_bound = FileIndexer::kLevelMaxIndex;
817
0
        }
818
0
      }
819
0
      return false;
820
0
    }
821
    // Loops over keys in the MultiGet batch until it finds a file with
822
    // atleast one of the keys. Then it keeps moving forward until the
823
    // last key in the batch that falls in that file
824
0
    while (batch_iter_ != current_level_range_.end() &&
825
0
           (fp_ctx_array_[batch_iter_.index()].curr_index_in_curr_level ==
826
0
                curr_file_index ||
827
0
            !file_hit)) {
828
0
      struct FilePickerContext& fp_ctx = fp_ctx_array_[batch_iter_.index()];
829
0
      f = &curr_file_level_->files[fp_ctx.curr_index_in_curr_level];
830
0
      Slice& user_key = batch_iter_->ukey_without_ts;
831
832
      // Do key range filtering of files or/and fractional cascading if:
833
      // (1) not all the files are in level 0, or
834
      // (2) there are more than 3 current level files
835
      // If there are only 3 or less current level files in the system, we
836
      // skip the key range filtering. In this case, more likely, the system
837
      // is highly tuned to minimize number of tables queried by each query,
838
      // so it is unlikely that key range filtering is more efficient than
839
      // querying the files.
840
0
      if (num_levels_ > 1 || curr_file_level_->num_files > 3) {
841
        // Check if key is within a file's range. If search left bound and
842
        // right bound point to the same find, we are sure key falls in
843
        // range.
844
0
        cmp_smallest = user_comparator_->CompareWithoutTimestamp(
845
0
            user_key, false, ExtractUserKey(f->smallest_key), true);
846
847
0
        assert(curr_level_ == 0 ||
848
0
               fp_ctx.curr_index_in_curr_level ==
849
0
                   fp_ctx.start_index_in_curr_level ||
850
0
               cmp_smallest <= 0);
851
852
0
        if (cmp_smallest >= 0) {
853
0
          cmp_largest = user_comparator_->CompareWithoutTimestamp(
854
0
              user_key, false, ExtractUserKey(f->largest_key), true);
855
0
        } else {
856
0
          cmp_largest = -1;
857
0
        }
858
859
        // Setup file search bound for the next level based on the
860
        // comparison results
861
0
        if (curr_level_ > 0) {
862
0
          file_indexer_->GetNextLevelIndex(
863
0
              curr_level_, fp_ctx.curr_index_in_curr_level, cmp_smallest,
864
0
              cmp_largest, &fp_ctx.search_left_bound,
865
0
              &fp_ctx.search_right_bound);
866
0
        }
867
        // Key falls out of current file's range
868
0
        if (cmp_smallest < 0 || cmp_largest > 0) {
869
0
          next_file_range->SkipKey(batch_iter_);
870
0
        } else {
871
0
          file_hit = true;
872
0
        }
873
0
      } else {
874
0
        file_hit = true;
875
0
      }
876
0
      if (cmp_largest == 0) {
877
        // cmp_largest is 0, which means the next key will not be in this
878
        // file, so stop looking further. However, its possible there are
879
        // duplicates in the batch, so find the upper bound for the batch
880
        // in this file (upper_key_) by skipping past the duplicates. We
881
        // leave batch_iter_ as is since we may have to pick up from there
882
        // for the next file, if this file has a merge value rather than
883
        // final value
884
0
        upper_key_ = batch_iter_;
885
0
        ++upper_key_;
886
0
        while (upper_key_ != current_level_range_.end() &&
887
0
               user_comparator_->CompareWithoutTimestamp(
888
0
                   batch_iter_->ukey_without_ts, false,
889
0
                   upper_key_->ukey_without_ts, false) == 0) {
890
0
          if (curr_level_ > 0) {
891
0
            struct FilePickerContext& ctx = fp_ctx_array_[upper_key_.index()];
892
0
            file_indexer_->GetNextLevelIndex(
893
0
                curr_level_, ctx.curr_index_in_curr_level, cmp_smallest,
894
0
                cmp_largest, &ctx.search_left_bound, &ctx.search_right_bound);
895
0
          }
896
0
          ++upper_key_;
897
0
        }
898
0
        break;
899
0
      } else {
900
0
        if (curr_level_ == 0) {
901
          // We need to look through all files in level 0
902
0
          ++fp_ctx.curr_index_in_curr_level;
903
0
        }
904
0
        ++batch_iter_;
905
0
      }
906
0
      if (!file_hit) {
907
0
        curr_file_index =
908
0
            (batch_iter_ != current_level_range_.end())
909
0
                ? fp_ctx_array_[batch_iter_.index()].curr_index_in_curr_level
910
0
                : curr_file_level_->num_files;
911
0
      }
912
0
    }
913
914
0
    *fd = f;
915
0
    *file_index = curr_file_index;
916
0
    *is_last_key_in_file = cmp_largest == 0;
917
0
    if (!*is_last_key_in_file) {
918
      // If the largest key in the batch overlapping the file is not the
919
      // largest key in the file, upper_ley_ would not have been updated so
920
      // update it here
921
0
      upper_key_ = batch_iter_;
922
0
    }
923
0
    return file_hit;
924
0
  }
925
926
  // Setup local variables to search next level.
927
  // Returns false if there are no more levels to search.
928
0
  bool PrepareNextLevel() {
929
0
    if (curr_level_ == 0) {
930
0
      MultiGetRange::Iterator mget_iter = current_level_range_.begin();
931
0
      if (fp_ctx_array_[mget_iter.index()].curr_index_in_curr_level <
932
0
          curr_file_level_->num_files) {
933
0
        batch_iter_prev_ = current_level_range_.begin();
934
0
        upper_key_ = batch_iter_ = current_level_range_.begin();
935
0
        return true;
936
0
      }
937
0
    }
938
939
0
    curr_level_++;
940
    // Reset key range to saved value
941
0
    while (curr_level_ < num_levels_) {
942
0
      bool level_contains_keys = false;
943
0
      curr_file_level_ = &(*level_files_brief_)[curr_level_];
944
0
      if (curr_file_level_->num_files == 0) {
945
        // When current level is empty, the search bound generated from upper
946
        // level must be [0, -1] or [0, FileIndexer::kLevelMaxIndex] if it is
947
        // also empty.
948
949
0
        for (auto mget_iter = current_level_range_.begin();
950
0
             mget_iter != current_level_range_.end(); ++mget_iter) {
951
0
          struct FilePickerContext& fp_ctx = fp_ctx_array_[mget_iter.index()];
952
953
0
          assert(fp_ctx.search_left_bound == 0);
954
0
          assert(fp_ctx.search_right_bound == -1 ||
955
0
                 fp_ctx.search_right_bound == FileIndexer::kLevelMaxIndex);
956
          // Since current level is empty, it will need to search all files in
957
          // the next level
958
0
          fp_ctx.search_left_bound = 0;
959
0
          fp_ctx.search_right_bound = FileIndexer::kLevelMaxIndex;
960
0
        }
961
        // Skip all subsequent empty levels
962
0
        do {
963
0
          ++curr_level_;
964
0
        } while ((curr_level_ < num_levels_) &&
965
0
                 (*level_files_brief_)[curr_level_].num_files == 0);
966
0
        continue;
967
0
      }
968
969
      // Some files may overlap each other. We find
970
      // all files that overlap user_key and process them in order from
971
      // newest to oldest. In the context of merge-operator, this can occur at
972
      // any level. Otherwise, it only occurs at Level-0 (since Put/Deletes
973
      // are always compacted into a single entry).
974
0
      int32_t start_index = -1;
975
0
      current_level_range_ =
976
0
          MultiGetRange(range_, range_.begin(), range_.end());
977
0
      for (auto mget_iter = current_level_range_.begin();
978
0
           mget_iter != current_level_range_.end(); ++mget_iter) {
979
0
        struct FilePickerContext& fp_ctx = fp_ctx_array_[mget_iter.index()];
980
0
        if (curr_level_ == 0) {
981
          // On Level-0, we read through all files to check for overlap.
982
0
          start_index = 0;
983
0
          level_contains_keys = true;
984
0
        } else {
985
          // On Level-n (n>=1), files are sorted. Binary search to find the
986
          // earliest file whose largest key >= ikey. Search left bound and
987
          // right bound are used to narrow the range.
988
0
          if (fp_ctx.search_left_bound <= fp_ctx.search_right_bound) {
989
0
            if (fp_ctx.search_right_bound == FileIndexer::kLevelMaxIndex) {
990
0
              fp_ctx.search_right_bound =
991
0
                  static_cast<int32_t>(curr_file_level_->num_files) - 1;
992
0
            }
993
            // `search_right_bound_` is an inclusive upper-bound, but since it
994
            // was determined based on user key, it is still possible the lookup
995
            // key falls to the right of `search_right_bound_`'s corresponding
996
            // file. So, pass a limit one higher, which allows us to detect this
997
            // case.
998
0
            Slice& ikey = mget_iter->ikey;
999
0
            start_index = FindFileInRange(
1000
0
                *internal_comparator_, *curr_file_level_, ikey,
1001
0
                static_cast<uint32_t>(fp_ctx.search_left_bound),
1002
0
                static_cast<uint32_t>(fp_ctx.search_right_bound) + 1);
1003
0
            if (start_index == fp_ctx.search_right_bound + 1) {
1004
              // `ikey_` comes after `search_right_bound_`. The lookup key does
1005
              // not exist on this level, so let's skip this level and do a full
1006
              // binary search on the next level.
1007
0
              fp_ctx.search_left_bound = 0;
1008
0
              fp_ctx.search_right_bound = FileIndexer::kLevelMaxIndex;
1009
0
              current_level_range_.SkipKey(mget_iter);
1010
0
              continue;
1011
0
            } else {
1012
0
              level_contains_keys = true;
1013
0
            }
1014
0
          } else {
1015
            // search_left_bound > search_right_bound, key does not exist in
1016
            // this level. Since no comparison is done in this level, it will
1017
            // need to search all files in the next level.
1018
0
            fp_ctx.search_left_bound = 0;
1019
0
            fp_ctx.search_right_bound = FileIndexer::kLevelMaxIndex;
1020
0
            current_level_range_.SkipKey(mget_iter);
1021
0
            continue;
1022
0
          }
1023
0
        }
1024
0
        assert(start_index >= 0);
1025
0
        assert(start_index < static_cast<int32_t>(curr_file_level_->num_files));
1026
0
        fp_ctx.start_index_in_curr_level = start_index;
1027
0
        fp_ctx.curr_index_in_curr_level = start_index;
1028
0
      }
1029
0
      if (level_contains_keys) {
1030
0
        batch_iter_prev_ = current_level_range_.begin();
1031
0
        upper_key_ = batch_iter_ = current_level_range_.begin();
1032
0
        return true;
1033
0
      }
1034
0
      curr_level_++;
1035
0
    }
1036
    // curr_level_ = num_levels_. So, no more levels to search.
1037
0
    return false;
1038
0
  }
1039
};
1040
1041
303k
VersionStorageInfo::~VersionStorageInfo() { delete[] files_; }
1042
1043
303k
Version::~Version() {
1044
303k
  assert(refs_ == 0);
1045
1046
  // Remove from linked list
1047
303k
  prev_->next_ = next_;
1048
303k
  next_->prev_ = prev_;
1049
1050
  // Drop references to files
1051
1.81M
  for (int level = 0; level < storage_info_.num_levels_; level++) {
1052
1.68M
    for (size_t i = 0; i < storage_info_.files_[level].size(); i++) {
1053
173k
      FileMetaData* f = storage_info_.files_[level][i];
1054
173k
      assert(f->refs > 0);
1055
173k
      f->refs--;
1056
173k
      if (f->refs <= 0) {
1057
93.9k
        assert(cfd_ != nullptr);
1058
        // When not in the process of closing the DB, we'll have a superversion
1059
        // to get current mutable options from
1060
93.9k
        auto* sv = cfd_->GetSuperVersion();
1061
93.9k
        uint32_t path_id = f->fd.GetPathId();
1062
93.9k
        assert(path_id < cfd_->ioptions().cf_paths.size());
1063
93.9k
        vset_->obsolete_files_.emplace_back(
1064
93.9k
            f, cfd_->ioptions().cf_paths[path_id].path,
1065
93.9k
            sv ? sv->mutable_cf_options.uncache_aggressiveness : 0,
1066
93.9k
            cfd_->GetFileMetadataCacheReservationManager());
1067
93.9k
      }
1068
173k
    }
1069
1.51M
  }
1070
303k
}
1071
1072
int FindFile(const InternalKeyComparator& icmp,
1073
2.65k
             const LevelFilesBrief& file_level, const Slice& key) {
1074
2.65k
  return FindFileInRange(icmp, file_level, key, 0,
1075
2.65k
                         static_cast<uint32_t>(file_level.num_files));
1076
2.65k
}
1077
1078
void DoGenerateLevelFilesBrief(LevelFilesBrief* file_level,
1079
                               const std::vector<FileMetaData*>& files,
1080
223k
                               Arena* arena) {
1081
223k
  assert(file_level);
1082
223k
  assert(arena);
1083
1084
223k
  size_t num = files.size();
1085
223k
  file_level->num_files = num;
1086
223k
  char* mem = arena->AllocateAligned(num * sizeof(FdWithKeyRange));
1087
223k
  file_level->files = new (mem) FdWithKeyRange[num];
1088
1089
414k
  for (size_t i = 0; i < num; i++) {
1090
190k
    Slice smallest_key = files[i]->smallest.Encode();
1091
190k
    Slice largest_key = files[i]->largest.Encode();
1092
1093
    // Copy key slice to sequential memory
1094
190k
    size_t smallest_size = smallest_key.size();
1095
190k
    size_t largest_size = largest_key.size();
1096
190k
    mem = arena->AllocateAligned(smallest_size + largest_size);
1097
190k
    memcpy(mem, smallest_key.data(), smallest_size);
1098
190k
    memcpy(mem + smallest_size, largest_key.data(), largest_size);
1099
1100
190k
    FdWithKeyRange& f = file_level->files[i];
1101
190k
    f.fd = files[i]->fd;
1102
190k
    f.file_metadata = files[i];
1103
190k
    f.smallest_key = Slice(mem, smallest_size);
1104
190k
    f.largest_key = Slice(mem + smallest_size, largest_size);
1105
190k
  }
1106
223k
}
1107
1108
static bool AfterFile(const Comparator* ucmp, const Slice* user_key,
1109
3.00k
                      const FdWithKeyRange* f) {
1110
  // nullptr user_key occurs before all keys and is therefore never after *f
1111
3.00k
  return (user_key != nullptr &&
1112
3.00k
          ucmp->CompareWithoutTimestamp(*user_key,
1113
3.00k
                                        ExtractUserKey(f->largest_key)) > 0);
1114
3.00k
}
1115
1116
static bool BeforeFile(const Comparator* ucmp, const Slice* user_key,
1117
2.94k
                       const FdWithKeyRange* f) {
1118
  // nullptr user_key occurs after all keys and is therefore never before *f
1119
2.94k
  return (user_key != nullptr &&
1120
2.94k
          ucmp->CompareWithoutTimestamp(*user_key,
1121
2.94k
                                        ExtractUserKey(f->smallest_key)) < 0);
1122
2.94k
}
1123
1124
bool SomeFileOverlapsRange(const InternalKeyComparator& icmp,
1125
                           bool disjoint_sorted_files,
1126
                           const LevelFilesBrief& file_level,
1127
                           const Slice* smallest_user_key,
1128
0
                           const Slice* largest_user_key) {
1129
0
  const Comparator* ucmp = icmp.user_comparator();
1130
0
  if (!disjoint_sorted_files) {
1131
    // Need to check against all files
1132
0
    for (size_t i = 0; i < file_level.num_files; i++) {
1133
0
      const FdWithKeyRange* f = &(file_level.files[i]);
1134
0
      if (AfterFile(ucmp, smallest_user_key, f) ||
1135
0
          BeforeFile(ucmp, largest_user_key, f)) {
1136
        // No overlap
1137
0
      } else {
1138
0
        return true;  // Overlap
1139
0
      }
1140
0
    }
1141
0
    return false;
1142
0
  }
1143
1144
  // Binary search over file list
1145
0
  uint32_t index = 0;
1146
0
  if (smallest_user_key != nullptr) {
1147
    // Find the leftmost possible internal key for smallest_user_key
1148
0
    InternalKey small;
1149
0
    small.SetMinPossibleForUserKey(*smallest_user_key);
1150
0
    index = FindFile(icmp, file_level, small.Encode());
1151
0
  }
1152
1153
0
  if (index >= file_level.num_files) {
1154
    // beginning of range is after all files, so no overlap.
1155
0
    return false;
1156
0
  }
1157
1158
0
  return !BeforeFile(ucmp, largest_user_key, &file_level.files[index]);
1159
0
}
1160
1161
namespace {
1162
1163
TableCacheOpenOptions GetCompactionTableCacheOpenOptions(
1164
8.15k
    bool open_ephemeral_table_reader) {
1165
8.15k
  TableCacheOpenOptions options;
1166
8.15k
  options.open_ephemeral_table_reader = open_ephemeral_table_reader;
1167
8.15k
  options.avoid_shared_metadata_cache = open_ephemeral_table_reader;
1168
8.15k
  options.skip_filters = open_ephemeral_table_reader;
1169
8.15k
  return options;
1170
8.15k
}
1171
1172
class LevelIterator final : public InternalIterator {
1173
 public:
1174
  // NOTE: many of the const& parameters are saved in this object (so
1175
  // must outlive this object)
1176
  LevelIterator(
1177
      TableCache* table_cache, const ReadOptions& read_options,
1178
      const FileOptions& file_options, const InternalKeyComparator& icomparator,
1179
      const LevelFilesBrief* flevel, const MutableCFOptions& mutable_cf_options,
1180
      HistogramImpl* file_read_hist, TableReaderCaller caller,
1181
      bool skip_filters, int level, RangeDelAggregator* range_del_agg,
1182
      const std::vector<AtomicCompactionUnitBoundary>* compaction_boundaries =
1183
          nullptr,
1184
      bool allow_unprepared_value = false,
1185
      std::unique_ptr<TruncatedRangeDelIterator>*** range_tombstone_iter_ptr_ =
1186
          nullptr,
1187
      SequenceNumber read_seq = kMaxSequenceNumber,
1188
      Statistics* db_statistics = nullptr, SystemClock* clock = nullptr,
1189
      bool open_ephemeral_table_reader = false)
1190
5.38k
      : table_cache_(table_cache),
1191
5.38k
        read_options_(read_options),
1192
5.38k
        file_options_(file_options),
1193
5.38k
        icomparator_(icomparator),
1194
5.38k
        user_comparator_(icomparator.user_comparator()),
1195
5.38k
        flevel_(flevel),
1196
5.38k
        mutable_cf_options_(mutable_cf_options),
1197
5.38k
        prefix_extractor_(mutable_cf_options.prefix_extractor.get()),
1198
5.38k
        file_read_hist_(file_read_hist),
1199
5.38k
        caller_(caller),
1200
5.38k
        file_index_(flevel_->num_files),
1201
5.38k
        range_del_agg_(range_del_agg),
1202
5.38k
        pinned_iters_mgr_(nullptr),
1203
5.38k
        compaction_boundaries_(compaction_boundaries),
1204
5.38k
        range_tombstone_iter_(nullptr),
1205
5.38k
        range_tombstone_iter_required_(range_tombstone_iter_ptr_ != nullptr),
1206
5.38k
        read_seq_(read_seq != kMaxSequenceNumber ||
1207
2.91k
                          read_options.snapshot == nullptr
1208
5.38k
                      ? read_seq
1209
5.38k
                      : read_options.snapshot->GetSequenceNumber()),
1210
5.38k
        level_(level),
1211
5.38k
        skip_filters_(skip_filters),
1212
5.38k
        allow_unprepared_value_(allow_unprepared_value),
1213
5.38k
        is_next_read_sequential_(false),
1214
5.38k
        to_return_sentinel_(false),
1215
5.38k
        scan_opts_(nullptr),
1216
5.38k
        db_statistics_(db_statistics),
1217
5.38k
        clock_(clock),
1218
        table_cache_open_options_(
1219
5.38k
            GetCompactionTableCacheOpenOptions(open_ephemeral_table_reader)),
1220
        // Same condition under which a block-based table iterator checks the
1221
        // prefix filter on Seek(). TableCache hands out no reference to an
1222
        // ephemeral table reader, which a stand-in would need.
1223
        files_filterable_on_seek_(
1224
5.38k
            !skip_filters_ && !table_cache_open_options_.skip_filters &&
1225
5.38k
            !table_cache_open_options_.open_ephemeral_table_reader &&
1226
5.38k
            GetIteratorPrefixMode(read_options, prefix_extractor_ != nullptr) !=
1227
5.38k
                IteratorPrefixMode::kTotalOrder) {
1228
    // Empty level is not supported.
1229
5.38k
    assert(flevel_ != nullptr && flevel_->num_files > 0);
1230
5.38k
    if (range_tombstone_iter_ptr_) {
1231
4.51k
      *range_tombstone_iter_ptr_ = &range_tombstone_iter_;
1232
4.51k
    }
1233
5.38k
  }
1234
1235
  // No copying allowed
1236
  LevelIterator(const LevelIterator&) = delete;
1237
  LevelIterator& operator=(const LevelIterator&) = delete;
1238
  LevelIterator(LevelIterator&&) = delete;
1239
  LevelIterator& operator=(LevelIterator&&) = delete;
1240
1241
5.38k
  ~LevelIterator() override {
1242
5.38k
    delete file_iter_.Set(nullptr);
1243
    // Clean up any prepared iterators that weren't used
1244
5.38k
    for (auto& entry : prepared_iters_) {
1245
0
      delete entry.second;
1246
0
    }
1247
5.38k
    prepared_iters_.clear();
1248
5.38k
    assert(prepared_iters_.size() == 0);
1249
5.38k
  }
1250
1251
  // Seek to the first file with a key >= target.
1252
  // If range_tombstone_iter_ is not nullptr, then we pretend that file
1253
  // boundaries are fake keys (sentinel keys). These keys are used to keep range
1254
  // tombstones alive even when all point keys in an SST file are exhausted.
1255
  // These sentinel keys will be skipped in merging iterator.
1256
  void Seek(const Slice& target) override;
1257
  void SeekForPrev(const Slice& target) override;
1258
  void SeekToFirst() override;
1259
  void SeekToLast() override;
1260
  void Next() final;
1261
  bool NextAndGetResult(IterateResult* result) override;
1262
  void Prev() override;
1263
1264
  // In addition to valid and invalid state (!file_iter.Valid() and
1265
  // status.ok()), a third state of the iterator is when !file_iter_.Valid() and
1266
  // to_return_sentinel_. This means we are at the end of a file, and a sentinel
1267
  // key (the file boundary that we pretend as a key) is to be returned next.
1268
  // file_iter_.Valid() and to_return_sentinel_ should not both be true.
1269
24.7k
  bool Valid() const override {
1270
24.7k
    assert(!(file_iter_.Valid() && to_return_sentinel_));
1271
24.7k
    return file_iter_.Valid() || to_return_sentinel_;
1272
24.7k
  }
1273
14.5k
  Slice key() const override {
1274
14.5k
    assert(Valid());
1275
14.5k
    if (to_return_sentinel_) {
1276
      // Sentinel should be returned after file_iter_ reaches the end of the
1277
      // file
1278
2.62k
      assert(!file_iter_.Valid());
1279
2.62k
      return sentinel_;
1280
2.62k
    }
1281
11.8k
    return file_iter_.key();
1282
14.5k
  }
1283
1284
5.41k
  Slice value() const override {
1285
5.41k
    assert(Valid());
1286
5.41k
    assert(!to_return_sentinel_);
1287
5.41k
    return file_iter_.value();
1288
5.41k
  }
1289
1290
2.44k
  uint64_t write_unix_time() const override {
1291
2.44k
    assert(Valid());
1292
2.44k
    return file_iter_.write_unix_time();
1293
2.44k
  }
1294
1295
9.24k
  Status status() const override {
1296
9.24k
    return file_iter_.iter() ? file_iter_.status() : Status::OK();
1297
9.24k
  }
1298
1299
3.13k
  bool PrepareValue() override { return file_iter_.PrepareValue(); }
1300
1301
0
  inline bool MayBeOutOfLowerBound() override {
1302
0
    assert(Valid());
1303
0
    return may_be_out_of_lower_bound_ && file_iter_.MayBeOutOfLowerBound();
1304
0
  }
1305
1306
0
  inline IterBoundCheck UpperBoundCheckResult() override {
1307
0
    if (Valid()) {
1308
0
      return file_iter_.UpperBoundCheckResult();
1309
0
    } else {
1310
0
      return IterBoundCheck::kUnknown;
1311
0
    }
1312
0
  }
1313
1314
2.46k
  void SetPinnedItersMgr(PinnedIteratorsManager* pinned_iters_mgr) override {
1315
2.46k
    pinned_iters_mgr_ = pinned_iters_mgr;
1316
2.46k
    if (file_iter_.iter()) {
1317
0
      file_iter_.SetPinnedItersMgr(pinned_iters_mgr);
1318
0
    }
1319
2.46k
  }
1320
1321
34
  bool IsKeyPinned() const override {
1322
34
    return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
1323
34
           file_iter_.iter() && file_iter_.IsKeyPinned();
1324
34
  }
1325
1326
1.81k
  bool IsValuePinned() const override {
1327
1.81k
    return pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
1328
1.81k
           file_iter_.iter() && file_iter_.IsValuePinned();
1329
1.81k
  }
1330
1331
19.7k
  bool IsDeleteRangeSentinelKey() const override { return to_return_sentinel_; }
1332
1333
0
  void SetRangeDelReadSeqno(SequenceNumber read_seq) override {
1334
0
    read_seq_ = read_seq;
1335
0
  }
1336
1337
8.88k
  inline bool FileHasMultiScanArg(size_t file_index) {
1338
8.88k
    if (file_to_scan_opts_.get()) {
1339
0
      auto it = file_to_scan_opts_->find(file_index);
1340
0
      if (it != file_to_scan_opts_->end()) {
1341
0
        return !it->second.empty();
1342
0
      }
1343
0
    }
1344
8.88k
    return false;
1345
8.88k
  }
1346
1347
0
  MultiScanArgs& GetMultiScanArgForFile(size_t file_index) {
1348
0
    auto multi_scan_args_it = file_to_scan_opts_->find(file_index);
1349
0
    if (multi_scan_args_it == file_to_scan_opts_->end()) {
1350
0
      auto ret = file_to_scan_opts_->emplace(
1351
0
          file_index, MultiScanArgs(user_comparator_.user_comparator()));
1352
0
      multi_scan_args_it = ret.first;
1353
0
      assert(ret.second);
1354
0
    }
1355
0
    return multi_scan_args_it->second;
1356
0
  }
1357
1358
0
  void Prepare(const MultiScanArgs* so) override {
1359
    // We assume here that scan_opts is sorted such that
1360
    // scan_opts[0].range.start < scan_opts[1].range.start, and non
1361
    // overlapping
1362
0
    if (so == nullptr) {
1363
0
      return;
1364
0
    }
1365
1366
0
    scan_opts_ = so;
1367
1368
    // Verify comparator is consistent
1369
0
    assert(so->GetComparator() == user_comparator_.user_comparator());
1370
1371
0
    file_to_scan_opts_ = std::make_unique<ScanOptionsMap>();
1372
0
    ForEachMultiScanOverlappingFile(
1373
0
        icomparator_, user_comparator_, *flevel_, level_, *scan_opts_,
1374
0
        [&](size_t file_index, const ScanOptions& scan_range) {
1375
0
          auto const metadata = flevel_->files[file_index].file_metadata;
1376
0
          if (metadata->FileIsStandAloneRangeTombstone()) {
1377
            // Skip stand alone range deletion files.
1378
0
            return;
1379
0
          }
1380
0
          auto& args = GetMultiScanArgForFile(file_index);
1381
0
          args.insert(scan_range.range.start.value(),
1382
0
                      scan_range.range.limit.value(), scan_range.property_bag);
1383
0
        });
1384
1385
0
    StopWatch timer(clock_, db_statistics_, MULTISCAN_PREPARE_ITERATORS);
1386
1387
    // Propagate multiscan configs
1388
0
    for (auto& file_to_arg : *file_to_scan_opts_) {
1389
0
      file_to_arg.second.CopyConfigFrom(*so);
1390
0
      assert(OverlapRange(*file_to_arg.second.GetScanRanges().begin(),
1391
0
                          file_to_arg.first) &&
1392
0
             OverlapRange(*file_to_arg.second.GetScanRanges().rbegin(),
1393
0
                          file_to_arg.first));
1394
0
    }
1395
1396
0
    if (so->use_async_io &&
1397
0
        (!range_tombstone_iter_required_ || range_tombstone_iter_ != nullptr)) {
1398
0
      auto before = file_index_;
1399
      // Pre-create and prepare only relevant file iterators
1400
0
      for (auto& file_to_arg : *file_to_scan_opts_) {
1401
0
        size_t file_index = file_to_arg.first;
1402
1403
0
        file_index_ = file_index;
1404
        // Create iterator for this file
1405
0
        auto iter = NewFileIterator(/*seek_target=*/nullptr,
1406
0
                                    /*prefix_filtered_table=*/nullptr);
1407
0
        if (iter != nullptr) {
1408
          // If we have async enabled, lets prepare all our iterators.
1409
0
          iter->Prepare(&file_to_arg.second);
1410
          // Store the prepared iterator
1411
0
          prepared_iters_[file_index] = iter;
1412
0
        }
1413
0
      }
1414
0
      file_index_ = before;
1415
0
    }
1416
0
  }
1417
1418
 private:
1419
  // Return true if at least one invalid file is seen and skipped.
1420
  bool SkipEmptyFileForward();
1421
  void SkipEmptyFileBackward();
1422
  void SetFileIterator(InternalIterator* iter);
1423
  void InitFileIterator(size_t new_file_index, const Slice* seek_target);
1424
  void RetryPrefixFilteredFile(const Slice* seek_target);
1425
1426
9.10k
  const Slice& file_smallest_key(size_t file_index) {
1427
9.10k
    assert(file_index < flevel_->num_files);
1428
9.10k
    return flevel_->files[file_index].smallest_key;
1429
9.10k
  }
1430
1431
9.89k
  const Slice& file_largest_key(size_t file_index) {
1432
9.89k
    assert(file_index < flevel_->num_files);
1433
9.89k
    return flevel_->files[file_index].largest_key;
1434
9.89k
  }
1435
1436
2.02k
  bool KeyReachedUpperBound(const Slice& internal_key) {
1437
2.02k
    return read_options_.iterate_upper_bound != nullptr &&
1438
0
           user_comparator_.CompareWithoutTimestamp(
1439
0
               ExtractUserKey(internal_key), /*a_has_ts=*/true,
1440
0
               *read_options_.iterate_upper_bound, /*b_has_ts=*/false) >= 0;
1441
2.02k
  }
1442
1443
  template <bool IsSeek>
1444
20.4k
  void SampleRead() {
1445
20.4k
    bool sampled =
1446
20.4k
        IsSeek ? should_sample_file_read() : should_sample_file_read_next();
1447
20.4k
    if (!sampled) {
1448
20.4k
      return;
1449
20.4k
    }
1450
1451
7
    if (file_index_ >= flevel_->num_files || !file_iter_.Valid()) {
1452
0
      return;
1453
0
    }
1454
7
    const FileMetaData* meta = flevel_->files[file_index_].file_metadata;
1455
7
    sample_file_read_inc(meta);
1456
7
    ValueType type = ExtractValueType(file_iter_.key());
1457
7
    if (type == kTypeDeletion || type == kTypeSingleDeletion ||
1458
6
        type == kTypeDeletionWithTimestamp || type == kTypeMerge) {
1459
1
      sample_collapsible_entry_file_read_inc(meta);
1460
1
    }
1461
7
  }
version_set.cc:void rocksdb::(anonymous namespace)::LevelIterator::SampleRead<true>()
Line
Count
Source
1444
5.39k
  void SampleRead() {
1445
5.39k
    bool sampled =
1446
5.39k
        IsSeek ? should_sample_file_read() : should_sample_file_read_next();
1447
5.39k
    if (!sampled) {
1448
5.38k
      return;
1449
5.38k
    }
1450
1451
7
    if (file_index_ >= flevel_->num_files || !file_iter_.Valid()) {
1452
0
      return;
1453
0
    }
1454
7
    const FileMetaData* meta = flevel_->files[file_index_].file_metadata;
1455
7
    sample_file_read_inc(meta);
1456
7
    ValueType type = ExtractValueType(file_iter_.key());
1457
7
    if (type == kTypeDeletion || type == kTypeSingleDeletion ||
1458
6
        type == kTypeDeletionWithTimestamp || type == kTypeMerge) {
1459
1
      sample_collapsible_entry_file_read_inc(meta);
1460
1
    }
1461
7
  }
version_set.cc:void rocksdb::(anonymous namespace)::LevelIterator::SampleRead<false>()
Line
Count
Source
1444
15.0k
  void SampleRead() {
1445
15.0k
    bool sampled =
1446
15.0k
        IsSeek ? should_sample_file_read() : should_sample_file_read_next();
1447
15.0k
    if (!sampled) {
1448
15.0k
      return;
1449
15.0k
    }
1450
1451
0
    if (file_index_ >= flevel_->num_files || !file_iter_.Valid()) {
1452
0
      return;
1453
0
    }
1454
0
    const FileMetaData* meta = flevel_->files[file_index_].file_metadata;
1455
0
    sample_file_read_inc(meta);
1456
0
    ValueType type = ExtractValueType(file_iter_.key());
1457
0
    if (type == kTypeDeletion || type == kTypeSingleDeletion ||
1458
0
        type == kTypeDeletionWithTimestamp || type == kTypeMerge) {
1459
0
      sample_collapsible_entry_file_read_inc(meta);
1460
0
    }
1461
0
  }
1462
1463
13.1k
  void ClearRangeTombstoneIter() {
1464
13.1k
    if (range_tombstone_iter_) {
1465
12.3k
      range_tombstone_iter_->reset();
1466
12.3k
    }
1467
13.1k
  }
1468
1469
  // Move file_iter_ to the file at file_index_.
1470
  // range_tombstone_iter_ is updated with a range tombstone iterator
1471
  // into the new file. Old range tombstone iterator is cleared.
1472
  // If seek_target is non-null, the caller seeks the returned iterator to it
1473
  // next. When the file's prefix filter rules that seek out, the returned
1474
  // iterator is an empty stand-in and *prefix_filtered_table is set to the
1475
  // file's table reader, which the stand-in keeps alive. Otherwise
1476
  // *prefix_filtered_table is left unchanged.
1477
  InternalIterator* NewFileIterator(const Slice* seek_target,
1478
8.88k
                                    TableReader** prefix_filtered_table) {
1479
8.88k
    assert(file_index_ < flevel_->num_files);
1480
8.88k
    auto file_meta = flevel_->files[file_index_];
1481
1482
8.88k
    const InternalKey* smallest_compaction_key = nullptr;
1483
8.88k
    const InternalKey* largest_compaction_key = nullptr;
1484
8.88k
    if (compaction_boundaries_ != nullptr) {
1485
3.05k
      smallest_compaction_key = (*compaction_boundaries_)[file_index_].smallest;
1486
3.05k
      largest_compaction_key = (*compaction_boundaries_)[file_index_].largest;
1487
3.05k
    }
1488
    // TableCache hands out no reference to an ephemeral table reader.
1489
8.88k
    assert(seek_target == nullptr ||
1490
8.88k
           !table_cache_open_options_.open_ephemeral_table_reader);
1491
8.88k
    CheckMayBeOutOfLowerBound();
1492
8.88k
    ClearRangeTombstoneIter();
1493
8.88k
    return table_cache_->NewIterator(
1494
8.88k
        read_options_, file_options_, icomparator_, *file_meta.file_metadata,
1495
8.88k
        range_del_agg_, mutable_cf_options_,
1496
8.88k
        nullptr /* don't need reference to table */, file_read_hist_, caller_,
1497
8.88k
        /*arena=*/nullptr, skip_filters_, level_,
1498
8.88k
        /*max_file_size_for_l0_meta_pin=*/0, smallest_compaction_key,
1499
8.88k
        largest_compaction_key, allow_unprepared_value_, seek_target,
1500
8.88k
        prefix_filtered_table, &read_seq_, range_tombstone_iter_,
1501
        /*maybe_pin_table_handle=*/
1502
8.88k
        !table_cache_open_options_.open_ephemeral_table_reader,
1503
8.88k
        /*file_open_metadata=*/nullptr, table_cache_open_options_);
1504
8.88k
  }
1505
1506
  // Check if current file being fully within iterate_lower_bound.
1507
  //
1508
  // Note MyRocks may update iterate bounds between seek. To workaround it,
1509
  // we need to check and update may_be_out_of_lower_bound_ accordingly.
1510
14.6k
  void CheckMayBeOutOfLowerBound() {
1511
14.6k
    if (read_options_.iterate_lower_bound != nullptr &&
1512
0
        file_index_ < flevel_->num_files) {
1513
0
      may_be_out_of_lower_bound_ =
1514
0
          user_comparator_.CompareWithoutTimestamp(
1515
0
              ExtractUserKey(file_smallest_key(file_index_)), /*a_has_ts=*/true,
1516
0
              *read_options_.iterate_lower_bound, /*b_has_ts=*/false) < 0;
1517
0
    }
1518
14.6k
  }
1519
1520
#ifndef NDEBUG
1521
  bool OverlapRange(const ScanOptions& opts, size_t file_index);
1522
#endif
1523
1524
  TableCache* table_cache_;
1525
  const ReadOptions& read_options_;
1526
  const FileOptions& file_options_;
1527
  const InternalKeyComparator& icomparator_;
1528
  const UserComparatorWrapper user_comparator_;
1529
  const LevelFilesBrief* flevel_;
1530
  mutable FileDescriptor current_value_;
1531
  const MutableCFOptions& mutable_cf_options_;
1532
  const SliceTransform* prefix_extractor_;
1533
1534
  HistogramImpl* file_read_hist_;
1535
  TableReaderCaller caller_;
1536
  size_t file_index_;
1537
  RangeDelAggregator* range_del_agg_;
1538
  IteratorWrapper file_iter_;  // May be nullptr
1539
  // Non-null while file_iter_ is an empty stand-in for the file at
1540
  // file_index_, because that file's prefix filter ruled out the last Seek()
1541
  // target. Points to the file's table reader so that later seeks into the
1542
  // same file can check the filter again without the table cache.
1543
  TableReader* prefix_filtered_table_ = nullptr;
1544
  PinnedIteratorsManager* pinned_iters_mgr_;
1545
1546
  // To be propagated to RangeDelAggregator in order to safely truncate range
1547
  // tombstones.
1548
  const std::vector<AtomicCompactionUnitBoundary>* compaction_boundaries_;
1549
1550
  // This is set when this level iterator is used under a merging iterator
1551
  // that processes range tombstones. range_tombstone_iter_ points to where the
1552
  // merging iterator stores the range tombstones iterator for this level. When
1553
  // this level iterator moves to a new SST file, it updates the range
1554
  // tombstones accordingly through this pointer. So the merging iterator always
1555
  // has access to the current SST file's range tombstones.
1556
  //
1557
  // The level iterator treats file boundary as fake keys (sentinel keys) to
1558
  // keep range tombstones alive if needed and make upper level, i.e. merging
1559
  // iterator, aware of file changes (when level iterator moves to a new SST
1560
  // file, there is some bookkeeping work that needs to be done at merging
1561
  // iterator end).
1562
  //
1563
  // *range_tombstone_iter_ points to range tombstones of the current SST file
1564
  std::unique_ptr<TruncatedRangeDelIterator>* range_tombstone_iter_;
1565
  bool range_tombstone_iter_required_;
1566
1567
  // The sentinel key to be returned
1568
  Slice sentinel_;
1569
  SequenceNumber read_seq_;
1570
1571
  int level_;
1572
  bool skip_filters_;
1573
  bool allow_unprepared_value_;
1574
  bool may_be_out_of_lower_bound_ = true;
1575
  bool is_next_read_sequential_;
1576
  // Set in Seek() when a prefix seek reaches end of the current file,
1577
  // and the next file has a different prefix. SkipEmptyFileForward()
1578
  // will not move to next file when this flag is set.
1579
  bool prefix_exhausted_ = false;
1580
  // Whether next/prev key is a sentinel key.
1581
  bool to_return_sentinel_ = false;
1582
  const MultiScanArgs* scan_opts_ = nullptr;
1583
1584
  Statistics* db_statistics_ = nullptr;
1585
  SystemClock* clock_ = nullptr;
1586
  TableCacheOpenOptions table_cache_open_options_;
1587
  // Whether Seek() lets a file's prefix filter rule the seek out before the
1588
  // file's iterator is built.
1589
  const bool files_filterable_on_seek_;
1590
1591
  // Our stored scan_opts for each prefix
1592
  std::unique_ptr<ScanOptionsMap> file_to_scan_opts_ = nullptr;
1593
1594
  // Map to store pre-created iterators by file index
1595
  std::unordered_map<size_t, InternalIterator*> prepared_iters_;
1596
1597
  // Sets flags for if we should return the sentinel key next.
1598
  // The condition for returning sentinel is reaching the end of current
1599
  // file_iter_: !Valid() && status.().ok().
1600
  void TrySetDeleteRangeSentinel(const Slice& boundary_key);
1601
12.9k
  void ClearSentinel() { to_return_sentinel_ = false; }
1602
};
1603
1604
15.1k
void LevelIterator::TrySetDeleteRangeSentinel(const Slice& boundary_key) {
1605
15.1k
  assert(range_tombstone_iter_);
1606
15.1k
  if (file_iter_.iter() != nullptr && !file_iter_.Valid() &&
1607
7.23k
      file_iter_.status().ok()) {
1608
7.23k
    to_return_sentinel_ = true;
1609
7.23k
    sentinel_ = boundary_key;
1610
7.23k
  }
1611
15.1k
}
1612
1613
1.07k
void LevelIterator::Seek(const Slice& target) {
1614
1.07k
  prefix_exhausted_ = false;
1615
1.07k
  ClearSentinel();
1616
  // A new file iterator is sought to target right away, so its file's prefix
1617
  // filter can rule the seek out before the iterator is built. MultiScan
1618
  // positions files through their prepared ranges instead.
1619
1.07k
  const Slice* seek_target =
1620
1.07k
      files_filterable_on_seek_ && scan_opts_ == nullptr ? &target : nullptr;
1621
  // Check whether the seek key fall under the same file
1622
1.07k
  bool need_to_reseek = true;
1623
1.07k
  if (file_iter_.iter() != nullptr && file_index_ < flevel_->num_files) {
1624
72
    const FdWithKeyRange& cur_file = flevel_->files[file_index_];
1625
72
    if (icomparator_.InternalKeyComparator::Compare(
1626
72
            target, cur_file.largest_key) <= 0 &&
1627
20
        icomparator_.InternalKeyComparator::Compare(
1628
20
            target, cur_file.smallest_key) >= 0) {
1629
10
      need_to_reseek = false;
1630
10
      assert(static_cast<size_t>(FindFile(icomparator_, *flevel_, target)) ==
1631
10
             file_index_);
1632
10
    }
1633
72
  }
1634
1.07k
  if (need_to_reseek) {
1635
1.06k
    TEST_SYNC_POINT("LevelIterator::Seek:BeforeFindFile");
1636
1.06k
    size_t new_file_index = FindFile(icomparator_, *flevel_, target);
1637
1.06k
    InitFileIterator(new_file_index, seek_target);
1638
1.06k
  } else if (prefix_filtered_table_ != nullptr) {
1639
0
    InitFileIterator(file_index_, seek_target);
1640
0
  }
1641
1642
1.07k
  if (file_iter_.iter() != nullptr) {
1643
1.01k
    if (scan_opts_) {
1644
      // At this point, we only know that the seek target is < largest_key
1645
      // in the file. We need to check whether there is actual overlap.
1646
0
      const FdWithKeyRange& cur_file = flevel_->files[file_index_];
1647
0
      if (KeyReachedUpperBound(cur_file.smallest_key)) {
1648
0
        return;
1649
0
      }
1650
0
    }
1651
1.01k
    file_iter_.Seek(target);
1652
    // Status::TryAgain indicates asynchronous request for retrieval of data
1653
    // blocks has been submitted. So it should return at this point and Seek
1654
    // should be called again to retrieve the requested block and execute the
1655
    // remaining code.
1656
1.01k
    if (file_iter_.status() == Status::TryAgain()) {
1657
0
      return;
1658
0
    }
1659
1.01k
    if (!file_iter_.Valid() && file_iter_.status().ok() &&
1660
0
        prefix_extractor_ != nullptr && !read_options_.total_order_seek &&
1661
0
        !read_options_.auto_prefix_mode &&
1662
0
        file_index_ < flevel_->num_files - 1) {
1663
0
      size_t ts_sz = user_comparator_.user_comparator()->timestamp_size();
1664
0
      Slice target_user_key_without_ts =
1665
0
          ExtractUserKeyAndStripTimestamp(target, ts_sz);
1666
0
      Slice next_file_first_user_key_without_ts =
1667
0
          ExtractUserKeyAndStripTimestamp(file_smallest_key(file_index_ + 1),
1668
0
                                          ts_sz);
1669
0
      if (prefix_extractor_->InDomain(target_user_key_without_ts) &&
1670
0
          (!prefix_extractor_->InDomain(next_file_first_user_key_without_ts) ||
1671
0
           prefix_extractor_->Transform(target_user_key_without_ts)
1672
0
                   .compare(prefix_extractor_->Transform(
1673
0
                       next_file_first_user_key_without_ts)) != 0)) {
1674
        // SkipEmptyFileForward() will not advance to next file when this flag
1675
        // is set for reason detailed below.
1676
        //
1677
        // The file we initially positioned to has no keys under the target
1678
        // prefix, and the next file's smallest key has a different prefix than
1679
        // target. When doing prefix iterator seek, when keys for one prefix
1680
        // have been exhausted, it can jump to any key that is larger. Here we
1681
        // are enforcing a stricter contract than that, in order to make it
1682
        // easier for higher layers (merging and DB iterator) to reason the
1683
        // correctness:
1684
        // 1. Within the prefix, the result should be accurate.
1685
        // 2. If keys for the prefix is exhausted, it is either positioned to
1686
        // the next key after the prefix, or make the iterator invalid.
1687
        // A side benefit will be that it invalidates the iterator earlier so
1688
        // that the upper level merging iterator can merge fewer child
1689
        // iterators.
1690
        //
1691
        // The flag is cleared in Seek*() calls. There is no need to clear the
1692
        // flag in Prev() since Prev() will not be called when the flag is set
1693
        // for reasons explained below. If range_tombstone_iter_ is nullptr,
1694
        // then there is no file boundary sentinel key. Since
1695
        // !file_iter_.Valid() from the if condition above, this level iterator
1696
        // is !Valid(), so Prev() will not be called. If range_tombstone_iter_
1697
        // is not nullptr, there are two cases depending on if this level
1698
        // iterator reaches top of the heap in merging iterator (the upper
1699
        // layer).
1700
        //  If so, merging iterator will see the sentinel key, call
1701
        //  NextAndGetResult() and the call to NextAndGetResult() will skip the
1702
        //  sentinel key and makes this level iterator invalid. If not, then it
1703
        //  could be because the upper layer is done before any method of this
1704
        //  level iterator is called or another Seek*() call is invoked. Either
1705
        //  way, Prev() is never called before Seek*().
1706
        // The flag should not be cleared at the beginning of
1707
        // Next/NextAndGetResult() since it is used in SkipEmptyFileForward()
1708
        // called in Next/NextAndGetResult().
1709
0
        prefix_exhausted_ = true;
1710
0
      }
1711
0
    }
1712
1713
1.01k
    if (range_tombstone_iter_) {
1714
171
      TrySetDeleteRangeSentinel(file_largest_key(file_index_));
1715
171
    }
1716
1.01k
  }
1717
1.07k
  SkipEmptyFileForward();
1718
1.07k
  CheckMayBeOutOfLowerBound();
1719
1.07k
  SampleRead<true>();
1720
1.07k
}
1721
1722
1.58k
void LevelIterator::SeekForPrev(const Slice& target) {
1723
1.58k
  prefix_exhausted_ = false;
1724
1.58k
  ClearSentinel();
1725
1.58k
  size_t new_file_index = FindFile(icomparator_, *flevel_, target);
1726
  // Seek beyond this level's smallest key
1727
1.58k
  if (new_file_index == 0 &&
1728
584
      icomparator_.Compare(target, file_smallest_key(0)) < 0) {
1729
348
    SetFileIterator(nullptr);
1730
348
    ClearRangeTombstoneIter();
1731
348
    CheckMayBeOutOfLowerBound();
1732
348
    return;
1733
348
  }
1734
1.23k
  if (new_file_index >= flevel_->num_files) {
1735
729
    new_file_index = flevel_->num_files - 1;
1736
729
  }
1737
1738
1.23k
  InitFileIterator(new_file_index, /*seek_target=*/nullptr);
1739
1.23k
  if (file_iter_.iter() != nullptr) {
1740
1.23k
    file_iter_.SeekForPrev(target);
1741
1.23k
    if (range_tombstone_iter_ &&
1742
1.23k
        icomparator_.Compare(target, file_smallest_key(file_index_)) >= 0) {
1743
      // In SeekForPrev() case, it is possible that the target is less than
1744
      // file's lower boundary since largest key is used to determine file index
1745
      // (FindFile()). When target is less than file's lower boundary, sentinel
1746
      // key should not be set so that SeekForPrev() does not result in a key
1747
      // larger than target. This is correct in that there is no need to keep
1748
      // the range tombstones in this file alive as they only cover keys
1749
      // starting from the file's lower boundary, which is after `target`.
1750
1.07k
      TrySetDeleteRangeSentinel(file_smallest_key(file_index_));
1751
1.07k
    }
1752
1.23k
    SkipEmptyFileBackward();
1753
1.23k
  }
1754
1.23k
  CheckMayBeOutOfLowerBound();
1755
1.23k
  SampleRead<true>();
1756
1.23k
}
1757
1758
3.08k
void LevelIterator::SeekToFirst() {
1759
3.08k
  prefix_exhausted_ = false;
1760
3.08k
  ClearSentinel();
1761
3.08k
  InitFileIterator(0, /*seek_target=*/nullptr);
1762
3.08k
  if (file_iter_.iter() != nullptr) {
1763
3.08k
    file_iter_.SeekToFirst();
1764
3.08k
    if (range_tombstone_iter_) {
1765
      // We do this in SeekToFirst() and SeekToLast() since
1766
      // we could have an empty file with only range tombstones.
1767
3.08k
      TrySetDeleteRangeSentinel(file_largest_key(file_index_));
1768
3.08k
    }
1769
3.08k
  }
1770
3.08k
  SkipEmptyFileForward();
1771
3.08k
  CheckMayBeOutOfLowerBound();
1772
3.08k
  SampleRead<true>();
1773
3.08k
}
1774
1775
0
void LevelIterator::SeekToLast() {
1776
0
  prefix_exhausted_ = false;
1777
0
  ClearSentinel();
1778
0
  InitFileIterator(flevel_->num_files - 1, /*seek_target=*/nullptr);
1779
0
  if (file_iter_.iter() != nullptr) {
1780
0
    file_iter_.SeekToLast();
1781
0
    if (range_tombstone_iter_) {
1782
0
      TrySetDeleteRangeSentinel(file_smallest_key(file_index_));
1783
0
    }
1784
0
  }
1785
0
  SkipEmptyFileBackward();
1786
0
  CheckMayBeOutOfLowerBound();
1787
0
  SampleRead<true>();
1788
0
}
1789
1790
0
void LevelIterator::Next() {
1791
0
  assert(Valid());
1792
0
  if (to_return_sentinel_) {
1793
    // file_iter_ is at EOF already when to_return_sentinel_
1794
0
    ClearSentinel();
1795
0
  } else {
1796
0
    file_iter_.Next();
1797
0
    if (range_tombstone_iter_) {
1798
0
      TrySetDeleteRangeSentinel(file_largest_key(file_index_));
1799
0
    }
1800
0
  }
1801
0
  SkipEmptyFileForward();
1802
0
  SampleRead<false>();
1803
0
}
1804
1805
9.82k
bool LevelIterator::NextAndGetResult(IterateResult* result) {
1806
9.82k
  assert(Valid());
1807
  // file_iter_ is at EOF already when to_return_sentinel_
1808
9.82k
  bool is_valid = !to_return_sentinel_ && file_iter_.NextAndGetResult(result);
1809
9.82k
  if (!is_valid) {
1810
9.23k
    if (to_return_sentinel_) {
1811
4.61k
      ClearSentinel();
1812
4.61k
    } else if (range_tombstone_iter_) {
1813
4.61k
      TrySetDeleteRangeSentinel(file_largest_key(file_index_));
1814
4.61k
    }
1815
9.23k
    is_next_read_sequential_ = true;
1816
9.23k
    SkipEmptyFileForward();
1817
9.23k
    is_next_read_sequential_ = false;
1818
9.23k
    is_valid = Valid();
1819
9.23k
    if (is_valid) {
1820
      // This could be set in TrySetDeleteRangeSentinel() or
1821
      // SkipEmptyFileForward() above.
1822
6.64k
      if (to_return_sentinel_) {
1823
4.61k
        result->key = sentinel_;
1824
4.61k
        result->bound_check_result = IterBoundCheck::kUnknown;
1825
4.61k
        result->value_prepared = true;
1826
4.61k
      } else {
1827
2.02k
        result->key = key();
1828
2.02k
        result->bound_check_result = file_iter_.UpperBoundCheckResult();
1829
        // Ideally, we should return the real file_iter_.value_prepared but the
1830
        // information is not here. It would casue an extra PrepareValue()
1831
        // for the first key of a file.
1832
2.02k
        result->value_prepared = !allow_unprepared_value_;
1833
2.02k
      }
1834
6.64k
    }
1835
9.23k
  }
1836
9.82k
  SampleRead<false>();
1837
9.82k
  return is_valid;
1838
9.82k
}
1839
1840
5.25k
void LevelIterator::Prev() {
1841
5.25k
  assert(Valid());
1842
5.25k
  if (to_return_sentinel_) {
1843
2.62k
    ClearSentinel();
1844
2.63k
  } else {
1845
2.63k
    file_iter_.Prev();
1846
2.63k
    if (range_tombstone_iter_) {
1847
2.63k
      TrySetDeleteRangeSentinel(file_smallest_key(file_index_));
1848
2.63k
    }
1849
2.63k
  }
1850
5.25k
  SkipEmptyFileBackward();
1851
5.25k
  SampleRead<false>();
1852
5.25k
}
1853
1854
13.3k
bool LevelIterator::SkipEmptyFileForward() {
1855
13.3k
  bool seen_empty_file = false;
1856
  // Pause at sentinel key
1857
15.4k
  while (!to_return_sentinel_ &&
1858
10.8k
         (file_iter_.iter() == nullptr ||
1859
10.7k
          (!file_iter_.Valid() && file_iter_.status().ok() &&
1860
4.61k
           file_iter_.iter()->UpperBoundCheckResult() !=
1861
4.68k
               IterBoundCheck::kOutOfBound))) {
1862
4.68k
    seen_empty_file = true;
1863
    // Move to next file
1864
4.68k
    if (file_index_ >= flevel_->num_files - 1 ||
1865
2.02k
        KeyReachedUpperBound(file_smallest_key(file_index_ + 1)) ||
1866
2.65k
        prefix_exhausted_) {
1867
2.65k
      SetFileIterator(nullptr);
1868
2.65k
      ClearRangeTombstoneIter();
1869
2.65k
      break;
1870
2.65k
    }
1871
    // may init a new *range_tombstone_iter
1872
2.02k
    InitFileIterator(file_index_ + 1, /*seek_target=*/nullptr);
1873
    // We moved to a new SST file
1874
    // Seek range_tombstone_iter_ to reset its !Valid() default state.
1875
    // We do not need to call range_tombstone_iter_.Seek* in
1876
    // LevelIterator::Seek* since when the merging iterator calls
1877
    // LevelIterator::Seek*, it should also call Seek* into the corresponding
1878
    // range tombstone iterator.
1879
2.02k
    if (file_iter_.iter() != nullptr) {
1880
      // If we are doing prepared scan opts then we should seek to the values
1881
      // specified by the scan opts
1882
1883
2.02k
      if (scan_opts_ && FileHasMultiScanArg(file_index_)) {
1884
0
        const ScanOptions& opts =
1885
0
            GetMultiScanArgForFile(file_index_).GetScanRanges().front();
1886
0
        assert(opts.range.start.has_value());
1887
0
        InternalKey target;
1888
0
        const size_t ts_size =
1889
0
            user_comparator_.user_comparator()->timestamp_size();
1890
0
        if (ts_size == 0) {
1891
0
          target = InternalKey(opts.range.start.value(), kMaxSequenceNumber,
1892
0
                               kValueTypeForSeek);
1893
0
        } else {
1894
0
          std::string seek_key;
1895
0
          AppendKeyWithMaxTimestamp(&seek_key, opts.range.start.value(),
1896
0
                                    ts_size);
1897
0
          target = InternalKey(seek_key, kMaxSequenceNumber, kValueTypeForSeek);
1898
0
        }
1899
0
        file_iter_.Seek(target.Encode());
1900
2.02k
      } else {
1901
2.02k
        file_iter_.SeekToFirst();
1902
2.02k
      }
1903
2.02k
      if (range_tombstone_iter_) {
1904
2.02k
        if (*range_tombstone_iter_) {
1905
0
          (*range_tombstone_iter_)->SeekToFirst();
1906
0
        }
1907
2.02k
        TrySetDeleteRangeSentinel(file_largest_key(file_index_));
1908
2.02k
      }
1909
2.02k
    }
1910
2.02k
  }
1911
13.3k
  return seen_empty_file;
1912
13.3k
}
1913
1914
6.48k
void LevelIterator::SkipEmptyFileBackward() {
1915
  // Pause at sentinel key
1916
8.03k
  while (!to_return_sentinel_ &&
1917
5.41k
         (file_iter_.iter() == nullptr ||
1918
5.41k
          (!file_iter_.Valid() && file_iter_.status().ok()))) {
1919
    // Move to previous file
1920
2.77k
    if (file_index_ == 0) {
1921
      // Already the first file
1922
1.22k
      SetFileIterator(nullptr);
1923
1.22k
      ClearRangeTombstoneIter();
1924
1.22k
      return;
1925
1.22k
    }
1926
1.55k
    InitFileIterator(file_index_ - 1, /*seek_target=*/nullptr);
1927
    // We moved to a new SST file
1928
    // Seek range_tombstone_iter_ to reset its !Valid() default state.
1929
1.55k
    if (file_iter_.iter() != nullptr) {
1930
1.55k
      file_iter_.SeekToLast();
1931
1.55k
      if (range_tombstone_iter_) {
1932
1.55k
        if (*range_tombstone_iter_) {
1933
0
          (*range_tombstone_iter_)->SeekToLast();
1934
0
        }
1935
1.55k
        TrySetDeleteRangeSentinel(file_smallest_key(file_index_));
1936
1.55k
        if (to_return_sentinel_) {
1937
0
          break;
1938
0
        }
1939
1.55k
      }
1940
1.55k
    }
1941
1.55k
  }
1942
6.48k
}
1943
1944
#ifndef NDEBUG
1945
bool LevelIterator::OverlapRange(const ScanOptions& opts, size_t file_index) {
1946
  return MultiScanRangeOverlapsFile(user_comparator_, opts,
1947
                                    flevel_->files[file_index]);
1948
}
1949
#endif
1950
1951
13.1k
void LevelIterator::SetFileIterator(InternalIterator* iter) {
1952
13.1k
  prefix_filtered_table_ = nullptr;
1953
13.1k
  if (pinned_iters_mgr_ && iter) {
1954
4.99k
    iter->SetPinnedItersMgr(pinned_iters_mgr_);
1955
4.99k
  }
1956
1957
13.1k
  InternalIterator* old_iter = file_iter_.Set(iter);
1958
  // Update the read pattern for PrefetchBuffer.
1959
13.1k
  if (is_next_read_sequential_) {
1960
4.61k
    file_iter_.UpdateReadaheadState(old_iter);
1961
4.61k
  }
1962
1963
13.1k
  if (pinned_iters_mgr_ && pinned_iters_mgr_->PinningEnabled() &&
1964
2.91k
      old_iter != nullptr && !old_iter->IsSafeToDropWhilePinned()) {
1965
2.76k
    TEST_SYNC_POINT_CALLBACK("LevelIterator::SetFileIterator:PinIterator",
1966
2.76k
                             old_iter);
1967
2.76k
    pinned_iters_mgr_->PinIterator(old_iter);
1968
10.4k
  } else {
1969
10.4k
    TEST_SYNC_POINT_CALLBACK("LevelIterator::SetFileIterator:DeleteIterator",
1970
10.4k
                             old_iter);
1971
10.4k
    delete old_iter;
1972
10.4k
  }
1973
13.1k
}
1974
1975
void LevelIterator::InitFileIterator(size_t new_file_index,
1976
8.96k
                                     const Slice* seek_target) {
1977
8.96k
  if (new_file_index >= flevel_->num_files) {
1978
68
    file_index_ = new_file_index;
1979
68
    SetFileIterator(nullptr);
1980
68
    ClearRangeTombstoneIter();
1981
68
    return;
1982
8.89k
  } else {
1983
    // If the file iterator shows incomplete, we try it again if users seek
1984
    // to the same file, as this time we may go to a different data block
1985
    // which is cached in block cache.
1986
    //
1987
8.89k
    const bool same_file = file_iter_.iter() != nullptr &&
1988
3.59k
                           !file_iter_.status().IsIncomplete() &&
1989
3.59k
                           new_file_index == file_index_;
1990
8.89k
    if (same_file && prefix_filtered_table_ == nullptr) {
1991
      // file_iter_ is already constructed with this iterator, so
1992
      // no need to change anything
1993
8.88k
    } else if (same_file && scan_opts_ == nullptr) {
1994
0
      RetryPrefixFilteredFile(seek_target);
1995
8.88k
    } else {
1996
8.88k
      file_index_ = new_file_index;
1997
8.88k
      if (!prepared_iters_.empty()) {
1998
0
        auto prepared_it = prepared_iters_.find(file_index_);
1999
0
        if (prepared_it != prepared_iters_.end()) {
2000
0
          InternalIterator* iter = prepared_it->second;
2001
0
          prepared_iters_.erase(prepared_it);
2002
0
          SetFileIterator(iter);
2003
0
          return;
2004
0
        }
2005
0
      }
2006
2007
8.88k
      TableReader* prefix_filtered_table = nullptr;
2008
8.88k
      InternalIterator* iter =
2009
8.88k
          NewFileIterator(seek_target, &prefix_filtered_table);
2010
8.88k
      if (FileHasMultiScanArg(file_index_)) {
2011
0
        auto& args = GetMultiScanArgForFile(file_index_);
2012
0
        assert(OverlapRange(*args.GetScanRanges().begin(), file_index_) &&
2013
0
               OverlapRange(*args.GetScanRanges().rbegin(), file_index_));
2014
0
        iter->Prepare(&args);
2015
0
      }
2016
2017
8.88k
      SetFileIterator(iter);
2018
8.88k
      prefix_filtered_table_ = prefix_filtered_table;
2019
8.88k
    }
2020
8.89k
  }
2021
8.96k
}
2022
2023
// file_iter_ stands in for the file at file_index_ because the file's prefix
2024
// filter ruled out an earlier seek target. Replaces it with a real iterator
2025
// for the file, unless the filter rules out seek_target too. Only the point
2026
// iterator is replaced: TableCache::NewIterator() already set up the file's
2027
// range tombstones, if any, along with the stand-in.
2028
0
void LevelIterator::RetryPrefixFilteredFile(const Slice* seek_target) {
2029
0
  TEST_SYNC_POINT("LevelIterator::RetryPrefixFilteredFile");
2030
0
  PERF_TIMER_GUARD(new_table_iterator_nanos);
2031
0
  const bool skip_filters =
2032
0
      skip_filters_ || table_cache_open_options_.skip_filters;
2033
0
  InternalIterator* iter =
2034
0
      seek_target != nullptr
2035
0
          ? prefix_filtered_table_->NewIteratorForSeek(
2036
0
                read_options_, prefix_extractor_, /*arena=*/nullptr,
2037
0
                skip_filters, caller_, file_options_.compaction_readahead_size,
2038
0
                allow_unprepared_value_, *seek_target)
2039
0
          : prefix_filtered_table_->NewIterator(
2040
0
                read_options_, prefix_extractor_, /*arena=*/nullptr,
2041
0
                skip_filters, caller_, file_options_.compaction_readahead_size,
2042
0
                allow_unprepared_value_);
2043
0
  if (iter == nullptr) {
2044
0
    return;
2045
0
  }
2046
  // The stand-in holds the table cache handle that keeps the reader alive.
2047
0
  file_iter_.iter()->DelegateCleanupsTo(iter);
2048
0
  SetFileIterator(iter);
2049
0
}
2050
2051
}  // anonymous namespace
2052
2053
Status Version::GetTableProperties(const ReadOptions& read_options,
2054
                                   std::shared_ptr<const TableProperties>* tp,
2055
                                   const FileMetaData* file_meta,
2056
104k
                                   const std::string* fname) const {
2057
104k
  auto* table_cache = cfd_->table_cache();
2058
104k
  const auto& ioptions = cfd_->ioptions();
2059
104k
  Status s = table_cache->GetTableProperties(
2060
104k
      file_options_, read_options, cfd_->internal_comparator(), *file_meta, tp,
2061
104k
      mutable_cf_options_, true /* no io */);
2062
104k
  if (s.ok()) {
2063
104k
    return s;
2064
104k
  }
2065
2066
  // We only ignore error type `Incomplete` since it's by design that we
2067
  // disallow table when it's not in table cache.
2068
0
  if (!s.IsIncomplete()) {
2069
0
    return s;
2070
0
  }
2071
2072
  // 2. Table is not present in table cache, we'll read the table properties
2073
  // directly from the properties block in the file.
2074
0
  std::unique_ptr<FSRandomAccessFile> file;
2075
0
  std::string file_name;
2076
0
  if (fname != nullptr) {
2077
0
    file_name = *fname;
2078
0
  } else {
2079
0
    file_name = TableFileName(ioptions.cf_paths, file_meta->fd.GetNumber(),
2080
0
                              file_meta->fd.GetPathId());
2081
0
  }
2082
0
  FileOptions fopts = file_options_;
2083
0
  fopts.file_checksum = file_meta->file_checksum;
2084
0
  fopts.file_checksum_func_name = file_meta->file_checksum_func_name;
2085
0
  s = ioptions.fs->NewRandomAccessFile(file_name, fopts, &file, nullptr);
2086
0
  if (!s.ok()) {
2087
0
    return s;
2088
0
  }
2089
2090
  // By setting the magic number to kNullTableMagicNumber, we can bypass
2091
  // the magic number check in the footer.
2092
0
  std::unique_ptr<RandomAccessFileReader> file_reader(
2093
0
      new RandomAccessFileReader(
2094
0
          std::move(file), file_name, ioptions.clock /* clock */, io_tracer_,
2095
0
          ioptions.stats /* stats */,
2096
0
          Histograms::SST_READ_MICROS /* hist_type */,
2097
0
          nullptr /* file_read_hist */, nullptr /* rate_limiter */,
2098
0
          ioptions.listeners));
2099
0
  std::unique_ptr<TableProperties> props;
2100
0
  s = ReadTableProperties(
2101
0
      file_reader.get(), file_meta->fd.GetFileSize(),
2102
0
      Footer::kNullTableMagicNumber /* table's magic number */, ioptions,
2103
0
      read_options, &props);
2104
0
  if (!s.ok()) {
2105
0
    return s;
2106
0
  }
2107
0
  *tp = std::move(props);
2108
0
  RecordTick(ioptions.stats, NUMBER_DIRECT_LOAD_TABLE_PROPERTIES);
2109
0
  return s;
2110
0
}
2111
2112
Status Version::GetPropertiesOfAllTables(
2113
0
    const ReadOptions& read_options, TablePropertiesCollection* props) const {
2114
0
  Status s;
2115
0
  for (int level = 0; level < storage_info_.num_levels_; level++) {
2116
0
    s = GetPropertiesOfAllTables(read_options, props, level);
2117
0
    if (!s.ok()) {
2118
0
      return s;
2119
0
    }
2120
0
  }
2121
2122
0
  return Status::OK();
2123
0
}
2124
2125
Status Version::TablesRangeTombstoneSummary(int max_entries_to_print,
2126
0
                                            std::string* out_str) {
2127
0
  if (max_entries_to_print <= 0) {
2128
0
    return Status::OK();
2129
0
  }
2130
0
  int num_entries_left = max_entries_to_print;
2131
2132
0
  std::stringstream ss;
2133
2134
  // TODO: plumb Env::IOActivity, Env::IOPriority
2135
0
  const ReadOptions read_options;
2136
0
  for (int level = 0; level < storage_info_.num_levels_; level++) {
2137
0
    for (const auto& file_meta : storage_info_.files_[level]) {
2138
0
      auto fname =
2139
0
          TableFileName(cfd_->ioptions().cf_paths, file_meta->fd.GetNumber(),
2140
0
                        file_meta->fd.GetPathId());
2141
2142
0
      ss << "=== file : " << fname << " ===\n";
2143
2144
0
      TableCache* table_cache = cfd_->table_cache();
2145
0
      std::unique_ptr<FragmentedRangeTombstoneIterator> tombstone_iter;
2146
2147
0
      Status s = table_cache->GetRangeTombstoneIterator(
2148
0
          read_options, cfd_->internal_comparator(), *file_meta,
2149
0
          mutable_cf_options_, &tombstone_iter);
2150
0
      if (!s.ok()) {
2151
0
        return s;
2152
0
      }
2153
0
      if (tombstone_iter) {
2154
0
        tombstone_iter->SeekToFirst();
2155
2156
        // TODO: print timestamp
2157
0
        while (tombstone_iter->Valid() && num_entries_left > 0) {
2158
0
          ss << "start: " << tombstone_iter->start_key().ToString(true)
2159
0
             << " end: " << tombstone_iter->end_key().ToString(true)
2160
0
             << " seq: " << tombstone_iter->seq() << '\n';
2161
0
          tombstone_iter->Next();
2162
0
          num_entries_left--;
2163
0
        }
2164
0
        if (num_entries_left <= 0) {
2165
0
          break;
2166
0
        }
2167
0
      }
2168
0
    }
2169
0
    if (num_entries_left <= 0) {
2170
0
      break;
2171
0
    }
2172
0
  }
2173
0
  assert(num_entries_left >= 0);
2174
0
  if (num_entries_left <= 0) {
2175
0
    ss << "(results may not be complete)\n";
2176
0
  }
2177
2178
0
  *out_str = ss.str();
2179
0
  return Status::OK();
2180
0
}
2181
2182
Status Version::GetPropertiesOfAllTables(const ReadOptions& read_options,
2183
                                         TablePropertiesCollection* props,
2184
0
                                         int level) const {
2185
0
  for (const auto& file_meta : storage_info_.files_[level]) {
2186
0
    auto fname =
2187
0
        TableFileName(cfd_->ioptions().cf_paths, file_meta->fd.GetNumber(),
2188
0
                      file_meta->fd.GetPathId());
2189
    // 1. If the table is already present in table cache, load table
2190
    // properties from there.
2191
0
    std::shared_ptr<const TableProperties> table_properties;
2192
0
    Status s =
2193
0
        GetTableProperties(read_options, &table_properties, file_meta, &fname);
2194
0
    if (s.ok()) {
2195
0
      props->insert({fname, table_properties});
2196
0
    } else {
2197
0
      return s;
2198
0
    }
2199
0
  }
2200
2201
0
  return Status::OK();
2202
0
}
2203
2204
Status Version::GetPropertiesOfTablesInRange(
2205
    const ReadOptions& read_options, const autovector<UserKeyRange>& ranges,
2206
0
    TablePropertiesCollection* props) const {
2207
0
  for (int level = 0; level < storage_info_.num_non_empty_levels(); level++) {
2208
0
    for (const auto& range : ranges) {
2209
      // Convert user_key into a corresponding internal key.
2210
0
      InternalKey k1(range.start, kMaxSequenceNumber, kValueTypeForSeek);
2211
0
      InternalKey k2(range.limit, kMaxSequenceNumber, kValueTypeForSeek);
2212
0
      std::vector<FileMetaData*> files;
2213
0
      storage_info_.GetOverlappingInputs(level, &k1, &k2, &files, -1, nullptr,
2214
0
                                         false);
2215
0
      for (const auto& file_meta : files) {
2216
0
        auto fname =
2217
0
            TableFileName(cfd_->ioptions().cf_paths, file_meta->fd.GetNumber(),
2218
0
                          file_meta->fd.GetPathId());
2219
0
        if (props->count(fname) == 0) {
2220
          // 1. If the table is already present in table cache, load table
2221
          // properties from there.
2222
0
          std::shared_ptr<const TableProperties> table_properties;
2223
0
          Status s = GetTableProperties(read_options, &table_properties,
2224
0
                                        file_meta, &fname);
2225
0
          if (s.ok()) {
2226
0
            props->insert({fname, table_properties});
2227
0
          } else {
2228
0
            return s;
2229
0
          }
2230
0
        }
2231
0
      }
2232
0
    }
2233
0
  }
2234
2235
0
  return Status::OK();
2236
0
}
2237
2238
Status Version::GetPropertiesOfTablesByLevel(
2239
    const ReadOptions& read_options,
2240
    std::vector<std::unique_ptr<TablePropertiesCollection>>* props_by_level)
2241
0
    const {
2242
0
  Status s;
2243
2244
0
  props_by_level->reserve(storage_info_.num_levels_);
2245
0
  for (int level = 0; level < storage_info_.num_levels_; level++) {
2246
0
    props_by_level->push_back(std::make_unique<TablePropertiesCollection>());
2247
0
    s = GetPropertiesOfAllTables(read_options, props_by_level->back().get(),
2248
0
                                 level);
2249
0
    if (!s.ok()) {
2250
0
      return s;
2251
0
    }
2252
0
  }
2253
0
  return Status::OK();
2254
0
}
2255
2256
Status Version::GetAggregatedTableProperties(
2257
    const ReadOptions& read_options, std::shared_ptr<const TableProperties>* tp,
2258
0
    int level) {
2259
0
  TablePropertiesCollection props;
2260
0
  Status s;
2261
0
  if (level < 0) {
2262
0
    s = GetPropertiesOfAllTables(read_options, &props);
2263
0
  } else {
2264
0
    s = GetPropertiesOfAllTables(read_options, &props, level);
2265
0
  }
2266
0
  if (!s.ok()) {
2267
0
    return s;
2268
0
  }
2269
2270
0
  auto* new_tp = new TableProperties();
2271
0
  for (const auto& item : props) {
2272
0
    new_tp->Add(*item.second);
2273
0
  }
2274
0
  tp->reset(new_tp);
2275
0
  return Status::OK();
2276
0
}
2277
2278
0
size_t Version::GetMemoryUsageByTableReaders(const ReadOptions& read_options) {
2279
0
  size_t total_usage = 0;
2280
0
  for (auto& file_level : storage_info_.level_files_brief_) {
2281
0
    for (size_t i = 0; i < file_level.num_files; i++) {
2282
0
      total_usage += cfd_->table_cache()->GetMemoryUsageByTableReader(
2283
0
          file_options_, read_options, cfd_->internal_comparator(),
2284
0
          *file_level.files[i].file_metadata, mutable_cf_options_);
2285
0
    }
2286
0
  }
2287
0
  return total_usage;
2288
0
}
2289
2290
60.5k
void Version::GetColumnFamilyMetaData(ColumnFamilyMetaData* cf_meta) {
2291
60.5k
  assert(cf_meta);
2292
60.5k
  assert(cfd_);
2293
2294
60.5k
  cf_meta->name = cfd_->GetName();
2295
60.5k
  cf_meta->size = 0;
2296
60.5k
  cf_meta->file_count = 0;
2297
60.5k
  cf_meta->levels.clear();
2298
2299
60.5k
  cf_meta->blob_file_size = 0;
2300
60.5k
  cf_meta->blob_file_count = 0;
2301
60.5k
  cf_meta->blob_files.clear();
2302
2303
60.5k
  const auto& ioptions = cfd_->ioptions();
2304
60.5k
  auto* vstorage = storage_info();
2305
2306
484k
  for (int level = 0; level < cfd_->NumberLevels(); level++) {
2307
423k
    uint64_t level_size = 0;
2308
423k
    cf_meta->file_count += vstorage->LevelFiles(level).size();
2309
423k
    std::vector<SstFileMetaData> files;
2310
423k
    for (const auto& file : vstorage->LevelFiles(level)) {
2311
84.8k
      uint32_t path_id = file->fd.GetPathId();
2312
84.8k
      std::string file_path;
2313
84.8k
      if (path_id < ioptions.cf_paths.size()) {
2314
84.8k
        file_path = ioptions.cf_paths[path_id].path;
2315
84.8k
      } else {
2316
0
        assert(!ioptions.cf_paths.empty());
2317
0
        file_path = ioptions.cf_paths.back().path;
2318
0
      }
2319
84.8k
      const uint64_t file_number = file->fd.GetNumber();
2320
84.8k
      files.emplace_back(
2321
84.8k
          MakeTableFileName("", file_number), file_number, file_path,
2322
84.8k
          file->fd.GetFileSize(), file->fd.smallest_seqno,
2323
84.8k
          file->fd.largest_seqno, file->smallest.user_key().ToString(),
2324
84.8k
          file->largest.user_key().ToString(),
2325
84.8k
          file->stats.num_reads_sampled.load(std::memory_order_relaxed),
2326
84.8k
          file->being_compacted, file->temperature,
2327
84.8k
          file->oldest_blob_file_number, file->TryGetOldestAncestorTime(),
2328
84.8k
          file->TryGetFileCreationTime(), file->epoch_number,
2329
84.8k
          file->file_checksum, file->file_checksum_func_name);
2330
84.8k
      files.back().num_entries = file->num_entries;
2331
84.8k
      files.back().num_deletions = file->num_deletions;
2332
84.8k
      files.back().db_ownership_start_time = file->db_ownership_start_time;
2333
84.8k
      files.back().data_file_provenance = file->data_file_provenance;
2334
84.8k
      files.back().smallest = file->smallest.Encode().ToString();
2335
84.8k
      files.back().largest = file->largest.Encode().ToString();
2336
84.8k
      level_size += file->fd.GetFileSize();
2337
84.8k
    }
2338
423k
    cf_meta->levels.emplace_back(level, level_size, std::move(files));
2339
423k
    cf_meta->size += level_size;
2340
423k
  }
2341
60.5k
  for (const auto& meta : vstorage->GetBlobFiles()) {
2342
0
    assert(meta);
2343
2344
0
    cf_meta->blob_files.emplace_back(
2345
0
        meta->GetBlobFileNumber(), BlobFileName("", meta->GetBlobFileNumber()),
2346
0
        ioptions.cf_paths.front().path, meta->GetBlobFileSize(),
2347
0
        meta->GetTotalBlobCount(), meta->GetTotalBlobBytes(),
2348
0
        meta->GetGarbageBlobCount(), meta->GetGarbageBlobBytes(),
2349
0
        meta->GetChecksumMethod(), meta->GetChecksumValue());
2350
0
    ++cf_meta->blob_file_count;
2351
0
    cf_meta->blob_file_size += meta->GetBlobFileSize();
2352
0
  }
2353
60.5k
}
2354
2355
void Version::GetColumnFamilyMetaData(
2356
    const GetColumnFamilyMetaDataOptions& options,
2357
0
    ColumnFamilyMetaData* cf_meta) {
2358
0
  assert(cf_meta);
2359
0
  assert(cfd_);
2360
2361
0
  cf_meta->name = cfd_->GetName();
2362
0
  cf_meta->size = 0;
2363
0
  cf_meta->file_count = 0;
2364
0
  cf_meta->levels.clear();
2365
0
  cf_meta->blob_file_size = 0;
2366
0
  cf_meta->blob_file_count = 0;
2367
0
  cf_meta->blob_files.clear();
2368
2369
0
  const auto& ioptions = cfd_->ioptions();
2370
0
  auto* vstorage = storage_info();
2371
2372
0
  int first_level = (options.level >= 0) ? options.level : 0;
2373
0
  int last_level =
2374
0
      (options.level >= 0) ? options.level + 1 : cfd_->NumberLevels();
2375
2376
0
  InternalKey ikey_start;
2377
0
  InternalKey ikey_end;
2378
0
  const InternalKey* begin = nullptr;
2379
0
  const InternalKey* end = nullptr;
2380
2381
0
  if (options.range.start.has_value()) {
2382
0
    ikey_start = InternalKey(options.range.start.value(), kMaxSequenceNumber,
2383
0
                             kValueTypeForSeek);
2384
0
    begin = &ikey_start;
2385
0
  }
2386
2387
0
  if (options.range.limit.has_value()) {
2388
0
    ikey_end = InternalKey(options.range.limit.value(), kMaxSequenceNumber,
2389
0
                           kValueTypeForSeek);
2390
0
    end = &ikey_end;
2391
0
  }
2392
2393
0
  for (int l = first_level; l < last_level; ++l) {
2394
0
    uint64_t level_size = 0;
2395
0
    std::vector<SstFileMetaData> files;
2396
0
    std::vector<FileMetaData*> overlapping_files;
2397
0
    vstorage->GetOverlappingInputs(l, begin, end, &overlapping_files);
2398
2399
0
    for (const auto& file : overlapping_files) {
2400
0
      uint32_t path_id = file->fd.GetPathId();
2401
0
      const auto& file_path = (path_id < ioptions.cf_paths.size())
2402
0
                                  ? ioptions.cf_paths[path_id].path
2403
0
                                  : ioptions.cf_paths.back().path;
2404
0
      const uint64_t file_number = file->fd.GetNumber();
2405
0
      files.emplace_back(
2406
0
          MakeTableFileName("", file_number), file_number, file_path,
2407
0
          file->fd.GetFileSize(), file->fd.smallest_seqno,
2408
0
          file->fd.largest_seqno, file->smallest.user_key().ToString(),
2409
0
          file->largest.user_key().ToString(),
2410
0
          file->stats.num_reads_sampled.load(std::memory_order_relaxed),
2411
0
          file->being_compacted, file->temperature,
2412
0
          file->oldest_blob_file_number, file->TryGetOldestAncestorTime(),
2413
0
          file->TryGetFileCreationTime(), file->epoch_number,
2414
0
          file->file_checksum, file->file_checksum_func_name);
2415
0
      files.back().num_entries = file->num_entries;
2416
0
      files.back().num_deletions = file->num_deletions;
2417
0
      files.back().db_ownership_start_time = file->db_ownership_start_time;
2418
0
      files.back().data_file_provenance = file->data_file_provenance;
2419
0
      files.back().smallest = file->smallest.Encode().ToString();
2420
0
      files.back().largest = file->largest.Encode().ToString();
2421
0
      level_size += file->fd.GetFileSize();
2422
0
      cf_meta->file_count++;
2423
0
    }
2424
0
    if (!files.empty()) {
2425
0
      cf_meta->levels.emplace_back(l, level_size, std::move(files));
2426
0
      cf_meta->size += level_size;
2427
0
    }
2428
0
  }
2429
0
}
2430
2431
0
uint64_t Version::GetSstFilesSize() {
2432
0
  uint64_t sst_files_size = 0;
2433
0
  for (int level = 0; level < storage_info_.num_levels_; level++) {
2434
0
    for (const auto& file_meta : storage_info_.LevelFiles(level)) {
2435
0
      sst_files_size += file_meta->fd.GetFileSize();
2436
0
    }
2437
0
  }
2438
0
  return sst_files_size;
2439
0
}
2440
2441
void Version::GetSstFilesBoundaryKeys(Slice* smallest_user_key,
2442
0
                                      Slice* largest_user_key) {
2443
0
  smallest_user_key->clear();
2444
0
  largest_user_key->clear();
2445
0
  bool initialized = false;
2446
0
  const Comparator* ucmp = storage_info_.user_comparator_;
2447
0
  for (int level = 0; level < cfd_->NumberLevels(); level++) {
2448
0
    if (storage_info_.LevelFiles(level).size() == 0) {
2449
0
      continue;
2450
0
    }
2451
0
    if (level == 0) {
2452
      // we need to consider all files on level 0
2453
0
      for (const auto& file : storage_info_.LevelFiles(level)) {
2454
0
        const Slice& start_user_key = file->smallest.user_key();
2455
0
        if (!initialized ||
2456
0
            ucmp->Compare(start_user_key, *smallest_user_key) < 0) {
2457
0
          *smallest_user_key = start_user_key;
2458
0
        }
2459
0
        const Slice& end_user_key = file->largest.user_key();
2460
0
        if (!initialized ||
2461
0
            ucmp->Compare(end_user_key, *largest_user_key) > 0) {
2462
0
          *largest_user_key = end_user_key;
2463
0
        }
2464
0
        initialized = true;
2465
0
      }
2466
0
    } else {
2467
      // we only need to consider the first and last file
2468
0
      const Slice& start_user_key =
2469
0
          storage_info_.LevelFiles(level)[0]->smallest.user_key();
2470
0
      if (!initialized ||
2471
0
          ucmp->Compare(start_user_key, *smallest_user_key) < 0) {
2472
0
        *smallest_user_key = start_user_key;
2473
0
      }
2474
0
      const Slice& end_user_key =
2475
0
          storage_info_.LevelFiles(level).back()->largest.user_key();
2476
0
      if (!initialized || ucmp->Compare(end_user_key, *largest_user_key) > 0) {
2477
0
        *largest_user_key = end_user_key;
2478
0
      }
2479
0
      initialized = true;
2480
0
    }
2481
0
  }
2482
0
}
2483
2484
0
void Version::GetCreationTimeOfOldestFile(uint64_t* creation_time) {
2485
0
  uint64_t oldest_time = std::numeric_limits<uint64_t>::max();
2486
0
  for (int level = 0; level < storage_info_.num_non_empty_levels_; level++) {
2487
0
    for (FileMetaData* meta : storage_info_.LevelFiles(level)) {
2488
0
      uint64_t file_creation_time = meta->TryGetFileCreationTime();
2489
0
      TEST_SYNC_POINT_CALLBACK(
2490
0
          "Version::GetCreationTimeOfOldestFile::FileCreationTime",
2491
0
          &file_creation_time);
2492
0
      if (file_creation_time == kUnknownFileCreationTime) {
2493
0
        *creation_time = 0;
2494
0
        return;
2495
0
      }
2496
0
      if (file_creation_time < oldest_time) {
2497
0
        oldest_time = file_creation_time;
2498
0
      }
2499
0
    }
2500
0
  }
2501
0
  *creation_time = oldest_time;
2502
0
}
2503
2504
InternalIterator* Version::TEST_GetLevelIterator(
2505
    const ReadOptions& read_options, MergeIteratorBuilder* merge_iter_builder,
2506
0
    int level, bool allow_unprepared_value) {
2507
0
  auto* arena = merge_iter_builder->GetArena();
2508
0
  auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
2509
0
  std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
2510
0
  auto level_iter = new (mem) LevelIterator(
2511
0
      cfd_->table_cache(), read_options, file_options_,
2512
0
      cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
2513
0
      mutable_cf_options_, cfd_->internal_stats()->GetFileReadHist(level),
2514
0
      TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
2515
0
      nullptr /* range_del_agg */, nullptr /* compaction_boundaries */,
2516
0
      allow_unprepared_value,
2517
0
      read_options.ignore_range_deletions ? nullptr : &tombstone_iter_ptr,
2518
0
      kMaxSequenceNumber, db_statistics_, clock_);
2519
0
  if (read_options.ignore_range_deletions) {
2520
0
    merge_iter_builder->AddIterator(level_iter);
2521
0
  } else {
2522
0
    merge_iter_builder->AddPointAndTombstoneIterator(
2523
0
        level_iter, nullptr /* tombstone_iter */, tombstone_iter_ptr);
2524
0
  }
2525
0
  return level_iter;
2526
0
}
2527
2528
0
uint64_t VersionStorageInfo::GetEstimatedActiveKeys() const {
2529
  // Estimation will be inaccurate when:
2530
  // (1) there exist merge keys
2531
  // (2) keys are directly overwritten
2532
  // (3) deletion on non-existing keys
2533
  // (4) low number of samples
2534
0
  if (current_num_samples_ == 0) {
2535
0
    return 0;
2536
0
  }
2537
2538
0
  if (current_num_non_deletions_ <= current_num_deletions_) {
2539
0
    return 0;
2540
0
  }
2541
2542
0
  uint64_t est = current_num_non_deletions_ - current_num_deletions_;
2543
2544
0
  uint64_t file_count = 0;
2545
0
  for (int level = 0; level < num_levels_; ++level) {
2546
0
    file_count += files_[level].size();
2547
0
  }
2548
2549
0
  if (current_num_samples_ < file_count) {
2550
0
    assert(current_num_samples_ != 0);
2551
0
    assert(est != 0);
2552
0
    double multiplier = static_cast<double>(file_count) / current_num_samples_;
2553
0
    double maximum_multiplier =
2554
0
        static_cast<double>(std::numeric_limits<uint64_t>::max()) / est;
2555
    // If it can overflow, we return the maximum unsigned long.
2556
0
    if (multiplier >= maximum_multiplier) {
2557
0
      return std::numeric_limits<uint64_t>::max();
2558
0
    }
2559
0
    return static_cast<uint64_t>(est * multiplier);
2560
0
  } else {
2561
0
    return est;
2562
0
  }
2563
0
}
2564
2565
double VersionStorageInfo::GetEstimatedCompressionRatioAtLevel(
2566
0
    int level) const {
2567
0
  assert(level < num_levels_);
2568
0
  uint64_t sum_file_size_bytes = 0;
2569
0
  uint64_t sum_data_size_bytes = 0;
2570
0
  for (auto* file_meta : files_[level]) {
2571
0
    auto raw_size = file_meta->raw_key_size + file_meta->raw_value_size;
2572
    // Check if the table property is properly initialized. It might not be
2573
    // because in `UpdateAccumulatedStats` we limit the maximum number of
2574
    // properties to read once.
2575
0
    if (raw_size > 0) {
2576
0
      sum_file_size_bytes += file_meta->fd.GetFileSize();
2577
0
      sum_data_size_bytes += raw_size;
2578
0
    }
2579
0
  }
2580
0
  if (sum_file_size_bytes == 0) {
2581
0
    return -1.0;
2582
0
  }
2583
0
  return static_cast<double>(sum_data_size_bytes) / sum_file_size_bytes;
2584
0
}
2585
2586
void Version::AddIterators(const ReadOptions& read_options,
2587
                           const FileOptions& soptions,
2588
                           MergeIteratorBuilder* merge_iter_builder,
2589
                           bool allow_unprepared_value, SequenceNumber read_seq,
2590
9.76k
                           const MultiScanArgs* scan_opts) {
2591
9.76k
  assert(storage_info_.finalized_);
2592
2593
33.0k
  for (int level = 0; level < storage_info_.num_non_empty_levels(); level++) {
2594
23.2k
    AddIteratorsForLevel(read_options, soptions, merge_iter_builder, level,
2595
23.2k
                         allow_unprepared_value, read_seq, scan_opts);
2596
23.2k
  }
2597
9.76k
}
2598
2599
void Version::AddIteratorsForLevel(const ReadOptions& read_options,
2600
                                   const FileOptions& soptions,
2601
                                   MergeIteratorBuilder* merge_iter_builder,
2602
                                   int level, bool allow_unprepared_value,
2603
                                   SequenceNumber read_seq,
2604
23.2k
                                   const MultiScanArgs* scan_opts) {
2605
23.2k
  assert(storage_info_.finalized_);
2606
23.2k
  if (level >= storage_info_.num_non_empty_levels()) {
2607
    // This is an empty level
2608
0
    return;
2609
23.2k
  } else if (storage_info_.LevelFilesBrief(level).num_files == 0) {
2610
    // No files in this level
2611
13.4k
    return;
2612
13.4k
  }
2613
2614
9.84k
  auto* arena = merge_iter_builder->GetArena();
2615
9.84k
  const LevelFilesBrief& file_level = storage_info_.LevelFilesBrief(level);
2616
9.84k
  const UserComparatorWrapper user_comparator(
2617
9.84k
      cfd_->internal_comparator().user_comparator());
2618
2619
9.84k
  if (scan_opts != nullptr) {
2620
0
    const std::vector<size_t> file_indexes = GetMultiScanOverlappingFiles(
2621
0
        cfd_->internal_comparator(), user_comparator, file_level, level,
2622
0
        *scan_opts, level == 0 ? std::numeric_limits<size_t>::max() : 2);
2623
0
    if (file_indexes.empty()) {
2624
0
      return;
2625
0
    }
2626
2627
0
    if (level == 0) {
2628
0
      const bool should_sample = should_sample_file_read();
2629
0
      for (size_t file_index : file_indexes) {
2630
0
        AddTableIteratorForLevel(
2631
0
            cfd_, read_options, soptions, mutable_cf_options_,
2632
0
            merge_iter_builder, arena, file_level, level, file_index,
2633
0
            /*skip_filters=*/false, max_file_size_for_l0_meta_pin_,
2634
0
            allow_unprepared_value, read_seq);
2635
0
        if (should_sample) {
2636
0
          sample_file_read_inc(file_level.files[file_index].file_metadata);
2637
0
        }
2638
0
      }
2639
0
      return;
2640
0
    }
2641
2642
0
    if (file_indexes.size() == 1) {
2643
0
      AddTableIteratorForLevel(cfd_, read_options, soptions,
2644
0
                               mutable_cf_options_, merge_iter_builder, arena,
2645
0
                               file_level, level, file_indexes.front(),
2646
0
                               IsFilterSkipped(level),
2647
0
                               /*max_file_size_for_l0_meta_pin=*/0,
2648
0
                               allow_unprepared_value, read_seq);
2649
0
      return;
2650
0
    }
2651
2652
#ifndef NDEBUG
2653
    for (size_t file_index = 0; file_index < file_level.num_files;
2654
         ++file_index) {
2655
      TEST_SYNC_POINT_CALLBACK("Version::AddIteratorsForLevel:AddedFile",
2656
                               file_level.files[file_index].file_metadata);
2657
    }
2658
#endif
2659
0
    auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
2660
0
    std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
2661
0
    auto level_iter = new (mem) LevelIterator(
2662
0
        cfd_->table_cache(), read_options, soptions,
2663
0
        cfd_->internal_comparator(), &file_level, mutable_cf_options_,
2664
0
        cfd_->internal_stats()->GetFileReadHist(level),
2665
0
        TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
2666
0
        /*range_del_agg=*/nullptr,
2667
0
        /*compaction_boundaries=*/nullptr, allow_unprepared_value,
2668
0
        read_options.ignore_range_deletions ? nullptr : &tombstone_iter_ptr,
2669
0
        read_seq, db_statistics_, clock_);
2670
#ifndef NDEBUG
2671
    std::pair<bool, bool> iterator_type(
2672
        false /* is_block_based_table_iterator */,
2673
        true /* is_level_iterator */);
2674
    TEST_SYNC_POINT_CALLBACK("Version::AddIteratorsForLevel:IteratorType",
2675
                             &iterator_type);
2676
#endif
2677
0
    if (read_options.ignore_range_deletions) {
2678
0
      merge_iter_builder->AddIterator(level_iter);
2679
0
    } else {
2680
0
      assert(tombstone_iter_ptr);
2681
0
      merge_iter_builder->AddPointAndTombstoneIterator(
2682
0
          level_iter, nullptr /* tombstone_iter */, tombstone_iter_ptr);
2683
0
    }
2684
0
    return;
2685
0
  }
2686
2687
9.84k
  if (level == 0) {
2688
    // Merge all level zero files together since they may overlap
2689
7.37k
    const bool should_sample = should_sample_file_read();
2690
17.7k
    for (size_t i = 0; i < file_level.num_files; i++) {
2691
10.3k
      AddTableIteratorForLevel(
2692
10.3k
          cfd_, read_options, soptions, mutable_cf_options_, merge_iter_builder,
2693
10.3k
          arena, file_level, level, i,
2694
10.3k
          /*skip_filters=*/false, max_file_size_for_l0_meta_pin_,
2695
10.3k
          allow_unprepared_value, read_seq);
2696
10.3k
      if (should_sample) {
2697
        // Count once for every L0 file. This is done per iterator creation
2698
        // rather than Seek(), while files in other levels are sampled on
2699
        // seek/next/prev.
2700
8
        sample_file_read_inc(file_level.files[i].file_metadata);
2701
8
      }
2702
10.3k
    }
2703
7.37k
  } else if (file_level.num_files > 0) {
2704
    // For levels > 0, we can use a concatenating iterator that sequentially
2705
    // walks through the non-overlapping files in the level, opening them
2706
    // lazily.
2707
2.46k
    auto* mem = arena->AllocateAligned(sizeof(LevelIterator));
2708
2.46k
    std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr = nullptr;
2709
2.46k
    auto level_iter = new (mem) LevelIterator(
2710
2.46k
        cfd_->table_cache(), read_options, soptions,
2711
2.46k
        cfd_->internal_comparator(), &file_level, mutable_cf_options_,
2712
2.46k
        cfd_->internal_stats()->GetFileReadHist(level),
2713
2.46k
        TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
2714
2.46k
        /*range_del_agg=*/nullptr,
2715
2.46k
        /*compaction_boundaries=*/nullptr, allow_unprepared_value,
2716
2.46k
        read_options.ignore_range_deletions ? nullptr : &tombstone_iter_ptr,
2717
2.46k
        read_seq, db_statistics_, clock_);
2718
2.46k
    if (read_options.ignore_range_deletions) {
2719
0
      merge_iter_builder->AddIterator(level_iter);
2720
2.46k
    } else {
2721
2.46k
      merge_iter_builder->AddPointAndTombstoneIterator(
2722
2.46k
          level_iter, nullptr /* tombstone_iter */, tombstone_iter_ptr);
2723
2.46k
    }
2724
2.46k
  }
2725
9.84k
}
2726
2727
Status Version::OverlapWithLevelIterator(const ReadOptions& read_options,
2728
                                         const FileOptions& file_options,
2729
                                         const Slice& smallest_user_key,
2730
                                         const Slice& largest_user_key,
2731
8.51k
                                         int level, bool* overlap) {
2732
8.51k
  assert(storage_info_.finalized_);
2733
2734
8.51k
  auto icmp = cfd_->internal_comparator();
2735
8.51k
  auto ucmp = icmp.user_comparator();
2736
2737
8.51k
  Arena arena;
2738
8.51k
  Status status;
2739
8.51k
  ReadRangeDelAggregator range_del_agg(&icmp,
2740
8.51k
                                       kMaxSequenceNumber /* upper_bound */);
2741
2742
8.51k
  *overlap = false;
2743
2744
8.51k
  if (level == 0) {
2745
4.09k
    for (size_t i = 0; i < storage_info_.LevelFilesBrief(0).num_files; i++) {
2746
3.00k
      const auto file = &storage_info_.LevelFilesBrief(0).files[i];
2747
3.00k
      if (AfterFile(ucmp, &smallest_user_key, file) ||
2748
2.94k
          BeforeFile(ucmp, &largest_user_key, file)) {
2749
742
        continue;
2750
742
      }
2751
2.26k
      ScopedArenaPtr<InternalIterator> iter(cfd_->table_cache()->NewIterator(
2752
2.26k
          read_options, file_options, cfd_->internal_comparator(),
2753
2.26k
          *file->file_metadata, &range_del_agg, mutable_cf_options_, nullptr,
2754
2.26k
          cfd_->internal_stats()->GetFileReadHist(0),
2755
2.26k
          TableReaderCaller::kUserIterator, &arena,
2756
2.26k
          /*skip_filters=*/false, /*level=*/0, max_file_size_for_l0_meta_pin_,
2757
2.26k
          /*smallest_compaction_key=*/nullptr,
2758
2.26k
          /*largest_compaction_key=*/nullptr,
2759
2.26k
          /*allow_unprepared_value=*/false, /*seek_target=*/nullptr,
2760
2.26k
          /*prefix_filtered_table=*/nullptr));
2761
2.26k
      status = OverlapWithIterator(ucmp, smallest_user_key, largest_user_key,
2762
2.26k
                                   iter.get(), overlap);
2763
2.26k
      if (!status.ok() || *overlap) {
2764
2.25k
        break;
2765
2.25k
      }
2766
2.26k
    }
2767
5.16k
  } else if (storage_info_.LevelFilesBrief(level).num_files > 0) {
2768
861
    auto mem = arena.AllocateAligned(sizeof(LevelIterator));
2769
861
    ScopedArenaPtr<InternalIterator> iter(new (mem) LevelIterator(
2770
861
        cfd_->table_cache(), read_options, file_options,
2771
861
        cfd_->internal_comparator(), &storage_info_.LevelFilesBrief(level),
2772
861
        mutable_cf_options_, cfd_->internal_stats()->GetFileReadHist(level),
2773
861
        TableReaderCaller::kUserIterator, IsFilterSkipped(level), level,
2774
861
        &range_del_agg, nullptr, false, nullptr, kMaxSequenceNumber,
2775
861
        db_statistics_, clock_));
2776
861
    status = OverlapWithIterator(ucmp, smallest_user_key, largest_user_key,
2777
861
                                 iter.get(), overlap);
2778
861
  }
2779
2780
8.51k
  if (status.ok() && *overlap == false &&
2781
5.49k
      range_del_agg.IsRangeOverlapped(smallest_user_key, largest_user_key,
2782
5.49k
                                      /*end_exclusive=*/false)) {
2783
0
    *overlap = true;
2784
0
  }
2785
8.51k
  return status;
2786
8.51k
}
2787
2788
VersionStorageInfo::VersionStorageInfo(
2789
    const InternalKeyComparator* internal_comparator,
2790
    const Comparator* user_comparator, int levels,
2791
    CompactionStyle compaction_style, VersionStorageInfo* ref_vstorage,
2792
    bool _force_consistency_checks,
2793
    EpochNumberRequirement epoch_number_requirement, SystemClock* clock,
2794
    uint32_t bottommost_file_compaction_delay,
2795
    OffpeakTimeOption offpeak_time_option,
2796
    PeriodicCompactionPhaseParams periodic_compaction_phase_params)
2797
303k
    : internal_comparator_(internal_comparator),
2798
303k
      user_comparator_(user_comparator),
2799
      // cfd is nullptr if Version is dummy
2800
303k
      num_levels_(levels),
2801
303k
      num_non_empty_levels_(0),
2802
303k
      file_indexer_(user_comparator),
2803
303k
      compaction_style_(compaction_style),
2804
303k
      files_(new std::vector<FileMetaData*>[num_levels_]),
2805
303k
      base_level_(num_levels_ == 1 ? -1 : 1),
2806
303k
      lowest_unnecessary_level_(-1),
2807
303k
      level_multiplier_(0.0),
2808
303k
      files_by_compaction_pri_(num_levels_),
2809
303k
      level0_non_overlapping_(false),
2810
303k
      next_file_to_compact_by_size_(num_levels_),
2811
303k
      compaction_score_(num_levels_),
2812
303k
      compaction_level_(num_levels_),
2813
303k
      l0_delay_trigger_count_(0),
2814
303k
      compact_cursor_(num_levels_),
2815
303k
      accumulated_file_size_(0),
2816
303k
      accumulated_raw_key_size_(0),
2817
303k
      accumulated_raw_value_size_(0),
2818
303k
      accumulated_num_non_deletions_(0),
2819
303k
      accumulated_num_deletions_(0),
2820
303k
      current_num_non_deletions_(0),
2821
303k
      current_num_deletions_(0),
2822
303k
      current_num_samples_(0),
2823
303k
      estimated_compaction_needed_bytes_(0),
2824
303k
      clock_(clock),
2825
303k
      bottommost_file_compaction_delay_(bottommost_file_compaction_delay),
2826
303k
      finalized_(false),
2827
303k
      force_consistency_checks_(_force_consistency_checks),
2828
303k
      epoch_number_requirement_(epoch_number_requirement),
2829
303k
      offpeak_time_option_(std::move(offpeak_time_option)),
2830
303k
      periodic_compaction_phase_params_(periodic_compaction_phase_params) {
2831
303k
  if (ref_vstorage != nullptr) {
2832
128k
    accumulated_file_size_ = ref_vstorage->accumulated_file_size_;
2833
128k
    accumulated_raw_key_size_ = ref_vstorage->accumulated_raw_key_size_;
2834
128k
    accumulated_raw_value_size_ = ref_vstorage->accumulated_raw_value_size_;
2835
128k
    accumulated_num_non_deletions_ =
2836
128k
        ref_vstorage->accumulated_num_non_deletions_;
2837
128k
    accumulated_num_deletions_ = ref_vstorage->accumulated_num_deletions_;
2838
128k
    current_num_non_deletions_ = ref_vstorage->current_num_non_deletions_;
2839
128k
    current_num_deletions_ = ref_vstorage->current_num_deletions_;
2840
128k
    current_num_samples_ = ref_vstorage->current_num_samples_;
2841
128k
    oldest_snapshot_seqnum_ = ref_vstorage->oldest_snapshot_seqnum_;
2842
128k
    preserve_time_min_seqno_ = ref_vstorage->preserve_time_min_seqno_;
2843
128k
    compact_cursor_ = ref_vstorage->compact_cursor_;
2844
128k
    compact_cursor_.resize(num_levels_);
2845
128k
  }
2846
303k
}
2847
2848
Version::Version(ColumnFamilyData* column_family_data, VersionSet* vset,
2849
                 const FileOptions& file_opt,
2850
                 const MutableCFOptions& mutable_cf_options,
2851
                 const std::shared_ptr<IOTracer>& io_tracer,
2852
                 uint64_t version_number,
2853
                 EpochNumberRequirement epoch_number_requirement)
2854
303k
    : env_(vset->env_),
2855
303k
      clock_(vset->clock_),
2856
303k
      cfd_(column_family_data),
2857
303k
      info_log_((cfd_ == nullptr) ? nullptr : cfd_->ioptions().logger),
2858
303k
      db_statistics_((cfd_ == nullptr) ? nullptr : cfd_->ioptions().stats),
2859
303k
      table_cache_((cfd_ == nullptr) ? nullptr : cfd_->table_cache()),
2860
303k
      blob_source_(cfd_ ? cfd_->blob_source() : nullptr),
2861
      merge_operator_(
2862
303k
          (cfd_ == nullptr) ? nullptr : cfd_->ioptions().merge_operator.get()),
2863
303k
      storage_info_(
2864
303k
          (cfd_ == nullptr) ? nullptr : &cfd_->internal_comparator(),
2865
303k
          (cfd_ == nullptr) ? nullptr : cfd_->user_comparator(),
2866
303k
          cfd_ == nullptr ? 0 : cfd_->NumberLevels(),
2867
303k
          cfd_ == nullptr ? kCompactionStyleLevel
2868
303k
                          : cfd_->ioptions().compaction_style,
2869
303k
          (cfd_ == nullptr || cfd_->current() == nullptr)
2870
303k
              ? nullptr
2871
303k
              : cfd_->current()->storage_info(),
2872
303k
          cfd_ == nullptr ? false : cfd_->ioptions().force_consistency_checks,
2873
303k
          epoch_number_requirement,
2874
303k
          cfd_ == nullptr ? nullptr : cfd_->ioptions().clock,
2875
303k
          cfd_ == nullptr ? 0
2876
303k
                          : mutable_cf_options.bottommost_file_compaction_delay,
2877
303k
          vset->offpeak_time_option(),
2878
303k
          cfd_ == nullptr
2879
303k
              ? PeriodicCompactionPhaseParams{}
2880
303k
              : vset->GetPeriodicCompactionPhaseParams(cfd_->GetID())),
2881
303k
      vset_(vset),
2882
303k
      next_(this),
2883
303k
      prev_(this),
2884
303k
      refs_(0),
2885
303k
      file_options_(file_opt),
2886
303k
      mutable_cf_options_(mutable_cf_options),
2887
      max_file_size_for_l0_meta_pin_(
2888
303k
          MaxFileSizeForL0MetaPin(mutable_cf_options_)),
2889
303k
      version_number_(version_number),
2890
303k
      io_tracer_(io_tracer) {}
2891
2892
Status Version::GetBlob(const ReadOptions& read_options, const Slice& user_key,
2893
                        const BlobIndex& blob_index,
2894
                        FilePrefetchBuffer* prefetch_buffer,
2895
0
                        PinnableSlice* value, uint64_t* bytes_read) const {
2896
0
  assert(value);
2897
2898
0
  if (blob_index.HasTTL() || blob_index.IsInlined()) {
2899
0
    return Status::Corruption("Unexpected TTL/inlined blob index");
2900
0
  }
2901
2902
0
  const uint64_t blob_file_number = blob_index.file_number();
2903
2904
0
  auto blob_file_meta = storage_info_.GetBlobFileMetaData(blob_file_number);
2905
0
  if (!blob_file_meta) {
2906
    // INTEGRITY CHECK -- do not weaken. The blob index must reference a known
2907
    // external blob file. No metadata (including file_number 0, the
2908
    // kInvalidBlobFileNumber / same-file "embedded" sentinel) means a corrupt
2909
    // index, or a same-file reference that should have been resolved by
2910
    // EmbeddedBlobResolvingIterator before reaching the integrated-BlobDB read
2911
    // path. See FileMetaData::UpdateBoundaries for the write-side tripwire.
2912
0
    return UnknownBlobFileStatus(storage_info_, blob_file_number);
2913
0
  }
2914
2915
0
  assert(blob_source_);
2916
0
  value->Reset();
2917
0
  const BlobFileOpenInfo blob_file{blob_file_number,
2918
0
                                   blob_file_meta->GetChecksumValue(),
2919
0
                                   blob_file_meta->GetChecksumMethod()};
2920
0
  const Status s = blob_source_->GetBlob(
2921
0
      read_options, user_key, blob_file, blob_index.offset(),
2922
0
      blob_file_meta->GetBlobFileSize(), blob_index.size(),
2923
0
      blob_index.compression(), prefetch_buffer, value, bytes_read);
2924
2925
0
  return s;
2926
0
}
2927
2928
Status Version::GetBlobRange(const ReadOptions& read_options,
2929
                             const Slice& user_key, const BlobIndex& blob_index,
2930
                             uint64_t range_offset, size_t range_length,
2931
0
                             PinnableSlice* value, uint64_t* bytes_read) const {
2932
0
  assert(value);
2933
2934
0
  if (blob_index.HasTTL() || blob_index.IsInlined()) {
2935
0
    return Status::Corruption("Unexpected TTL/inlined blob index");
2936
0
  }
2937
2938
  // A strict sub-range of a compressed blob cannot be decompressed in
2939
  // isolation; the caller resolves such columns whole and slices instead.
2940
0
  if (blob_index.compression() != kNoCompression) {
2941
0
    return Status::Corruption("Cannot range-read a compressed blob");
2942
0
  }
2943
2944
0
  const uint64_t blob_file_number = blob_index.file_number();
2945
2946
0
  auto blob_file_meta = storage_info_.GetBlobFileMetaData(blob_file_number);
2947
0
  if (!blob_file_meta) {
2948
    // INTEGRITY CHECK -- see Version::GetBlob. No metadata (including
2949
    // file_number 0, the same-file "embedded" sentinel) means a corrupt index
2950
    // or a same-file reference that the lazy caller must not route here.
2951
0
    return UnknownBlobFileStatus(storage_info_, blob_file_number);
2952
0
  }
2953
2954
0
  assert(blob_source_);
2955
0
  value->Reset();
2956
0
  const BlobFileOpenInfo blob_file{blob_file_number,
2957
0
                                   blob_file_meta->GetChecksumValue(),
2958
0
                                   blob_file_meta->GetChecksumMethod()};
2959
0
  return blob_source_->GetBlobRange(
2960
0
      read_options, user_key, blob_file, blob_index.offset(),
2961
0
      blob_file_meta->GetBlobFileSize(), blob_index.size(),
2962
0
      blob_index.compression(), range_offset, range_length, value, bytes_read);
2963
0
}
2964
2965
void Version::MultiGetBlobLazy(const ReadOptions& read_options,
2966
0
                               autovector<LazyBlobReadRequest>& reqs) const {
2967
0
  assert(blob_source_);
2968
2969
  // Turn the flat list of separate-file blob references into per-blob-file
2970
  // batches that BlobSource can each resolve with a single coalesced MultiRead.
2971
  // Whole-value and sub-range requests use different BlobSource entry points,
2972
  // so they are grouped independently:
2973
  //   whole_reqs -- batches of whole-value reads (BlobReadRequest)
2974
  //   range_reqs -- batches of sub-range reads (BlobRangeReadRequest)
2975
  // Each batch holds at most MultiGetContext::MAX_BATCH_SIZE requests (see the
2976
  // loop below), so one file may contribute several batches. whole_idx /
2977
  // range_idx map a file number to the index, within whole_reqs / range_reqs,
2978
  // of that file's current (most recently started, still fillable) batch.
2979
0
  autovector<BlobFileReadRequests> whole_reqs;
2980
0
  autovector<BlobFileRangeReadRequests> range_reqs;
2981
0
  std::unordered_map<uint64_t, size_t> whole_idx;
2982
0
  std::unordered_map<uint64_t, size_t> range_idx;
2983
2984
0
  for (auto& req : reqs) {
2985
0
    assert(req.user_key);
2986
0
    assert(req.blob_index);
2987
0
    assert(req.result);
2988
0
    assert(req.status);
2989
0
    const BlobIndex& blob_index = *req.blob_index;
2990
2991
0
    if (blob_index.HasTTL() || blob_index.IsInlined()) {
2992
0
      *req.status = Status::Corruption("Unexpected TTL/inlined blob index");
2993
0
      continue;
2994
0
    }
2995
2996
0
    const uint64_t file_number = blob_index.file_number();
2997
0
    auto blob_file_meta = storage_info_.GetBlobFileMetaData(file_number);
2998
0
    if (!blob_file_meta) {
2999
      // INTEGRITY CHECK -- see Version::GetBlob. A same-file/embedded reference
3000
      // (file_number 0) must be resolved via SameFileBlobReader, not here.
3001
0
      *req.status = UnknownBlobFileStatus(storage_info_, file_number);
3002
0
      continue;
3003
0
    }
3004
0
    const uint64_t file_size = blob_file_meta->GetBlobFileSize();
3005
0
    const BlobFileOpenInfo blob_file{file_number,
3006
0
                                     blob_file_meta->GetChecksumValue(),
3007
0
                                     blob_file_meta->GetChecksumMethod()};
3008
3009
0
    if (req.range_length == kWholeBlobLength) {
3010
      // Append to this file's current batch, or start a new batch when the file
3011
      // has none yet or its current batch is full. Capping each batch at
3012
      // MAX_BATCH_SIZE is required, not just an optimization:
3013
      // BlobFileReader::MultiGetBlob[Range] asserts a batch is at most
3014
      // MAX_BATCH_SIZE, and BlobSource tracks a batch's cache hits in a 64-bit
3015
      // mask -- while MultiGetEntityLazy does not otherwise bound how many keys
3016
      // can reference a single blob file.
3017
0
      size_t idx;
3018
0
      auto it = whole_idx.find(file_number);
3019
0
      if (it != whole_idx.end() && std::get<2>(whole_reqs[it->second]).size() <
3020
0
                                       MultiGetContext::MAX_BATCH_SIZE) {
3021
0
        idx = it->second;  // this file's current batch still has room
3022
0
      } else {
3023
0
        idx = whole_reqs.size();  // no batch yet for this file, or it is full
3024
0
        whole_idx[file_number] = idx;
3025
0
        whole_reqs.emplace_back(blob_file, file_size,
3026
0
                                autovector<BlobReadRequest>());
3027
0
      }
3028
0
      std::get<2>(whole_reqs[idx])
3029
0
          .emplace_back(*req.user_key, blob_index.offset(), blob_index.size(),
3030
0
                        blob_index.compression(), req.result, req.status);
3031
0
    } else {
3032
      // A strict sub-range of a compressed blob cannot be decompressed in
3033
      // isolation; the caller resolves such columns whole and slices instead.
3034
0
      if (blob_index.compression() != kNoCompression) {
3035
0
        *req.status = Status::Corruption("Cannot range-read a compressed blob");
3036
0
        continue;
3037
0
      }
3038
      // Same per-file batching and MAX_BATCH_SIZE cap as the whole-value path
3039
      // above.
3040
0
      size_t idx;
3041
0
      auto it = range_idx.find(file_number);
3042
0
      if (it != range_idx.end() && std::get<2>(range_reqs[it->second]).size() <
3043
0
                                       MultiGetContext::MAX_BATCH_SIZE) {
3044
0
        idx = it->second;  // this file's current batch still has room
3045
0
      } else {
3046
0
        idx = range_reqs.size();  // no batch yet for this file, or it is full
3047
0
        range_idx[file_number] = idx;
3048
0
        range_reqs.emplace_back(blob_file, file_size,
3049
0
                                autovector<BlobRangeReadRequest>());
3050
0
      }
3051
0
      std::get<2>(range_reqs[idx])
3052
0
          .emplace_back(*req.user_key, blob_index.offset(), blob_index.size(),
3053
0
                        req.range_offset, req.range_length, req.result,
3054
0
                        req.status);
3055
0
    }
3056
0
  }
3057
3058
  // Resolve every batch; BlobSource issues one coalesced MultiRead per batch.
3059
0
  if (!whole_reqs.empty()) {
3060
0
    blob_source_->MultiGetBlob(read_options, whole_reqs,
3061
0
                               /*bytes_read=*/nullptr);
3062
0
  }
3063
0
  if (!range_reqs.empty()) {
3064
0
    blob_source_->MultiGetBlobRange(read_options, range_reqs,
3065
0
                                    /*bytes_read=*/nullptr);
3066
0
  }
3067
0
}
3068
3069
void Version::MultiGetBlob(
3070
    const ReadOptions& read_options, MultiGetRange& range,
3071
0
    std::unordered_map<uint64_t, BlobReadContexts>& blob_ctxs) {
3072
0
  assert(!blob_ctxs.empty());
3073
3074
0
  autovector<BlobFileReadRequests> blob_reqs;
3075
3076
0
  for (auto& ctx : blob_ctxs) {
3077
0
    const auto file_number = ctx.first;
3078
0
    const auto blob_file_meta = storage_info_.GetBlobFileMetaData(file_number);
3079
3080
0
    autovector<BlobReadRequest> blob_reqs_in_file;
3081
0
    BlobReadContexts& blobs_in_file = ctx.second;
3082
0
    for (auto& blob : blobs_in_file) {
3083
0
      const BlobIndex& blob_index = blob.blob_index;
3084
0
      KeyContext* const key_context = blob.key_context;
3085
0
      assert(key_context);
3086
0
      assert(key_context->get_context);
3087
0
      assert(key_context->s);
3088
3089
0
      if (key_context->value) {
3090
0
        key_context->value->Reset();
3091
0
      } else {
3092
0
        assert(key_context->columns);
3093
0
        key_context->columns->Reset();
3094
0
      }
3095
3096
0
      if (!blob_file_meta) {
3097
        // INTEGRITY CHECK -- do not weaken; see Version::GetBlob and
3098
        // FileMetaData::UpdateBoundaries. A same-file/embedded reference must
3099
        // be resolved before reaching the integrated-BlobDB read path.
3100
0
        *key_context->s = UnknownBlobFileStatus(storage_info_, file_number);
3101
0
        continue;
3102
0
      }
3103
3104
0
      if (blob_index.HasTTL() || blob_index.IsInlined()) {
3105
0
        *key_context->s =
3106
0
            Status::Corruption("Unexpected TTL/inlined blob index");
3107
0
        continue;
3108
0
      }
3109
3110
0
      blob_reqs_in_file.emplace_back(
3111
0
          key_context->get_context->ukey_to_get_blob_value(),
3112
0
          blob_index.offset(), blob_index.size(), blob_index.compression(),
3113
0
          &blob.result, key_context->s);
3114
0
    }
3115
0
    if (blob_reqs_in_file.size() > 0) {
3116
0
      const auto file_size = blob_file_meta->GetBlobFileSize();
3117
0
      const BlobFileOpenInfo blob_file{file_number,
3118
0
                                       blob_file_meta->GetChecksumValue(),
3119
0
                                       blob_file_meta->GetChecksumMethod()};
3120
0
      blob_reqs.emplace_back(blob_file, file_size, blob_reqs_in_file);
3121
0
    }
3122
0
  }
3123
3124
0
  if (blob_reqs.size() > 0) {
3125
0
    blob_source_->MultiGetBlob(read_options, blob_reqs,
3126
0
                               /*bytes_read=*/nullptr);
3127
0
  }
3128
3129
0
  for (auto& ctx : blob_ctxs) {
3130
0
    BlobReadContexts& blobs_in_file = ctx.second;
3131
0
    for (auto& blob : blobs_in_file) {
3132
0
      KeyContext* const key_context = blob.key_context;
3133
0
      assert(key_context);
3134
0
      assert(key_context->get_context);
3135
0
      assert(key_context->s);
3136
3137
0
      if (key_context->s->ok()) {
3138
0
        if (key_context->value) {
3139
0
          *key_context->value = std::move(blob.result);
3140
0
          range.AddValueSize(key_context->value->size());
3141
0
        } else {
3142
0
          assert(key_context->columns);
3143
0
          key_context->columns->SetPlainValue(std::move(blob.result));
3144
0
          range.AddValueSize(key_context->columns->payload_size());
3145
0
        }
3146
        // MultiGetBlob() has already materialized the blob reference into the
3147
        // user-visible value, so clear this flag before later MultiGet
3148
        // post-processing checks whether further blob resolution is needed.
3149
0
        key_context->is_blob_index = false;
3150
3151
0
        if (range.GetValueSize() > read_options.value_size_soft_limit) {
3152
0
          *key_context->s = Status::Aborted();
3153
0
        }
3154
0
      } else if (key_context->s->IsIncomplete()) {
3155
        // read_options.read_tier == kBlockCacheTier
3156
        // Cannot read blob(s): no disk I/O allowed
3157
0
        auto& get_context = *(key_context->get_context);
3158
0
        get_context.MarkKeyMayExist();
3159
0
      }
3160
0
    }
3161
0
  }
3162
0
}
3163
3164
#ifdef USE_COROUTINES
3165
Status Version::ProcessBatch(
3166
    const ReadOptions& read_options, FilePickerMultiGet* batch,
3167
    std::vector<folly::coro::Task<Status>>& mget_tasks,
3168
    std::unordered_map<uint64_t, BlobReadContexts>* blob_ctxs,
3169
    autovector<FilePickerMultiGet, 4>& batches, std::deque<size_t>& waiting,
3170
    std::deque<size_t>& to_process, unsigned int& num_tasks_queued,
3171
    std::unordered_map<int, std::tuple<uint64_t, uint64_t, uint64_t>>&
3172
        mget_stats) {
3173
  FilePickerMultiGet& fp = *batch;
3174
  MultiGetRange range = fp.GetRange();
3175
  // Initialize a new empty range. Any keys that are not in this level will
3176
  // eventually become part of the new range.
3177
  MultiGetRange leftover(range, range.begin(), range.begin());
3178
  FdWithKeyRange* f = nullptr;
3179
  Status s;
3180
3181
  f = fp.GetNextFileInLevel();
3182
  while (!f) {
3183
    fp.PrepareNextLevelForSearch();
3184
    if (!fp.IsSearchEnded()) {
3185
      f = fp.GetNextFileInLevel();
3186
    } else {
3187
      break;
3188
    }
3189
  }
3190
  while (f) {
3191
    MultiGetRange file_range = fp.CurrentFileRange();
3192
    TableCache::TypedHandle* table_handle = nullptr;
3193
    bool skip_filters = IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
3194
                                        fp.IsHitFileLastInLevel());
3195
    bool skip_range_deletions = false;
3196
    if (!skip_filters) {
3197
      Status status = table_cache_->MultiGetFilter(
3198
          read_options, *internal_comparator(), *f->file_metadata,
3199
          mutable_cf_options_,
3200
          cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
3201
          fp.GetHitFileLevel(), &file_range, &table_handle);
3202
      if (status.ok()) {
3203
        skip_filters = true;
3204
        skip_range_deletions = true;
3205
      } else if (!status.IsNotSupported()) {
3206
        s = status;
3207
      }
3208
    }
3209
    if (!s.ok()) {
3210
      break;
3211
    }
3212
    // At this point, file_range contains any keys that are likely in this
3213
    // file. It may have false positives, but that's ok since higher level
3214
    // lookups for the key are dependent on this lookup anyway.
3215
    // Add the complement of file_range to leftover. That's the set of keys
3216
    // definitely not in this level.
3217
    // Subtract the complement of file_range from range, since they will be
3218
    // processed in a separate batch in parallel.
3219
    leftover += ~file_range;
3220
    range -= ~file_range;
3221
    if (!file_range.empty()) {
3222
      int level = fp.GetHitFileLevel();
3223
      auto stat = mget_stats.find(level);
3224
      if (stat == mget_stats.end()) {
3225
        auto entry = mget_stats.insert({level, {0, 0, 0}});
3226
        assert(entry.second);
3227
        stat = entry.first;
3228
      }
3229
3230
      if (waiting.empty() && to_process.empty() &&
3231
          !fp.RemainingOverlapInLevel() && leftover.empty() &&
3232
          mget_tasks.empty()) {
3233
        // All keys are in one SST file, so take the fast path
3234
        s = MultiGetFromSST(read_options, file_range, fp.GetHitFileLevel(),
3235
                            skip_filters, skip_range_deletions, f, *blob_ctxs,
3236
                            table_handle, std::get<0>(stat->second),
3237
                            std::get<1>(stat->second),
3238
                            std::get<2>(stat->second));
3239
      } else {
3240
        mget_tasks.emplace_back(MultiGetFromSSTCoroutine(
3241
            read_options, file_range, fp.GetHitFileLevel(), skip_filters,
3242
            skip_range_deletions, f, *blob_ctxs, table_handle,
3243
            std::get<0>(stat->second), std::get<1>(stat->second),
3244
            std::get<2>(stat->second)));
3245
        ++num_tasks_queued;
3246
      }
3247
    }
3248
    if (fp.KeyMaySpanNextFile() && !file_range.empty()) {
3249
      break;
3250
    }
3251
    f = fp.GetNextFileInLevel();
3252
  }
3253
  // Split the current batch only if some keys are likely in this level and
3254
  // some are not. Only split if we're done with this level, i.e f is null.
3255
  // Otherwise, it means there are more files in this level to look at.
3256
  if (s.ok() && !f && !leftover.empty() && !range.empty()) {
3257
    fp.ReplaceRange(range);
3258
    batches.emplace_back(&leftover, fp);
3259
    to_process.emplace_back(batches.size() - 1);
3260
  }
3261
  // 1. If f is non-null, that means we might not be done with this level.
3262
  //    This can happen if one of the keys is the last key in the file, i.e
3263
  //    fp.KeyMaySpanNextFile() is true.
3264
  // 2. If range is empty, then we're done with this range and no need to
3265
  //    prepare the next level
3266
  // 3. If some tasks were queued for this range, then the next level will be
3267
  //    prepared after executing those tasks
3268
  if (!f && !range.empty() && !num_tasks_queued) {
3269
    fp.PrepareNextLevelForSearch();
3270
  }
3271
  return s;
3272
}
3273
3274
Status Version::MultiGetAsync(
3275
    const ReadOptions& options, MultiGetRange* range,
3276
    std::unordered_map<uint64_t, BlobReadContexts>* blob_ctxs) {
3277
  autovector<FilePickerMultiGet, 4> batches;
3278
  std::deque<size_t> waiting;
3279
  std::deque<size_t> to_process;
3280
  Status s;
3281
  std::vector<folly::coro::Task<Status>> mget_tasks;
3282
  std::unordered_map<int, std::tuple<uint64_t, uint64_t, uint64_t>> mget_stats;
3283
3284
  // Create the initial batch with the input range
3285
  batches.emplace_back(range, &storage_info_.level_files_brief_,
3286
                       storage_info_.num_non_empty_levels_,
3287
                       &storage_info_.file_indexer_, user_comparator(),
3288
                       internal_comparator());
3289
  to_process.emplace_back(0);
3290
3291
  while (!to_process.empty()) {
3292
    // As we process a batch, it may get split into two. So reserve space for
3293
    // an additional batch in the autovector in order to prevent later moves
3294
    // of elements in ProcessBatch().
3295
    batches.reserve(batches.size() + 1);
3296
3297
    size_t idx = to_process.front();
3298
    FilePickerMultiGet* batch = &batches.at(idx);
3299
    unsigned int num_tasks_queued = 0;
3300
    to_process.pop_front();
3301
    if (batch->IsSearchEnded() || batch->GetRange().empty()) {
3302
      // If to_process is empty, i.e no more batches to look at, then we need
3303
      // schedule the enqueued coroutines and wait for them. Otherwise, we
3304
      // skip this batch and move to the next one in to_process.
3305
      if (!to_process.empty()) {
3306
        continue;
3307
      }
3308
    } else {
3309
      // Look through one level. This may split the batch and enqueue it to
3310
      // to_process
3311
      s = ProcessBatch(options, batch, mget_tasks, blob_ctxs, batches, waiting,
3312
                       to_process, num_tasks_queued, mget_stats);
3313
      // If ProcessBatch didn't enqueue any coroutine tasks, it means all
3314
      // keys were filtered out. So put the batch back in to_process to
3315
      // lookup in the next level
3316
      if (!num_tasks_queued && !batch->IsSearchEnded()) {
3317
        // Put this back in the processing queue
3318
        to_process.emplace_back(idx);
3319
      } else if (num_tasks_queued) {
3320
        waiting.emplace_back(idx);
3321
      }
3322
    }
3323
    // If ProcessBatch() returned an error, then schedule the enqueued
3324
    // coroutines and wait for them, then abort the MultiGet.
3325
    if (to_process.empty() || !s.ok()) {
3326
      if (mget_tasks.size() > 0) {
3327
        assert(waiting.size());
3328
        RecordTick(db_statistics_, MULTIGET_COROUTINE_COUNT, mget_tasks.size());
3329
        // Collect all results so far
3330
        std::vector<Status> statuses =
3331
            folly::coro::blockingWait(co_withExecutor(
3332
                &range->context()->executor(),
3333
                folly::coro::collectAllRange(std::move(mget_tasks))));
3334
        mget_tasks.clear();
3335
        if (s.ok()) {
3336
          for (Status stat : statuses) {
3337
            if (!stat.ok()) {
3338
              s = std::move(stat);
3339
              break;
3340
            }
3341
          }
3342
        }
3343
3344
        if (!s.ok()) {
3345
          break;
3346
        }
3347
3348
        for (size_t wait_idx : waiting) {
3349
          FilePickerMultiGet& fp = batches.at(wait_idx);
3350
          // 1. If fp.GetHitFile() is non-null, then there could be more
3351
          // overlap in this level. So skip preparing next level.
3352
          // 2. If fp.GetRange() is empty, then this batch is completed
3353
          // and no need to prepare the next level.
3354
          if (!fp.GetHitFile() && !fp.GetRange().empty()) {
3355
            fp.PrepareNextLevelForSearch();
3356
          }
3357
        }
3358
        to_process.swap(waiting);
3359
      } else {
3360
        assert(!s.ok() || waiting.size() == 0);
3361
      }
3362
    }
3363
    if (!s.ok()) {
3364
      break;
3365
    }
3366
  }
3367
3368
  uint64_t num_levels = 0;
3369
  for (auto& stat : mget_stats) {
3370
    if (stat.first == 0) {
3371
      num_levels += std::get<2>(stat.second);
3372
    } else {
3373
      num_levels++;
3374
    }
3375
3376
    uint64_t num_meta_reads =
3377
        std::get<0>(stat.second) + std::get<1>(stat.second);
3378
    uint64_t num_sst_reads = std::get<2>(stat.second);
3379
    if (num_meta_reads > 0) {
3380
      RecordInHistogram(db_statistics_,
3381
                        NUM_INDEX_AND_FILTER_BLOCKS_READ_PER_LEVEL,
3382
                        num_meta_reads);
3383
    }
3384
    if (num_sst_reads > 0) {
3385
      RecordInHistogram(db_statistics_, NUM_SST_READ_PER_LEVEL, num_sst_reads);
3386
    }
3387
  }
3388
  if (num_levels > 0) {
3389
    RecordInHistogram(db_statistics_, NUM_LEVEL_READ_PER_MULTIGET, num_levels);
3390
  }
3391
3392
  return s;
3393
}
3394
#endif
3395
3396
4.42k
bool Version::IsFilterSkipped(int level, bool is_file_last_in_level) {
3397
  // Reaching the bottom level implies misses at all upper levels, so we'll
3398
  // skip checking the filters when we predict a hit.
3399
4.42k
  return cfd_->ioptions().optimize_filters_for_hits &&
3400
0
         (level > 0 || is_file_last_in_level) &&
3401
0
         level == storage_info_.num_non_empty_levels() - 1;
3402
4.42k
}
3403
3404
215k
void VersionStorageInfo::GenerateLevelFilesBrief() {
3405
215k
  level_files_brief_.resize(num_non_empty_levels_);
3406
432k
  for (int level = 0; level < num_non_empty_levels_; level++) {
3407
216k
    DoGenerateLevelFilesBrief(&level_files_brief_[level], files_[level],
3408
216k
                              &arena_);
3409
216k
  }
3410
215k
}
3411
3412
void VersionStorageInfo::PrepareForVersionAppend(
3413
    const ImmutableOptions& immutable_options,
3414
215k
    const MutableCFOptions& mutable_cf_options) {
3415
215k
  ComputeCompensatedSizes();
3416
215k
  UpdateNumNonEmptyLevels();
3417
215k
  CalculateBaseBytes(immutable_options, mutable_cf_options);
3418
215k
  UpdateFilesByCompactionPri(immutable_options, mutable_cf_options);
3419
215k
  GenerateFileIndexer();
3420
215k
  GenerateLevelFilesBrief();
3421
215k
  GenerateLevel0NonOverlapping();
3422
215k
  GenerateBottommostFiles();
3423
215k
  GenerateFileLocationIndex();
3424
215k
}
3425
3426
void Version::PrepareAppend(const ReadOptions& read_options,
3427
215k
                            bool update_stats) {
3428
215k
  TEST_SYNC_POINT_CALLBACK(
3429
215k
      "Version::PrepareAppend:forced_check",
3430
215k
      static_cast<void*>(&storage_info_.force_consistency_checks_));
3431
3432
215k
  if (update_stats) {
3433
128k
    UpdateAccumulatedStats(read_options);
3434
128k
  }
3435
3436
215k
  storage_info_.PrepareForVersionAppend(cfd_->ioptions(), mutable_cf_options_);
3437
215k
}
3438
3439
bool Version::MaybeInitializeFileMetaData(const ReadOptions& read_options,
3440
                                          FileMetaData* file_meta,
3441
303k
                                          bool populate_referenced_blob_bytes) {
3442
303k
  if (file_meta->init_stats_from_file || file_meta->compensated_file_size > 0) {
3443
209k
    return false;
3444
209k
  }
3445
93.9k
  std::shared_ptr<const TableProperties> tp;
3446
93.9k
  Status s = GetTableProperties(read_options, &tp, file_meta);
3447
93.9k
  file_meta->init_stats_from_file = true;
3448
93.9k
  if (!s.ok()) {
3449
0
    ROCKS_LOG_ERROR(vset_->db_options_->info_log,
3450
0
                    "Unable to load table properties for file %" PRIu64
3451
0
                    " --- %s\n",
3452
0
                    file_meta->fd.GetNumber(), s.ToString().c_str());
3453
0
    return false;
3454
0
  }
3455
93.9k
  if (tp.get() == nullptr) {
3456
0
    return false;
3457
0
  }
3458
93.9k
  file_meta->num_entries = tp->num_entries;
3459
93.9k
  file_meta->num_deletions = tp->num_deletions;
3460
93.9k
  file_meta->raw_value_size = tp->raw_value_size;
3461
93.9k
  file_meta->raw_key_size = tp->raw_key_size;
3462
93.9k
  if (populate_referenced_blob_bytes) {
3463
93.9k
    file_meta->referenced_blob_bytes = tp->referenced_blob_bytes;
3464
93.9k
  }
3465
93.9k
  file_meta->num_range_deletions = tp->num_range_deletions;
3466
  // Ensure new invariants on old files
3467
93.9k
  file_meta->num_deletions =
3468
93.9k
      std::max(tp->num_deletions, tp->num_range_deletions);
3469
93.9k
  file_meta->num_entries = std::max(tp->num_entries, file_meta->num_deletions);
3470
93.9k
  return true;
3471
93.9k
}
3472
3473
93.9k
void VersionStorageInfo::UpdateAccumulatedStats(FileMetaData* file_meta) {
3474
93.9k
  TEST_SYNC_POINT_CALLBACK("VersionStorageInfo::UpdateAccumulatedStats",
3475
93.9k
                           nullptr);
3476
3477
93.9k
  assert(file_meta->init_stats_from_file);
3478
93.9k
  accumulated_file_size_ += file_meta->fd.GetFileSize();
3479
93.9k
  accumulated_raw_key_size_ += file_meta->raw_key_size;
3480
93.9k
  accumulated_raw_value_size_ += file_meta->raw_value_size;
3481
93.9k
  assert(file_meta->num_entries >= file_meta->num_deletions);
3482
93.9k
  accumulated_num_non_deletions_ +=
3483
93.9k
      file_meta->num_entries - file_meta->num_deletions;
3484
93.9k
  accumulated_num_deletions_ += file_meta->num_deletions;
3485
3486
93.9k
  current_num_non_deletions_ +=
3487
93.9k
      file_meta->num_entries - file_meta->num_deletions;
3488
93.9k
  current_num_deletions_ += file_meta->num_deletions;
3489
93.9k
  current_num_samples_++;
3490
93.9k
}
3491
3492
11.9k
void VersionStorageInfo::RemoveCurrentStats(FileMetaData* file_meta) {
3493
11.9k
  if (file_meta->init_stats_from_file) {
3494
11.9k
    current_num_non_deletions_ -=
3495
11.9k
        file_meta->num_entries - file_meta->num_deletions;
3496
11.9k
    current_num_deletions_ -= file_meta->num_deletions;
3497
11.9k
    current_num_samples_--;
3498
11.9k
  }
3499
11.9k
}
3500
3501
128k
void Version::UpdateAccumulatedStats(const ReadOptions& read_options) {
3502
  // maximum number of table properties loaded from files.
3503
128k
  const int kMaxInitCount = 20;
3504
128k
  int init_count = 0;
3505
  // here only the first kMaxInitCount files which haven't been
3506
  // initialized from file will be updated with num_deletions.
3507
  // The motivation here is to cap the maximum I/O per Version creation.
3508
  // The reason for choosing files from lower-level instead of higher-level
3509
  // is that such design is able to propagate the initialization from
3510
  // lower-level to higher-level:  When the num_deletions of lower-level
3511
  // files are updated, it will make the lower-level files have accurate
3512
  // compensated_file_size, making lower-level to higher-level compaction
3513
  // will be triggered, which creates higher-level files whose num_deletions
3514
  // will be updated here.
3515
128k
  for (int level = 0;
3516
1.02M
       level < storage_info_.num_levels_ && init_count < kMaxInitCount;
3517
897k
       ++level) {
3518
897k
    for (auto* file_meta : storage_info_.files_[level]) {
3519
173k
      if (MaybeInitializeFileMetaData(
3520
173k
              read_options, file_meta,
3521
173k
              /*populate_referenced_blob_bytes=*/true)) {
3522
        // each FileMeta will be initialized only once.
3523
93.9k
        storage_info_.UpdateAccumulatedStats(file_meta);
3524
        // when option "max_open_files" is -1, all the file metadata has
3525
        // already been read, so MaybeInitializeFileMetaData() won't incur
3526
        // any I/O cost. "max_open_files=-1" means that the table cache passed
3527
        // to the VersionSet and then to the ColumnFamilySet has a size of
3528
        // TableCache::kInfiniteCapacity
3529
93.9k
        if (vset_->GetColumnFamilySet()->get_table_cache()->GetCapacity() ==
3530
93.9k
            TableCache::kInfiniteCapacity) {
3531
93.9k
          continue;
3532
93.9k
        }
3533
0
        if (++init_count >= kMaxInitCount) {
3534
0
          break;
3535
0
        }
3536
0
      }
3537
173k
    }
3538
897k
  }
3539
  // In case all sampled-files contain only deletion entries, then we
3540
  // load the table-property of a file in higher-level to initialize
3541
  // that value.
3542
128k
  for (int level = storage_info_.num_levels_ - 1;
3543
931k
       storage_info_.accumulated_raw_value_size_ == 0 && level >= 0; --level) {
3544
803k
    for (int i = static_cast<int>(storage_info_.files_[level].size()) - 1;
3545
932k
         storage_info_.accumulated_raw_value_size_ == 0 && i >= 0; --i) {
3546
129k
      if (MaybeInitializeFileMetaData(
3547
129k
              read_options, storage_info_.files_[level][i],
3548
129k
              /*populate_referenced_blob_bytes=*/true)) {
3549
0
        storage_info_.UpdateAccumulatedStats(storage_info_.files_[level][i]);
3550
0
      }
3551
129k
    }
3552
803k
  }
3553
128k
}
3554
3555
215k
void VersionStorageInfo::ComputeCompensatedSizes() {
3556
215k
  static const int kDeletionWeightOnCompaction = 2;
3557
215k
  uint64_t average_value_size = GetAverageValueSize();
3558
3559
  // compute the compensated size
3560
1.72M
  for (int level = 0; level < num_levels_; level++) {
3561
1.51M
    for (auto* file_meta : files_[level]) {
3562
      // Here we only compute compensated_file_size for those file_meta
3563
      // which compensated_file_size is uninitialized (== 0). This is true only
3564
      // for files that have been created right now and no other thread has
3565
      // access to them. That's why we can safely mutate compensated_file_size.
3566
173k
      if (file_meta->compensated_file_size == 0) {
3567
93.9k
        file_meta->compensated_file_size = file_meta->fd.GetFileSize();
3568
        // Here we only boost the size of deletion entries of a file only
3569
        // when the number of deletion entries is greater than the number of
3570
        // non-deletion entries in the file.  The motivation here is that in
3571
        // a stable workload, the number of deletion entries should be roughly
3572
        // equal to the number of non-deletion entries.  If we compensate the
3573
        // size of deletion entries in a stable workload, the deletion
3574
        // compensation logic might introduce unwanted effet which changes the
3575
        // shape of LSM tree.
3576
93.9k
        if ((file_meta->num_deletions - file_meta->num_range_deletions) * 2 >=
3577
93.9k
            file_meta->num_entries) {
3578
48.9k
          file_meta->compensated_file_size +=
3579
48.9k
              ((file_meta->num_deletions - file_meta->num_range_deletions) * 2 -
3580
48.9k
               file_meta->num_entries) *
3581
48.9k
              average_value_size * kDeletionWeightOnCompaction;
3582
48.9k
        }
3583
93.9k
        file_meta->compensated_file_size +=
3584
93.9k
            file_meta->compensated_range_deletion_size;
3585
93.9k
      }
3586
173k
    }
3587
1.51M
  }
3588
215k
}
3589
3590
2.37M
int VersionStorageInfo::MaxInputLevel() const {
3591
2.37M
  if (compaction_style_ == kCompactionStyleLevel) {
3592
2.37M
    return num_levels() - 2;
3593
2.37M
  }
3594
0
  return 0;
3595
2.37M
}
3596
3597
223k
int VersionStorageInfo::MaxOutputLevel(bool allow_ingest_behind) const {
3598
223k
  if (allow_ingest_behind) {
3599
0
    assert(num_levels() > 1);
3600
0
    return num_levels() - 2;
3601
0
  }
3602
223k
  return num_levels() - 1;
3603
223k
}
3604
3605
void VersionStorageInfo::EstimateCompactionBytesNeeded(
3606
221k
    const MutableCFOptions& mutable_cf_options) {
3607
  // Only implemented for level-based compaction
3608
221k
  if (compaction_style_ != kCompactionStyleLevel) {
3609
0
    estimated_compaction_needed_bytes_ = 0;
3610
0
    return;
3611
0
  }
3612
3613
  // Start from Level 0, if level 0 qualifies compaction to level 1,
3614
  // we estimate the size of compaction.
3615
  // Then we move on to the next level and see whether it qualifies compaction
3616
  // to the next level. The size of the level is estimated as the actual size
3617
  // on the level plus the input bytes from the previous level if there is any.
3618
  // If it exceeds, take the exceeded bytes as compaction input and add the size
3619
  // of the compaction size to tatal size.
3620
  // We keep doing it to Level 2, 3, etc, until the last level and return the
3621
  // accumulated bytes.
3622
3623
221k
  uint64_t bytes_compact_to_next_level = 0;
3624
221k
  uint64_t level_size = 0;
3625
221k
  for (auto* f : files_[0]) {
3626
127k
    level_size += f->fd.GetFileSize();
3627
127k
  }
3628
  // Level 0
3629
221k
  bool level0_compact_triggered = false;
3630
221k
  if (static_cast<int>(files_[0].size()) >=
3631
221k
          mutable_cf_options.level0_file_num_compaction_trigger ||
3632
214k
      level_size >= mutable_cf_options.max_bytes_for_level_base) {
3633
6.85k
    level0_compact_triggered = true;
3634
6.85k
    estimated_compaction_needed_bytes_ = level_size;
3635
6.85k
    bytes_compact_to_next_level = level_size;
3636
214k
  } else {
3637
214k
    estimated_compaction_needed_bytes_ = 0;
3638
214k
  }
3639
3640
  // Level 1 and up.
3641
221k
  uint64_t bytes_next_level = 0;
3642
221k
  for (int level = base_level(); level <= MaxInputLevel(); level++) {
3643
0
    level_size = 0;
3644
0
    if (bytes_next_level > 0) {
3645
#ifndef NDEBUG
3646
      uint64_t level_size2 = 0;
3647
      for (auto* f : files_[level]) {
3648
        level_size2 += f->fd.GetFileSize();
3649
      }
3650
      assert(level_size2 == bytes_next_level);
3651
#endif
3652
0
      level_size = bytes_next_level;
3653
0
      bytes_next_level = 0;
3654
0
    } else {
3655
0
      for (auto* f : files_[level]) {
3656
0
        level_size += f->fd.GetFileSize();
3657
0
      }
3658
0
    }
3659
0
    if (level == base_level() && level0_compact_triggered) {
3660
      // Add base level size to compaction if level0 compaction triggered.
3661
0
      estimated_compaction_needed_bytes_ += level_size;
3662
0
    }
3663
    // Add size added by previous compaction
3664
0
    level_size += bytes_compact_to_next_level;
3665
0
    bytes_compact_to_next_level = 0;
3666
0
    uint64_t level_target = MaxBytesForLevel(level);
3667
0
    if (level_size > level_target) {
3668
0
      bytes_compact_to_next_level = level_size - level_target;
3669
      // Estimate the actual compaction fan-out ratio as size ratio between
3670
      // the two levels.
3671
3672
0
      assert(bytes_next_level == 0);
3673
0
      if (level + 1 < num_levels_) {
3674
0
        for (auto* f : files_[level + 1]) {
3675
0
          bytes_next_level += f->fd.GetFileSize();
3676
0
        }
3677
0
      }
3678
0
      if (bytes_next_level > 0) {
3679
0
        assert(level_size > 0);
3680
0
        estimated_compaction_needed_bytes_ += static_cast<uint64_t>(
3681
0
            static_cast<double>(bytes_compact_to_next_level) *
3682
0
            (static_cast<double>(bytes_next_level) /
3683
0
                 static_cast<double>(level_size) +
3684
0
             1));
3685
0
      }
3686
0
    }
3687
0
  }
3688
221k
}
3689
3690
namespace {
3691
uint32_t GetExpiredTtlFilesCount(const ImmutableOptions& ioptions,
3692
                                 const MutableCFOptions& mutable_cf_options,
3693
0
                                 const std::vector<FileMetaData*>& files) {
3694
0
  uint32_t ttl_expired_files_count = 0;
3695
3696
0
  int64_t _current_time;
3697
0
  auto status = ioptions.clock->GetCurrentTime(&_current_time);
3698
0
  if (status.ok()) {
3699
0
    const uint64_t current_time = static_cast<uint64_t>(_current_time);
3700
0
    for (FileMetaData* f : files) {
3701
0
      if (!f->being_compacted) {
3702
0
        uint64_t oldest_ancestor_time = f->TryGetOldestAncestorTime();
3703
0
        if (oldest_ancestor_time != 0 &&
3704
0
            oldest_ancestor_time < (current_time - mutable_cf_options.ttl)) {
3705
0
          ttl_expired_files_count++;
3706
0
        }
3707
0
      }
3708
0
    }
3709
0
  }
3710
0
  return ttl_expired_files_count;
3711
0
}
3712
3713
bool ShouldChangeFileTemperature(const ImmutableOptions& ioptions,
3714
                                 const MutableCFOptions& mutable_cf_options,
3715
0
                                 const std::vector<FileMetaData*>& files) {
3716
0
  const std::vector<FileTemperatureAge>& ages =
3717
0
      mutable_cf_options.compaction_options_fifo
3718
0
          .file_temperature_age_thresholds;
3719
0
  if (ages.empty()) {
3720
0
    return false;
3721
0
  }
3722
0
  if (files.empty()) {
3723
0
    return false;
3724
0
  }
3725
0
  int64_t _current_time;
3726
0
  auto status = ioptions.clock->GetCurrentTime(&_current_time);
3727
0
  const uint64_t current_time = static_cast<uint64_t>(_current_time);
3728
  // This is the same logic used in
3729
  // FIFOCompactionPicker::PickTemperatureChangeCompaction().
3730
0
  if (status.ok() && current_time >= ages[0].age) {
3731
0
    uint64_t create_time_threshold = current_time - ages[0].age;
3732
0
    Temperature target_temp;
3733
0
    assert(files.size() >= 1);
3734
0
    for (size_t index = files.size(); index >= 1; --index) {
3735
0
      FileMetaData* cur_file = files[index - 1];
3736
0
      FileMetaData* prev_file = index < 2 ? nullptr : files[index - 2];
3737
0
      if (!cur_file->being_compacted) {
3738
0
        uint64_t est_newest_key_time = cur_file->TryGetNewestKeyTime(prev_file);
3739
        // Newer file could have newest_key_time populated
3740
0
        if (est_newest_key_time == kUnknownNewestKeyTime) {
3741
0
          continue;
3742
0
        }
3743
0
        if (est_newest_key_time > create_time_threshold) {
3744
0
          return false;
3745
0
        }
3746
0
        target_temp = ages[0].temperature;
3747
0
        for (size_t i = 1; i < ages.size(); ++i) {
3748
0
          if (current_time >= ages[i].age &&
3749
0
              est_newest_key_time <= current_time - ages[i].age) {
3750
0
            target_temp = ages[i].temperature;
3751
0
          }
3752
0
        }
3753
0
        if (cur_file->temperature != target_temp) {
3754
0
          return true;
3755
0
        }
3756
0
      }
3757
0
    }
3758
0
  }
3759
0
  return false;
3760
0
}
3761
}  // anonymous namespace
3762
3763
void VersionStorageInfo::ComputeCompactionScore(
3764
    const ImmutableOptions& immutable_options,
3765
    const MutableCFOptions& mutable_cf_options,
3766
221k
    const std::string& full_history_ts_low) {
3767
221k
  double total_downcompact_bytes = 0.0;
3768
  // Historically, score is defined as actual bytes in a level divided by
3769
  // the level's target size, and 1.0 is the threshold for triggering
3770
  // compaction. Higher score means higher prioritization.
3771
  // Now we keep the compaction triggering condition, but consider more
3772
  // factors for prioritization, while still keeping the 1.0 threshold.
3773
  // In order to provide flexibility for reducing score while still
3774
  // maintaining it to be over 1.0, we scale the original score by 10x
3775
  // if it is larger than 1.0.
3776
221k
  const double kScoreScale = 10.0;
3777
221k
  int max_output_level =
3778
221k
      MaxOutputLevel(immutable_options.cf_allow_ingest_behind ||
3779
221k
                     immutable_options.allow_ingest_behind);
3780
1.55M
  for (int level = 0; level <= MaxInputLevel(); level++) {
3781
1.33M
    double score;
3782
1.33M
    if (level == 0) {
3783
      // We treat level-0 specially by bounding the number of files
3784
      // instead of number of bytes for two reasons:
3785
      //
3786
      // (1) With larger write-buffer sizes, it is nice not to do too
3787
      // many level-0 compactions.
3788
      //
3789
      // (2) The files in level-0 are merged on every read and
3790
      // therefore we wish to avoid too many files when the individual
3791
      // file size is small (perhaps because of a small write-buffer
3792
      // setting, or very high compression ratios, or lots of
3793
      // overwrites/deletions).
3794
221k
      int num_sorted_runs = 0;
3795
221k
      uint64_t total_size = 0;
3796
221k
      for (auto* f : files_[level]) {
3797
127k
        total_downcompact_bytes += static_cast<double>(f->fd.GetFileSize());
3798
127k
        if (!f->being_compacted) {
3799
114k
          total_size += f->compensated_file_size;
3800
114k
          num_sorted_runs++;
3801
114k
        }
3802
127k
      }
3803
221k
      if (compaction_style_ == kCompactionStyleUniversal) {
3804
        // For universal compaction, we use level0 score to indicate
3805
        // compaction score for the whole DB. Adding other levels as if
3806
        // they are L0 files.
3807
0
        for (int i = 1; i <= max_output_level; i++) {
3808
          // It's possible that a subset of the files in a level may be in a
3809
          // compaction, due to delete triggered compaction or trivial move.
3810
          // In that case, the below check may not catch a level being
3811
          // compacted as it only checks the first file. The worst that can
3812
          // happen is a scheduled compaction thread will find nothing to do.
3813
0
          if (!files_[i].empty() && !files_[i][0]->being_compacted) {
3814
0
            num_sorted_runs++;
3815
0
          }
3816
0
        }
3817
0
      }
3818
3819
221k
      if (compaction_style_ == kCompactionStyleFIFO) {
3820
0
        const auto& fifo_opts = mutable_cf_options.compaction_options_fifo;
3821
0
        uint64_t effective_size = total_size;
3822
0
        uint64_t effective_max = fifo_opts.max_table_files_size;
3823
0
        if (fifo_opts.max_data_files_size > 0) {
3824
          // Blob-aware: include blob file sizes in the total
3825
0
          effective_size += GetBlobStats().total_file_size;
3826
0
          effective_max = fifo_opts.max_data_files_size;
3827
0
        }
3828
0
        if (effective_max == 0) {
3829
          // avoid divide 0
3830
0
          effective_max = 1;
3831
0
        }
3832
0
        score = static_cast<double>(effective_size) / effective_max;
3833
0
        if (score < 1 && fifo_opts.allow_compaction) {
3834
0
          score = std::max(
3835
0
              static_cast<double>(num_sorted_runs) /
3836
0
                  mutable_cf_options.level0_file_num_compaction_trigger,
3837
0
              score);
3838
0
        }
3839
0
        if (score < 1 && mutable_cf_options.ttl > 0) {
3840
0
          score =
3841
0
              std::max(static_cast<double>(GetExpiredTtlFilesCount(
3842
0
                           immutable_options, mutable_cf_options, files_[0])),
3843
0
                       score);
3844
0
        }
3845
0
        if (score < 1 &&
3846
0
            ShouldChangeFileTemperature(immutable_options, mutable_cf_options,
3847
0
                                        files_[0])) {
3848
          // For FIFO, just need a large enough score to trigger compaction.
3849
0
          const double kScoreForNeedCompaction = 1.1;
3850
0
          score = kScoreForNeedCompaction;
3851
0
        }
3852
221k
      } else {
3853
        // For universal compaction, if a user configures `max_read_amp`, then
3854
        // the score may be a false positive signal.
3855
        // `level0_file_num_compaction_trigger` is used as a trigger to check
3856
        // if there is any compaction work to do.
3857
221k
        score = static_cast<double>(num_sorted_runs) /
3858
221k
                mutable_cf_options.level0_file_num_compaction_trigger;
3859
221k
        if (compaction_style_ == kCompactionStyleLevel && num_levels() > 1) {
3860
          // Level-based involves L0->L0 compactions that can lead to oversized
3861
          // L0 files. Take into account size as well to avoid later giant
3862
          // compactions to the base level.
3863
          // If score in L0 is always too high, L0->LBase will always be
3864
          // prioritized over LBase->LBase+1 compaction and LBase will
3865
          // accumulate to too large. But if L0 score isn't high enough, L0 will
3866
          // accumulate and data is not moved to LBase fast enough. The score
3867
          // calculation below takes into account L0 size vs LBase size.
3868
221k
          if (immutable_options.level_compaction_dynamic_level_bytes) {
3869
221k
            if (total_size >= mutable_cf_options.max_bytes_for_level_base) {
3870
              // When calculating estimated_compaction_needed_bytes, we assume
3871
              // L0 is qualified as pending compactions. We will need to make
3872
              // sure that it qualifies for compaction.
3873
              // It might be guaranteed by logic below anyway, but we are
3874
              // explicit here to make sure we don't stop writes with no
3875
              // compaction scheduled.
3876
0
              score = std::max(score, 1.01);
3877
0
            }
3878
221k
            if (total_size > level_max_bytes_[base_level_]) {
3879
              // In this case, we compare L0 size with actual LBase size and
3880
              // make sure score is more than 1.0 (10.0 after scaled) if L0 is
3881
              // larger than LBase. Since LBase score = LBase size /
3882
              // (target size + total_downcompact_bytes) where
3883
              // total_downcompact_bytes = total_size > LBase size,
3884
              // LBase score is lower than 10.0. So L0->LBase is prioritized
3885
              // over LBase -> LBase+1.
3886
0
              uint64_t base_level_size = 0;
3887
0
              for (auto f : files_[base_level_]) {
3888
0
                base_level_size += f->compensated_file_size;
3889
0
              }
3890
0
              score = std::max(score, static_cast<double>(total_size) /
3891
0
                                          static_cast<double>(std::max(
3892
0
                                              base_level_size,
3893
0
                                              level_max_bytes_[base_level_])));
3894
0
            }
3895
221k
            if (score > 1.0) {
3896
831
              score *= kScoreScale;
3897
831
            }
3898
221k
          } else {
3899
0
            score = std::max(score,
3900
0
                             static_cast<double>(total_size) /
3901
0
                                 mutable_cf_options.max_bytes_for_level_base);
3902
0
          }
3903
221k
        }
3904
221k
      }
3905
1.10M
    } else {  // level > 0
3906
      // Compute the ratio of current size to size limit.
3907
1.10M
      uint64_t level_bytes_no_compacting = 0;
3908
1.10M
      uint64_t level_total_bytes = 0;
3909
1.10M
      for (auto f : files_[level]) {
3910
0
        level_total_bytes += f->fd.GetFileSize();
3911
0
        if (!f->being_compacted) {
3912
0
          level_bytes_no_compacting += f->compensated_file_size;
3913
0
        }
3914
0
      }
3915
1.10M
      if (!immutable_options.level_compaction_dynamic_level_bytes) {
3916
0
        score = static_cast<double>(level_bytes_no_compacting) /
3917
0
                MaxBytesForLevel(level);
3918
1.10M
      } else {
3919
1.10M
        if (level_bytes_no_compacting < MaxBytesForLevel(level)) {
3920
1.10M
          score = static_cast<double>(level_bytes_no_compacting) /
3921
1.10M
                  MaxBytesForLevel(level);
3922
1.10M
        } else {
3923
          // If there are a large mount of data being compacted down to the
3924
          // current level soon, we would de-prioritize compaction from
3925
          // a level where the incoming data would be a large ratio. We do
3926
          // it by dividing level size not by target level size, but
3927
          // the target size and the incoming compaction bytes.
3928
0
          score = static_cast<double>(level_bytes_no_compacting) /
3929
0
                  (MaxBytesForLevel(level) + total_downcompact_bytes) *
3930
0
                  kScoreScale;
3931
0
        }
3932
        // Drain unnecessary levels, but with lower priority compared to
3933
        // when L0 is eligible. Only non-empty levels can be unnecessary.
3934
        // If there is no unnecessary levels, lowest_unnecessary_level_ = -1.
3935
1.10M
        if (level_bytes_no_compacting > 0 &&
3936
0
            level <= lowest_unnecessary_level_) {
3937
0
          score = std::max(
3938
0
              score, kScoreScale *
3939
0
                         (1.001 + 0.001 * (lowest_unnecessary_level_ - level)));
3940
0
        }
3941
1.10M
      }
3942
1.10M
      if (level <= lowest_unnecessary_level_) {
3943
0
        total_downcompact_bytes += level_total_bytes;
3944
1.10M
      } else if (level_total_bytes > MaxBytesForLevel(level)) {
3945
0
        total_downcompact_bytes +=
3946
0
            static_cast<double>(level_total_bytes - MaxBytesForLevel(level));
3947
0
      }
3948
1.10M
    }
3949
1.33M
    compaction_level_[level] = level;
3950
1.33M
    compaction_score_[level] = score;
3951
1.33M
  }
3952
3953
  // sort all the levels based on their score. Higher scores get listed
3954
  // first. Use bubble sort because the number of entries are small.
3955
1.33M
  for (int i = 0; i < num_levels() - 2; i++) {
3956
4.43M
    for (int j = i + 1; j < num_levels() - 1; j++) {
3957
3.32M
      if (compaction_score_[i] < compaction_score_[j]) {
3958
0
        double score = compaction_score_[i];
3959
0
        int level = compaction_level_[i];
3960
0
        compaction_score_[i] = compaction_score_[j];
3961
0
        compaction_level_[i] = compaction_level_[j];
3962
0
        compaction_score_[j] = score;
3963
0
        compaction_level_[j] = level;
3964
0
      }
3965
3.32M
    }
3966
1.10M
  }
3967
221k
  ComputeFilesMarkedForCompaction(max_output_level);
3968
221k
  ComputeBottommostFilesMarkedForCompaction(
3969
221k
      immutable_options.cf_allow_ingest_behind ||
3970
221k
          immutable_options.allow_ingest_behind,
3971
221k
      immutable_options.user_comparator, full_history_ts_low);
3972
221k
  ComputeExpiredTtlFiles(immutable_options, mutable_cf_options.ttl);
3973
221k
  ComputeFilesMarkedForPeriodicCompaction(
3974
221k
      immutable_options, mutable_cf_options.periodic_compaction_seconds,
3975
221k
      max_output_level);
3976
221k
  ComputeFilesMarkedForForcedBlobGC(
3977
221k
      mutable_cf_options.blob_garbage_collection_age_cutoff,
3978
221k
      mutable_cf_options.blob_garbage_collection_force_threshold,
3979
221k
      mutable_cf_options.enable_blob_garbage_collection);
3980
221k
  ComputeFilesMarkedForReadTriggeredCompaction(
3981
221k
      mutable_cf_options.read_triggered_compaction_threshold,
3982
221k
      immutable_options.compaction_style);
3983
3984
221k
  EstimateCompactionBytesNeeded(mutable_cf_options);
3985
221k
}
3986
3987
0
void VersionStorageInfo::RemoveFilesBeingCompactedFromCompactionCandidates() {
3988
0
  for (auto* candidates :
3989
0
       {&files_marked_for_compaction_, &bottommost_files_marked_for_compaction_,
3990
0
        &expired_ttl_files_, &files_marked_for_periodic_compaction_,
3991
0
        &files_marked_for_forced_blob_gc_, &read_triggered_compaction_files_}) {
3992
0
    const size_t remaining =
3993
0
        std::remove_if(candidates->begin(), candidates->end(),
3994
0
                       [](const auto& level_file) {
3995
0
                         return level_file.second->being_compacted;
3996
0
                       }) -
3997
0
        candidates->begin();
3998
0
    candidates->resize(remaining);
3999
0
  }
4000
0
  standalone_range_tombstone_files_mark_threshold_ = kMaxSequenceNumber;
4001
0
  for (const auto& level_file : files_marked_for_compaction_) {
4002
0
    if (level_file.second->FileIsStandAloneRangeTombstone()) {
4003
0
      standalone_range_tombstone_files_mark_threshold_ =
4004
0
          std::min(standalone_range_tombstone_files_mark_threshold_,
4005
0
                   level_file.second->fd.smallest_seqno);
4006
0
    }
4007
0
  }
4008
0
}
4009
4010
221k
void VersionStorageInfo::ComputeFilesMarkedForCompaction(int last_level) {
4011
221k
  files_marked_for_compaction_.clear();
4012
221k
  int last_qualify_level = 0;
4013
221k
  standalone_range_tombstone_files_mark_threshold_ = kMaxSequenceNumber;
4014
4015
  // Do not include files from the last level with data
4016
  // If table properties collector suggests a file on the last level,
4017
  // we should not move it to a new level.
4018
1.37M
  for (int level = last_level; level >= 1; level--) {
4019
1.18M
    if (!files_[level].empty()) {
4020
28.7k
      last_qualify_level = level - 1;
4021
28.7k
      break;
4022
28.7k
    }
4023
1.18M
  }
4024
4025
587k
  for (int level = 0; level <= last_qualify_level; level++) {
4026
365k
    for (auto* f : files_[level]) {
4027
127k
      if (!f->being_compacted && f->marked_for_compaction) {
4028
0
        files_marked_for_compaction_.emplace_back(level, f);
4029
0
        if (f->FileIsStandAloneRangeTombstone()) {
4030
0
          standalone_range_tombstone_files_mark_threshold_ =
4031
0
              std::min(standalone_range_tombstone_files_mark_threshold_,
4032
0
                       f->fd.smallest_seqno);
4033
0
        }
4034
0
      }
4035
127k
    }
4036
365k
  }
4037
221k
}
4038
4039
void VersionStorageInfo::ComputeExpiredTtlFiles(
4040
221k
    const ImmutableOptions& ioptions, const uint64_t ttl) {
4041
221k
  expired_ttl_files_.clear();
4042
221k
  if (ttl == 0 || compaction_style_ != CompactionStyle::kCompactionStyleLevel) {
4043
0
    return;
4044
0
  }
4045
4046
221k
  int64_t _current_time;
4047
221k
  auto status = ioptions.clock->GetCurrentTime(&_current_time);
4048
221k
  if (!status.ok()) {
4049
0
    return;
4050
0
  }
4051
221k
  const uint64_t current_time = static_cast<uint64_t>(_current_time);
4052
4053
1.55M
  for (int level = 0; level < num_levels() - 1; level++) {
4054
1.33M
    for (FileMetaData* f : files_[level]) {
4055
127k
      if (!f->being_compacted) {
4056
114k
        uint64_t oldest_ancestor_time = f->TryGetOldestAncestorTime();
4057
114k
        if (oldest_ancestor_time > 0 &&
4058
114k
            oldest_ancestor_time < (current_time - ttl)) {
4059
0
          expired_ttl_files_.emplace_back(level, f);
4060
0
        }
4061
114k
      }
4062
127k
    }
4063
1.33M
  }
4064
221k
}
4065
4066
void VersionStorageInfo::ComputeFilesMarkedForPeriodicCompaction(
4067
    const ImmutableOptions& ioptions,
4068
221k
    const uint64_t periodic_compaction_seconds, int last_level) {
4069
221k
  files_marked_for_periodic_compaction_.clear();
4070
221k
  if (periodic_compaction_seconds == 0) {
4071
221k
    return;
4072
221k
  }
4073
4074
0
  int64_t temp_current_time;
4075
0
  auto status = ioptions.clock->GetCurrentTime(&temp_current_time);
4076
0
  if (!status.ok()) {
4077
0
    return;
4078
0
  }
4079
0
  const uint64_t current_time = static_cast<uint64_t>(temp_current_time);
4080
4081
  // If periodic_compaction_seconds is larger than current time, periodic
4082
  // compaction can't possibly be triggered.
4083
0
  if (periodic_compaction_seconds > current_time) {
4084
0
    return;
4085
0
  }
4086
4087
  // Existing offpeak behavior pulls a file's deadline earlier by the time until
4088
  // the next daily offpeak window (so a whole TTL's worth can be marked at the
4089
  // start of offpeak).
4090
0
  const OffpeakTimeInfo offpeak_time_info =
4091
0
      offpeak_time_option_.GetOffpeakTimeInfo(current_time);
4092
0
  const uint64_t offpeak_pull =
4093
0
      offpeak_time_info.is_now_offpeak
4094
0
          ? offpeak_time_info.seconds_till_next_offpeak_start
4095
0
          : 0;
4096
4097
  // Preferred-phase scheduling (see
4098
  // DBOptions::periodic_compaction_phase_recovery_percent). When
4099
  // recovery_percent == 0 this is disabled and marking matches the classic
4100
  // "file age >= periodic_compaction_seconds" behavior (adjusted for offpeak).
4101
0
  const PeriodicCompactionPhaseParams& phase_params =
4102
0
      periodic_compaction_phase_params_;
4103
4104
0
  for (int level = 0; level <= last_level; level++) {
4105
0
    for (auto f : files_[level]) {
4106
0
      if (!f->being_compacted) {
4107
        // Compute a file's modification time in the following order:
4108
        // 1. Use file_creation_time table property if it is > 0.
4109
        // 2. Use oldest_ancestor_time table property if it is > 0.
4110
        // 3. Use file's mtime metadata if the above two table properties are 0.
4111
        // Don't consider the file at all if the modification time cannot be
4112
        // correctly determined based on the above conditions.
4113
0
        uint64_t file_modification_time = f->TryGetFileCreationTime();
4114
0
        if (file_modification_time == kUnknownFileCreationTime) {
4115
0
          file_modification_time = f->TryGetOldestAncestorTime();
4116
0
        }
4117
0
        if (file_modification_time == kUnknownOldestAncestorTime) {
4118
0
          auto file_path = TableFileName(ioptions.cf_paths, f->fd.GetNumber(),
4119
0
                                         f->fd.GetPathId());
4120
0
          status = ioptions.env->GetFileModificationTime(
4121
0
              file_path, &file_modification_time);
4122
0
          if (!status.ok()) {
4123
0
            ROCKS_LOG_WARN(ioptions.logger,
4124
0
                           "Can't get file modification time: %s: %s",
4125
0
                           file_path.c_str(), status.ToString().c_str());
4126
0
            continue;
4127
0
          }
4128
0
        }
4129
0
        if (file_modification_time == 0) {
4130
0
          continue;
4131
0
        }
4132
4133
0
        const uint64_t trigger_time = PeriodicCompactionPhaser::TriggerTime(
4134
0
            file_modification_time, periodic_compaction_seconds, phase_params);
4135
0
        if (current_time + offpeak_pull > trigger_time) {
4136
0
          files_marked_for_periodic_compaction_.emplace_back(level, f);
4137
0
        }
4138
0
      }
4139
0
    }
4140
0
  }
4141
0
}
4142
4143
void VersionStorageInfo::ComputeFilesMarkedForForcedBlobGC(
4144
    double blob_garbage_collection_age_cutoff,
4145
    double blob_garbage_collection_force_threshold,
4146
221k
    bool enable_blob_garbage_collection) {
4147
221k
  files_marked_for_forced_blob_gc_.clear();
4148
221k
  if (!(enable_blob_garbage_collection &&
4149
0
        blob_garbage_collection_age_cutoff > 0.0 &&
4150
221k
        blob_garbage_collection_force_threshold < 1.0)) {
4151
221k
    return;
4152
221k
  }
4153
4154
0
  if (blob_files_.empty()) {
4155
0
    return;
4156
0
  }
4157
4158
  // Number of blob files eligible for GC based on age
4159
0
  const size_t cutoff_count = static_cast<size_t>(
4160
0
      blob_garbage_collection_age_cutoff * blob_files_.size());
4161
0
  if (!cutoff_count) {
4162
0
    return;
4163
0
  }
4164
4165
  // Compute the sum of total and garbage bytes over the batch of blob files
4166
  // currently eligible for garbage collection based on
4167
  // blob_garbage_collection_age_cutoff, and if the garbage ratio exceeds
4168
  // blob_garbage_collection_force_threshold, schedule compaction for the
4169
  // SST files that reference the oldest batch of blob files. Here is a toy
4170
  // example. Let's assume we have three SSTs 1, 2, and 3, and four blob files
4171
  // 10, 11, 12, and 13, which correspond to the range that is eligible for GC
4172
  // and satisfy the garbage ratio threshold. Also, let's say SSTs 1 and 2 both
4173
  // rely on blob file 10 and potentially some higher-numbered ones, while SST 3
4174
  // relies on blob file 12 and potentially some higher-numbered ones. Then, the
4175
  // SST to oldest blob file mapping is as follows:
4176
  //
4177
  // SST file number               Oldest blob file number
4178
  // 1                             10
4179
  // 2                             10
4180
  // 3                             12
4181
  //
4182
  // This is what the same thing looks like from the blob files' POV. (Note that
4183
  // the linked SSTs simply denote the inverse mapping of the above.)
4184
  //
4185
  // Blob file number              Linked SST set
4186
  // 10                            {1, 2}
4187
  // 11                            {}
4188
  // 12                            {3}
4189
  // 13                            {}
4190
  //
4191
  // Then, the oldest batch of blob files consists of blob files 10 and 11,
4192
  // and we can get rid of them by forcing the compaction of SSTs 1 and 2.
4193
0
  const auto& oldest_meta = blob_files_.front();
4194
0
  assert(oldest_meta);
4195
4196
0
  const auto& linked_ssts = oldest_meta->GetLinkedSsts();
4197
0
  if (linked_ssts.empty()) {
4198
    // SST files that reference the oldest blob file are linked to an older,
4199
    // dropped blob file instead, so the SST files to compact are unknown.
4200
0
    assert(!dropped_blob_files_.empty());
4201
0
    return;
4202
0
  }
4203
4204
0
  size_t count = 1;
4205
0
  uint64_t sum_total_blob_bytes = oldest_meta->GetTotalBlobBytes();
4206
0
  uint64_t sum_garbage_blob_bytes = oldest_meta->GetGarbageBlobBytes();
4207
4208
0
  assert(cutoff_count <= blob_files_.size());
4209
4210
0
  for (; count < cutoff_count; ++count) {
4211
0
    const auto& meta = blob_files_[count];
4212
0
    assert(meta);
4213
4214
0
    sum_total_blob_bytes += meta->GetTotalBlobBytes();
4215
0
    sum_garbage_blob_bytes += meta->GetGarbageBlobBytes();
4216
0
  }
4217
4218
0
  if (sum_garbage_blob_bytes <
4219
0
      blob_garbage_collection_force_threshold * sum_total_blob_bytes) {
4220
0
    return;
4221
0
  }
4222
4223
0
  for (uint64_t sst_file_number : linked_ssts) {
4224
0
    const FileLocation location = GetFileLocation(sst_file_number);
4225
0
    assert(location.IsValid());
4226
4227
0
    const int level = location.GetLevel();
4228
0
    assert(level >= 0);
4229
4230
0
    const size_t pos = location.GetPosition();
4231
4232
0
    FileMetaData* const sst_meta = files_[level][pos];
4233
0
    assert(sst_meta);
4234
4235
0
    if (sst_meta->being_compacted) {
4236
0
      continue;
4237
0
    }
4238
4239
0
    files_marked_for_forced_blob_gc_.emplace_back(level, sst_meta);
4240
0
  }
4241
0
}
4242
4243
void VersionStorageInfo::ComputeFilesMarkedForReadTriggeredCompaction(
4244
221k
    double threshold, CompactionStyle compaction_style) {
4245
221k
  read_triggered_compaction_files_.clear();
4246
221k
  if (threshold <= 0.0 || compaction_style == kCompactionStyleFIFO) {
4247
221k
    return;
4248
221k
  }
4249
4250
  // Skip files at the last non-empty level -- there is no lower level to
4251
  // compact into. Exception: L0 files are allowed even when L0 is the last
4252
  // non-empty level, because in single-level universal or FIFO compaction, L0
4253
  // files can be compacted together (L0 -> L0).
4254
0
  int last_non_empty = num_non_empty_levels_ - 1;
4255
4256
0
  for (int level = 0; level < num_levels(); level++) {
4257
0
    if (level >= last_non_empty && level != 0) {
4258
0
      continue;
4259
0
    }
4260
0
    for (auto* f : files_[level]) {
4261
0
      if (f->being_compacted) {
4262
0
        continue;
4263
0
      }
4264
0
      uint64_t file_size = f->fd.GetFileSize();
4265
0
      if (file_size == 0) {
4266
0
        continue;
4267
0
      }
4268
0
      double reads_per_byte =
4269
0
          static_cast<double>(f->stats.num_collapsible_entry_reads_sampled.load(
4270
0
              std::memory_order_relaxed)) /
4271
0
          static_cast<double>(file_size);
4272
0
      if (reads_per_byte > threshold) {
4273
0
        read_triggered_compaction_files_.emplace_back(level, f);
4274
0
      }
4275
0
    }
4276
0
  }
4277
4278
  // Snapshot reads_per_byte for each file so the sort comparator sees stable
4279
  // values.
4280
0
  std::unordered_map<const FileMetaData*, double> reads_per_byte_snapshot;
4281
0
  reads_per_byte_snapshot.reserve(read_triggered_compaction_files_.size());
4282
0
  for (const auto& entry : read_triggered_compaction_files_) {
4283
0
    const auto* f = entry.second;
4284
0
    uint64_t file_size = f->fd.GetFileSize();
4285
0
    reads_per_byte_snapshot[f] =
4286
0
        file_size > 0 ? static_cast<double>(
4287
0
                            f->stats.num_collapsible_entry_reads_sampled.load(
4288
0
                                std::memory_order_relaxed)) /
4289
0
                            static_cast<double>(file_size)
4290
0
                      : 0.0;
4291
0
  }
4292
4293
  // Sort by reads_per_byte descending so the hottest files are picked first
4294
0
  std::sort(read_triggered_compaction_files_.begin(),
4295
0
            read_triggered_compaction_files_.end(),
4296
0
            [&reads_per_byte_snapshot](const auto& a, const auto& b) {
4297
0
              return reads_per_byte_snapshot.at(a.second) >
4298
0
                     reads_per_byte_snapshot.at(b.second);
4299
0
            });
4300
0
}
4301
4302
namespace {
4303
4304
// used to sort files by size
4305
struct Fsize {
4306
  size_t index;
4307
  FileMetaData* file;
4308
};
4309
4310
// Comparator that is used to sort files based on their size
4311
// In normal mode: descending size
4312
0
bool CompareCompensatedSizeDescending(const Fsize& first, const Fsize& second) {
4313
0
  return (first.file->compensated_file_size >
4314
0
          second.file->compensated_file_size);
4315
0
}
4316
}  // anonymous namespace
4317
4318
173k
void VersionStorageInfo::AddFile(int level, FileMetaData* f) {
4319
173k
  auto& level_files = files_[level];
4320
173k
  level_files.push_back(f);
4321
4322
173k
  f->refs++;
4323
173k
}
4324
4325
void VersionStorageInfo::AddBlobFile(
4326
0
    std::shared_ptr<BlobFileMetaData> blob_file_meta) {
4327
0
  assert(blob_file_meta);
4328
4329
0
  assert(blob_files_.empty() ||
4330
0
         (blob_files_.back() && blob_files_.back()->GetBlobFileNumber() <
4331
0
                                    blob_file_meta->GetBlobFileNumber()));
4332
4333
0
  blob_files_.emplace_back(std::move(blob_file_meta));
4334
0
}
4335
4336
VersionStorageInfo::BlobFiles::const_iterator
4337
0
VersionStorageInfo::GetBlobFileMetaDataLB(uint64_t blob_file_number) const {
4338
0
  return std::lower_bound(
4339
0
      blob_files_.begin(), blob_files_.end(), blob_file_number,
4340
0
      [](const std::shared_ptr<BlobFileMetaData>& lhs, uint64_t rhs) {
4341
0
        assert(lhs);
4342
0
        return lhs->GetBlobFileNumber() < rhs;
4343
0
      });
4344
0
}
4345
4346
215k
void VersionStorageInfo::SetFinalized() {
4347
215k
  finalized_ = true;
4348
4349
#ifndef NDEBUG
4350
  if (compaction_style_ != kCompactionStyleLevel) {
4351
    // Not level based compaction.
4352
    return;
4353
  }
4354
  assert(base_level_ < 0 || num_levels() == 1 ||
4355
         (base_level_ >= 1 && base_level_ < num_levels()));
4356
  // Verify all levels newer than base_level are empty except L0
4357
  for (int level = 1; level < base_level(); level++) {
4358
    assert(NumLevelBytes(level) == 0);
4359
  }
4360
  uint64_t max_bytes_prev_level = 0;
4361
  for (int level = base_level(); level < num_levels() - 1; level++) {
4362
    if (LevelFiles(level).size() == 0) {
4363
      continue;
4364
    }
4365
    assert(MaxBytesForLevel(level) >= max_bytes_prev_level);
4366
    max_bytes_prev_level = MaxBytesForLevel(level);
4367
  }
4368
  for (int level = 0; level < num_levels(); level++) {
4369
    assert(LevelFiles(level).size() == 0 ||
4370
           LevelFiles(level).size() == LevelFilesBrief(level).num_files);
4371
    if (LevelFiles(level).size() > 0) {
4372
      assert(level < num_non_empty_levels());
4373
    }
4374
  }
4375
  assert(compaction_level_.size() > 0);
4376
  assert(compaction_level_.size() == compaction_score_.size());
4377
#endif
4378
215k
}
4379
4380
215k
void VersionStorageInfo::UpdateNumNonEmptyLevels() {
4381
215k
  num_non_empty_levels_ = num_levels_;
4382
1.50M
  for (int i = num_levels_ - 1; i >= 0; i--) {
4383
1.35M
    if (files_[i].size() != 0) {
4384
62.4k
      return;
4385
1.29M
    } else {
4386
1.29M
      num_non_empty_levels_ = i;
4387
1.29M
    }
4388
1.35M
  }
4389
215k
}
4390
4391
namespace {
4392
// Sort `temp` based on ratio of overlapping size over file size
4393
void SortFileByOverlappingRatio(
4394
    const InternalKeyComparator& icmp, const std::vector<FileMetaData*>& files,
4395
    const std::vector<FileMetaData*>& next_level_files, SystemClock* clock,
4396
    int level, int num_non_empty_levels, uint64_t ttl,
4397
1.29M
    std::vector<Fsize>* temp) {
4398
1.29M
  std::unordered_map<uint64_t, uint64_t> file_to_order;
4399
1.29M
  auto next_level_it = next_level_files.begin();
4400
4401
1.29M
  int64_t curr_time;
4402
1.29M
  Status status = clock->GetCurrentTime(&curr_time);
4403
1.29M
  if (!status.ok()) {
4404
    // If we can't get time, disable TTL.
4405
0
    ttl = 0;
4406
0
  }
4407
4408
1.29M
  FileTtlBooster ttl_booster(static_cast<uint64_t>(curr_time), ttl,
4409
1.29M
                             num_non_empty_levels, level);
4410
4411
1.29M
  for (auto& file : files) {
4412
109k
    uint64_t overlapping_bytes = 0;
4413
    // Skip files in next level that is smaller than current file
4414
109k
    while (next_level_it != next_level_files.end() &&
4415
0
           icmp.Compare((*next_level_it)->largest, file->smallest) < 0) {
4416
0
      next_level_it++;
4417
0
    }
4418
4419
109k
    while (next_level_it != next_level_files.end() &&
4420
0
           icmp.Compare((*next_level_it)->smallest, file->largest) < 0) {
4421
0
      overlapping_bytes += (*next_level_it)->fd.file_size;
4422
4423
0
      if (icmp.Compare((*next_level_it)->largest, file->largest) > 0) {
4424
        // next level file cross large boundary of current file.
4425
0
        break;
4426
0
      }
4427
0
      next_level_it++;
4428
0
    }
4429
4430
109k
    uint64_t ttl_boost_score = (ttl > 0) ? ttl_booster.GetBoostScore(file) : 1;
4431
109k
    assert(ttl_boost_score > 0);
4432
109k
    assert(file->compensated_file_size != 0);
4433
109k
    file_to_order[file->fd.GetNumber()] = overlapping_bytes * 1024U /
4434
109k
                                          file->compensated_file_size /
4435
109k
                                          ttl_boost_score;
4436
109k
  }
4437
4438
1.29M
  size_t num_to_sort = temp->size() > VersionStorageInfo::kNumberFilesToSort
4439
1.29M
                           ? VersionStorageInfo::kNumberFilesToSort
4440
1.29M
                           : temp->size();
4441
4442
1.29M
  std::partial_sort(
4443
1.29M
      temp->begin(), temp->begin() + num_to_sort, temp->end(),
4444
1.29M
      [&](const Fsize& f1, const Fsize& f2) -> bool {
4445
        // If score is the same, pick file with smaller keys.
4446
        // This makes the algorithm more deterministic, and also
4447
        // help the trivial move case to have more files to
4448
        // extend.
4449
99.9k
        if (f1.file->marked_for_compaction == f2.file->marked_for_compaction) {
4450
99.9k
          if (file_to_order[f1.file->fd.GetNumber()] ==
4451
99.9k
              file_to_order[f2.file->fd.GetNumber()]) {
4452
99.9k
            return icmp.Compare(f1.file->smallest, f2.file->smallest) < 0;
4453
99.9k
          }
4454
0
          return file_to_order[f1.file->fd.GetNumber()] <
4455
0
                 file_to_order[f2.file->fd.GetNumber()];
4456
99.9k
        } else {
4457
0
          return f1.file->marked_for_compaction >
4458
0
                 f2.file->marked_for_compaction;
4459
0
        }
4460
99.9k
      });
4461
1.29M
}
4462
4463
void SortFileByRoundRobin(const InternalKeyComparator& icmp,
4464
                          std::vector<InternalKey>* compact_cursor,
4465
                          bool level0_non_overlapping, int level,
4466
0
                          std::vector<Fsize>* temp) {
4467
0
  if (level == 0 && !level0_non_overlapping) {
4468
    // Using kOldestSmallestSeqFirst when level === 0, since the
4469
    // files may overlap (not fully sorted)
4470
0
    std::sort(temp->begin(), temp->end(),
4471
0
              [](const Fsize& f1, const Fsize& f2) -> bool {
4472
0
                return f1.file->fd.smallest_seqno < f2.file->fd.smallest_seqno;
4473
0
              });
4474
0
    return;
4475
0
  }
4476
4477
0
  bool should_move_files =
4478
0
      compact_cursor->at(level).size() > 0 && temp->size() > 1;
4479
4480
  // The iterator points to the Fsize with smallest key larger than or equal to
4481
  // the given cursor
4482
0
  std::vector<Fsize>::iterator current_file_iter;
4483
0
  if (should_move_files) {
4484
    // Find the file of which the smallest key is larger than or equal to
4485
    // the cursor (the smallest key in the successor file of the last
4486
    // chosen file), skip this if the cursor is invalid or there is only
4487
    // one file in this level
4488
0
    current_file_iter = std::lower_bound(
4489
0
        temp->begin(), temp->end(), compact_cursor->at(level),
4490
0
        [&](const Fsize& f, const InternalKey& cursor) -> bool {
4491
0
          return icmp.Compare(cursor, f.file->smallest) > 0;
4492
0
        });
4493
4494
0
    should_move_files =
4495
0
        current_file_iter != temp->end() && current_file_iter != temp->begin();
4496
0
  }
4497
0
  if (should_move_files) {
4498
    // Construct a local temporary vector
4499
0
    std::vector<Fsize> local_temp;
4500
0
    local_temp.reserve(temp->size());
4501
    // Move the selected File into the first position and its successors
4502
    // into the second, third, ..., positions
4503
0
    for (auto iter = current_file_iter; iter != temp->end(); iter++) {
4504
0
      local_temp.push_back(*iter);
4505
0
    }
4506
    // Move the origin predecessors of the selected file in a round-robin
4507
    // manner
4508
0
    for (auto iter = temp->begin(); iter != current_file_iter; iter++) {
4509
0
      local_temp.push_back(*iter);
4510
0
    }
4511
    // Replace all the items in temp
4512
0
    for (size_t i = 0; i < local_temp.size(); i++) {
4513
0
      temp->at(i) = local_temp[i];
4514
0
    }
4515
0
  }
4516
0
}
4517
}  // anonymous namespace
4518
4519
void VersionStorageInfo::UpdateFilesByCompactionPri(
4520
215k
    const ImmutableOptions& ioptions, const MutableCFOptions& options) {
4521
215k
  if (compaction_style_ == kCompactionStyleNone ||
4522
215k
      compaction_style_ == kCompactionStyleFIFO ||
4523
215k
      compaction_style_ == kCompactionStyleUniversal) {
4524
    // don't need this
4525
0
    return;
4526
0
  }
4527
  // No need to sort the highest level because it is never compacted.
4528
1.51M
  for (int level = 0; level < num_levels() - 1; level++) {
4529
1.29M
    const std::vector<FileMetaData*>& files = files_[level];
4530
1.29M
    auto& files_by_compaction_pri = files_by_compaction_pri_[level];
4531
1.29M
    assert(files_by_compaction_pri.size() == 0);
4532
4533
    // populate a temp vector for sorting based on size
4534
1.29M
    std::vector<Fsize> temp(files.size());
4535
1.40M
    for (size_t i = 0; i < files.size(); i++) {
4536
109k
      temp[i].index = i;
4537
109k
      temp[i].file = files[i];
4538
109k
    }
4539
4540
    // sort the top kNumberFilesToSort based on file size
4541
1.29M
    size_t num = VersionStorageInfo::kNumberFilesToSort;
4542
1.29M
    if (num > temp.size()) {
4543
1.29M
      num = temp.size();
4544
1.29M
    }
4545
1.29M
    switch (ioptions.compaction_pri) {
4546
0
      case kByCompensatedSize:
4547
0
        std::partial_sort(temp.begin(), temp.begin() + num, temp.end(),
4548
0
                          CompareCompensatedSizeDescending);
4549
0
        break;
4550
0
      case kOldestLargestSeqFirst:
4551
0
        std::sort(temp.begin(), temp.end(),
4552
0
                  [](const Fsize& f1, const Fsize& f2) -> bool {
4553
0
                    return f1.file->fd.largest_seqno <
4554
0
                           f2.file->fd.largest_seqno;
4555
0
                  });
4556
0
        break;
4557
0
      case kOldestSmallestSeqFirst:
4558
0
        std::sort(temp.begin(), temp.end(),
4559
0
                  [](const Fsize& f1, const Fsize& f2) -> bool {
4560
0
                    return f1.file->fd.smallest_seqno <
4561
0
                           f2.file->fd.smallest_seqno;
4562
0
                  });
4563
0
        break;
4564
1.29M
      case kMinOverlappingRatio:
4565
1.29M
        SortFileByOverlappingRatio(*internal_comparator_, files_[level],
4566
1.29M
                                   files_[level + 1], ioptions.clock, level,
4567
1.29M
                                   num_non_empty_levels_, options.ttl, &temp);
4568
1.29M
        break;
4569
0
      case kRoundRobin:
4570
0
        SortFileByRoundRobin(*internal_comparator_, &compact_cursor_,
4571
0
                             level0_non_overlapping_, level, &temp);
4572
0
        break;
4573
0
      default:
4574
0
        assert(false);
4575
1.29M
    }
4576
1.29M
    assert(temp.size() == files.size());
4577
4578
    // initialize files_by_compaction_pri_
4579
1.40M
    for (size_t i = 0; i < temp.size(); i++) {
4580
109k
      files_by_compaction_pri.push_back(static_cast<int>(temp[i].index));
4581
109k
    }
4582
1.29M
    next_file_to_compact_by_size_[level] = 0;
4583
1.29M
    assert(files_[level].size() == files_by_compaction_pri_[level].size());
4584
1.29M
  }
4585
215k
}
4586
4587
215k
void VersionStorageInfo::GenerateLevel0NonOverlapping() {
4588
215k
  assert(!finalized_);
4589
215k
  level0_non_overlapping_ = true;
4590
215k
  if (level_files_brief_.size() == 0) {
4591
153k
    return;
4592
153k
  }
4593
4594
  // A copy of L0 files sorted by smallest key
4595
62.4k
  std::vector<FdWithKeyRange> level0_sorted_file(
4596
62.4k
      level_files_brief_[0].files,
4597
62.4k
      level_files_brief_[0].files + level_files_brief_[0].num_files);
4598
62.4k
  std::sort(level0_sorted_file.begin(), level0_sorted_file.end(),
4599
62.4k
            [this](const FdWithKeyRange& f1, const FdWithKeyRange& f2) -> bool {
4600
59.7k
              return (internal_comparator_->Compare(f1.smallest_key,
4601
59.7k
                                                    f2.smallest_key) < 0);
4602
59.7k
            });
4603
4604
112k
  for (size_t i = 1; i < level0_sorted_file.size(); ++i) {
4605
51.9k
    FdWithKeyRange& f = level0_sorted_file[i];
4606
51.9k
    FdWithKeyRange& prev = level0_sorted_file[i - 1];
4607
51.9k
    if (internal_comparator_->Compare(prev.largest_key, f.smallest_key) >= 0) {
4608
1.69k
      level0_non_overlapping_ = false;
4609
1.69k
      break;
4610
1.69k
    }
4611
51.9k
  }
4612
62.4k
}
4613
4614
215k
void VersionStorageInfo::GenerateBottommostFiles() {
4615
215k
  assert(!finalized_);
4616
215k
  assert(bottommost_files_.empty());
4617
432k
  for (size_t level = 0; level < level_files_brief_.size(); ++level) {
4618
389k
    for (size_t file_idx = 0; file_idx < level_files_brief_[level].num_files;
4619
216k
         ++file_idx) {
4620
173k
      const FdWithKeyRange& f = level_files_brief_[level].files[file_idx];
4621
173k
      int l0_file_idx;
4622
173k
      if (level == 0) {
4623
109k
        l0_file_idx = static_cast<int>(file_idx);
4624
109k
      } else {
4625
63.9k
        l0_file_idx = -1;
4626
63.9k
      }
4627
173k
      Slice smallest_user_key = ExtractUserKey(f.smallest_key);
4628
173k
      Slice largest_user_key = ExtractUserKey(f.largest_key);
4629
173k
      if (!RangeMightExistAfterSortedRun(smallest_user_key, largest_user_key,
4630
173k
                                         static_cast<int>(level),
4631
173k
                                         l0_file_idx)) {
4632
100k
        bottommost_files_.emplace_back(static_cast<int>(level),
4633
100k
                                       f.file_metadata);
4634
100k
      }
4635
173k
    }
4636
216k
  }
4637
215k
}
4638
4639
215k
void VersionStorageInfo::GenerateFileLocationIndex() {
4640
215k
  size_t num_files = 0;
4641
4642
1.72M
  for (int level = 0; level < num_levels_; ++level) {
4643
1.51M
    num_files += files_[level].size();
4644
1.51M
  }
4645
4646
215k
  file_locations_.reserve(num_files);
4647
4648
1.72M
  for (int level = 0; level < num_levels_; ++level) {
4649
1.68M
    for (size_t pos = 0; pos < files_[level].size(); ++pos) {
4650
173k
      const FileMetaData* const meta = files_[level][pos];
4651
173k
      assert(meta);
4652
4653
173k
      const uint64_t file_number = meta->fd.GetNumber();
4654
4655
173k
      assert(file_locations_.find(file_number) == file_locations_.end());
4656
173k
      file_locations_.emplace(file_number, FileLocation(level, pos));
4657
173k
    }
4658
1.51M
  }
4659
215k
}
4660
4661
void VersionStorageInfo::UpdateOldestSnapshot(
4662
    SequenceNumber seqnum, bool allow_ingest_behind, const Comparator* ucmp,
4663
1.36k
    const std::string& full_history_ts_low) {
4664
1.36k
  assert(seqnum >= oldest_snapshot_seqnum_);
4665
1.36k
  oldest_snapshot_seqnum_ = seqnum;
4666
1.36k
  if (oldest_snapshot_seqnum_ > bottommost_files_mark_threshold_) {
4667
644
    ComputeBottommostFilesMarkedForCompaction(allow_ingest_behind, ucmp,
4668
644
                                              full_history_ts_low);
4669
644
  }
4670
1.36k
}
4671
4672
void VersionStorageInfo::ComputeBottommostFilesMarkedForCompaction(
4673
    bool allow_ingest_behind, const Comparator* ucmp,
4674
222k
    const std::string& full_history_ts_low) {
4675
222k
  bottommost_files_marked_for_compaction_.clear();
4676
222k
  bottommost_files_mark_threshold_ = kMaxSequenceNumber;
4677
222k
  if (allow_ingest_behind) {
4678
0
    return;
4679
0
  }
4680
  // If this DB began holding a file later than ownership_start_time_ub, it is
4681
  // too new to be marked for compaction. (Measuring from when the DB took the
4682
  // file rather than from file creation means a file ingested with old
4683
  // contents still gets the delay.)
4684
222k
  int64_t ownership_start_time_ub = 0;
4685
222k
  bool needs_delay = bottommost_file_compaction_delay_ > 0;
4686
222k
  if (needs_delay) {
4687
0
    int64_t current_time = 0;
4688
0
    clock_->GetCurrentTime(&current_time).PermitUncheckedError();
4689
    // Note that if GetCurrentTime() fails, current_time will be 0.
4690
    // We will treat it as is and treat all files as too new.
4691
    // The subtraction will not underflow since
4692
    // bottommost_file_compaction_delay_ is of type uint32_t.
4693
0
    ownership_start_time_ub =
4694
0
        current_time - static_cast<int64_t>(bottommost_file_compaction_delay_);
4695
0
  }
4696
4697
  // A bottommost file should only be marked for compaction when that compaction
4698
  // could actually zero out its largest sequence number; otherwise the rewrite
4699
  // makes no progress and the file is re-marked forever (infinite compaction
4700
  // loop). The zeroability conditions -- the seqno->time "preserve" window and
4701
  // the UDT history cutoff -- are shared with CompactionIterator::PrepareOutput
4702
  // through BottommostSeqnoCanBeZeroed() so the two decisions cannot drift.
4703
  // Snapshot visibility is checked separately (largest_seqno <
4704
  // oldest_snapshot_seqnum_) since that gate legitimately differs between the
4705
  // two sites.
4706
222k
  const size_t ts_sz = ucmp ? ucmp->timestamp_size() : 0;
4707
222k
  const bool full_history_ts_low_set = !full_history_ts_low.empty();
4708
  // Max seqno a bottommost compaction could zero given the preserve window.
4709
  // CompactionJob computes preserve_seqno_after_ the same way but then further
4710
  // caps it with min(., earliest_snapshot_). We intentionally omit that cap
4711
  // here: the snapshot-visibility gate above (largest_seqno <
4712
  // oldest_snapshot_seqnum_) already guarantees a marked file's largest seqno
4713
  // is below the earliest snapshot, so the cap can never make compaction unable
4714
  // to zero a file we marked. Omitting it can at most leave unmarked a file a
4715
  // compaction could still zero -- a missed optimization, not a loop.
4716
222k
  const SequenceNumber preserve_seqno_after =
4717
222k
      std::max(preserve_time_min_seqno_, SequenceNumber{1}) - 1;
4718
4719
222k
  for (auto& level_and_file : bottommost_files_) {
4720
112k
    if (!level_and_file.second->being_compacted &&
4721
106k
        level_and_file.second->fd.largest_seqno != 0) {
4722
      // largest_seqno might be nonzero due to containing the final key in an
4723
      // earlier compaction, whose seqnum we didn't zero out.
4724
98.8k
      if (level_and_file.second->fd.largest_seqno < oldest_snapshot_seqnum_) {
4725
        // Compute the file's timestamp collapsibility (only meaningful under
4726
        // UDT with full_history_ts_low set). A file whose max timestamp is
4727
        // below full_history_ts_low can have its timestamp history collapsed
4728
        // and its seqno zeroed. An unknown (empty) max timestamp means the file
4729
        // predates timestamp metadata (e.g. written by an older version); mark
4730
        // it once so a bounded compaction backfills FileMetaData::max_timestamp
4731
        // and reclaims obsolete versions/tombstones. When full_history_ts_low
4732
        // is unset, the guard below (full_history_ts_low_set == false) keeps
4733
        // such a file unmarked, since compaction could not collapse it and the
4734
        // rewrite would loop.
4735
1.80k
        bool ts_below_full_history_ts_low = false;
4736
1.80k
        if (ts_sz > 0 && full_history_ts_low_set) {
4737
0
          const std::string& max_ts = level_and_file.second->max_timestamp;
4738
0
          ts_below_full_history_ts_low =
4739
0
              max_ts.empty() ||
4740
0
              ucmp->CompareTimestamp(Slice(max_ts), full_history_ts_low) < 0;
4741
0
        }
4742
1.80k
        if (!BottommostSeqnoCanBeZeroed(
4743
1.80k
                level_and_file.second->fd.largest_seqno, preserve_seqno_after,
4744
1.80k
                ts_sz, full_history_ts_low_set, ts_below_full_history_ts_low)) {
4745
0
          continue;
4746
0
        }
4747
4748
1.80k
        if (!needs_delay) {
4749
1.80k
          bottommost_files_marked_for_compaction_.push_back(level_and_file);
4750
1.80k
        } else if (ownership_start_time_ub > 0) {
4751
0
          int64_t ownership_start_time = static_cast<int64_t>(
4752
0
              level_and_file.second->TryGetDbOwnershipStartTime());
4753
0
          if (ownership_start_time == kUnknownDbOwnershipStartTime ||
4754
0
              ownership_start_time <= ownership_start_time_ub) {
4755
0
            bottommost_files_marked_for_compaction_.push_back(level_and_file);
4756
0
          } else {
4757
            // Just ignore this file for both
4758
            // bottommost_files_marked_for_compaction_ and
4759
            // bottommost_files_mark_threshold_. The next time
4760
            // this method is called, it will try this file again. The method
4761
            // is called after a new Version creation (compaction, flush, etc.),
4762
            // after a compaction is picked, and after a snapshot newer than
4763
            // bottommost_files_mark_threshold_ is released.
4764
0
          }
4765
0
        } else {
4766
          // ownership_start_time_ub <= 0, all files are too new to be marked
4767
          // for compaction.
4768
0
        }
4769
97.0k
      } else {
4770
97.0k
        bottommost_files_mark_threshold_ =
4771
97.0k
            std::min(bottommost_files_mark_threshold_,
4772
97.0k
                     level_and_file.second->fd.largest_seqno);
4773
97.0k
      }
4774
98.8k
    }
4775
112k
  }
4776
222k
}
4777
4778
551k
void Version::Ref() { ++refs_; }
4779
4780
551k
bool Version::Unref() {
4781
551k
  assert(refs_ >= 1);
4782
551k
  --refs_;
4783
551k
  if (refs_ == 0) {
4784
303k
    delete this;
4785
303k
    return true;
4786
303k
  }
4787
248k
  return false;
4788
551k
}
4789
4790
bool VersionStorageInfo::OverlapInLevel(int level,
4791
                                        const Slice* smallest_user_key,
4792
0
                                        const Slice* largest_user_key) {
4793
0
  if (level >= num_non_empty_levels_) {
4794
    // empty level, no overlap
4795
0
    return false;
4796
0
  }
4797
0
  return SomeFileOverlapsRange(*internal_comparator_, (level > 0),
4798
0
                               level_files_brief_[level], smallest_user_key,
4799
0
                               largest_user_key);
4800
0
}
4801
4802
// Store in "*inputs" all files in "level" that overlap [begin,end]
4803
// If hint_index is specified, then it points to a file in the
4804
// overlapping range.
4805
// The file_index returns a pointer to any file in an overlapping range.
4806
void VersionStorageInfo::GetOverlappingInputs(
4807
    int level, const InternalKey* begin, const InternalKey* end,
4808
    std::vector<FileMetaData*>* inputs, int hint_index, int* file_index,
4809
    bool expand_range, const FileMetaData* starting_l0_file,
4810
21.8k
    InternalKey** next_smallest) const {
4811
21.8k
  if (level >= num_non_empty_levels_) {
4812
    // this level is empty, no overlapping inputs
4813
5.21k
    return;
4814
5.21k
  }
4815
4816
16.6k
  inputs->clear();
4817
16.6k
  if (file_index) {
4818
5.94k
    *file_index = -1;
4819
5.94k
  }
4820
16.6k
  const Comparator* user_cmp = user_comparator_;
4821
16.6k
  if (level > 0) {
4822
10.1k
    GetOverlappingInputsRangeBinarySearch(level, begin, end, inputs, hint_index,
4823
10.1k
                                          file_index, false, next_smallest);
4824
10.1k
    return;
4825
10.1k
  }
4826
4827
6.50k
  if (next_smallest) {
4828
    // next_smallest key only makes sense for non-level 0, where files are
4829
    // non-overlapping
4830
0
    *next_smallest = nullptr;
4831
0
  }
4832
4833
6.50k
  Slice user_begin;
4834
6.50k
  Slice user_end;
4835
6.50k
  if (begin != nullptr) {
4836
6.50k
    user_begin = begin->user_key();
4837
6.50k
  }
4838
6.50k
  if (end != nullptr) {
4839
6.50k
    user_end = end->user_key();
4840
6.50k
  }
4841
4842
  // index stores the file index need to check.
4843
6.50k
  std::list<size_t> index;
4844
6.50k
  size_t start_index = 0;
4845
6.50k
  if (starting_l0_file != nullptr) {
4846
0
    uint64_t starting_file_number = starting_l0_file->fd.GetNumber();
4847
0
    for (size_t i = 0; i < level_files_brief_[level].num_files; i++) {
4848
0
      if (level_files_brief_[level].files[i].fd.GetNumber() ==
4849
0
          starting_file_number) {
4850
0
        start_index = i;
4851
0
        break;
4852
0
      }
4853
0
    }
4854
0
    assert(start_index < level_files_brief_[level].num_files);
4855
0
  }
4856
27.5k
  for (size_t i = start_index; i < level_files_brief_[level].num_files; i++) {
4857
21.0k
    index.emplace_back(i);
4858
21.0k
  }
4859
4860
12.9k
  while (!index.empty()) {
4861
8.68k
    bool found_overlapping_file = false;
4862
8.68k
    auto iter = index.begin();
4863
33.6k
    while (iter != index.end()) {
4864
24.9k
      FdWithKeyRange* f = &(level_files_brief_[level].files[*iter]);
4865
24.9k
      const Slice file_start = ExtractUserKey(f->smallest_key);
4866
24.9k
      const Slice file_limit = ExtractUserKey(f->largest_key);
4867
24.9k
      if (begin != nullptr &&
4868
24.9k
          user_cmp->CompareWithoutTimestamp(file_limit, user_begin) < 0) {
4869
        // "f" is completely before specified range; skip it
4870
12
        iter++;
4871
24.9k
      } else if (end != nullptr &&
4872
24.9k
                 user_cmp->CompareWithoutTimestamp(file_start, user_end) > 0) {
4873
        // "f" is completely after specified range; skip it
4874
7.90k
        iter++;
4875
17.0k
      } else {
4876
        // if overlap
4877
17.0k
        inputs->emplace_back(files_[level][*iter]);
4878
17.0k
        found_overlapping_file = true;
4879
        // record the first file index.
4880
17.0k
        if (file_index && *file_index == -1) {
4881
0
          *file_index = static_cast<int>(*iter);
4882
0
        }
4883
        // the related file is overlap, erase to avoid checking again.
4884
17.0k
        iter = index.erase(iter);
4885
17.0k
        if (expand_range) {
4886
17.0k
          if (begin != nullptr &&
4887
17.0k
              user_cmp->CompareWithoutTimestamp(file_start, user_begin) < 0) {
4888
6
            user_begin = file_start;
4889
6
          }
4890
17.0k
          if (end != nullptr &&
4891
17.0k
              user_cmp->CompareWithoutTimestamp(file_limit, user_end) > 0) {
4892
758
            user_end = file_limit;
4893
758
          }
4894
17.0k
        }
4895
17.0k
      }
4896
24.9k
    }
4897
    // if all the files left are not overlap, break
4898
8.68k
    if (!found_overlapping_file) {
4899
2.26k
      break;
4900
2.26k
    }
4901
8.68k
  }
4902
6.50k
}
4903
4904
// Store in "*inputs" files in "level" that within range [begin,end]
4905
// Guarantee a "clean cut" boundary between the files in inputs
4906
// and the surrounding files and the maxinum number of files.
4907
// This will ensure that no parts of a key are lost during compaction.
4908
// If hint_index is specified, then it points to a file in the range.
4909
// The file_index returns a pointer to any file in an overlapping range.
4910
void VersionStorageInfo::GetCleanInputsWithinInterval(
4911
    int level, const InternalKey* begin, const InternalKey* end,
4912
1.61k
    std::vector<FileMetaData*>* inputs, int hint_index, int* file_index) const {
4913
1.61k
  inputs->clear();
4914
1.61k
  if (file_index) {
4915
0
    *file_index = -1;
4916
0
  }
4917
1.61k
  if (level >= num_non_empty_levels_ || level == 0 ||
4918
1.61k
      level_files_brief_[level].num_files == 0) {
4919
    // this level is empty, no inputs within range
4920
    // also don't support clean input interval within L0
4921
1.61k
    return;
4922
1.61k
  }
4923
4924
0
  GetOverlappingInputsRangeBinarySearch(level, begin, end, inputs, hint_index,
4925
0
                                        file_index, true /* within_interval */);
4926
0
}
4927
4928
// Store in "*inputs" all files in "level" that overlap [begin,end]
4929
// Employ binary search to find at least one file that overlaps the
4930
// specified range. From that file, iterate backwards and
4931
// forwards to find all overlapping files.
4932
// if within_range is set, then only store the maximum clean inputs
4933
// within range [begin, end]. "clean" means there is a boundary
4934
// between the files in "*inputs" and the surrounding files
4935
void VersionStorageInfo::GetOverlappingInputsRangeBinarySearch(
4936
    int level, const InternalKey* begin, const InternalKey* end,
4937
    std::vector<FileMetaData*>* inputs, int hint_index, int* file_index,
4938
10.1k
    bool within_interval, InternalKey** next_smallest) const {
4939
10.1k
  assert(level > 0);
4940
4941
10.1k
  auto user_cmp = user_comparator_;
4942
10.1k
  const FdWithKeyRange* files = level_files_brief_[level].files;
4943
10.1k
  const int num_files = static_cast<int>(level_files_brief_[level].num_files);
4944
4945
  // begin to use binary search to find lower bound
4946
  // and upper bound.
4947
10.1k
  int start_index = 0;
4948
10.1k
  int end_index = num_files;
4949
4950
10.1k
  if (begin != nullptr) {
4951
    // if within_interval is true, with file_key would find
4952
    // not overlapping ranges in std::lower_bound.
4953
10.1k
    auto cmp = [&user_cmp, &within_interval](const FdWithKeyRange& f,
4954
18.2k
                                             const InternalKey* k) {
4955
18.2k
      auto& file_key = within_interval ? f.file_metadata->smallest
4956
18.2k
                                       : f.file_metadata->largest;
4957
18.2k
      return sstableKeyCompare(user_cmp, file_key, *k) < 0;
4958
18.2k
    };
4959
4960
10.1k
    start_index = static_cast<int>(
4961
10.1k
        std::lower_bound(files,
4962
10.1k
                         files + (hint_index == -1 ? num_files : hint_index),
4963
10.1k
                         begin, cmp) -
4964
10.1k
        files);
4965
4966
10.1k
    if (start_index > 0 && within_interval) {
4967
0
      bool is_overlapping = true;
4968
0
      while (is_overlapping && start_index < num_files) {
4969
0
        auto& pre_limit = files[start_index - 1].file_metadata->largest;
4970
0
        auto& cur_start = files[start_index].file_metadata->smallest;
4971
0
        is_overlapping = sstableKeyCompare(user_cmp, pre_limit, cur_start) == 0;
4972
0
        start_index += is_overlapping;
4973
0
      }
4974
0
    }
4975
10.1k
  }
4976
4977
10.1k
  if (end != nullptr) {
4978
    // if within_interval is true, with file_key would find
4979
    // not overlapping ranges in std::upper_bound.
4980
10.1k
    auto cmp = [&user_cmp, &within_interval](const InternalKey* k,
4981
15.9k
                                             const FdWithKeyRange& f) {
4982
15.9k
      auto& file_key = within_interval ? f.file_metadata->largest
4983
15.9k
                                       : f.file_metadata->smallest;
4984
15.9k
      return sstableKeyCompare(user_cmp, *k, file_key) < 0;
4985
15.9k
    };
4986
4987
10.1k
    end_index = static_cast<int>(
4988
10.1k
        std::upper_bound(files + start_index, files + num_files, end, cmp) -
4989
10.1k
        files);
4990
4991
10.1k
    if (end_index < num_files && within_interval) {
4992
0
      bool is_overlapping = true;
4993
0
      while (is_overlapping && end_index > start_index) {
4994
0
        auto& next_start = files[end_index].file_metadata->smallest;
4995
0
        auto& cur_limit = files[end_index - 1].file_metadata->largest;
4996
0
        is_overlapping =
4997
0
            sstableKeyCompare(user_cmp, cur_limit, next_start) == 0;
4998
0
        end_index -= is_overlapping;
4999
0
      }
5000
0
    }
5001
10.1k
  }
5002
5003
10.1k
  assert(start_index <= end_index);
5004
5005
  // If there were no overlapping files, return immediately.
5006
10.1k
  if (start_index == end_index) {
5007
2.27k
    if (next_smallest) {
5008
0
      *next_smallest = nullptr;
5009
0
    }
5010
2.27k
    return;
5011
2.27k
  }
5012
5013
10.1k
  assert(start_index < end_index);
5014
5015
  // returns the index where an overlap is found
5016
7.87k
  if (file_index) {
5017
5.60k
    *file_index = start_index;
5018
5.60k
  }
5019
5020
  // insert overlapping files into vector
5021
23.5k
  for (int i = start_index; i < end_index; i++) {
5022
15.7k
    inputs->push_back(files_[level][i]);
5023
15.7k
  }
5024
5025
7.87k
  if (next_smallest != nullptr) {
5026
    // Provide the next key outside the range covered by inputs
5027
0
    if (end_index < static_cast<int>(files_[level].size())) {
5028
0
      **next_smallest = files_[level][end_index]->smallest;
5029
0
    } else {
5030
0
      *next_smallest = nullptr;
5031
0
    }
5032
0
  }
5033
7.87k
}
5034
5035
378
uint64_t VersionStorageInfo::NumLevelBytes(int level) const {
5036
378
  assert(level >= 0);
5037
378
  assert(level < num_levels());
5038
378
  return TotalFileSize(files_[level]);
5039
378
}
5040
5041
const char* VersionStorageInfo::LevelSummary(
5042
7.79k
    LevelSummaryStorage* scratch) const {
5043
7.79k
  int len = 0;
5044
7.79k
  if (compaction_style_ == kCompactionStyleLevel && num_levels() > 1) {
5045
7.79k
    assert(base_level_ < static_cast<int>(level_max_bytes_.size()));
5046
7.79k
    if (level_multiplier_ != 0.0) {
5047
5.86k
      len = snprintf(
5048
5.86k
          scratch->buffer, sizeof(scratch->buffer),
5049
5.86k
          "base level %d level multiplier %.2f max bytes base %" PRIu64 " ",
5050
5.86k
          base_level_, level_multiplier_, level_max_bytes_[base_level_]);
5051
5.86k
    }
5052
7.79k
  }
5053
7.79k
  len +=
5054
7.79k
      snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, "files[");
5055
62.3k
  for (int i = 0; i < num_levels(); i++) {
5056
54.5k
    int sz = sizeof(scratch->buffer) - len;
5057
54.5k
    int ret = snprintf(scratch->buffer + len, sz, "%d ", int(files_[i].size()));
5058
54.5k
    if (ret < 0 || ret >= sz) {
5059
0
      break;
5060
0
    }
5061
54.5k
    len += ret;
5062
54.5k
  }
5063
7.79k
  if (len > 0) {
5064
    // overwrite the last space
5065
7.79k
    --len;
5066
7.79k
  }
5067
7.79k
  len +=
5068
7.79k
      snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
5069
7.79k
               "] max score %.2f, estimated pending compaction bytes %" PRIu64,
5070
7.79k
               compaction_score_[0], estimated_compaction_needed_bytes_);
5071
5072
7.79k
  if (!files_marked_for_compaction_.empty()) {
5073
0
    snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len,
5074
0
             " (%" ROCKSDB_PRIszt " files need compaction)",
5075
0
             files_marked_for_compaction_.size());
5076
0
  }
5077
5078
7.79k
  return scratch->buffer;
5079
7.79k
}
5080
5081
const char* VersionStorageInfo::LevelFileSummary(FileSummaryStorage* scratch,
5082
0
                                                 int level) const {
5083
0
  int len = snprintf(scratch->buffer, sizeof(scratch->buffer), "files_size[");
5084
0
  for (const auto& f : files_[level]) {
5085
0
    int sz = sizeof(scratch->buffer) - len;
5086
0
    char sztxt[16];
5087
0
    AppendHumanBytes(f->fd.GetFileSize(), sztxt, sizeof(sztxt));
5088
0
    int ret = snprintf(scratch->buffer + len, sz,
5089
0
                       "#%" PRIu64 "(seq=%" PRIu64 ",sz=%s,%d) ",
5090
0
                       f->fd.GetNumber(), f->fd.smallest_seqno, sztxt,
5091
0
                       static_cast<int>(f->being_compacted));
5092
0
    if (ret < 0 || ret >= sz) {
5093
0
      break;
5094
0
    }
5095
0
    len += ret;
5096
0
  }
5097
  // overwrite the last space (only if files_[level].size() is non-zero)
5098
0
  if (files_[level].size() && len > 0) {
5099
0
    --len;
5100
0
  }
5101
0
  snprintf(scratch->buffer + len, sizeof(scratch->buffer) - len, "]");
5102
0
  return scratch->buffer;
5103
0
}
5104
5105
121k
bool VersionStorageInfo::HasMissingEpochNumber() const {
5106
968k
  for (int level = 0; level < num_levels_; ++level) {
5107
847k
    for (const FileMetaData* f : files_[level]) {
5108
68.7k
      if (f->epoch_number == kUnknownEpochNumber) {
5109
0
        return true;
5110
0
      }
5111
68.7k
    }
5112
847k
  }
5113
121k
  return false;
5114
121k
}
5115
5116
60.5k
uint64_t VersionStorageInfo::GetMaxEpochNumberOfFiles() const {
5117
60.5k
  uint64_t max_epoch_number = kUnknownEpochNumber;
5118
484k
  for (int level = 0; level < num_levels_; ++level) {
5119
423k
    for (const FileMetaData* f : files_[level]) {
5120
68.7k
      max_epoch_number = std::max(max_epoch_number, f->epoch_number);
5121
68.7k
    }
5122
423k
  }
5123
60.5k
  return max_epoch_number;
5124
60.5k
}
5125
5126
void VersionStorageInfo::RecoverEpochNumbers(ColumnFamilyData* cfd,
5127
60.5k
                                             bool restart_epoch, bool force) {
5128
60.5k
  if (restart_epoch) {
5129
60.5k
    cfd->ResetNextEpochNumber();
5130
5131
60.5k
    bool reserve_epoch_num_for_file_ingested_behind = cfd->AllowIngestBehind();
5132
60.5k
    if (reserve_epoch_num_for_file_ingested_behind) {
5133
0
      uint64_t reserved_epoch_number = cfd->NewEpochNumber();
5134
0
      assert(reserved_epoch_number ==
5135
0
             kReservedEpochNumberForFileIngestedBehind);
5136
0
      ROCKS_LOG_INFO(cfd->ioptions().info_log.get(),
5137
0
                     "[%s]CF has reserved epoch number %" PRIu64
5138
0
                     " for files ingested "
5139
0
                     "behind since `Options::allow_ingest_behind` or "
5140
0
                     "`Options::cf_allow_ingest_behind` is true",
5141
0
                     cfd->GetName().c_str(), reserved_epoch_number);
5142
0
    }
5143
60.5k
  }
5144
5145
60.5k
  bool missing_epoch_number = HasMissingEpochNumber();
5146
60.5k
  if (missing_epoch_number || force) {
5147
0
    for (int level = num_levels_ - 1; level >= 1; --level) {
5148
0
      auto& files_at_level = files_[level];
5149
0
      if (files_at_level.empty()) {
5150
0
        continue;
5151
0
      }
5152
0
      uint64_t next_epoch_number = cfd->NewEpochNumber();
5153
0
      for (FileMetaData* f : files_at_level) {
5154
0
        f->epoch_number = next_epoch_number;
5155
0
      }
5156
0
    }
5157
0
    for (auto file_meta_iter = files_[0].rbegin();
5158
0
         file_meta_iter != files_[0].rend(); file_meta_iter++) {
5159
0
      FileMetaData* f = *file_meta_iter;
5160
0
      f->epoch_number = cfd->NewEpochNumber();
5161
0
    }
5162
0
    if (missing_epoch_number) {
5163
0
      assert(epoch_number_requirement_ ==
5164
0
             EpochNumberRequirement::kMightMissing);
5165
0
      ROCKS_LOG_WARN(cfd->ioptions().info_log.get(),
5166
0
                     "[%s]CF's epoch numbers are inferred based on seqno",
5167
0
                     cfd->GetName().c_str());
5168
0
      epoch_number_requirement_ = EpochNumberRequirement::kMustPresent;
5169
0
    }
5170
60.5k
  } else {
5171
60.5k
    assert(epoch_number_requirement_ == EpochNumberRequirement::kMustPresent);
5172
60.5k
    cfd->SetNextEpochNumber(
5173
60.5k
        std::max(GetMaxEpochNumberOfFiles() + 1, cfd->GetNextEpochNumber()));
5174
60.5k
  }
5175
60.5k
}
5176
5177
0
uint64_t VersionStorageInfo::MaxNextLevelOverlappingBytes() {
5178
0
  uint64_t result = 0;
5179
0
  std::vector<FileMetaData*> overlaps;
5180
0
  for (int level = 1; level < num_levels() - 1; level++) {
5181
0
    for (const auto& f : files_[level]) {
5182
0
      GetOverlappingInputs(level + 1, &f->smallest, &f->largest, &overlaps);
5183
0
      const uint64_t sum = TotalFileSize(overlaps);
5184
0
      if (sum > result) {
5185
0
        result = sum;
5186
0
      }
5187
0
    }
5188
0
  }
5189
0
  return result;
5190
0
}
5191
5192
3.32M
uint64_t VersionStorageInfo::MaxBytesForLevel(int level) const {
5193
  // Note: the result for level zero is not really used since we set
5194
  // the level-0 compaction threshold based on number of files.
5195
3.32M
  assert(level >= 0);
5196
3.32M
  assert(level < static_cast<int>(level_max_bytes_.size()));
5197
3.32M
  return level_max_bytes_[level];
5198
3.32M
}
5199
5200
void VersionStorageInfo::CalculateBaseBytes(const ImmutableOptions& ioptions,
5201
215k
                                            const MutableCFOptions& options) {
5202
  // Special logic to set number of sorted runs.
5203
  // It is to match the previous behavior when all files are in L0.
5204
215k
  int num_l0_count = static_cast<int>(files_[0].size());
5205
215k
  if (compaction_style_ == kCompactionStyleUniversal) {
5206
    // For universal compaction, we use level0 score to indicate
5207
    // compaction score for the whole DB. Adding other levels as if
5208
    // they are L0 files.
5209
0
    for (int i = 1; i < num_levels(); i++) {
5210
0
      if (!files_[i].empty()) {
5211
0
        num_l0_count++;
5212
0
      }
5213
0
    }
5214
0
  }
5215
215k
  set_l0_delay_trigger_count(num_l0_count);
5216
5217
215k
  level_max_bytes_.resize(ioptions.num_levels);
5218
215k
  if (!ioptions.level_compaction_dynamic_level_bytes) {
5219
0
    base_level_ = (ioptions.compaction_style == kCompactionStyleLevel) ? 1 : -1;
5220
5221
    // Calculate for static bytes base case
5222
0
    for (int i = 0; i < ioptions.num_levels; ++i) {
5223
0
      if (i == 0 && ioptions.compaction_style == kCompactionStyleUniversal) {
5224
0
        level_max_bytes_[i] = options.max_bytes_for_level_base;
5225
0
      } else if (i > 1) {
5226
0
        level_max_bytes_[i] = MultiplyCheckOverflow(
5227
0
            MultiplyCheckOverflow(level_max_bytes_[i - 1],
5228
0
                                  options.max_bytes_for_level_multiplier),
5229
0
            options.MaxBytesMultiplerAdditional(i - 1));
5230
0
      } else {
5231
0
        level_max_bytes_[i] = options.max_bytes_for_level_base;
5232
0
      }
5233
0
    }
5234
215k
  } else {
5235
215k
    assert(ioptions.compaction_style == kCompactionStyleLevel);
5236
215k
    uint64_t max_level_size = 0;
5237
5238
215k
    int first_non_empty_level = -1;
5239
    // Find size of non-L0 level of most data.
5240
    // Cannot use the size of the last level because it can be empty or less
5241
    // than previous levels after compaction.
5242
1.51M
    for (int i = 1; i < num_levels_; i++) {
5243
1.29M
      uint64_t total_size = 0;
5244
1.29M
      for (const auto& f : files_[i]) {
5245
63.9k
        total_size += f->fd.GetFileSize();
5246
63.9k
      }
5247
1.29M
      if (total_size > 0 && first_non_empty_level == -1) {
5248
25.6k
        first_non_empty_level = i;
5249
25.6k
      }
5250
1.29M
      if (total_size > max_level_size) {
5251
25.6k
        max_level_size = total_size;
5252
25.6k
      }
5253
1.29M
    }
5254
5255
    // Prefill every level's max bytes to disallow compaction from there.
5256
1.72M
    for (int i = 0; i < num_levels_; i++) {
5257
1.51M
      level_max_bytes_[i] = std::numeric_limits<uint64_t>::max();
5258
1.51M
    }
5259
5260
215k
    lowest_unnecessary_level_ = -1;
5261
215k
    if (max_level_size == 0) {
5262
      // No data for L1 and up. L0 compacts to last level directly.
5263
      // No compaction from L1+ needs to be scheduled.
5264
190k
      base_level_ = num_levels_ - 1;
5265
190k
    } else {
5266
25.6k
      assert(first_non_empty_level >= 1);
5267
25.6k
      uint64_t base_bytes_max = options.max_bytes_for_level_base;
5268
25.6k
      uint64_t base_bytes_min = static_cast<uint64_t>(
5269
25.6k
          base_bytes_max / options.max_bytes_for_level_multiplier);
5270
5271
      // Try whether we can make last level's target size to be max_level_size
5272
25.6k
      uint64_t cur_level_size = max_level_size;
5273
25.6k
      for (int i = num_levels_ - 2; i >= first_non_empty_level; i--) {
5274
        // Round up after dividing
5275
0
        cur_level_size = static_cast<uint64_t>(
5276
0
            cur_level_size / options.max_bytes_for_level_multiplier);
5277
0
        if (lowest_unnecessary_level_ == -1 &&
5278
0
            cur_level_size <= base_bytes_min &&
5279
0
            (options.preclude_last_level_data_seconds == 0 ||
5280
0
             i < num_levels_ - 2)) {
5281
          // When per_key_placement is enabled, the proximal level is
5282
          // necessary.
5283
0
          lowest_unnecessary_level_ = i;
5284
0
        }
5285
0
      }
5286
5287
      // Calculate base level and its size.
5288
25.6k
      uint64_t base_level_size;
5289
25.6k
      if (cur_level_size <= base_bytes_min) {
5290
        // If per_key_placement is not enabled,
5291
        // either there is only one non-empty level after level 0,
5292
        // which can less than base_bytes_min AND necessary,
5293
        // or there is some unnecessary level.
5294
25.6k
        assert(first_non_empty_level == num_levels_ - 1 ||
5295
25.6k
               options.preclude_last_level_data_seconds > 0 ||
5296
25.6k
               lowest_unnecessary_level_ != -1);
5297
        // Case 1. If we make target size of last level to be max_level_size,
5298
        // target size of the first non-empty level would be smaller than
5299
        // base_bytes_min. We set it be base_bytes_min.
5300
25.6k
        base_level_size = base_bytes_min + 1U;
5301
25.6k
        base_level_ = first_non_empty_level;
5302
25.6k
        if (base_level_ < num_levels_ - 1) {
5303
0
          ROCKS_LOG_INFO(
5304
0
              ioptions.logger,
5305
0
              "More existing levels in DB than needed: all non-zero "
5306
0
              "levels <= level %d are unnecessary.  "
5307
0
              "max_bytes_for_level_multiplier may not be guaranteed.",
5308
0
              lowest_unnecessary_level_);
5309
0
        }
5310
25.6k
      } else {
5311
0
        assert(lowest_unnecessary_level_ == -1);
5312
        // Find base level (where L0 data is compacted to).
5313
0
        base_level_ = first_non_empty_level;
5314
0
        while (base_level_ > 1 && cur_level_size > base_bytes_max) {
5315
0
          --base_level_;
5316
0
          cur_level_size = static_cast<uint64_t>(
5317
0
              cur_level_size / options.max_bytes_for_level_multiplier);
5318
0
        }
5319
0
        if (cur_level_size > base_bytes_max) {
5320
          // Even L1 will be too large
5321
0
          assert(base_level_ == 1);
5322
0
          base_level_size = base_bytes_max;
5323
0
        } else {
5324
0
          base_level_size = std::max(static_cast<uint64_t>(1), cur_level_size);
5325
0
        }
5326
0
      }
5327
5328
25.6k
      level_multiplier_ = options.max_bytes_for_level_multiplier;
5329
25.6k
      assert(base_level_size > 0);
5330
5331
25.6k
      uint64_t level_size = base_level_size;
5332
51.2k
      for (int i = base_level_; i < num_levels_; i++) {
5333
25.6k
        if (i > base_level_) {
5334
0
          level_size = MultiplyCheckOverflow(level_size, level_multiplier_);
5335
0
        }
5336
        // Don't set any level below base_bytes_max. Otherwise, the LSM can
5337
        // assume an hourglass shape where L1+ sizes are smaller than L0. This
5338
        // causes compaction scoring, which depends on level sizes, to favor L1+
5339
        // at the expense of L0, which may fill up and stall.
5340
25.6k
        level_max_bytes_[i] = std::max(level_size, base_bytes_max);
5341
25.6k
      }
5342
25.6k
    }
5343
215k
  }
5344
215k
}
5345
5346
0
uint64_t VersionStorageInfo::EstimateLiveDataSize() const {
5347
  // Estimate the live data size by adding up the size of a maximal set of
5348
  // sst files with no range overlap in same or higher level. The less
5349
  // compacted, the more optimistic (smaller) this estimate is. Also,
5350
  // for multiple sorted runs within a level, file order will matter.
5351
0
  uint64_t size = 0;
5352
5353
0
  auto ikey_lt = [this](InternalKey* x, InternalKey* y) {
5354
0
    return internal_comparator_->Compare(*x, *y) < 0;
5355
0
  };
5356
  // (Ordered) map of largest keys in files being included in size estimate
5357
0
  std::map<InternalKey*, FileMetaData*, decltype(ikey_lt)> ranges(ikey_lt);
5358
5359
0
  for (int l = num_levels_ - 1; l >= 0; l--) {
5360
0
    bool found_end = false;
5361
0
    for (auto file : files_[l]) {
5362
      // Find the first file already included with largest key is larger than
5363
      // the smallest key of `file`. If that file does not overlap with the
5364
      // current file, none of the files in the map does. If there is
5365
      // no potential overlap, we can safely insert the rest of this level
5366
      // (if the level is not 0) into the map without checking again because
5367
      // the elements in the level are sorted and non-overlapping.
5368
0
      auto lb = (found_end && l != 0) ? ranges.end()
5369
0
                                      : ranges.lower_bound(&file->smallest);
5370
0
      found_end = (lb == ranges.end());
5371
0
      if (found_end || internal_comparator_->Compare(
5372
0
                           file->largest, (*lb).second->smallest) < 0) {
5373
0
        ranges.emplace_hint(lb, &file->largest, file);
5374
0
        size += file->fd.file_size;
5375
0
      }
5376
0
    }
5377
0
  }
5378
5379
  // For BlobDB, the result also includes the exact value of live bytes in the
5380
  // blob files of the version.
5381
0
  for (const auto& meta : blob_files_) {
5382
0
    assert(meta);
5383
5384
0
    size += meta->GetTotalBlobBytes();
5385
0
    size -= meta->GetGarbageBlobBytes();
5386
0
  }
5387
5388
0
  return size;
5389
0
}
5390
5391
bool VersionStorageInfo::RangeMightExistAfterSortedRun(
5392
    const Slice& smallest_user_key, const Slice& largest_user_key,
5393
179k
    int last_level, int last_l0_idx) {
5394
179k
  assert((last_l0_idx != -1) == (last_level == 0));
5395
  // TODO(ajkr): this preserves earlier behavior where we considered an L0 file
5396
  // bottommost only if it's the oldest L0 file and there are no files on older
5397
  // levels. It'd be better to consider it bottommost if there's no overlap in
5398
  // older levels/files.
5399
179k
  if (last_level == 0 &&
5400
109k
      last_l0_idx != static_cast<int>(LevelFiles(0).size() - 1)) {
5401
52.9k
    return true;
5402
52.9k
  }
5403
5404
  // Checks whether there are files living beyond the `last_level`. If lower
5405
  // levels have files, it checks for overlap between [`smallest_key`,
5406
  // `largest_key`] and those files. Bottomlevel optimizations can be made if
5407
  // there are no files in lower levels or if there is no overlap with the files
5408
  // in the lower levels.
5409
448k
  for (int level = last_level + 1; level < num_levels(); level++) {
5410
    // The range is not in the bottommost level if there are files in lower
5411
    // levels when the `last_level` is 0 or if there are files in lower levels
5412
    // which overlap with [`smallest_key`, `largest_key`].
5413
341k
    if (files_[level].size() > 0 &&
5414
19.7k
        (last_level == 0 ||
5415
19.7k
         OverlapInLevel(level, &smallest_user_key, &largest_user_key))) {
5416
19.7k
      return true;
5417
19.7k
    }
5418
341k
  }
5419
106k
  return false;
5420
126k
}
5421
5422
Env::WriteLifeTimeHint VersionStorageInfo::CalculateSSTWriteHint(
5423
20.7k
    int level, CompactionStyleSet compaction_style_set) const {
5424
20.7k
  if (!compaction_style_set.Contains(compaction_style_)) {
5425
0
    return Env::WLTH_NOT_SET;
5426
0
  }
5427
5428
20.7k
  switch (compaction_style_) {
5429
20.7k
    case kCompactionStyleLevel:
5430
20.7k
      if (level == 0) {
5431
18.3k
        return Env::WLTH_MEDIUM;
5432
18.3k
      }
5433
5434
      // L1: medium, L2: long, ...
5435
2.40k
      if (level - base_level_ >= 2) {
5436
0
        return Env::WLTH_EXTREME;
5437
2.40k
      } else if (level < base_level_) {
5438
        // There is no restriction which prevents level passed in to be smaller
5439
        // than base_level.
5440
0
        return Env::WLTH_MEDIUM;
5441
0
      }
5442
2.40k
      return static_cast<Env::WriteLifeTimeHint>(
5443
2.40k
          level - base_level_ + static_cast<int>(Env::WLTH_MEDIUM));
5444
0
    case kCompactionStyleUniversal:
5445
0
      if (level == 0) {
5446
0
        return Env::WLTH_SHORT;
5447
0
      }
5448
0
      if (level == 1) {
5449
0
        return Env::WLTH_MEDIUM;
5450
0
      }
5451
0
      return Env::WLTH_LONG;
5452
0
    case kCompactionStyleFIFO:
5453
0
    case kCompactionStyleNone:
5454
0
    default:
5455
0
      return Env::WLTH_NOT_SET;
5456
20.7k
  }
5457
20.7k
}
5458
5459
void Version::AddLiveFiles(std::vector<uint64_t>* live_table_files,
5460
121k
                           std::vector<uint64_t>* live_blob_files) const {
5461
121k
  assert(live_table_files);
5462
121k
  assert(live_blob_files);
5463
5464
968k
  for (int level = 0; level < storage_info_.num_levels(); ++level) {
5465
847k
    const auto& level_files = storage_info_.LevelFiles(level);
5466
847k
    for (const auto& meta : level_files) {
5467
166k
      assert(meta);
5468
5469
166k
      live_table_files->emplace_back(meta->fd.GetNumber());
5470
166k
    }
5471
847k
  }
5472
5473
121k
  const auto& blob_files = storage_info_.GetBlobFiles();
5474
121k
  for (const auto& meta : blob_files) {
5475
0
    assert(meta);
5476
5477
0
    live_blob_files->emplace_back(meta->GetBlobFileNumber());
5478
0
  }
5479
121k
}
5480
5481
void Version::RemoveLiveFiles(
5482
    std::vector<ObsoleteFileInfo>& sst_delete_candidates,
5483
24.7k
    std::vector<ObsoleteBlobFileInfo>& blob_delete_candidates) const {
5484
24.7k
  for (ObsoleteFileInfo& fi : sst_delete_candidates) {
5485
14.7k
    if (!fi.only_delete_metadata &&
5486
13.5k
        storage_info()->GetFileLocation(fi.metadata->fd.GetNumber()) !=
5487
13.5k
            VersionStorageInfo::FileLocation::Invalid()) {
5488
6.08k
      fi.only_delete_metadata = true;
5489
6.08k
    }
5490
14.7k
  }
5491
5492
24.7k
  blob_delete_candidates.erase(
5493
24.7k
      std::remove_if(
5494
24.7k
          blob_delete_candidates.begin(), blob_delete_candidates.end(),
5495
24.7k
          [this](ObsoleteBlobFileInfo& x) {
5496
0
            return storage_info()->GetBlobFileMetaData(x.GetBlobFileNumber());
5497
0
          }),
5498
24.7k
      blob_delete_candidates.end());
5499
24.7k
}
5500
5501
0
std::string Version::DebugString(bool hex, bool print_stats) const {
5502
0
  std::string r;
5503
0
  for (int level = 0; level < storage_info_.num_levels_; level++) {
5504
    // E.g.,
5505
    //   --- level 1 ---
5506
    //   17:123[1 .. 124]['a' .. 'd']
5507
    //   20:43[124 .. 128]['e' .. 'g']
5508
    //
5509
    // if print_stats=true:
5510
    //   17:123[1 .. 124]['a' .. 'd'](4096)
5511
0
    r.append("--- level ");
5512
0
    AppendNumberTo(&r, level);
5513
0
    r.append(" --- version# ");
5514
0
    AppendNumberTo(&r, version_number_);
5515
0
    if (storage_info_.compact_cursor_[level].Valid()) {
5516
0
      r.append(" --- compact_cursor: ");
5517
0
      r.append(storage_info_.compact_cursor_[level].DebugString(hex));
5518
0
    }
5519
0
    r.append(" ---\n");
5520
0
    const std::vector<FileMetaData*>& files = storage_info_.files_[level];
5521
0
    for (size_t i = 0; i < files.size(); i++) {
5522
0
      r.push_back(' ');
5523
0
      AppendNumberTo(&r, files[i]->fd.GetNumber());
5524
0
      r.push_back(':');
5525
0
      AppendNumberTo(&r, files[i]->fd.GetFileSize());
5526
0
      r.append("[");
5527
0
      AppendNumberTo(&r, files[i]->fd.smallest_seqno);
5528
0
      r.append(" .. ");
5529
0
      AppendNumberTo(&r, files[i]->fd.largest_seqno);
5530
0
      r.append("]");
5531
0
      r.append("[");
5532
0
      r.append(files[i]->smallest.DebugString(hex));
5533
0
      r.append(" .. ");
5534
0
      r.append(files[i]->largest.DebugString(hex));
5535
0
      r.append("]");
5536
0
      if (files[i]->oldest_blob_file_number != kInvalidBlobFileNumber) {
5537
0
        r.append(" blob_file:");
5538
0
        AppendNumberTo(&r, files[i]->oldest_blob_file_number);
5539
0
      }
5540
0
      if (print_stats) {
5541
0
        r.append("(");
5542
0
        r.append(std::to_string(
5543
0
            files[i]->stats.num_reads_sampled.load(std::memory_order_relaxed)));
5544
0
        r.append(")");
5545
0
      }
5546
0
      r.append("\n");
5547
0
    }
5548
0
  }
5549
5550
0
  const auto& blob_files = storage_info_.GetBlobFiles();
5551
0
  if (!blob_files.empty()) {
5552
0
    r.append("--- blob files --- version# ");
5553
0
    AppendNumberTo(&r, version_number_);
5554
0
    r.append(" ---\n");
5555
0
    for (const auto& blob_file_meta : blob_files) {
5556
0
      assert(blob_file_meta);
5557
5558
0
      r.append(blob_file_meta->DebugString());
5559
0
      r.push_back('\n');
5560
0
    }
5561
0
  }
5562
5563
0
  return r;
5564
0
}
5565
5566
// this is used to batch writes to the manifest file
5567
struct VersionSet::ManifestWriter {
5568
  Status status;
5569
  bool done;
5570
  InstrumentedCondVar cv;
5571
  ColumnFamilyData* cfd;
5572
  const autovector<VersionEdit*>& edit_list;
5573
  const std::function<void(const Status&)> manifest_write_callback;
5574
  int max_file_opening_threads;
5575
5576
  explicit ManifestWriter(
5577
      InstrumentedMutex* mu, ColumnFamilyData* _cfd,
5578
      const autovector<VersionEdit*>& e,
5579
      const std::function<void(const Status&)>& manifest_wcb,
5580
      int _max_file_opening_threads = 1)
5581
98.9k
      : done(false),
5582
98.9k
        cv(mu),
5583
98.9k
        cfd(_cfd),
5584
98.9k
        edit_list(e),
5585
98.9k
        manifest_write_callback(manifest_wcb),
5586
98.9k
        max_file_opening_threads(_max_file_opening_threads) {}
5587
  // No copying allowed
5588
  ManifestWriter(const ManifestWriter&) = delete;
5589
  ManifestWriter& operator=(const ManifestWriter&) = delete;
5590
  ManifestWriter(ManifestWriter&&) = delete;
5591
  ManifestWriter& operator=(ManifestWriter&&) = delete;
5592
98.9k
  ~ManifestWriter() { status.PermitUncheckedError(); }
5593
5594
67.6k
  bool IsAllVersionIndependentEdits() const {
5595
67.6k
    for (const auto& e : edit_list) {
5596
67.6k
      if (!e->IsWalManipulation() && !e->IsOptionsFileManipulation()) {
5597
67.6k
        return false;
5598
67.6k
      }
5599
67.6k
    }
5600
0
    return true;
5601
67.6k
  }
5602
};
5603
5604
393k
Status AtomicGroupReadBuffer::AddEdit(VersionEdit* edit) {
5605
393k
  assert(edit);
5606
393k
  if (edit->IsInAtomicGroup()) {
5607
0
    TEST_SYNC_POINT("AtomicGroupReadBuffer::AddEdit:AtomicGroup");
5608
0
    if (replay_buffer_.empty()) {
5609
0
      replay_buffer_.resize(edit->GetRemainingEntries() + 1);
5610
0
      TEST_SYNC_POINT_CALLBACK(
5611
0
          "AtomicGroupReadBuffer::AddEdit:FirstInAtomicGroup", edit);
5612
0
    }
5613
0
    read_edits_in_atomic_group_++;
5614
0
    if (read_edits_in_atomic_group_ + edit->GetRemainingEntries() !=
5615
0
        static_cast<uint32_t>(replay_buffer_.size())) {
5616
0
      TEST_SYNC_POINT_CALLBACK(
5617
0
          "AtomicGroupReadBuffer::AddEdit:IncorrectAtomicGroupSize", edit);
5618
0
      return Status::Corruption("corrupted atomic group");
5619
0
    }
5620
0
    replay_buffer_[read_edits_in_atomic_group_ - 1] = *edit;
5621
0
    if (read_edits_in_atomic_group_ == replay_buffer_.size()) {
5622
0
      TEST_SYNC_POINT_CALLBACK(
5623
0
          "AtomicGroupReadBuffer::AddEdit:LastInAtomicGroup", edit);
5624
0
      return Status::OK();
5625
0
    }
5626
0
    return Status::OK();
5627
0
  }
5628
5629
  // A normal edit.
5630
393k
  if (!replay_buffer().empty()) {
5631
0
    TEST_SYNC_POINT_CALLBACK(
5632
0
        "AtomicGroupReadBuffer::AddEdit:AtomicGroupMixedWithNormalEdits", edit);
5633
0
    return Status::Corruption("corrupted atomic group");
5634
0
  }
5635
393k
  return Status::OK();
5636
393k
}
5637
5638
0
bool AtomicGroupReadBuffer::IsFull() const {
5639
0
  return read_edits_in_atomic_group_ == replay_buffer_.size();
5640
0
}
5641
5642
0
bool AtomicGroupReadBuffer::IsEmpty() const { return replay_buffer_.empty(); }
5643
5644
0
void AtomicGroupReadBuffer::Clear() {
5645
0
  read_edits_in_atomic_group_ = 0;
5646
0
  replay_buffer_.clear();
5647
0
}
5648
5649
VersionSet::VersionSet(
5650
    const std::string& dbname, const ImmutableDBOptions* _db_options,
5651
    const MutableDBOptions& mutable_db_options,
5652
    const FileOptions& storage_options, Cache* table_cache,
5653
    WriteBufferManager* write_buffer_manager, WriteController* write_controller,
5654
    BlockCacheTracer* const block_cache_tracer,
5655
    const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
5656
    const std::string& db_session_id, const std::string& daily_offpeak_time_utc,
5657
    ErrorHandler* error_handler, bool unchanging)
5658
44.8k
    : column_family_set_(new ColumnFamilySet(
5659
44.8k
          dbname, _db_options, storage_options, table_cache,
5660
44.8k
          write_buffer_manager, write_controller, block_cache_tracer, io_tracer,
5661
44.8k
          db_id, db_session_id, mutable_db_options.fast_sst_open)),
5662
44.8k
      table_cache_(table_cache),
5663
44.8k
      env_(_db_options->env),
5664
44.8k
      fs_(_db_options->fs, io_tracer),
5665
44.8k
      clock_(_db_options->clock),
5666
44.8k
      dbname_(dbname),
5667
44.8k
      db_id_(db_id),
5668
44.8k
      db_options_(_db_options),
5669
44.8k
      next_file_number_(2),
5670
44.8k
      manifest_file_number_(0),  // Filled by Recover()
5671
44.8k
      options_file_number_(0),
5672
44.8k
      options_file_size_(0),
5673
44.8k
      has_committed_options_file_number_(false),
5674
44.8k
      pending_manifest_file_number_(0),
5675
44.8k
      last_sequence_(0),
5676
44.8k
      last_allocated_sequence_(0),
5677
44.8k
      last_published_sequence_(0),
5678
44.8k
      prev_log_number_(0),
5679
44.8k
      current_version_number_(0),
5680
44.8k
      manifest_file_size_(0),
5681
44.8k
      manifest_recovery_last_valid_record_end_(0),
5682
44.8k
      manifest_last_valid_record_end_(0),
5683
44.8k
      manifest_last_valid_record_end_file_number_(0),
5684
44.8k
      force_new_manifest_on_open_(false),
5685
44.8k
      last_compacted_manifest_file_size_(0),
5686
44.8k
      file_options_(storage_options),
5687
44.8k
      block_cache_tracer_(block_cache_tracer),
5688
44.8k
      io_tracer_(io_tracer),
5689
44.8k
      db_session_id_(db_session_id),
5690
44.8k
      offpeak_time_option_(OffpeakTimeOption(daily_offpeak_time_utc)),
5691
44.8k
      error_handler_(error_handler),
5692
44.8k
      unchanging_(unchanging),
5693
44.8k
      closed_(false) {
5694
44.8k
  UpdatedMutableDbOptions(mutable_db_options, /*mu=*/nullptr);
5695
44.8k
}
5696
5697
44.8k
Status VersionSet::Close(FSDirectory* db_dir, InstrumentedMutex* mu) {
5698
44.8k
  Status s;
5699
44.8k
  if (closed_ || unchanging_ || !manifest_file_number_ || !descriptor_log_) {
5700
0
    return s;
5701
0
  }
5702
5703
44.8k
  std::string manifest_file_name =
5704
44.8k
      DescriptorFileName(dbname_, manifest_file_number_);
5705
44.8k
  uint64_t size = 0;
5706
44.8k
  IOStatus io_s = descriptor_log_->Close(WriteOptions());
5707
44.8k
  descriptor_log_.reset();
5708
44.8k
  TEST_SYNC_POINT("VersionSet::Close:AfterClose");
5709
44.8k
  if (io_s.ok()) {
5710
44.8k
    io_s = fs_->GetFileSize(manifest_file_name, IOOptions(), &size, nullptr);
5711
44.8k
  }
5712
44.8k
  if (!io_s.ok() || size != manifest_file_size_) {
5713
0
    if (io_s.ok()) {
5714
      // This means the size is not as expected. So we treat it as a
5715
      // corruption and set io_s appropriately
5716
0
      io_s = IOStatus::Corruption();
5717
0
    }
5718
0
    ColumnFamilyData* cfd = GetColumnFamilySet()->GetDefault();
5719
0
    IOErrorInfo io_error_info(io_s, FileOperationType::kVerify,
5720
0
                              manifest_file_name, /*length=*/size,
5721
0
                              /*offset=*/0);
5722
5723
0
    for (auto& listener : cfd->ioptions().listeners) {
5724
0
      listener->OnIOError(io_error_info);
5725
0
    }
5726
0
    io_s.PermitUncheckedError();
5727
0
    io_error_info.io_status.PermitUncheckedError();
5728
0
    ROCKS_LOG_ERROR(db_options_->info_log,
5729
0
                    "MANIFEST verification on Close, "
5730
0
                    "filename %s, expected size %" PRIu64
5731
0
                    " failed with status %s and "
5732
0
                    "actual size %" PRIu64 "\n",
5733
0
                    manifest_file_name.c_str(), manifest_file_size_,
5734
0
                    io_s.ToString().c_str(), size);
5735
0
    VersionEdit edit;
5736
0
    assert(cfd);
5737
0
    s = LogAndApply(cfd, ReadOptions(), WriteOptions(), &edit, mu, db_dir);
5738
0
  }
5739
5740
  // Content validation: read back the manifest and verify CRC + decode.
5741
  // Loop up to 2 checks with 1 rewrite attempt in between, so we also verify
5742
  // the rewritten manifest is healthy.
5743
44.8k
  if (s.ok() && verify_manifest_content_on_close_) {
5744
0
    TEST_SYNC_POINT("VersionSet::Close:BeforeContentValidation");
5745
0
    constexpr int kMaxContentChecks = 2;
5746
0
    for (int content_check = 0; s.ok() && content_check < kMaxContentChecks;
5747
0
         ++content_check) {
5748
      // Re-read the manifest file name in case it was rotated by a rewrite
5749
0
      std::string content_manifest_name =
5750
0
          DescriptorFileName(dbname_, manifest_file_number_);
5751
0
      std::unique_ptr<FSSequentialFile> manifest_file;
5752
0
      IOStatus content_io_s = fs_->NewSequentialFile(
5753
0
          content_manifest_name, fs_->OptimizeForManifestRead(file_options_),
5754
0
          &manifest_file, nullptr);
5755
0
      if (!content_io_s.ok()) {
5756
        // Surface I/O errors to the caller -- users who call DB::Close() and
5757
        // check the status should know about filesystem problems.
5758
0
        s = content_io_s;
5759
0
        ROCKS_LOG_ERROR(db_options_->info_log,
5760
0
                        "MANIFEST content verification on Close: "
5761
0
                        "could not open %s for reading: %s\n",
5762
0
                        content_manifest_name.c_str(),
5763
0
                        content_io_s.ToString().c_str());
5764
0
        break;
5765
0
      }
5766
0
      std::unique_ptr<SequentialFileReader> manifest_file_reader(
5767
0
          new SequentialFileReader(std::move(manifest_file),
5768
0
                                   content_manifest_name,
5769
0
                                   db_options_->log_readahead_size, io_tracer_,
5770
0
                                   db_options_->listeners));
5771
0
      LogReporter reporter;
5772
0
      Status log_read_status;
5773
0
      reporter.status = &log_read_status;
5774
0
      log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
5775
0
                         /*checksum=*/true, /*log_num=*/0);
5776
0
      Slice record;
5777
0
      std::string scratch;
5778
0
      bool content_corrupt = false;
5779
0
      while (reader.ReadRecord(&record, &scratch,
5780
0
                               WALRecoveryMode::kAbsoluteConsistency)) {
5781
0
        VersionEdit edit;
5782
0
        Status decode_s = edit.DecodeFrom(record);
5783
0
        if (!decode_s.ok()) {
5784
0
          content_corrupt = true;
5785
0
          break;
5786
0
        }
5787
0
      }
5788
0
      if (!content_corrupt && !log_read_status.ok()) {
5789
0
        content_corrupt = true;
5790
0
      }
5791
0
      if (!content_corrupt) {
5792
        // Manifest is healthy, no need to check again
5793
0
        break;
5794
0
      }
5795
0
      RecordTick(db_options_->statistics.get(),
5796
0
                 MANIFEST_VALIDATION_FAILURE_COUNT);
5797
0
      IOStatus corrupt_io_s =
5798
0
          IOStatus::Corruption("MANIFEST content validation failed");
5799
0
      IOErrorInfo io_error_info(corrupt_io_s, FileOperationType::kVerify,
5800
0
                                content_manifest_name, /*length=*/0,
5801
0
                                /*offset=*/0);
5802
0
      for (auto& listener : db_options_->listeners) {
5803
0
        listener->OnIOError(io_error_info);
5804
0
      }
5805
0
      corrupt_io_s.PermitUncheckedError();
5806
0
      io_error_info.io_status.PermitUncheckedError();
5807
0
      if (content_check == 0) {
5808
        // First check failed -- rewrite and verify again
5809
0
        ROCKS_LOG_ERROR(db_options_->info_log,
5810
0
                        "MANIFEST content verification on Close failed, "
5811
0
                        "filename %s, rewriting manifest\n",
5812
0
                        content_manifest_name.c_str());
5813
0
        ColumnFamilyData* cfd = GetColumnFamilySet()->GetDefault();
5814
0
        VersionEdit recovery_edit;
5815
0
        assert(cfd);
5816
0
        s = LogAndApply(cfd, ReadOptions(), WriteOptions(), &recovery_edit, mu,
5817
0
                        db_dir);
5818
0
      } else {
5819
        // Rewritten manifest is also corrupt -- likely a recurring filesystem
5820
        // issue. Surface it so DB::Close() callers can detect the problem.
5821
0
        ROCKS_LOG_ERROR(db_options_->info_log,
5822
0
                        "MANIFEST content verification on Close failed again "
5823
0
                        "after rewrite, filename %s\n",
5824
0
                        content_manifest_name.c_str());
5825
0
        s = Status::Corruption(
5826
0
            "MANIFEST content verification failed after rewrite: " +
5827
0
            content_manifest_name);
5828
0
      }
5829
0
    }
5830
0
  }
5831
5832
44.8k
  closed_ = true;
5833
44.8k
  return s;
5834
44.8k
}
5835
5836
44.8k
VersionSet::~VersionSet() {
5837
  // Must clean up column families to make all files "obsolete"
5838
44.8k
  column_family_set_.reset();
5839
5840
81.9k
  for (auto& file : obsolete_files_) {
5841
    // NOTE: DB is shutting down, so file is probably not obsolete, just
5842
    // no longer referenced by Versions in memory.
5843
    // For more context, see comment on "table_cache_->EraseUnRefEntries()"
5844
    // in DBImpl::CloseHelper().
5845
    // Using uncache_aggressiveness=0 overrides any previous marking to
5846
    // attempt to uncache the file's blocks (which after cleaning up
5847
    // column families could cause use-after-free)
5848
81.9k
    TableCache::ReleaseObsolete(
5849
81.9k
        table_cache_, file.metadata->fd.GetNumber(),
5850
81.9k
        file.metadata->fd.pinned_reader.GetCacheHandle(),
5851
81.9k
        /*uncache_aggressiveness=*/0);
5852
81.9k
    file.DeleteMetadata();
5853
81.9k
  }
5854
44.8k
  obsolete_files_.clear();
5855
44.8k
  io_status_.PermitUncheckedError();
5856
44.8k
}
5857
5858
0
void VersionSet::ApplyCommittedOptionsFileNumber(uint64_t number) {
5859
0
  options_file_number_ = number;
5860
0
  has_committed_options_file_number_ = true;
5861
0
}
5862
5863
121k
void VersionSet::ApplyLegacyOptionsFileNumber(uint64_t number) {
5864
121k
  options_file_number_ = number;
5865
121k
  has_committed_options_file_number_ = false;
5866
121k
}
5867
5868
0
void VersionSet::Reset() {
5869
0
  if (column_family_set_) {
5870
0
    WriteBufferManager* wbm = column_family_set_->write_buffer_manager();
5871
0
    WriteController* wc = column_family_set_->write_controller();
5872
0
    FlushInitiator* flush_initiator = column_family_set_->flush_initiator();
5873
0
    const bool fast_sst_open = column_family_set_->GetFastSstOpen();
5874
5875
    // Destruction removes column families one at a time. Detach the accounting
5876
    // target first so each removal does not rescan all remaining column
5877
    // families while falling back to another MANIFEST.
5878
0
    column_family_set_->SetFlushInitiator(nullptr);
5879
5880
    // Clear TableCache to prevent use-after-free: Reset() deletes old
5881
    // ColumnFamilySet but reuses table_cache_, which may contain
5882
    // BlockBasedTable entries with dangling references to deleted CFD's
5883
    // ioptions.
5884
0
    if (table_cache_) {
5885
0
      table_cache_->EraseUnRefEntries();
5886
0
    }
5887
5888
    // db_id becomes the source of truth after DBImpl::Recover():
5889
    // https://github.com/meta/rocksdb/blob/v7.3.1/db/db_impl/db_impl_open.cc#L527
5890
    // Note: we may not be able to recover db_id from MANIFEST if
5891
    // options.write_dbid_to_manifest is false (default).
5892
0
    column_family_set_.reset(
5893
0
        new ColumnFamilySet(dbname_, db_options_, file_options_, table_cache_,
5894
0
                            wbm, wc, block_cache_tracer_, io_tracer_, db_id_,
5895
0
                            db_session_id_, fast_sst_open));
5896
0
    column_family_set_->SetFlushInitiator(flush_initiator);
5897
0
  }
5898
0
  db_id_.clear();
5899
0
  next_file_number_.store(2);
5900
0
  min_log_number_to_keep_.store(0);
5901
0
  manifest_file_number_ = 0;
5902
0
  options_file_number_ = 0;
5903
0
  options_file_size_ = 0;
5904
0
  has_committed_options_file_number_ = false;
5905
0
  pending_manifest_file_number_ = 0;
5906
0
  last_sequence_.store(0);
5907
0
  last_allocated_sequence_.store(0);
5908
0
  last_published_sequence_.store(0);
5909
0
  prev_log_number_ = 0;
5910
0
  descriptor_log_.reset();
5911
0
  current_version_number_ = 0;
5912
0
  manifest_writers_.clear();
5913
0
  manifest_file_size_ = 0;
5914
0
  manifest_recovery_last_valid_record_end_ = 0;
5915
0
  manifest_last_valid_record_end_ = 0;
5916
0
  manifest_last_valid_record_end_file_number_ = 0;
5917
0
  force_new_manifest_on_open_ = false;
5918
0
  last_compacted_manifest_file_size_ = 0;
5919
0
  TuneMaxManifestFileSize();
5920
0
  obsolete_files_.clear();
5921
0
  obsolete_manifests_.clear();
5922
0
  wals_.Reset();
5923
0
}
5924
5925
void VersionSet::UpdatedMutableDbOptions(
5926
44.8k
    const MutableDBOptions& updated_options, InstrumentedMutex* mu) {
5927
  // Must be holding mutex if not called during initialization
5928
44.8k
  if (mu) {
5929
0
    mu->AssertHeld();
5930
44.8k
  } else {
5931
    // manifest_file_size_ must be 0 if called from the constructor
5932
44.8k
    assert(manifest_file_size_ == 0);
5933
44.8k
  }
5934
44.8k
  file_options_.writable_file_max_buffer_size =
5935
44.8k
      updated_options.writable_file_max_buffer_size;
5936
44.8k
  min_max_manifest_file_size_ = updated_options.max_manifest_file_size;
5937
44.8k
  max_manifest_space_amp_pct_ = static_cast<unsigned>(
5938
44.8k
      std::max(updated_options.max_manifest_space_amp_pct, 0));
5939
44.8k
  manifest_preallocation_size_ = updated_options.manifest_preallocation_size;
5940
44.8k
  verify_manifest_content_on_close_ =
5941
44.8k
      updated_options.verify_manifest_content_on_close;
5942
5943
44.8k
  if (periodic_compaction_phaser_.SetConfig(
5944
44.8k
          updated_options.periodic_compaction_phase_recovery_percent)) {
5945
    // (Re)anchor phasing so that switching it on (at open or via SetDBOptions)
5946
    // spreads the initial periodic-compaction catch-up burst over time rather
5947
    // than triggering it all at once.
5948
0
    int64_t now = 0;
5949
0
    if (clock_ != nullptr && clock_->GetCurrentTime(&now).ok()) {
5950
0
      periodic_compaction_phaser_.Reanchor(static_cast<uint64_t>(now));
5951
0
    }
5952
0
    if (mu != nullptr) {
5953
      // Live SetDBOptions change (not construction): push the refreshed phase
5954
      // params into each column family's current Version so the change takes
5955
      // effect on the next periodic re-evaluation (which recomputes scores on
5956
      // the current Version) rather than only when the next Version is built.
5957
      // Safe because periodic_compaction_phase_params_ is read only under the
5958
      // DB mutex, which is held here.
5959
0
      for (auto* cfd : *column_family_set_) {
5960
0
        if (cfd->IsDropped()) {
5961
0
          continue;
5962
0
        }
5963
0
        Version* v = cfd->current();
5964
0
        if (v != nullptr) {
5965
0
          v->storage_info_.periodic_compaction_phase_params_ =
5966
0
              GetPeriodicCompactionPhaseParams(cfd->GetID());
5967
0
        }
5968
0
      }
5969
0
    }
5970
0
  }
5971
5972
44.8k
  TuneMaxManifestFileSize();
5973
44.8k
}
5974
5975
PeriodicCompactionPhaseParams VersionSet::GetPeriodicCompactionPhaseParams(
5976
215k
    uint32_t cf_id) const {
5977
215k
  return periodic_compaction_phaser_.ParamsForCf(cf_id);
5978
215k
}
5979
5980
0
void VersionSet::ReanchorCompactionPhase() {
5981
  // Caller holds the DB mutex. Move the phasing anchor to now and refresh each
5982
  // CF's current Version's cached params, mirroring the refresh done for
5983
  // seed/recovery changes in UpdatedMutableDbOptions(). Used when a CF's
5984
  // periodic_compaction_seconds changes, so a turn-down's newly past-due cohort
5985
  // is spread rather than fired at once. Re-anchoring is DB-level and benign to
5986
  // CFs whose interval did not change (their not-past-due files keep phasing).
5987
0
  int64_t now = 0;
5988
0
  if (clock_ != nullptr && clock_->GetCurrentTime(&now).ok()) {
5989
0
    periodic_compaction_phaser_.Reanchor(static_cast<uint64_t>(now));
5990
0
  }
5991
0
  for (auto* cfd : *column_family_set_) {
5992
0
    if (cfd->IsDropped()) {
5993
0
      continue;
5994
0
    }
5995
0
    Version* v = cfd->current();
5996
0
    if (v != nullptr) {
5997
0
      v->storage_info_.periodic_compaction_phase_params_ =
5998
0
          GetPeriodicCompactionPhaseParams(cfd->GetID());
5999
0
    }
6000
0
  }
6001
0
}
6002
6003
126k
void VersionSet::TuneMaxManifestFileSize() {
6004
126k
  tuned_max_manifest_file_size_ =
6005
126k
      std::max(min_max_manifest_file_size_,
6006
126k
               last_compacted_manifest_file_size_ *
6007
126k
                   (100U + max_manifest_space_amp_pct_) / 100U);
6008
126k
}
6009
6010
void VersionSet::AppendVersion(ColumnFamilyData* column_family_data,
6011
215k
                               Version* v) {
6012
  // compute new compaction score
6013
215k
  v->storage_info()->ComputeCompactionScore(
6014
215k
      column_family_data->ioptions(),
6015
215k
      column_family_data->GetLatestMutableCFOptions(),
6016
215k
      column_family_data->GetFullHistoryTsLow());
6017
6018
  // Mark v finalized
6019
215k
  v->storage_info_.SetFinalized();
6020
6021
  // Make "v" current
6022
215k
  assert(v->refs_ == 0);
6023
215k
  Version* current = column_family_data->current();
6024
215k
  assert(v != current);
6025
215k
  if (current != nullptr) {
6026
128k
    assert(current->refs_ > 0);
6027
128k
    current->Unref();
6028
128k
  }
6029
215k
  column_family_data->SetCurrent(v);
6030
215k
  v->Ref();
6031
6032
  // Append to linked list
6033
215k
  v->prev_ = column_family_data->dummy_versions()->prev_;
6034
215k
  v->next_ = column_family_data->dummy_versions();
6035
215k
  v->prev_->next_ = v;
6036
215k
  v->next_->prev_ = v;
6037
215k
}
6038
6039
Status VersionSet::ProcessManifestWrites(
6040
    std::deque<ManifestWriter>& writers, InstrumentedMutex* mu,
6041
    FSDirectory* dir_contains_current_file, bool new_descriptor_log,
6042
    const ColumnFamilyOptions* new_cf_options,
6043
    bool new_cf_options_are_sanitized, const ReadOptions& read_options,
6044
83.3k
    const WriteOptions& write_options) {
6045
83.3k
  mu->AssertHeld();
6046
83.3k
  assert(!writers.empty());
6047
83.3k
  ManifestWriter& first_writer = writers.front();
6048
83.3k
  ManifestWriter* last_writer = &first_writer;
6049
6050
  // Takes this batch, from the queue head through `last_writer`, off
6051
  // manifest_writers_: records `s` on each writer, runs its callback, and wakes
6052
  // writers other threads wait on, then the next batch's leader. Every return
6053
  // path must do this, because the writers in `writers` live on the caller's
6054
  // stack and a queued pointer to one would outlive them.
6055
83.3k
  auto finish_batch = [&](const Status& s) {
6056
98.9k
    while (true) {
6057
98.9k
      ManifestWriter* ready = manifest_writers_.front();
6058
98.9k
      manifest_writers_.pop_front();
6059
98.9k
      bool need_signal = true;
6060
114k
      for (const auto& w : writers) {
6061
114k
        if (&w == ready) {
6062
98.9k
          need_signal = false;
6063
98.9k
          break;
6064
98.9k
        }
6065
114k
      }
6066
98.9k
      ready->status = s;
6067
98.9k
      ready->done = true;
6068
98.9k
      if (ready->manifest_write_callback) {
6069
83.3k
        (ready->manifest_write_callback)(s);
6070
83.3k
      }
6071
98.9k
      if (need_signal) {
6072
0
        ready->cv.Signal();
6073
0
      }
6074
98.9k
      if (ready == last_writer) {
6075
83.3k
        break;
6076
83.3k
      }
6077
98.9k
    }
6078
83.3k
    if (!manifest_writers_.empty()) {
6079
898
      manifest_writers_.front()->cv.Signal();
6080
898
    }
6081
83.3k
  };
6082
6083
  // Supports opening table readers with multiple threads, mostly useful for
6084
  // batched external sst file ingestion.
6085
83.3k
  int batch_max_file_opening_threads = 1;
6086
6087
83.3k
  assert(!manifest_writers_.empty());
6088
83.3k
  assert(manifest_writers_.front() == &first_writer);
6089
6090
83.3k
  autovector<VersionEdit*> batch_edits;
6091
  // This vector keeps track of the corresponding user-defined timestamp size
6092
  // for `batch_edits` side by side, which is only needed for encoding a
6093
  // `VersionEdit` that adds new SST files.
6094
  // Note that anytime `batch_edits` has new element added or get existing
6095
  // element removed, `batch_edits_ts_sz` should be updated too.
6096
83.3k
  autovector<std::optional<size_t>> batch_edits_ts_sz;
6097
83.3k
  autovector<Version*> versions;
6098
83.3k
  std::vector<std::unique_ptr<BaseReferencedVersionBuilder>> builder_guards;
6099
83.3k
  autovector<const autovector<uint64_t>*> files_to_quarantine_if_commit_fail;
6100
83.3k
  autovector<uint64_t> limbo_descriptor_log_file_number;
6101
6102
  // Tracking `max_last_sequence` is needed to ensure we write
6103
  // `VersionEdit::last_sequence_`s in non-decreasing order according to the
6104
  // recovery code's requirement. It also allows us to defer updating
6105
  // `descriptor_last_sequence_` until the apply phase, after the log phase
6106
  // succeeds.
6107
83.3k
  SequenceNumber max_last_sequence = descriptor_last_sequence_;
6108
6109
83.3k
  bool skip_manifest_write =
6110
83.3k
      first_writer.edit_list.front()->IsNoManifestWriteDummy();
6111
6112
83.3k
  if (first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
6113
    // No group commits for column family add or drop
6114
31.3k
    LogAndApplyCFHelper(first_writer.edit_list.front(), &max_last_sequence);
6115
31.3k
    batch_edits.push_back(first_writer.edit_list.front());
6116
31.3k
    batch_edits_ts_sz.push_back(std::nullopt);
6117
51.9k
  } else {
6118
51.9k
    auto it = manifest_writers_.cbegin();
6119
51.9k
    size_t group_start = std::numeric_limits<size_t>::max();
6120
51.9k
    Status batch_apply_status;
6121
67.6k
    for (;;) {
6122
67.6k
      assert(!(*it)->edit_list.front()->IsColumnFamilyManipulation());
6123
67.6k
      last_writer = *it;
6124
67.6k
      batch_max_file_opening_threads =
6125
67.6k
          std::max(batch_max_file_opening_threads,
6126
67.6k
                   last_writer->max_file_opening_threads);
6127
67.6k
      assert(last_writer != nullptr);
6128
67.6k
      assert(last_writer->cfd != nullptr);
6129
67.6k
      if (!batch_apply_status.ok()) {
6130
        // An earlier edit failed, so apply no more edits, but keep grouping
6131
        // until the loop conditions below end the batch as usual. LogAndApply()
6132
        // returns once a caller's first writer is done, so none of its writers
6133
        // may be left queued.
6134
67.6k
      } else if (last_writer->cfd->IsDropped()) {
6135
        // If we detect a dropped CF at this point, and the corresponding
6136
        // version edits belong to an atomic group, then we need to find out
6137
        // the preceding version edits in the same atomic group, and update
6138
        // their `remaining_entries_` member variable because we are NOT going
6139
        // to write the version edits' of dropped CF to the MANIFEST. If we
6140
        // don't update, then Recover can report corrupted atomic group
6141
        // because the `remaining_entries_` do not match.
6142
0
        if (!batch_edits.empty()) {
6143
0
          if (batch_edits.back()->IsInAtomicGroup() &&
6144
0
              batch_edits.back()->GetRemainingEntries() > 0) {
6145
0
            assert(group_start < batch_edits.size());
6146
0
            const auto& edit_list = last_writer->edit_list;
6147
0
            size_t k = 0;
6148
0
            while (k < edit_list.size()) {
6149
0
              if (!edit_list[k]->IsInAtomicGroup()) {
6150
0
                break;  // inner while-`k` loop
6151
0
              } else if (edit_list[k]->GetRemainingEntries() == 0) {
6152
0
                ++k;
6153
0
                break;  // inner while-`k` loop
6154
0
              }
6155
0
              ++k;
6156
0
            }
6157
0
            for (auto i = group_start; i < batch_edits.size(); ++i) {
6158
0
              assert(static_cast<uint32_t>(k) <=
6159
0
                     batch_edits.back()->GetRemainingEntries());
6160
0
              batch_edits[i]->SetRemainingEntries(
6161
0
                  batch_edits[i]->GetRemainingEntries() -
6162
0
                  static_cast<uint32_t>(k));
6163
0
            }
6164
0
          }
6165
0
        }
6166
67.6k
      } else {
6167
        // We do a linear search on versions because versions is small.
6168
        // TODO(yanqin) maybe consider unordered_map
6169
67.6k
        Version* version = nullptr;
6170
67.6k
        VersionBuilder* builder = nullptr;
6171
83.3k
        for (int i = 0; i != static_cast<int>(versions.size()); ++i) {
6172
15.6k
          uint32_t cf_id = last_writer->cfd->GetID();
6173
15.6k
          if (versions[i]->cfd()->GetID() == cf_id) {
6174
0
            version = versions[i];
6175
0
            assert(!builder_guards.empty() &&
6176
0
                   builder_guards.size() == versions.size());
6177
0
            builder = builder_guards[i]->version_builder();
6178
0
            TEST_SYNC_POINT_CALLBACK(
6179
0
                "VersionSet::ProcessManifestWrites:SameColumnFamily", &cf_id);
6180
0
            break;  // inner for-`i` loop
6181
0
          }
6182
15.6k
        }
6183
67.6k
        if (version == nullptr) {
6184
          // WAL and OPTIONS protocol manipulations do not change LSM Versions.
6185
67.6k
          if (!last_writer->IsAllVersionIndependentEdits()) {
6186
67.6k
            version = new Version(
6187
67.6k
                last_writer->cfd, this, file_options_,
6188
67.6k
                last_writer->cfd ? last_writer->cfd->GetLatestMutableCFOptions()
6189
67.6k
                                 : MutableCFOptions(*new_cf_options),
6190
67.6k
                io_tracer_, current_version_number_++);
6191
67.6k
            versions.push_back(version);
6192
67.6k
            builder_guards.emplace_back(new BaseReferencedVersionBuilder(
6193
67.6k
                last_writer->cfd, last_writer->cfd->current()));
6194
67.6k
            builder = builder_guards.back()->version_builder();
6195
67.6k
          }
6196
67.6k
          assert(last_writer->IsAllVersionIndependentEdits() || builder);
6197
67.6k
          assert(last_writer->IsAllVersionIndependentEdits() || version);
6198
67.6k
          TEST_SYNC_POINT_CALLBACK(
6199
67.6k
              "VersionSet::ProcessManifestWrites:NewVersion", version);
6200
67.6k
        }
6201
67.6k
        const Comparator* ucmp = last_writer->cfd->user_comparator();
6202
67.6k
        assert(ucmp);
6203
67.6k
        std::optional<size_t> edit_ts_sz = ucmp->timestamp_size();
6204
179k
        for (const auto& e : last_writer->edit_list) {
6205
179k
          if (e->IsInAtomicGroup()) {
6206
0
            if (batch_edits.empty() || !batch_edits.back()->IsInAtomicGroup() ||
6207
0
                (batch_edits.back()->IsInAtomicGroup() &&
6208
0
                 batch_edits.back()->GetRemainingEntries() == 0)) {
6209
0
              group_start = batch_edits.size();
6210
0
            }
6211
179k
          } else if (group_start != std::numeric_limits<size_t>::max()) {
6212
0
            group_start = std::numeric_limits<size_t>::max();
6213
0
          }
6214
179k
          batch_apply_status = LogAndApplyHelper(last_writer->cfd, builder, e,
6215
179k
                                                 &max_last_sequence, mu);
6216
179k
          if (!batch_apply_status.ok()) {
6217
0
            break;  // inner for-`e` loop
6218
0
          }
6219
179k
          batch_edits.push_back(e);
6220
179k
          batch_edits_ts_sz.push_back(edit_ts_sz);
6221
179k
        }
6222
67.6k
      }
6223
      // Loop increment/conditions
6224
67.6k
      ++it;
6225
67.6k
      if (it == manifest_writers_.cend()) {
6226
51.9k
        break;
6227
51.9k
      }
6228
15.6k
      if (skip_manifest_write) {
6229
        // no grouping when skipping manifest write
6230
0
        break;
6231
0
      }
6232
15.6k
      const auto* next = (*it)->edit_list.front();
6233
15.6k
      if (next->IsColumnFamilyManipulation() ||
6234
15.6k
          next->IsNoManifestWriteDummy()) {
6235
        // no group commits for column family add or drop
6236
        // nor for dummy skipping manifest write
6237
0
        break;
6238
0
      }
6239
15.6k
    }
6240
51.9k
    if (!batch_apply_status.ok()) {
6241
0
      for (auto v : versions) {
6242
0
        delete v;
6243
0
      }
6244
0
      finish_batch(batch_apply_status);
6245
0
      return batch_apply_status;
6246
0
    }
6247
119k
    for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
6248
67.6k
      assert(!builder_guards.empty() &&
6249
67.6k
             builder_guards.size() == versions.size());
6250
67.6k
      auto* builder = builder_guards[i]->version_builder();
6251
67.6k
      Status s = builder->SaveTo(versions[i]->storage_info());
6252
67.6k
      if (!s.ok()) {
6253
        // free up the allocated memory
6254
0
        for (auto v : versions) {
6255
0
          delete v;
6256
0
        }
6257
0
        finish_batch(s);
6258
0
        return s;
6259
0
      }
6260
67.6k
    }
6261
51.9k
  }
6262
6263
#ifndef NDEBUG
6264
  // Verify that version edits of atomic groups have correct
6265
  // remaining_entries_.
6266
  size_t k = 0;
6267
  while (k < batch_edits.size()) {
6268
    while (k < batch_edits.size() && !batch_edits[k]->IsInAtomicGroup()) {
6269
      ++k;
6270
    }
6271
    if (k == batch_edits.size()) {
6272
      break;
6273
    }
6274
    size_t i = k;
6275
    while (i < batch_edits.size()) {
6276
      if (!batch_edits[i]->IsInAtomicGroup()) {
6277
        break;
6278
      }
6279
      assert(i - k + batch_edits[i]->GetRemainingEntries() ==
6280
             batch_edits[k]->GetRemainingEntries());
6281
      if (batch_edits[i]->GetRemainingEntries() == 0) {
6282
        ++i;
6283
        break;
6284
      }
6285
      ++i;
6286
    }
6287
    assert(batch_edits[i - 1]->IsInAtomicGroup());
6288
    assert(0 == batch_edits[i - 1]->GetRemainingEntries());
6289
    std::vector<VersionEdit*> tmp;
6290
    for (size_t j = k; j != i; ++j) {
6291
      tmp.emplace_back(batch_edits[j]);
6292
    }
6293
    TEST_SYNC_POINT_CALLBACK(
6294
        "VersionSet::ProcessManifestWrites:CheckOneAtomicGroup", &tmp);
6295
    k = i;
6296
  }
6297
  if (skip_manifest_write) {
6298
    // no grouping when skipping manifest write
6299
    assert(last_writer == &first_writer);
6300
  }
6301
#endif  // NDEBUG
6302
6303
83.3k
  uint64_t prev_manifest_file_size = manifest_file_size_;
6304
83.3k
  assert(pending_manifest_file_number_ == 0);
6305
83.3k
  bool has_foreground_operation = false;
6306
210k
  for (const VersionEdit* e : batch_edits) {
6307
210k
    if (e->IsForegroundOperation()) {
6308
31.3k
      has_foreground_operation = true;
6309
31.3k
      break;
6310
31.3k
    }
6311
210k
  }
6312
  // For MANIFEST write batches with any foreground operation (external file
6313
  // ingestion/import, DeleteFilesInRange(s), and column family manipulations
6314
  // like CreateColumnFamily and DropColumnFamily), relax the size limit by 25%
6315
  // to reduce the likelihood of a user operation blocking on MANIFEST rotation.
6316
  // Background-only batches (flush/compaction) still rotate at the normal
6317
  // threshold.
6318
  // TODO/future: for workloads like atomic-replace ingestion-only, with zero
6319
  // or few flushes and compactions, it might be nice to trigger background
6320
  // manifest rotation if we are beyond the soft limit. But the vast majority
6321
  // of workloads should have plenty of background manifest ops to avoid
6322
  // foreground rotation.
6323
83.3k
  uint64_t enforced_limit = tuned_max_manifest_file_size_;
6324
83.3k
  if (has_foreground_operation) {
6325
31.3k
    uint64_t new_limit = enforced_limit + enforced_limit / 4;
6326
    // don't keep in case of overflow
6327
31.3k
    enforced_limit = std::max(enforced_limit, new_limit);
6328
31.3k
  }
6329
83.3k
  if (!skip_manifest_write &&
6330
83.3k
      (!descriptor_log_ || prev_manifest_file_size >= enforced_limit)) {
6331
44.8k
    TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:BeforeNewManifest");
6332
44.8k
    new_descriptor_log = true;
6333
44.8k
  } else {
6334
38.4k
    pending_manifest_file_number_ = manifest_file_number_;
6335
38.4k
  }
6336
6337
  // Local cached copy of state variable(s). WriteCurrentStateToManifest()
6338
  // reads its content after releasing db mutex to avoid race with
6339
  // SwitchMemtable().
6340
83.3k
  std::unordered_map<uint32_t, MutableCFState> curr_state;
6341
83.3k
  VersionEdit wal_additions;
6342
83.3k
  OptionsFileManifestState options_manifest_state;
6343
83.3k
  if (new_descriptor_log) {
6344
44.8k
    pending_manifest_file_number_ = NewFileNumber();
6345
44.8k
    batch_edits.back()->SetNextFile(next_file_number_.load());
6346
6347
    // if we are writing out new snapshot make sure to persist max column
6348
    // family.
6349
44.8k
    if (column_family_set_->GetMaxColumnFamily() > 0) {
6350
22.1k
      first_writer.edit_list.front()->SetMaxColumnFamily(
6351
22.1k
          column_family_set_->GetMaxColumnFamily());
6352
22.1k
    }
6353
60.5k
    for (const auto* cfd : *column_family_set_) {
6354
60.5k
      assert(curr_state.find(cfd->GetID()) == curr_state.end());
6355
60.5k
      curr_state.emplace(
6356
60.5k
          cfd->GetID(),
6357
60.5k
          MutableCFState(cfd->GetLogNumber(), cfd->GetFullHistoryTsLow()));
6358
60.5k
    }
6359
6360
44.8k
    for (const auto& wal : wals_.GetWals()) {
6361
0
      wal_additions.AddWal(wal.first, wal.second);
6362
0
    }
6363
44.8k
    options_manifest_state = options_file_manifest_state();
6364
44.8k
  }
6365
6366
83.3k
  uint64_t new_manifest_file_size = 0;
6367
83.3k
  Status s;
6368
83.3k
  IOStatus io_s;
6369
83.3k
  IOStatus manifest_io_status;
6370
83.3k
  manifest_io_status.PermitUncheckedError();
6371
83.3k
  std::unique_ptr<log::Writer> new_desc_log_ptr;
6372
  // Save before releasing mu
6373
83.3k
  uint64_t manifest_preallocation_size = manifest_preallocation_size_;
6374
83.3k
  if (skip_manifest_write) {
6375
0
    if (s.ok()) {
6376
0
      constexpr bool update_stats = true;
6377
0
      for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
6378
        // NOTE: normally called with DB mutex released, but we don't
6379
        // want to release the DB mutex in this mode of LogAndApply
6380
0
        versions[i]->PrepareAppend(read_options, update_stats);
6381
0
      }
6382
0
    }
6383
83.3k
  } else {
6384
83.3k
    FileOptions opt_file_opts = GetFileOptionsForManifestWrite();
6385
83.3k
    mu->Unlock();
6386
83.3k
    TEST_SYNC_POINT("VersionSet::LogAndApply:WriteManifestStart");
6387
83.3k
    TEST_SYNC_POINT_CALLBACK("VersionSet::LogAndApply:WriteManifest", nullptr);
6388
83.3k
    if (!first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
6389
119k
      for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
6390
67.6k
        assert(!builder_guards.empty() &&
6391
67.6k
               builder_guards.size() == versions.size());
6392
67.6k
        ColumnFamilyData* cfd = versions[i]->cfd_;
6393
67.6k
        s = builder_guards[i]->version_builder()->LoadTableHandlers(
6394
67.6k
            cfd->internal_stats(), batch_max_file_opening_threads,
6395
67.6k
            true /* prefetch_index_and_filter_in_cache */,
6396
67.6k
            false /* is_initial_load */, versions[i]->GetMutableCFOptions(),
6397
67.6k
            MaxFileSizeForL0MetaPin(versions[i]->GetMutableCFOptions()),
6398
67.6k
            read_options);
6399
67.6k
        if (!s.ok()) {
6400
0
          if (db_options_->paranoid_checks) {
6401
0
            break;
6402
0
          }
6403
0
          s = Status::OK();
6404
0
        }
6405
67.6k
      }
6406
51.9k
    }
6407
6408
83.3k
    log::Writer* raw_desc_log_ptr = descriptor_log_.get();
6409
83.3k
    if (s.ok() && new_descriptor_log) {
6410
      // This is fine because everything inside of this block is serialized --
6411
      // only one thread can be here at the same time
6412
      // create new manifest file
6413
44.8k
      std::string descriptor_fname =
6414
44.8k
          DescriptorFileName(dbname_, pending_manifest_file_number_);
6415
44.8k
      std::unique_ptr<FSWritableFile> descriptor_file;
6416
44.8k
      io_s = NewWritableFile(fs_.get(), descriptor_fname, &descriptor_file,
6417
44.8k
                             opt_file_opts);
6418
44.8k
      if (io_s.ok()) {
6419
44.8k
        new_desc_log_ptr =
6420
44.8k
            CreateManifestWriter(std::move(descriptor_file), descriptor_fname,
6421
44.8k
                                 opt_file_opts, manifest_preallocation_size);
6422
44.8k
        raw_desc_log_ptr = new_desc_log_ptr.get();
6423
44.8k
        s = WriteCurrentStateToManifest(write_options, curr_state,
6424
44.8k
                                        wal_additions, options_manifest_state,
6425
44.8k
                                        raw_desc_log_ptr, io_s);
6426
44.8k
        assert(s == io_s);
6427
44.8k
      }
6428
44.8k
      if (!io_s.ok()) {
6429
0
        manifest_io_status = io_s;
6430
0
        s = io_s;
6431
0
      }
6432
44.8k
    }
6433
6434
83.3k
    if (s.ok()) {
6435
83.3k
      if (!first_writer.edit_list.front()->IsColumnFamilyManipulation()) {
6436
51.9k
        constexpr bool update_stats = true;
6437
6438
119k
        for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
6439
67.6k
          versions[i]->PrepareAppend(read_options, update_stats);
6440
67.6k
        }
6441
51.9k
      }
6442
6443
      // Write new records to MANIFEST log
6444
#ifndef NDEBUG
6445
      size_t idx = 0;
6446
#endif
6447
83.3k
      assert(batch_edits.size() == batch_edits_ts_sz.size());
6448
294k
      for (size_t bidx = 0; bidx < batch_edits.size(); bidx++) {
6449
210k
        auto& e = batch_edits[bidx];
6450
210k
        files_to_quarantine_if_commit_fail.push_back(
6451
210k
            e->GetFilesToQuarantineIfCommitFail());
6452
210k
        std::string record;
6453
210k
        if (!e->EncodeTo(&record, batch_edits_ts_sz[bidx])) {
6454
0
          s = Status::Corruption("Unable to encode VersionEdit:" +
6455
0
                                 e->DebugString(true));
6456
0
          break;
6457
0
        }
6458
210k
        TEST_KILL_RANDOM_WITH_WEIGHT("VersionSet::LogAndApply:BeforeAddRecord",
6459
210k
                                     REDUCE_ODDS2);
6460
#ifndef NDEBUG
6461
        if (batch_edits.size() > 1 && batch_edits.size() - 1 == idx) {
6462
          TEST_SYNC_POINT_CALLBACK(
6463
              "VersionSet::ProcessManifestWrites:BeforeWriteLastVersionEdit:"
6464
              "0",
6465
              nullptr);
6466
          TEST_SYNC_POINT(
6467
              "VersionSet::ProcessManifestWrites:BeforeWriteLastVersionEdit:"
6468
              "1");
6469
        }
6470
        ++idx;
6471
#endif /* !NDEBUG */
6472
210k
        TEST_SYNC_POINT("VersionSet::ProcessManifestWrites:AddRecord");
6473
210k
        io_s = raw_desc_log_ptr->AddRecord(write_options, record);
6474
210k
        if (!io_s.ok()) {
6475
0
          s = io_s;
6476
0
          manifest_io_status = io_s;
6477
0
          break;
6478
0
        }
6479
210k
      }
6480
6481
83.3k
      if (s.ok()) {
6482
83.3k
        io_s =
6483
83.3k
            SyncManifest(db_options_, write_options, raw_desc_log_ptr->file());
6484
83.3k
        manifest_io_status = io_s;
6485
83.3k
        TEST_SYNC_POINT_CALLBACK(
6486
83.3k
            "VersionSet::ProcessManifestWrites:AfterSyncManifest", &io_s);
6487
83.3k
      }
6488
83.3k
      if (!io_s.ok()) {
6489
0
        s = io_s;
6490
0
        ROCKS_LOG_ERROR(db_options_->info_log, "MANIFEST write %s\n",
6491
0
                        s.ToString().c_str());
6492
0
      }
6493
83.3k
    }
6494
6495
    // If we just created a new descriptor file, install it by writing a
6496
    // new CURRENT file that points to it.
6497
83.3k
    if (s.ok()) {
6498
83.3k
      assert(manifest_io_status.ok());
6499
83.3k
    }
6500
83.3k
    if (s.ok() && new_descriptor_log) {
6501
44.8k
      io_s = SetCurrentFile(
6502
44.8k
          write_options, fs_.get(), dbname_, pending_manifest_file_number_,
6503
44.8k
          file_options_.temperature, dir_contains_current_file);
6504
44.8k
      TEST_SYNC_POINT_CALLBACK(
6505
44.8k
          "VersionSet::ProcessManifestWrites:AfterSetCurrentFile", &io_s);
6506
44.8k
      if (!io_s.ok()) {
6507
0
        s = io_s;
6508
        // Quarantine old manifest file in case new manifest file's CURRENT
6509
        // file wasn't created successfully and the old manifest is needed.
6510
0
        limbo_descriptor_log_file_number.push_back(manifest_file_number_);
6511
0
        files_to_quarantine_if_commit_fail.push_back(
6512
0
            &limbo_descriptor_log_file_number);
6513
0
      }
6514
44.8k
    }
6515
6516
83.3k
    if (s.ok()) {
6517
      // find offset in manifest file where this version is stored.
6518
83.3k
      new_manifest_file_size = raw_desc_log_ptr->file()->GetFileSize();
6519
83.3k
      if (new_descriptor_log) {
6520
44.8k
        ROCKS_LOG_INFO(db_options_->info_log,
6521
44.8k
                       "Created manifest %" PRIu64
6522
44.8k
                       ", compacted+appended from %" PRIu64 " to %" PRIu64
6523
44.8k
                       ", db_session_id=%s\n",
6524
44.8k
                       pending_manifest_file_number_, prev_manifest_file_size,
6525
44.8k
                       new_manifest_file_size, db_session_id_.c_str());
6526
44.8k
      }
6527
83.3k
    }
6528
6529
83.3k
    if (first_writer.edit_list.front()->IsColumnFamilyDrop()) {
6530
15.6k
      TEST_SYNC_POINT("VersionSet::LogAndApply::ColumnFamilyDrop:0");
6531
15.6k
      TEST_SYNC_POINT("VersionSet::LogAndApply::ColumnFamilyDrop:1");
6532
15.6k
      TEST_SYNC_POINT("VersionSet::LogAndApply::ColumnFamilyDrop:2");
6533
15.6k
    }
6534
6535
83.3k
    LogFlush(db_options_->info_log);
6536
83.3k
    TEST_SYNC_POINT("VersionSet::LogAndApply:WriteManifestDone");
6537
83.3k
    mu->Lock();
6538
83.3k
  }
6539
6540
83.3k
  if (s.ok()) {
6541
    // Apply WAL edits, DB mutex must be held.
6542
210k
    for (auto& e : batch_edits) {
6543
210k
      if (e->IsWalAddition()) {
6544
0
        s = wals_.AddWals(e->GetWalAdditions());
6545
210k
      } else if (e->IsWalDeletion()) {
6546
0
        s = wals_.DeleteWalsBefore(e->GetWalDeletion().GetLogNumber());
6547
0
      }
6548
210k
      if (!s.ok()) {
6549
0
        break;
6550
0
      }
6551
210k
    }
6552
83.3k
  }
6553
6554
83.3k
  if (!io_s.ok()) {
6555
0
    if (io_status_.ok()) {
6556
0
      io_status_ = io_s;
6557
0
      if (error_handler_) {
6558
0
        error_handler_->AddFilesToQuarantine(
6559
0
            files_to_quarantine_if_commit_fail);
6560
0
      }
6561
0
    }
6562
83.3k
  } else if (!io_status_.ok()) {
6563
0
    io_status_ = io_s;
6564
0
    if (error_handler_) {
6565
0
      error_handler_->ClearFilesToQuarantine();
6566
0
    }
6567
0
  }
6568
6569
  // Append the old manifest file to the obsolete_manifest_ list to be deleted
6570
  // by PurgeObsoleteFiles later.
6571
83.3k
  if (s.ok() && new_descriptor_log) {
6572
44.8k
    descriptor_log_ = std::move(new_desc_log_ptr);
6573
44.8k
    obsolete_manifests_.emplace_back(
6574
44.8k
        DescriptorFileName("", manifest_file_number_));
6575
44.8k
    last_compacted_manifest_file_size_ = new_manifest_file_size;
6576
44.8k
    TuneMaxManifestFileSize();
6577
44.8k
  }
6578
6579
  // Install the new versions
6580
83.3k
  if (s.ok()) {
6581
83.3k
    if (first_writer.edit_list.front()->IsColumnFamilyAdd()) {
6582
15.6k
      assert(batch_edits.size() == 1);
6583
15.6k
      assert(new_cf_options != nullptr);
6584
15.6k
      assert(max_last_sequence == descriptor_last_sequence_);
6585
15.6k
      CreateColumnFamily(*new_cf_options, read_options,
6586
15.6k
                         first_writer.edit_list.front(),
6587
15.6k
                         /*read_only=*/false, new_cf_options_are_sanitized);
6588
67.6k
    } else if (first_writer.edit_list.front()->IsColumnFamilyDrop()) {
6589
15.6k
      assert(batch_edits.size() == 1);
6590
15.6k
      assert(max_last_sequence == descriptor_last_sequence_);
6591
15.6k
      first_writer.cfd->SetDropped();
6592
15.6k
      first_writer.cfd->UnrefAndTryDelete();
6593
51.9k
    } else {
6594
      // Each version in versions corresponds to a column family.
6595
      // For each column family, update its log number indicating that logs
6596
      // with number smaller than this should be ignored.
6597
51.9k
      uint64_t last_min_log_number_to_keep = 0;
6598
179k
      for (const auto& e : batch_edits) {
6599
179k
        ColumnFamilyData* cfd = nullptr;
6600
179k
        if (!e->IsColumnFamilyManipulation()) {
6601
179k
          cfd = column_family_set_->GetColumnFamily(e->GetColumnFamily());
6602
          // e would not have been added to batch_edits if its corresponding
6603
          // column family is dropped.
6604
179k
          assert(cfd);
6605
179k
        }
6606
179k
        if (cfd) {
6607
179k
          if (e->HasLogNumber() && e->GetLogNumber() > cfd->GetLogNumber()) {
6608
54.2k
            cfd->SetLogNumber(e->GetLogNumber());
6609
54.2k
          }
6610
179k
          if (e->HasFullHistoryTsLow()) {
6611
0
            cfd->SetFullHistoryTsLow(e->GetFullHistoryTsLow());
6612
0
          }
6613
179k
        }
6614
179k
        if (e->HasMinLogNumberToKeep()) {
6615
38.5k
          last_min_log_number_to_keep =
6616
38.5k
              std::max(last_min_log_number_to_keep, e->GetMinLogNumberToKeep());
6617
38.5k
        }
6618
179k
      }
6619
6620
51.9k
      if (last_min_log_number_to_keep != 0) {
6621
38.5k
        MarkMinLogNumberToKeep(last_min_log_number_to_keep);
6622
38.5k
      }
6623
6624
119k
      for (int i = 0; i < static_cast<int>(versions.size()); ++i) {
6625
67.6k
        ColumnFamilyData* cfd = versions[i]->cfd_;
6626
67.6k
        AppendVersion(cfd, versions[i]);
6627
67.6k
      }
6628
51.9k
    }
6629
83.3k
    if (!skip_manifest_write) {
6630
210k
      for (const auto* edit : batch_edits) {
6631
210k
        assert(edit != nullptr);
6632
210k
        if (edit->HasCommittedOptionsFileNumber()) {
6633
0
          ApplyCommittedOptionsFileNumber(
6634
0
              edit->GetCommittedOptionsFileNumber());
6635
0
        }
6636
210k
      }
6637
83.3k
      assert(max_last_sequence >= descriptor_last_sequence_);
6638
83.3k
      descriptor_last_sequence_ = max_last_sequence;
6639
83.3k
      manifest_file_number_ = pending_manifest_file_number_;
6640
83.3k
      manifest_file_size_ = new_manifest_file_size;
6641
83.3k
      manifest_last_valid_record_end_ = new_manifest_file_size;
6642
83.3k
      manifest_last_valid_record_end_file_number_ = manifest_file_number_;
6643
83.3k
      prev_log_number_ = first_writer.edit_list.front()->GetPrevLogNumber();
6644
83.3k
    }
6645
83.3k
  } else {
6646
0
    std::string version_edits;
6647
0
    for (auto& e : batch_edits) {
6648
0
      version_edits += ("\n" + e->DebugString(true));
6649
0
    }
6650
0
    ROCKS_LOG_ERROR(db_options_->info_log,
6651
0
                    "Error in committing version edit to MANIFEST: %s",
6652
0
                    version_edits.c_str());
6653
0
    for (auto v : versions) {
6654
0
      delete v;
6655
0
    }
6656
    // If manifest append failed for whatever reason, the file could be
6657
    // corrupted. So we need to force the next version update to start a
6658
    // new manifest file.
6659
0
    descriptor_log_.reset();
6660
0
    new_desc_log_ptr.reset();
6661
    // If manifest operations failed, then we know the CURRENT file still
6662
    // points to the original MANIFEST. Therefore, we can safely delete the
6663
    // new MANIFEST.
6664
    // If manifest operations succeeded, and we are here, then it is possible
6665
    // that renaming tmp file to CURRENT failed.
6666
    //
6667
    // On local POSIX-compliant FS, the CURRENT must point to the original
6668
    // MANIFEST. We can delete the new MANIFEST for simplicity, but we can
6669
    // also keep it. Future recovery will ignore this MANIFEST. It's also ok
6670
    // for the process not to crash and continue using the db. Any future
6671
    // LogAndApply() call will switch to a new MANIFEST and update CURRENT,
6672
    // still ignoring this one.
6673
    //
6674
    // On non-local FS, it is
6675
    // possible that the rename operation succeeded on the server (remote)
6676
    // side, but the client somehow returns a non-ok status to RocksDB. Note
6677
    // that this does not violate atomicity. Should we delete the new MANIFEST
6678
    // successfully, a subsequent recovery attempt will likely see the CURRENT
6679
    // pointing to the new MANIFEST, thus fail. We will not be able to open
6680
    // the DB again. Therefore, if manifest operations succeed, we should keep
6681
    // the the new MANIFEST. If the process proceeds, any future LogAndApply()
6682
    // call will switch to a new MANIFEST and update CURRENT. If user tries to
6683
    // re-open the DB,
6684
    // a) CURRENT points to the new MANIFEST, and the new MANIFEST is present.
6685
    // b) CURRENT points to the original MANIFEST, and the original MANIFEST
6686
    //    also exists.
6687
0
    if (!manifest_io_status.ok() && new_descriptor_log) {
6688
0
      ROCKS_LOG_INFO(db_options_->info_log,
6689
0
                     "Deleting manifest %" PRIu64 " current manifest %" PRIu64
6690
0
                     "\n",
6691
0
                     pending_manifest_file_number_, manifest_file_number_);
6692
0
      Status manifest_del_status = env_->DeleteFile(
6693
0
          DescriptorFileName(dbname_, pending_manifest_file_number_));
6694
0
      if (!manifest_del_status.ok()) {
6695
0
        ROCKS_LOG_WARN(db_options_->info_log,
6696
0
                       "Failed to delete manifest %" PRIu64 ": %s",
6697
0
                       pending_manifest_file_number_,
6698
0
                       manifest_del_status.ToString().c_str());
6699
0
      }
6700
0
    }
6701
0
  }
6702
6703
83.3k
  pending_manifest_file_number_ = 0;
6704
6705
#ifndef NDEBUG
6706
  // This is here kind of awkwardly because there's no other consistency
6707
  // checks on `VersionSet`'s updates for the new `Version`s. We might want
6708
  // to move it to a dedicated function, or remove it if we gain enough
6709
  // confidence in `descriptor_last_sequence_`.
6710
  if (s.ok()) {
6711
    for (const auto* v : versions) {
6712
      const auto* vstorage = v->storage_info();
6713
      for (int level = 0; level < vstorage->num_levels(); ++level) {
6714
        for (const auto& file : vstorage->LevelFiles(level)) {
6715
          assert(file->fd.largest_seqno <= descriptor_last_sequence_);
6716
        }
6717
      }
6718
    }
6719
  }
6720
#endif  // NDEBUG
6721
6722
83.3k
  finish_batch(s);
6723
83.3k
  return s;
6724
83.3k
}
6725
6726
0
void VersionSet::WakeUpWaitingManifestWriters() {
6727
  // wake up all the waiting writers
6728
  // Notify new head of manifest write queue.
6729
0
  if (!manifest_writers_.empty()) {
6730
0
    manifest_writers_.front()->cv.Signal();
6731
0
  }
6732
0
}
6733
6734
// 'datas' is grammatically incorrect. We still use this notation to indicate
6735
// that this variable represents a collection of column_family_data.
6736
Status VersionSet::LogAndApply(
6737
    const autovector<ColumnFamilyData*>& column_family_datas,
6738
    const ReadOptions& read_options, const WriteOptions& write_options,
6739
    const autovector<autovector<VersionEdit*>>& edit_lists,
6740
    InstrumentedMutex* mu, FSDirectory* dir_contains_current_file,
6741
    bool new_descriptor_log, const ColumnFamilyOptions* new_cf_options,
6742
    const std::vector<std::function<void(const Status&)>>& manifest_wcbs,
6743
67.6k
    const std::function<Status()>& pre_cb, int max_file_opening_threads) {
6744
67.6k
  return LogAndApplyImpl(column_family_datas, read_options, write_options,
6745
67.6k
                         edit_lists, mu, dir_contains_current_file,
6746
67.6k
                         new_descriptor_log, new_cf_options,
6747
67.6k
                         /*new_cf_options_are_sanitized=*/false, manifest_wcbs,
6748
67.6k
                         pre_cb, max_file_opening_threads);
6749
67.6k
}
6750
6751
Status VersionSet::LogAndApplyColumnFamilyAddWithSanitizedOptions(
6752
    const ReadOptions& read_options, const WriteOptions& write_options,
6753
    VersionEdit* edit, InstrumentedMutex* mu,
6754
    FSDirectory* dir_contains_current_file,
6755
15.6k
    const ColumnFamilyOptions& sanitized_options) {
6756
15.6k
  assert(edit != nullptr);
6757
15.6k
  assert(edit->IsColumnFamilyAdd());
6758
15.6k
  autovector<ColumnFamilyData*> cfds{nullptr};
6759
15.6k
  autovector<VersionEdit*> edit_list{edit};
6760
15.6k
  autovector<autovector<VersionEdit*>> edit_lists{edit_list};
6761
15.6k
  return LogAndApplyImpl(cfds, read_options, write_options, edit_lists, mu,
6762
15.6k
                         dir_contains_current_file,
6763
15.6k
                         /*new_descriptor_log=*/false, &sanitized_options,
6764
15.6k
                         /*new_cf_options_are_sanitized=*/true,
6765
15.6k
                         /*manifest_wcbs=*/{}, /*pre_cb=*/{},
6766
15.6k
                         /*max_file_opening_threads=*/1);
6767
15.6k
}
6768
6769
Status VersionSet::LogAndApplyImpl(
6770
    const autovector<ColumnFamilyData*>& column_family_datas,
6771
    const ReadOptions& read_options, const WriteOptions& write_options,
6772
    const autovector<autovector<VersionEdit*>>& edit_lists,
6773
    InstrumentedMutex* mu, FSDirectory* dir_contains_current_file,
6774
    bool new_descriptor_log, const ColumnFamilyOptions* new_cf_options,
6775
    bool new_cf_options_are_sanitized,
6776
    const std::vector<std::function<void(const Status&)>>& manifest_wcbs,
6777
83.3k
    const std::function<Status()>& pre_cb, int max_file_opening_threads) {
6778
83.3k
  mu->AssertHeld();
6779
83.3k
  int num_edits = 0;
6780
98.9k
  for (const auto& elist : edit_lists) {
6781
98.9k
    num_edits += static_cast<int>(elist.size());
6782
98.9k
  }
6783
83.3k
  if (num_edits == 0) {
6784
0
    return Status::OK();
6785
83.3k
  } else if (num_edits > 1) {
6786
#ifndef NDEBUG
6787
    for (const auto& edit_list : edit_lists) {
6788
      for (const auto& edit : edit_list) {
6789
        assert(!edit->IsColumnFamilyManipulation());
6790
        assert(!edit->IsNoManifestWriteDummy());
6791
      }
6792
    }
6793
#endif /* ! NDEBUG */
6794
38.5k
  }
6795
6796
83.3k
  int num_cfds = static_cast<int>(column_family_datas.size());
6797
83.3k
  if (num_cfds == 1 && column_family_datas[0] == nullptr) {
6798
15.6k
    assert(edit_lists.size() == 1 && edit_lists[0].size() == 1);
6799
15.6k
    assert(edit_lists[0][0]->IsColumnFamilyAdd());
6800
15.6k
    assert(new_cf_options != nullptr);
6801
15.6k
  }
6802
83.3k
  std::deque<ManifestWriter> writers;
6803
83.3k
  if (num_cfds > 0) {
6804
83.3k
    assert(static_cast<size_t>(num_cfds) == edit_lists.size());
6805
83.3k
  }
6806
182k
  for (int i = 0; i < num_cfds; ++i) {
6807
98.9k
    const auto wcb =
6808
98.9k
        manifest_wcbs.empty() ? [](const Status&) {} : manifest_wcbs[i];
6809
98.9k
    writers.emplace_back(mu, column_family_datas[i], edit_lists[i], wcb,
6810
98.9k
                         max_file_opening_threads);
6811
98.9k
    manifest_writers_.push_back(&writers[i]);
6812
98.9k
  }
6813
83.3k
  assert(!writers.empty());
6814
83.3k
  ManifestWriter& first_writer = writers.front();
6815
83.3k
  TEST_SYNC_POINT_CALLBACK("VersionSet::LogAndApply:BeforeWriterWaiting",
6816
83.3k
                           nullptr);
6817
84.1k
  while (!first_writer.done && &first_writer != manifest_writers_.front()) {
6818
898
    first_writer.cv.Wait();
6819
898
  }
6820
83.3k
  if (first_writer.done) {
6821
    // All non-CF-manipulation operations can be grouped together and
6822
    // committed to MANIFEST. They should all have finished. The status code
6823
    // is stored in the first manifest writer.
6824
#ifndef NDEBUG
6825
    for (const auto& writer : writers) {
6826
      assert(writer.done);
6827
    }
6828
    TEST_SYNC_POINT_CALLBACK("VersionSet::LogAndApply:WakeUpAndDone", mu);
6829
#endif /* !NDEBUG */
6830
    // FIXME: One MANIFEST write failure can cause all writes to SetBGError,
6831
    // should only SetBGError once.
6832
0
    return first_writer.status;
6833
0
  }
6834
83.3k
  TEST_SYNC_POINT_CALLBACK("VersionSet::LogAndApply:WakeUpAndNotDone", mu);
6835
6836
83.3k
  int num_undropped_cfds = 0;
6837
98.9k
  for (auto cfd : column_family_datas) {
6838
    // if cfd == nullptr, it is a column family add.
6839
98.9k
    if (cfd == nullptr || !cfd->IsDropped()) {
6840
98.9k
      ++num_undropped_cfds;
6841
98.9k
    }
6842
98.9k
  }
6843
83.3k
  Status s;
6844
83.3k
  if (0 == num_undropped_cfds) {
6845
0
    s = Status::ColumnFamilyDropped();
6846
0
  }
6847
  // Call pre_cb once we know we have work to do and are scheduled as the
6848
  // exclusive manifest writer (and new Version appender)
6849
83.3k
  if (s.ok() && pre_cb) {
6850
0
    s = pre_cb();
6851
0
  }
6852
83.3k
  if (!s.ok()) {
6853
    // Revert manifest_writers_
6854
0
    for (int i = 0; i != num_cfds; ++i) {
6855
0
      manifest_writers_.pop_front();
6856
0
    }
6857
    // Notify new head of manifest write queue.
6858
0
    if (!manifest_writers_.empty()) {
6859
0
      manifest_writers_.front()->cv.Signal();
6860
0
    }
6861
0
    return s;
6862
83.3k
  } else {
6863
83.3k
    return ProcessManifestWrites(writers, mu, dir_contains_current_file,
6864
83.3k
                                 new_descriptor_log, new_cf_options,
6865
83.3k
                                 new_cf_options_are_sanitized, read_options,
6866
83.3k
                                 write_options);
6867
83.3k
  }
6868
83.3k
}
6869
6870
void VersionSet::LogAndApplyCFHelper(VersionEdit* edit,
6871
31.3k
                                     SequenceNumber* max_last_sequence) {
6872
31.3k
  assert(max_last_sequence != nullptr);
6873
31.3k
  assert(edit->IsColumnFamilyManipulation());
6874
31.3k
  edit->SetNextFile(next_file_number_.load());
6875
31.3k
  assert(!edit->HasLastSequence());
6876
31.3k
  edit->SetLastSequence(*max_last_sequence);
6877
31.3k
  if (edit->IsColumnFamilyDrop()) {
6878
    // if we drop column family, we have to make sure to save max column
6879
    // family, so that we don't reuse existing ID
6880
15.6k
    edit->SetMaxColumnFamily(column_family_set_->GetMaxColumnFamily());
6881
15.6k
  }
6882
31.3k
}
6883
6884
Status VersionSet::LogAndApplyHelper(ColumnFamilyData* cfd,
6885
                                     VersionBuilder* builder, VersionEdit* edit,
6886
                                     SequenceNumber* max_last_sequence,
6887
179k
                                     InstrumentedMutex* mu) {
6888
179k
#ifdef NDEBUG
6889
179k
  (void)cfd;
6890
179k
#endif
6891
179k
  mu->AssertHeld();
6892
179k
  assert(!edit->IsColumnFamilyManipulation());
6893
179k
  assert(max_last_sequence != nullptr);
6894
6895
179k
  if (edit->HasLogNumber()) {
6896
54.2k
    assert(edit->GetLogNumber() >= cfd->GetLogNumber());
6897
54.2k
    assert(edit->GetLogNumber() < next_file_number_.load());
6898
54.2k
  }
6899
6900
179k
  if (!edit->HasPrevLogNumber()) {
6901
177k
    edit->SetPrevLogNumber(prev_log_number_);
6902
177k
  }
6903
179k
  edit->SetNextFile(next_file_number_.load());
6904
179k
  if (edit->HasLastSequence() && edit->GetLastSequence() > *max_last_sequence) {
6905
17.9k
    *max_last_sequence = edit->GetLastSequence();
6906
161k
  } else {
6907
161k
    edit->SetLastSequence(*max_last_sequence);
6908
161k
  }
6909
6910
  // WAL and OPTIONS protocol edits update DB-wide metadata without changing an
6911
  // LSM Version, so they do not need a VersionBuilder.
6912
179k
  assert(builder || edit->IsWalManipulation() ||
6913
179k
         edit->IsOptionsFileManipulation());
6914
179k
  return builder ? builder->Apply(edit) : Status::OK();
6915
179k
}
6916
6917
83.3k
FileOptions VersionSet::GetFileOptionsForManifestWrite() const {
6918
83.3k
  FileOptions opt_file_opts = fs_->OptimizeForManifestWrite(file_options_);
6919
  // DB-configured temperature wins over the FS's default (when not kUnknown).
6920
83.3k
  if (file_options_.temperature != Temperature::kUnknown) {
6921
0
    opt_file_opts.temperature = file_options_.temperature;
6922
0
  }
6923
83.3k
  return opt_file_opts;
6924
83.3k
}
6925
6926
std::unique_ptr<log::Writer> VersionSet::CreateManifestWriter(
6927
    std::unique_ptr<FSWritableFile> file, const std::string& fname,
6928
    const FileOptions& opts, uint64_t preallocation_size,
6929
44.8k
    uint64_t initial_file_size) const {
6930
44.8k
  file->SetPreallocationBlockSize(preallocation_size);
6931
44.8k
  FileTypeSet tmp_set = db_options_->checksum_handoff_file_types;
6932
44.8k
  auto file_writer = std::make_unique<WritableFileWriter>(
6933
44.8k
      std::move(file), fname, opts, clock_, io_tracer_,
6934
44.8k
      /*stats=*/nullptr, Histograms::HISTOGRAM_ENUM_MAX, db_options_->listeners,
6935
44.8k
      /*file_checksum_gen_factory=*/nullptr,
6936
44.8k
      tmp_set.Contains(FileType::kDescriptorFile),
6937
44.8k
      tmp_set.Contains(FileType::kDescriptorFile), initial_file_size);
6938
44.8k
  const size_t block_offset = initial_file_size % log::kBlockSize;
6939
44.8k
  return std::make_unique<log::Writer>(
6940
44.8k
      std::move(file_writer), /*log_number=*/0, /*recycle_log_files=*/false,
6941
44.8k
      /*manual_flush=*/false, kNoCompression,
6942
44.8k
      /*track_and_verify_wals=*/false, block_offset);
6943
44.8k
}
6944
6945
0
Status VersionSet::GetManifestAppendBoundary(uint64_t* manifest_size) const {
6946
0
  assert(manifest_size != nullptr);
6947
0
  if (manifest_last_valid_record_end_file_number_ != manifest_file_number_) {
6948
0
    return Status::TryAgain(
6949
0
        "Current MANIFEST changed while determining its append boundary");
6950
0
  }
6951
0
  if (manifest_last_valid_record_end_ == 0 ||
6952
0
      manifest_last_valid_record_end_ > manifest_file_size_) {
6953
0
    return Status::Corruption("Invalid MANIFEST append boundary");
6954
0
  }
6955
0
  *manifest_size = manifest_last_valid_record_end_;
6956
0
  return Status::OK();
6957
0
}
6958
6959
0
Status VersionSet::ReopenManifestForAppend(const std::string& manifest_path) {
6960
0
  assert(db_options_->reuse_manifest_on_open);
6961
0
  assert(manifest_recovery_last_valid_record_end_ > 0);
6962
0
  assert(manifest_last_valid_record_end_file_number_ == manifest_file_number_);
6963
6964
  // Disabled under best_efforts_recovery: that mode rebuilds the
6965
  // MANIFEST + CURRENT from scratch via the side-effect of
6966
  // LogAndApplyForRecovery emitting an edit. Reusing the (possibly
6967
  // stale) prior MANIFEST contradicts the salvage contract.
6968
0
  if (db_options_->UsesBestEffortsRecoveryPath()) {
6969
0
    return Status::OK();
6970
0
  }
6971
6972
0
  auto force_new_manifest_on_open = [&]() {
6973
0
    force_new_manifest_on_open_ = true;
6974
0
  };
6975
6976
0
  FileOptions opt_file_opts = GetFileOptionsForManifestWrite();
6977
6978
  // Bail if the on-disk size diverges from what Recover's Reader
6979
  // consumed -- likely a torn tail from a prior crash; mid-block append
6980
  // would mis-frame the record stream.
6981
0
  uint64_t physical_size = 0;
6982
0
  IOStatus stat_s = fs_->GetFileSize(manifest_path, IOOptions(), &physical_size,
6983
0
                                     /*dbg=*/nullptr);
6984
0
  if (!stat_s.ok() ||
6985
0
      physical_size != manifest_recovery_last_valid_record_end_) {
6986
0
    ROCKS_LOG_WARN(db_options_->info_log,
6987
0
                   "reuse_manifest_on_open: physical size %" PRIu64
6988
0
                   " != last valid record end %" PRIu64
6989
0
                   " (tail corruption?); creating fresh MANIFEST during open",
6990
0
                   physical_size, manifest_recovery_last_valid_record_end_);
6991
0
    force_new_manifest_on_open();
6992
0
    return Status::OK();
6993
0
  }
6994
6995
  // Direct-writes path is hard to reason about for an already-populated
6996
  // file (alignment + tail-pad). POSIX's OptimizeForManifestWrite forces
6997
  // it off, but custom FileSystems may not.
6998
0
  if (opt_file_opts.use_direct_writes) {
6999
0
    ROCKS_LOG_WARN(db_options_->info_log,
7000
0
                   "reuse_manifest_on_open: direct writes enabled; "
7001
0
                   "creating fresh MANIFEST during open");
7002
0
    force_new_manifest_on_open();
7003
0
    return Status::OK();
7004
0
  }
7005
7006
0
  std::unique_ptr<FSWritableFile> manifest_file;
7007
0
  IOStatus io_s = fs_->ReopenWritableFile(manifest_path, opt_file_opts,
7008
0
                                          &manifest_file, /*dbg=*/nullptr);
7009
0
  if (!io_s.ok()) {
7010
0
    ROCKS_LOG_WARN(db_options_->info_log,
7011
0
                   "Failed to reopen MANIFEST for append: %s",
7012
0
                   io_s.ToString().c_str());
7013
0
    force_new_manifest_on_open();
7014
0
    return Status::OK();
7015
0
  }
7016
7017
0
  const uint64_t reopened_size =
7018
0
      manifest_file->GetFileSize(opt_file_opts.io_options, /*dbg=*/nullptr);
7019
0
  if (reopened_size != manifest_recovery_last_valid_record_end_) {
7020
0
    ROCKS_LOG_WARN(db_options_->info_log,
7021
0
                   "reuse_manifest_on_open: reopened handle size %" PRIu64
7022
0
                   " != last valid record end %" PRIu64
7023
0
                   "; creating fresh MANIFEST during open",
7024
0
                   reopened_size, manifest_recovery_last_valid_record_end_);
7025
0
    force_new_manifest_on_open();
7026
0
    return Status::OK();
7027
0
  }
7028
7029
0
  descriptor_log_ = CreateManifestWriter(
7030
0
      std::move(manifest_file), manifest_path, opt_file_opts,
7031
0
      manifest_preallocation_size_, reopened_size);
7032
7033
0
  ROCKS_LOG_INFO(
7034
0
      db_options_->info_log,
7035
0
      "Reusing existing MANIFEST file: %s (valid data size: %" PRIu64 ")",
7036
0
      manifest_path.c_str(), manifest_recovery_last_valid_record_end_);
7037
0
  TEST_SYNC_POINT("VersionSet::ReopenManifestForAppend:Reopened");
7038
0
  return Status::OK();
7039
0
}
7040
7041
Status VersionSet::AppendColumnFamilyDropsToManifest(
7042
    const std::string& manifest_path, uint64_t manifest_size,
7043
    const std::vector<uint32_t>& cf_ids, const WriteOptions& write_options,
7044
    uint64_t manifest_preallocation_size,
7045
0
    uint64_t committed_options_file_number) {
7046
0
  if (cf_ids.empty() && committed_options_file_number == 0) {
7047
0
    return Status::OK();
7048
0
  }
7049
0
  if (committed_options_file_number == std::numeric_limits<uint64_t>::max()) {
7050
0
    return Status::InvalidArgument("OPTIONS file number is too large");
7051
0
  }
7052
0
  const uint64_t next_options_file_number =
7053
0
      committed_options_file_number == 0 ? 0
7054
0
                                         : committed_options_file_number + 1;
7055
7056
  // Rewrite the checkpoint MANIFEST via a bounded-memory buffered copy into a
7057
  // .tmp file (append drops, sync, close), then atomically rename over the
7058
  // original. Avoids depending on FileSystem::ReopenWritableFile (optional,
7059
  // unsupported on some backends) and avoids reading the whole MANIFEST into
7060
  // memory (which can reach ~1GB depending on configuration).
7061
0
  const std::string tmp_path = manifest_path + ".tmp";
7062
7063
  // Best-effort cleanup of any stale .tmp left by a prior aborted run.
7064
0
  fs_->DeleteFile(tmp_path, IOOptions(), /*dbg=*/nullptr)
7065
0
      .PermitUncheckedError();
7066
7067
0
  std::unique_ptr<FSSequentialFile> src_file;
7068
0
  {
7069
0
    IOStatus io_s = fs_->NewSequentialFile(manifest_path, FileOptions(),
7070
0
                                           &src_file, /*dbg=*/nullptr);
7071
0
    if (!io_s.ok()) {
7072
0
      return io_s;
7073
0
    }
7074
0
  }
7075
7076
0
  FileOptions opt_file_opts = GetFileOptionsForManifestWrite();
7077
  // Buffered writes only: direct writes would need alignment / tail-pad
7078
  // reasoning that isn't worth the complexity for a checkpoint MANIFEST.
7079
0
  opt_file_opts.use_direct_writes = false;
7080
7081
0
  std::unique_ptr<FSWritableFile> dst_file;
7082
0
  {
7083
0
    IOStatus io_s = fs_->NewWritableFile(tmp_path, opt_file_opts, &dst_file,
7084
0
                                         /*dbg=*/nullptr);
7085
0
    if (!io_s.ok()) {
7086
0
      return io_s;
7087
0
    }
7088
0
  }
7089
7090
  // Copy exactly manifest_size bytes through a fixed-size buffer.
7091
0
  constexpr size_t kBufSize = 1 * 1024 * 1024;
7092
0
  std::string buffer(kBufSize, '\0');
7093
0
  uint64_t remaining = manifest_size;
7094
0
  while (remaining > 0) {
7095
0
    Slice result;
7096
0
    const size_t to_read =
7097
0
        static_cast<size_t>(std::min<uint64_t>(kBufSize, remaining));
7098
0
    IOStatus read_s = src_file->Read(to_read, opt_file_opts.io_options, &result,
7099
0
                                     buffer.data(), /*dbg=*/nullptr);
7100
0
    if (!read_s.ok()) {
7101
0
      return read_s;
7102
0
    }
7103
0
    if (result.empty()) {
7104
0
      return Status::Corruption(
7105
0
          "Unexpected EOF reading source checkpoint MANIFEST");
7106
0
    }
7107
0
    IOStatus write_s = dst_file->Append(result, opt_file_opts.io_options,
7108
0
                                        /*dbg=*/nullptr);
7109
0
    if (!write_s.ok()) {
7110
0
      return write_s;
7111
0
    }
7112
0
    remaining -= result.size();
7113
0
  }
7114
0
  src_file.reset();
7115
7116
  // Seeds initial_block_offset = manifest_size % kBlockSize so drop-record
7117
  // framing continues from where the copied contents left off, matching the
7118
  // position-based log reader.
7119
0
  std::unique_ptr<log::Writer> writer =
7120
0
      CreateManifestWriter(std::move(dst_file), tmp_path, opt_file_opts,
7121
0
                           manifest_preallocation_size, manifest_size);
7122
7123
  // Always attempt to Close the writer on any exit path, folding any Close
7124
  // error into the primary status. Guards against dropping the underlying
7125
  // FSWritableFile with buffered data / no filesystem close hook.
7126
0
  Status s;
7127
0
  auto close_writer = [&]() {
7128
0
    if (writer == nullptr) {
7129
0
      return;
7130
0
    }
7131
0
    Status close_s = writer->Close(write_options);
7132
0
    if (s.ok() && !close_s.ok()) {
7133
0
      s = close_s;
7134
0
    }
7135
0
    writer.reset();
7136
0
  };
7137
7138
0
  for (size_t i = 0; i < cf_ids.size(); ++i) {
7139
0
    assert(writer != nullptr);
7140
0
    VersionEdit edit;
7141
0
    edit.SetColumnFamily(cf_ids[i]);
7142
0
    edit.DropColumnFamily();
7143
0
    if (i + 1 == cf_ids.size()) {
7144
0
      if (committed_options_file_number != 0) {
7145
0
        edit.SetCommittedOptionsFileNumber(committed_options_file_number);
7146
0
        edit.SetNextFile(next_options_file_number);
7147
0
      }
7148
0
    }
7149
0
    std::string record;
7150
0
    if (!edit.EncodeTo(&record)) {
7151
0
      s = Status::Corruption(
7152
0
          "Unable to encode column family drop VersionEdit for checkpoint");
7153
0
      close_writer();
7154
0
      return s;
7155
0
    }
7156
0
    IOStatus add_s = writer->AddRecord(write_options, record);
7157
0
    if (!add_s.ok()) {
7158
0
      s = add_s;
7159
0
      close_writer();
7160
0
      return s;
7161
0
    }
7162
0
  }
7163
7164
0
  if (cf_ids.empty()) {
7165
0
    assert(writer != nullptr);
7166
0
    VersionEdit edit;
7167
0
    if (committed_options_file_number != 0) {
7168
0
      edit.SetCommittedOptionsFileNumber(committed_options_file_number);
7169
0
      edit.SetNextFile(next_options_file_number);
7170
0
    }
7171
0
    std::string record;
7172
0
    if (!edit.EncodeTo(&record)) {
7173
0
      s = Status::Corruption("Unable to encode committed OPTIONS VersionEdit");
7174
0
      close_writer();
7175
0
      return s;
7176
0
    }
7177
0
    IOStatus add_s = writer->AddRecord(write_options, record);
7178
0
    if (!add_s.ok()) {
7179
0
      s = add_s;
7180
0
      close_writer();
7181
0
      return s;
7182
0
    }
7183
0
  }
7184
7185
0
  IOStatus sync_s = SyncManifest(db_options_, write_options, writer->file());
7186
0
  if (!sync_s.ok()) {
7187
0
    s = sync_s;
7188
0
    close_writer();
7189
0
    return s;
7190
0
  }
7191
0
  close_writer();
7192
0
  if (!s.ok()) {
7193
0
    return s;
7194
0
  }
7195
7196
  // Replace the original with the rewritten .tmp.
7197
0
  IOStatus rename_s =
7198
0
      fs_->RenameFile(tmp_path, manifest_path, IOOptions(), /*dbg=*/nullptr);
7199
0
  if (!rename_s.ok()) {
7200
0
    return rename_s;
7201
0
  }
7202
0
  const size_t slash = manifest_path.find_last_of("/\\");
7203
0
  const std::string manifest_dir =
7204
0
      slash == std::string::npos ? "." : manifest_path.substr(0, slash);
7205
0
  std::unique_ptr<FSDirectory> dir;
7206
0
  IOStatus dir_s = fs_->NewDirectory(manifest_dir, IOOptions(), &dir, nullptr);
7207
0
  if (!dir_s.ok()) {
7208
0
    return dir_s;
7209
0
  }
7210
0
  assert(dir != nullptr);
7211
0
  dir_s = dir->FsyncWithDirOptions(IOOptions(), nullptr,
7212
0
                                   DirFsyncOptions(manifest_path));
7213
0
  if (!dir_s.ok()) {
7214
0
    return dir_s;
7215
0
  }
7216
0
  return Status::OK();
7217
0
}
7218
7219
Status VersionSet::Recover(
7220
    const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
7221
    std::string* db_id, bool no_error_if_files_missing, bool is_retry,
7222
44.8k
    Status* log_status) {
7223
44.8k
  const ReadOptions read_options(Env::IOActivity::kDBOpen);
7224
  // Read "CURRENT" file, which contains a pointer to the current manifest
7225
  // file
7226
44.8k
  std::string manifest_path;
7227
44.8k
  Status s = GetCurrentManifestPath(dbname_, fs_.get(), is_retry,
7228
44.8k
                                    &manifest_path, &manifest_file_number_);
7229
44.8k
  if (!s.ok()) {
7230
0
    return s;
7231
0
  }
7232
7233
44.8k
  ROCKS_LOG_INFO(db_options_->info_log, "Recovering from manifest file: %s\n",
7234
44.8k
                 manifest_path.c_str());
7235
7236
44.8k
  std::unique_ptr<SequentialFileReader> manifest_file_reader;
7237
44.8k
  {
7238
44.8k
    std::unique_ptr<FSSequentialFile> manifest_file;
7239
44.8k
    s = fs_->NewSequentialFile(manifest_path,
7240
44.8k
                               fs_->OptimizeForManifestRead(file_options_),
7241
44.8k
                               &manifest_file, nullptr);
7242
44.8k
    if (!s.ok()) {
7243
0
      return s;
7244
0
    }
7245
44.8k
    manifest_file_reader.reset(new SequentialFileReader(
7246
44.8k
        std::move(manifest_file), manifest_path,
7247
44.8k
        db_options_->log_readahead_size, io_tracer_, db_options_->listeners,
7248
44.8k
        /*rate_limiter=*/nullptr, is_retry));
7249
44.8k
  }
7250
0
  TEST_SYNC_POINT("VersionSet::Recover:StartManifestRead");
7251
7252
44.8k
  uint64_t current_manifest_file_size = 0;
7253
44.8k
  uint64_t log_number = 0;
7254
44.8k
  {
7255
44.8k
    force_new_manifest_on_open_ = false;
7256
44.8k
    VersionSet::LogReporter reporter;
7257
44.8k
    Status log_read_status;
7258
44.8k
    reporter.status = &log_read_status;
7259
44.8k
    log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
7260
44.8k
                       true /* checksum */, 0 /* log_number */);
7261
44.8k
    VersionEditHandler handler(
7262
44.8k
        read_only, column_families, const_cast<VersionSet*>(this),
7263
44.8k
        /*track_found_and_missing_files=*/false, no_error_if_files_missing,
7264
44.8k
        io_tracer_, read_options, /*allow_incomplete_valid_version=*/false,
7265
44.8k
        EpochNumberRequirement::kMightMissing,
7266
44.8k
        /*skip_load_table_files=*/db_options_->open_files_async);
7267
44.8k
    handler.Iterate(reader, &log_read_status);
7268
44.8k
    s = handler.status();
7269
44.8k
    if (s.ok()) {
7270
44.8k
      log_number = handler.GetVersionEditParams().GetLogNumber();
7271
44.8k
      current_manifest_file_size = reader.GetReadOffset();
7272
44.8k
      assert(current_manifest_file_size != 0);
7273
44.8k
      handler.GetDbId(db_id);
7274
44.8k
    }
7275
44.8k
    if (s.ok()) {
7276
44.8k
      RecoverEpochNumbers();
7277
44.8k
    }
7278
44.8k
    if (log_status) {
7279
44.8k
      *log_status = log_read_status;
7280
44.8k
    }
7281
44.8k
  }
7282
7283
44.8k
  if (s.ok()) {
7284
44.8k
    manifest_file_size_ = current_manifest_file_size;
7285
44.8k
    ROCKS_LOG_INFO(
7286
44.8k
        db_options_->info_log,
7287
44.8k
        "Recovered from manifest file:%s succeeded,"
7288
44.8k
        "manifest_file_number is %" PRIu64 ", next_file_number is %" PRIu64
7289
44.8k
        ", last_sequence is %" PRIu64 ", log_number is %" PRIu64
7290
44.8k
        ",prev_log_number is %" PRIu64 ",max_column_family is %" PRIu32
7291
44.8k
        ",min_log_number_to_keep is %" PRIu64 "\n",
7292
44.8k
        manifest_path.c_str(), manifest_file_number_, next_file_number_.load(),
7293
44.8k
        last_sequence_.load(), log_number, prev_log_number_,
7294
44.8k
        column_family_set_->GetMaxColumnFamily(), min_log_number_to_keep());
7295
7296
60.5k
    for (auto cfd : *column_family_set_) {
7297
60.5k
      if (cfd->IsDropped()) {
7298
0
        continue;
7299
0
      }
7300
60.5k
      ROCKS_LOG_INFO(db_options_->info_log,
7301
60.5k
                     "Column family [%s] (ID %" PRIu32
7302
60.5k
                     "), log number is %" PRIu64 "\n",
7303
60.5k
                     cfd->GetName().c_str(), cfd->GetID(), cfd->GetLogNumber());
7304
60.5k
    }
7305
44.8k
  }
7306
7307
44.8k
  if (s.ok() && !read_only && db_options_->reuse_manifest_on_open &&
7308
0
      manifest_recovery_last_valid_record_end_ > 0) {
7309
0
    s = ReopenManifestForAppend(manifest_path);
7310
0
  }
7311
7312
44.8k
  return s;
7313
44.8k
}
7314
7315
namespace {
7316
class ManifestPicker {
7317
 public:
7318
  explicit ManifestPicker(const std::string& dbname,
7319
                          const std::vector<std::string>& files_in_dbname);
7320
  // REQUIRES Valid() == true
7321
  std::string GetNextManifest(uint64_t* file_number, std::string* file_name);
7322
0
  bool Valid() const { return manifest_file_iter_ != manifest_files_.end(); }
7323
7324
 private:
7325
  const std::string& dbname_;
7326
  // MANIFEST file names(s)
7327
  std::vector<std::string> manifest_files_;
7328
  std::vector<std::string>::const_iterator manifest_file_iter_;
7329
};
7330
7331
ManifestPicker::ManifestPicker(const std::string& dbname,
7332
                               const std::vector<std::string>& files_in_dbname)
7333
0
    : dbname_(dbname) {
7334
  // populate manifest files
7335
0
  assert(!files_in_dbname.empty());
7336
0
  for (const auto& fname : files_in_dbname) {
7337
0
    uint64_t file_num = 0;
7338
0
    FileType file_type;
7339
0
    bool parse_ok = ParseFileName(fname, &file_num, &file_type);
7340
0
    if (parse_ok && file_type == kDescriptorFile) {
7341
0
      manifest_files_.push_back(fname);
7342
0
    }
7343
0
  }
7344
  // seek to first manifest
7345
0
  std::sort(manifest_files_.begin(), manifest_files_.end(),
7346
0
            [](const std::string& lhs, const std::string& rhs) {
7347
0
              uint64_t num1 = 0;
7348
0
              uint64_t num2 = 0;
7349
0
              FileType type1;
7350
0
              FileType type2;
7351
0
              bool parse_ok1 = ParseFileName(lhs, &num1, &type1);
7352
0
              bool parse_ok2 = ParseFileName(rhs, &num2, &type2);
7353
#ifndef NDEBUG
7354
              assert(parse_ok1);
7355
              assert(parse_ok2);
7356
#else
7357
0
              (void)parse_ok1;
7358
0
              (void)parse_ok2;
7359
0
#endif
7360
0
              return num1 > num2;
7361
0
            });
7362
0
  manifest_file_iter_ = manifest_files_.begin();
7363
0
}
7364
7365
std::string ManifestPicker::GetNextManifest(uint64_t* number,
7366
0
                                            std::string* file_name) {
7367
0
  assert(Valid());
7368
0
  std::string ret;
7369
0
  if (manifest_file_iter_ != manifest_files_.end()) {
7370
0
    ret.assign(dbname_);
7371
0
    if (ret.back() != kFilePathSeparator) {
7372
0
      ret.push_back(kFilePathSeparator);
7373
0
    }
7374
0
    ret.append(*manifest_file_iter_);
7375
0
    if (number) {
7376
0
      FileType type;
7377
0
      bool parse = ParseFileName(*manifest_file_iter_, number, &type);
7378
0
      assert(type == kDescriptorFile);
7379
#ifndef NDEBUG
7380
      assert(parse);
7381
#else
7382
0
      (void)parse;
7383
0
#endif
7384
0
    }
7385
0
    if (file_name) {
7386
0
      *file_name = *manifest_file_iter_;
7387
0
    }
7388
0
    ++manifest_file_iter_;
7389
0
  }
7390
0
  return ret;
7391
0
}
7392
}  // anonymous namespace
7393
7394
Status VersionSet::TryRecover(
7395
    const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
7396
    const std::vector<std::string>& files_in_dbname, std::string* db_id,
7397
0
    bool* has_missing_table_file) {
7398
0
  ManifestPicker manifest_picker(dbname_, files_in_dbname);
7399
0
  if (!manifest_picker.Valid()) {
7400
0
    return Status::Corruption("Cannot locate MANIFEST file in " + dbname_);
7401
0
  }
7402
0
  Status s;
7403
0
  std::string manifest_path =
7404
0
      manifest_picker.GetNextManifest(&manifest_file_number_, nullptr);
7405
0
  while (!manifest_path.empty()) {
7406
0
    s = TryRecoverFromOneManifest(manifest_path, column_families, read_only,
7407
0
                                  db_id, has_missing_table_file);
7408
0
    if (s.ok() || !manifest_picker.Valid()) {
7409
0
      break;
7410
0
    }
7411
0
    Reset();
7412
0
    manifest_path =
7413
0
        manifest_picker.GetNextManifest(&manifest_file_number_, nullptr);
7414
0
  }
7415
0
  return s;
7416
0
}
7417
7418
Status VersionSet::TryRecoverFromOneManifest(
7419
    const std::string& manifest_path,
7420
    const std::vector<ColumnFamilyDescriptor>& column_families, bool read_only,
7421
0
    std::string* db_id, bool* has_missing_table_file) {
7422
0
  const ReadOptions read_options(Env::IOActivity::kDBOpen);
7423
0
  ROCKS_LOG_INFO(db_options_->info_log, "Trying to recover from manifest: %s\n",
7424
0
                 manifest_path.c_str());
7425
0
  std::unique_ptr<SequentialFileReader> manifest_file_reader;
7426
0
  Status s;
7427
0
  {
7428
0
    std::unique_ptr<FSSequentialFile> manifest_file;
7429
0
    s = fs_->NewSequentialFile(manifest_path,
7430
0
                               fs_->OptimizeForManifestRead(file_options_),
7431
0
                               &manifest_file, nullptr);
7432
0
    if (!s.ok()) {
7433
0
      return s;
7434
0
    }
7435
0
    manifest_file_reader.reset(new SequentialFileReader(
7436
0
        std::move(manifest_file), manifest_path,
7437
0
        db_options_->log_readahead_size, io_tracer_, db_options_->listeners));
7438
0
  }
7439
7440
0
  assert(s.ok());
7441
0
  VersionSet::LogReporter reporter;
7442
0
  reporter.status = &s;
7443
0
  log::Reader reader(nullptr, std::move(manifest_file_reader), &reporter,
7444
0
                     /*checksum=*/true, /*log_num=*/0);
7445
0
  VersionEditHandlerPointInTime handler_pit(
7446
0
      read_only, column_families, const_cast<VersionSet*>(this), io_tracer_,
7447
0
      read_options, /*allow_incomplete_valid_version=*/true,
7448
      /*record_missing_blob_files_as_dropped=*/
7449
0
      db_options_->best_efforts_blob_db_repair,
7450
0
      /*trust_manifest_recovery=*/false,
7451
0
      /*defer_sst_file_opening=*/false, EpochNumberRequirement::kMightMissing);
7452
7453
0
  handler_pit.Iterate(reader, &s);
7454
7455
0
  handler_pit.GetDbId(db_id);
7456
7457
0
  assert(nullptr != has_missing_table_file);
7458
0
  *has_missing_table_file = handler_pit.HasMissingFiles();
7459
7460
0
  s = handler_pit.status();
7461
0
  if (s.ok()) {
7462
0
    RecoverEpochNumbers();
7463
0
    if (db_options_->best_efforts_blob_db_repair) {
7464
0
      LogDroppedBlobFiles();
7465
0
    }
7466
0
  }
7467
0
  return s;
7468
0
}
7469
7470
0
void VersionSet::LogDroppedBlobFiles() const {
7471
0
  for (ColumnFamilyData* cfd : *column_family_set_) {
7472
0
    if (cfd->IsDropped()) {
7473
0
      continue;
7474
0
    }
7475
0
    const std::vector<uint64_t>& dropped_blob_files =
7476
0
        cfd->current()->storage_info()->GetDroppedBlobFiles();
7477
0
    if (dropped_blob_files.empty()) {
7478
0
      continue;
7479
0
    }
7480
0
    std::string numbers;
7481
0
    for (const uint64_t blob_file_number : dropped_blob_files) {
7482
0
      numbers.append(" #");
7483
0
      AppendNumberTo(&numbers, blob_file_number);
7484
0
    }
7485
0
    ROCKS_LOG_WARN(db_options_->info_log,
7486
0
                   "[%s] %" ROCKSDB_PRIszt
7487
0
                   " dropped blob files are in effect after best-efforts "
7488
0
                   "BlobDB repair:%s",
7489
0
                   cfd->GetName().c_str(), dropped_blob_files.size(),
7490
0
                   numbers.c_str());
7491
0
  }
7492
0
}
7493
7494
44.8k
void VersionSet::RecoverEpochNumbers() {
7495
60.5k
  for (auto cfd : *column_family_set_) {
7496
60.5k
    if (cfd->IsDropped()) {
7497
0
      continue;
7498
0
    }
7499
60.5k
    assert(cfd->initialized());
7500
60.5k
    cfd->RecoverEpochNumbers();
7501
60.5k
  }
7502
44.8k
}
7503
7504
Status VersionSet::ListColumnFamilies(std::vector<std::string>* column_families,
7505
                                      const std::string& dbname,
7506
0
                                      FileSystem* fs) {
7507
  // Read "CURRENT" file, which contains a pointer to the current manifest
7508
  // file
7509
0
  std::string manifest_path;
7510
0
  uint64_t manifest_file_number;
7511
0
  Status s = GetCurrentManifestPath(dbname, fs, /*is_retry=*/false,
7512
0
                                    &manifest_path, &manifest_file_number);
7513
0
  if (!s.ok()) {
7514
0
    return s;
7515
0
  }
7516
0
  return ListColumnFamiliesFromManifest(manifest_path, fs, column_families);
7517
0
}
7518
7519
Status VersionSet::ListColumnFamiliesFromManifest(
7520
    const std::string& manifest_path, FileSystem* fs,
7521
0
    std::vector<std::string>* column_families) {
7522
  // TODO: plumb Env::IOActivity, Env::IOPriority
7523
0
  const ReadOptions read_options;
7524
0
  std::unique_ptr<SequentialFileReader> file_reader;
7525
0
  Status s;
7526
0
  {
7527
0
    std::unique_ptr<FSSequentialFile> file;
7528
    // these are just for performance reasons, not correctness,
7529
    // so we're fine using the defaults
7530
0
    s = fs->NewSequentialFile(manifest_path, FileOptions(), &file, nullptr);
7531
0
    if (!s.ok()) {
7532
0
      return s;
7533
0
    }
7534
0
    file_reader = std::make_unique<SequentialFileReader>(
7535
0
        std::move(file), manifest_path, /*io_tracer=*/nullptr);
7536
0
  }
7537
7538
0
  VersionSet::LogReporter reporter;
7539
0
  reporter.status = &s;
7540
0
  log::Reader reader(nullptr, std::move(file_reader), &reporter,
7541
0
                     true /* checksum */, 0 /* log_number */);
7542
7543
0
  ListColumnFamiliesHandler handler(read_options);
7544
0
  handler.Iterate(reader, &s);
7545
7546
0
  assert(column_families);
7547
0
  column_families->clear();
7548
0
  if (handler.status().ok()) {
7549
0
    for (const auto& iter : handler.GetColumnFamilyNames()) {
7550
0
      column_families->push_back(iter.second);
7551
0
    }
7552
0
  }
7553
7554
0
  return handler.status();
7555
0
}
7556
7557
Status VersionSet::GetOptionsFileManifestState(
7558
    const std::string& dbname, FileSystem* fs,
7559
0
    OptionsFileManifestState* manifest_state) {
7560
0
  return GetOptionsFileManifestState(
7561
0
      dbname, fs, manifest_state, /*next_file_number=*/nullptr,
7562
0
      /*last_valid_manifest_record_end=*/nullptr);
7563
0
}
7564
7565
Status VersionSet::GetOptionsFileManifestState(
7566
    const std::string& dbname, FileSystem* fs,
7567
    OptionsFileManifestState* manifest_state, uint64_t* next_file_number,
7568
0
    uint64_t* last_valid_manifest_record_end) {
7569
0
  assert(fs != nullptr);
7570
0
  assert(manifest_state != nullptr);
7571
0
  *manifest_state = OptionsFileManifestState();
7572
0
  if (next_file_number != nullptr) {
7573
0
    *next_file_number = 0;
7574
0
  }
7575
0
  if (last_valid_manifest_record_end != nullptr) {
7576
0
    *last_valid_manifest_record_end = 0;
7577
0
  }
7578
7579
0
  constexpr int kMaxAttempts = 2;
7580
0
  for (int attempt = 0; attempt < kMaxAttempts; ++attempt) {
7581
0
    std::string manifest_path;
7582
0
    uint64_t manifest_file_number = 0;
7583
0
    Status s = GetCurrentManifestPath(dbname, fs, /*is_retry=*/attempt > 0,
7584
0
                                      &manifest_path, &manifest_file_number);
7585
0
    if (s.IsNotFound()) {
7586
0
      if (attempt + 1 == kMaxAttempts) {
7587
0
        return Status::OK();
7588
0
      }
7589
0
      continue;
7590
0
    }
7591
0
    if (!s.ok()) {
7592
0
      return s;
7593
0
    }
7594
7595
0
    std::unique_ptr<FSSequentialFile> file;
7596
0
    s = fs->NewSequentialFile(manifest_path, FileOptions(), &file, nullptr);
7597
0
    if (s.IsNotFound() || s.IsPathNotFound()) {
7598
0
      if (attempt + 1 == kMaxAttempts) {
7599
0
        return s;
7600
0
      }
7601
0
      continue;
7602
0
    }
7603
0
    if (!s.ok()) {
7604
0
      return s;
7605
0
    }
7606
0
    auto file_reader = std::make_unique<SequentialFileReader>(
7607
0
        std::move(file), manifest_path, /*io_tracer=*/nullptr);
7608
0
    LogReporter reporter;
7609
0
    reporter.status = &s;
7610
0
    log::Reader reader(nullptr, std::move(file_reader), &reporter,
7611
0
                       /*checksum=*/true, /*log_number=*/0);
7612
7613
0
    OptionsFileManifestState found_manifest_state;
7614
    // The default column family is implicit in MANIFEST snapshots and is not
7615
    // written as a kColumnFamilyAdd record.
7616
0
    found_manifest_state.column_family_names.emplace(0,
7617
0
                                                     kDefaultColumnFamilyName);
7618
0
    uint64_t found_next_file_number = 0;
7619
0
    uint64_t found_last_valid_record_end = 0;
7620
0
    auto apply_edit = [&](const VersionEdit& edit) {
7621
0
      found_manifest_state.ApplyColumnFamilyEdit(edit);
7622
0
      if (edit.HasCommittedOptionsFileNumber()) {
7623
0
        found_manifest_state.ApplyCommit(edit.GetCommittedOptionsFileNumber());
7624
0
      }
7625
0
      if (edit.HasNextFile()) {
7626
0
        found_next_file_number = edit.GetNextFile();
7627
0
      }
7628
0
    };
7629
7630
0
    AtomicGroupReadBuffer atomic_group;
7631
0
    Slice record;
7632
0
    std::string scratch;
7633
0
    while (s.ok() && reader.ReadRecord(&record, &scratch)) {
7634
0
      VersionEdit edit;
7635
0
      s = edit.DecodeFrom(record);
7636
0
      if (!s.ok()) {
7637
0
        break;
7638
0
      }
7639
0
      s = atomic_group.AddEdit(&edit);
7640
0
      if (!s.ok()) {
7641
0
        break;
7642
0
      }
7643
0
      if (edit.IsInAtomicGroup()) {
7644
0
        if (!atomic_group.IsFull()) {
7645
0
          continue;
7646
0
        }
7647
0
        found_last_valid_record_end = reader.LastRecordEnd();
7648
0
        for (const auto& grouped_edit : atomic_group.replay_buffer()) {
7649
0
          apply_edit(grouped_edit);
7650
0
        }
7651
0
        atomic_group.Clear();
7652
0
      } else {
7653
0
        found_last_valid_record_end = reader.LastRecordEnd();
7654
0
        apply_edit(edit);
7655
0
      }
7656
0
    }
7657
0
    if (!s.ok()) {
7658
0
      return s;
7659
0
    }
7660
7661
0
    *manifest_state = found_manifest_state;
7662
0
    if (next_file_number != nullptr) {
7663
0
      *next_file_number = found_next_file_number;
7664
0
    }
7665
0
    if (last_valid_manifest_record_end != nullptr) {
7666
0
      *last_valid_manifest_record_end = found_last_valid_record_end;
7667
0
    }
7668
0
    return Status::OK();
7669
0
  }
7670
7671
0
  return Status::OK();
7672
0
}
7673
7674
Status VersionSet::ResolveOptionsFileNumber(
7675
    const std::string& dbname, FileSystem* fs,
7676
    const OptionsFileManifestState& manifest_state,
7677
    const std::vector<std::string>& filenames,
7678
44.8k
    bool inspect_options_file_tracking, OptionsFileSelection* selection) {
7679
44.8k
  assert(fs != nullptr);
7680
44.8k
  assert(selection != nullptr);
7681
44.8k
  *selection = OptionsFileSelection();
7682
7683
44.8k
  std::map<uint64_t, std::string, std::greater<uint64_t>> options_files;
7684
704k
  for (const std::string& filename : filenames) {
7685
704k
    uint64_t number = 0;
7686
704k
    FileType type;
7687
704k
    if (ParseFileName(filename, &number, &type) && type == kOptionsFile) {
7688
67.1k
      options_files.emplace(number, filename);
7689
67.1k
    }
7690
704k
  }
7691
44.8k
  if (options_files.empty()) {
7692
8.13k
    return Status::OK();
7693
8.13k
  }
7694
7695
36.7k
  const auto highest = options_files.begin();
7696
36.7k
  if (!manifest_state.has_committed_options_file_number &&
7697
36.7k
      !inspect_options_file_tracking) {
7698
36.7k
    selection->file_number = highest->first;
7699
36.7k
    return Status::OK();
7700
36.7k
  }
7701
7702
0
  auto is_tracked = [&](const std::string& filename, bool* tracked) -> Status {
7703
0
    assert(tracked != nullptr);
7704
0
    *tracked = false;
7705
0
    const std::string path = dbname + "/" + filename;
7706
0
    TEST_SYNC_POINT(
7707
0
        "VersionSet::ResolveOptionsFileNumber:InspectTrackingMarker");
7708
0
    std::unique_ptr<FSSequentialFile> file;
7709
0
    Status s = fs->NewSequentialFile(path, FileOptions(), &file, nullptr);
7710
0
    if (!s.ok()) {
7711
0
      return s;
7712
0
    }
7713
0
    LineFileReader reader(std::move(file), path, /*io_tracer=*/nullptr,
7714
0
                          std::vector<std::shared_ptr<EventListener>>{},
7715
0
                          /*rate_limiter=*/nullptr);
7716
0
    bool in_db_options = false;
7717
0
    std::string line;
7718
0
    while (reader.ReadLine(&line, Env::IO_TOTAL)) {
7719
0
      line = RocksDBOptionsParser::TrimAndRemoveComment(line);
7720
0
      if (line.empty()) {
7721
0
        continue;
7722
0
      }
7723
0
      if (line.front() == '[' && line.back() == ']') {
7724
0
        if (in_db_options) {
7725
0
          return Status::OK();
7726
0
        }
7727
0
        in_db_options = line == "[DBOptions]";
7728
0
        continue;
7729
0
      }
7730
0
      if (!in_db_options) {
7731
0
        continue;
7732
0
      }
7733
0
      const size_t separator = line.find('=');
7734
0
      if (separator == std::string::npos) {
7735
0
        continue;
7736
0
      }
7737
0
      const std::string name = RocksDBOptionsParser::TrimAndRemoveComment(
7738
0
          line.substr(0, separator), /*trim_only=*/true);
7739
0
      if (name != "track_options_file_number_in_manifest") {
7740
0
        continue;
7741
0
      }
7742
0
      const std::string value = RocksDBOptionsParser::TrimAndRemoveComment(
7743
0
          line.substr(separator + 1), /*trim_only=*/true);
7744
0
      if (value == "true" || value == "1") {
7745
0
        *tracked = true;
7746
0
        return Status::OK();
7747
0
      }
7748
0
      if (value == "false" || value == "0") {
7749
0
        return Status::OK();
7750
0
      }
7751
0
      return Status::InvalidArgument(
7752
0
          "Invalid track_options_file_number_in_manifest value in " + path);
7753
0
    }
7754
0
    return reader.GetStatus();
7755
0
  };
7756
7757
0
  if (!manifest_state.has_committed_options_file_number) {
7758
0
    selection->file_number = highest->first;
7759
0
    bool highest_is_tracked = false;
7760
0
    Status marker_status = is_tracked(highest->second, &highest_is_tracked);
7761
    // Marker inspection only selects between legacy and MANIFEST semantics.
7762
    // The caller that consumes the file will report any actual read error.
7763
0
    selection->used_fallback = !marker_status.ok() || highest_is_tracked;
7764
0
    return Status::OK();
7765
0
  }
7766
7767
0
  std::vector<uint64_t> rejected_tracked_file_numbers;
7768
0
  for (auto it = options_files.begin(); it != options_files.end(); ++it) {
7769
0
    if (it->first == manifest_state.committed_options_file_number) {
7770
0
      selection->file_number = it->first;
7771
0
      selection->selected_by_manifest = true;
7772
0
      selection->rejected_tracked_file_numbers =
7773
0
          std::move(rejected_tracked_file_numbers);
7774
0
      return Status::OK();
7775
0
    }
7776
0
    if (it->first < manifest_state.committed_options_file_number) {
7777
0
      break;
7778
0
    }
7779
0
    bool tracked = false;
7780
0
    Status marker_status = is_tracked(it->second, &tracked);
7781
0
    if (!marker_status.ok()) {
7782
      // An unreadable higher-numbered file cannot safely override a committed
7783
      // snapshot, but it also must not make DB::Open fail. Loading the selected
7784
      // file remains responsible for reporting content errors to utility APIs.
7785
      // Keep the unreadable candidate because it might be a legacy snapshot.
7786
0
      continue;
7787
0
    }
7788
0
    if (!tracked) {
7789
0
      selection->file_number = it->first;
7790
0
      selection->rejected_tracked_file_numbers =
7791
0
          std::move(rejected_tracked_file_numbers);
7792
0
      return Status::OK();
7793
0
    }
7794
0
    rejected_tracked_file_numbers.push_back(it->first);
7795
0
  }
7796
7797
0
  selection->file_number = highest->first;
7798
0
  selection->used_fallback = true;
7799
0
  return Status::OK();
7800
0
}
7801
7802
Status VersionSet::ReduceNumberOfLevels(const std::string& dbname,
7803
                                        const Options* options,
7804
                                        const FileOptions& file_options,
7805
0
                                        int new_levels) {
7806
0
  if (new_levels <= 1) {
7807
0
    return Status::InvalidArgument(
7808
0
        "Number of levels needs to be bigger than 1");
7809
0
  }
7810
7811
  // TODO: plumb Env::IOActivity, Env::IOPriority
7812
0
  const ReadOptions read_options;
7813
0
  const WriteOptions write_options;
7814
7815
0
  ImmutableDBOptions imm_db_options(*options);
7816
0
  MutableDBOptions mutable_db_options(*options);
7817
0
  ColumnFamilyOptions cf_options(*options);
7818
0
  std::shared_ptr<Cache> tc(NewLRUCache(options->max_open_files - 10,
7819
0
                                        options->table_cache_numshardbits));
7820
0
  WriteController wc(options->delayed_write_rate);
7821
0
  WriteBufferManager wb(options->db_write_buffer_size);
7822
0
  VersionSet versions(dbname, &imm_db_options, mutable_db_options, file_options,
7823
0
                      tc.get(), &wb, &wc, nullptr /*BlockCacheTracer*/,
7824
0
                      nullptr /*IOTracer*/,
7825
0
                      /*db_id*/ "",
7826
0
                      /*db_session_id*/ "", options->daily_offpeak_time_utc,
7827
0
                      /*error_handler_*/ nullptr, /*unchanging=*/false);
7828
0
  Status status;
7829
7830
0
  std::vector<ColumnFamilyDescriptor> dummy;
7831
0
  ColumnFamilyDescriptor dummy_descriptor(kDefaultColumnFamilyName,
7832
0
                                          ColumnFamilyOptions(*options));
7833
0
  dummy.push_back(dummy_descriptor);
7834
0
  status = versions.Recover(dummy);
7835
0
  if (!status.ok()) {
7836
0
    return status;
7837
0
  }
7838
7839
0
  Version* current_version =
7840
0
      versions.GetColumnFamilySet()->GetDefault()->current();
7841
0
  auto* vstorage = current_version->storage_info();
7842
0
  int current_levels = vstorage->num_levels();
7843
7844
0
  if (current_levels <= new_levels) {
7845
0
    return Status::OK();
7846
0
  }
7847
7848
  // Make sure there are file only on one level from
7849
  // (new_levels-1) to (current_levels-1)
7850
0
  int first_nonempty_level = -1;
7851
0
  int first_nonempty_level_filenum = 0;
7852
0
  for (int i = new_levels - 1; i < current_levels; i++) {
7853
0
    int file_num = vstorage->NumLevelFiles(i);
7854
0
    if (file_num != 0) {
7855
0
      if (first_nonempty_level < 0) {
7856
0
        first_nonempty_level = i;
7857
0
        first_nonempty_level_filenum = file_num;
7858
0
      } else {
7859
0
        char msg[255];
7860
0
        snprintf(msg, sizeof(msg),
7861
0
                 "Found at least two levels containing files: "
7862
0
                 "[%d:%d],[%d:%d].\n",
7863
0
                 first_nonempty_level, first_nonempty_level_filenum, i,
7864
0
                 file_num);
7865
0
        return Status::InvalidArgument(msg);
7866
0
      }
7867
0
    }
7868
0
  }
7869
7870
  // we need to allocate an array with the old number of levels size to
7871
  // avoid SIGSEGV in WriteCurrentStatetoManifest()
7872
  // however, all levels bigger or equal to new_levels will be empty
7873
0
  std::vector<FileMetaData*>* new_files_list =
7874
0
      new std::vector<FileMetaData*>[current_levels];
7875
0
  for (int i = 0; i < new_levels - 1; i++) {
7876
0
    new_files_list[i] = vstorage->LevelFiles(i);
7877
0
  }
7878
7879
0
  if (first_nonempty_level > 0) {
7880
0
    auto& new_last_level = new_files_list[new_levels - 1];
7881
7882
0
    new_last_level = vstorage->LevelFiles(first_nonempty_level);
7883
7884
0
    for (size_t i = 0; i < new_last_level.size(); ++i) {
7885
0
      const FileMetaData* const meta = new_last_level[i];
7886
0
      assert(meta);
7887
7888
0
      const uint64_t file_number = meta->fd.GetNumber();
7889
7890
0
      vstorage->file_locations_[file_number] =
7891
0
          VersionStorageInfo::FileLocation(new_levels - 1, i);
7892
0
    }
7893
0
  }
7894
7895
0
  delete[] vstorage->files_;
7896
0
  vstorage->files_ = new_files_list;
7897
0
  vstorage->num_levels_ = new_levels;
7898
0
  vstorage->ResizeCompactCursors(new_levels);
7899
7900
0
  VersionEdit ve;
7901
0
  InstrumentedMutex dummy_mutex;
7902
0
  InstrumentedMutexLock l(&dummy_mutex);
7903
0
  return versions.LogAndApply(versions.GetColumnFamilySet()->GetDefault(),
7904
0
                              read_options, write_options, &ve, &dummy_mutex,
7905
0
                              nullptr, true);
7906
0
}
7907
7908
// Get the checksum information including the checksum and checksum function
7909
// name of all SST and blob files in VersionSet. Store the information in
7910
// FileChecksumList which contains a map from file number to its checksum
7911
// info. If DB is not running, make sure call VersionSet::Recover() to load
7912
// the file metadata from Manifest to VersionSet before calling this function.
7913
0
Status VersionSet::GetLiveFilesChecksumInfo(FileChecksumList* checksum_list) {
7914
  // Clean the previously stored checksum information if any.
7915
0
  Status s;
7916
0
  if (checksum_list == nullptr) {
7917
0
    s = Status::InvalidArgument("checksum_list is nullptr");
7918
0
    return s;
7919
0
  }
7920
0
  checksum_list->reset();
7921
7922
0
  for (auto cfd : *column_family_set_) {
7923
0
    assert(cfd);
7924
7925
0
    if (cfd->IsDropped() || !cfd->initialized()) {
7926
0
      continue;
7927
0
    }
7928
7929
0
    const auto* current = cfd->current();
7930
0
    assert(current);
7931
7932
0
    const auto* vstorage = current->storage_info();
7933
0
    assert(vstorage);
7934
7935
    /* SST files */
7936
0
    for (int level = 0; level < cfd->NumberLevels(); level++) {
7937
0
      const auto& level_files = vstorage->LevelFiles(level);
7938
7939
0
      for (const auto& file : level_files) {
7940
0
        assert(file);
7941
7942
0
        s = checksum_list->InsertOneFileChecksum(file->fd.GetNumber(),
7943
0
                                                 file->file_checksum,
7944
0
                                                 file->file_checksum_func_name);
7945
0
        if (!s.ok()) {
7946
0
          return s;
7947
0
        }
7948
0
      }
7949
0
    }
7950
7951
    /* Blob files */
7952
0
    const auto& blob_files = vstorage->GetBlobFiles();
7953
0
    for (const auto& meta : blob_files) {
7954
0
      assert(meta);
7955
7956
0
      std::string checksum_value = meta->GetChecksumValue();
7957
0
      std::string checksum_method = meta->GetChecksumMethod();
7958
0
      assert(checksum_value.empty() == checksum_method.empty());
7959
0
      if (meta->GetChecksumMethod().empty()) {
7960
0
        checksum_value = kUnknownFileChecksum;
7961
0
        checksum_method = kUnknownFileChecksumFuncName;
7962
0
      }
7963
7964
0
      s = checksum_list->InsertOneFileChecksum(meta->GetBlobFileNumber(),
7965
0
                                               checksum_value, checksum_method);
7966
0
      if (!s.ok()) {
7967
0
        return s;
7968
0
      }
7969
0
    }
7970
0
  }
7971
7972
0
  return s;
7973
0
}
7974
7975
Status VersionSet::DumpManifest(
7976
    Options& options, std::string& dscname, bool verbose, bool hex, bool json,
7977
0
    const std::vector<ColumnFamilyDescriptor>& cf_descs) {
7978
0
  assert(options.env);
7979
  // TODO: plumb Env::IOActivity, Env::IOPriority
7980
0
  const ReadOptions read_options;
7981
7982
0
  std::vector<std::string> column_families;
7983
0
  Status s = ListColumnFamiliesFromManifest(
7984
0
      dscname, options.env->GetFileSystem().get(), &column_families);
7985
0
  if (!s.ok()) {
7986
0
    return s;
7987
0
  }
7988
7989
  // Open the specified manifest file.
7990
0
  std::unique_ptr<SequentialFileReader> file_reader;
7991
0
  {
7992
0
    std::unique_ptr<FSSequentialFile> file;
7993
0
    const std::shared_ptr<FileSystem>& fs = options.env->GetFileSystem();
7994
0
    s = fs->NewSequentialFile(
7995
0
        dscname, fs->OptimizeForManifestRead(file_options_), &file, nullptr);
7996
0
    if (!s.ok()) {
7997
0
      return s;
7998
0
    }
7999
0
    file_reader = std::make_unique<SequentialFileReader>(
8000
0
        std::move(file), dscname, db_options_->log_readahead_size, io_tracer_);
8001
0
  }
8002
8003
0
  std::map<std::string, const ColumnFamilyDescriptor*> cf_name_to_desc;
8004
0
  for (const auto& cf_desc : cf_descs) {
8005
0
    cf_name_to_desc[cf_desc.name] = &cf_desc;
8006
0
  }
8007
0
  std::vector<ColumnFamilyDescriptor> final_cf_descs;
8008
0
  for (const auto& cf : column_families) {
8009
0
    const auto iter = cf_name_to_desc.find(cf);
8010
0
    if (iter != cf_name_to_desc.cend()) {
8011
0
      final_cf_descs.push_back(*iter->second);
8012
0
    } else {
8013
0
      final_cf_descs.emplace_back(cf, options);
8014
0
    }
8015
0
  }
8016
8017
0
  DumpManifestHandler handler(final_cf_descs, this, io_tracer_, read_options,
8018
0
                              verbose, hex, json);
8019
0
  {
8020
0
    VersionSet::LogReporter reporter;
8021
0
    reporter.status = &s;
8022
0
    log::Reader reader(nullptr, std::move(file_reader), &reporter,
8023
0
                       true /* checksum */, 0 /* log_number */);
8024
0
    handler.Iterate(reader, &s);
8025
0
  }
8026
8027
0
  return handler.status();
8028
0
}
8029
8030
163k
void VersionSet::MarkFileNumberUsed(uint64_t number) {
8031
  // only called during recovery and repair which are single threaded, so this
8032
  // works because there can't be concurrent calls
8033
163k
  if (next_file_number_.load(std::memory_order_relaxed) <= number) {
8034
30.4k
    next_file_number_.store(number + 1, std::memory_order_relaxed);
8035
30.4k
  }
8036
163k
}
8037
// Called only either from ::LogAndApply which is protected by mutex or during
8038
// recovery which is single-threaded.
8039
83.3k
void VersionSet::MarkMinLogNumberToKeep(uint64_t number) {
8040
83.3k
  if (min_log_number_to_keep_.load(std::memory_order_relaxed) < number) {
8041
67.7k
    min_log_number_to_keep_.store(number, std::memory_order_relaxed);
8042
67.7k
  }
8043
83.3k
}
8044
8045
Status VersionSet::WriteCurrentStateToManifest(
8046
    const WriteOptions& write_options,
8047
    const std::unordered_map<uint32_t, MutableCFState>& curr_state,
8048
    const VersionEdit& wal_additions,
8049
    const OptionsFileManifestState& options_file_manifest_state,
8050
44.8k
    log::Writer* log, IOStatus& io_s) {
8051
  // TODO: Break up into multiple records to reduce memory usage on recovery?
8052
8053
  // WARNING: This method doesn't hold a mutex!!
8054
8055
  // This is done without DB mutex lock held, but only within single-threaded
8056
  // LogAndApply. Column family manipulations can only happen within
8057
  // LogAndApply (the same single thread), so we're safe to iterate.
8058
8059
44.8k
  assert(io_s.ok());
8060
44.8k
  if (db_options_->write_dbid_to_manifest) {
8061
44.8k
    VersionEdit edit_for_db_id;
8062
44.8k
    assert(!db_id_.empty());
8063
44.8k
    edit_for_db_id.SetDBId(db_id_);
8064
44.8k
    std::string db_id_record;
8065
44.8k
    if (!edit_for_db_id.EncodeTo(&db_id_record)) {
8066
0
      return Status::Corruption("Unable to Encode VersionEdit:" +
8067
0
                                edit_for_db_id.DebugString(true));
8068
0
    }
8069
44.8k
    io_s = log->AddRecord(write_options, db_id_record);
8070
44.8k
    if (!io_s.ok()) {
8071
0
      return io_s;
8072
0
    }
8073
44.8k
  }
8074
8075
  // Save WALs.
8076
44.8k
  if (!wal_additions.GetWalAdditions().empty()) {
8077
0
    TEST_SYNC_POINT_CALLBACK("VersionSet::WriteCurrentStateToManifest:SaveWal",
8078
0
                             const_cast<VersionEdit*>(&wal_additions));
8079
0
    std::string record;
8080
0
    if (!wal_additions.EncodeTo(&record)) {
8081
0
      return Status::Corruption("Unable to Encode VersionEdit: " +
8082
0
                                wal_additions.DebugString(true));
8083
0
    }
8084
0
    io_s = log->AddRecord(write_options, record);
8085
0
    if (!io_s.ok()) {
8086
0
      return io_s;
8087
0
    }
8088
0
  }
8089
8090
  // New manifest should rollover the WAL deletion record from previous
8091
  // manifest. Otherwise, when an addition record of a deleted WAL gets added
8092
  // to this new manifest later (which can happens in e.g, SyncWAL()), this
8093
  // new manifest creates an illusion that such WAL hasn't been deleted.
8094
44.8k
  VersionEdit wal_deletions;
8095
44.8k
  wal_deletions.DeleteWalsBefore(min_log_number_to_keep());
8096
44.8k
  std::string wal_deletions_record;
8097
44.8k
  if (!wal_deletions.EncodeTo(&wal_deletions_record)) {
8098
0
    return Status::Corruption("Unable to Encode VersionEdit: " +
8099
0
                              wal_deletions.DebugString(true));
8100
0
  }
8101
44.8k
  io_s = log->AddRecord(write_options, wal_deletions_record);
8102
44.8k
  if (!io_s.ok()) {
8103
0
    return io_s;
8104
0
  }
8105
8106
60.5k
  for (auto cfd : *column_family_set_) {
8107
60.5k
    assert(cfd);
8108
8109
60.5k
    if (cfd->IsDropped()) {
8110
0
      continue;
8111
0
    }
8112
60.5k
    assert(cfd->initialized());
8113
60.5k
    {
8114
      // Store column family info
8115
60.5k
      VersionEdit edit;
8116
60.5k
      if (cfd->GetID() != 0) {
8117
        // default column family is always there,
8118
        // no need to explicitly write it
8119
15.6k
        edit.AddColumnFamily(cfd->GetName());
8120
15.6k
        edit.SetColumnFamily(cfd->GetID());
8121
15.6k
      }
8122
60.5k
      edit.SetComparatorName(
8123
60.5k
          cfd->internal_comparator().user_comparator()->Name());
8124
60.5k
      edit.SetPersistUserDefinedTimestamps(
8125
60.5k
          cfd->ioptions().persist_user_defined_timestamps);
8126
60.5k
      std::string record;
8127
60.5k
      if (!edit.EncodeTo(&record)) {
8128
0
        return Status::Corruption("Unable to Encode VersionEdit:" +
8129
0
                                  edit.DebugString(true));
8130
0
      }
8131
60.5k
      io_s = log->AddRecord(write_options, record);
8132
60.5k
      if (!io_s.ok()) {
8133
0
        return io_s;
8134
0
      }
8135
60.5k
    }
8136
8137
60.5k
    {
8138
      // Save files
8139
60.5k
      VersionEdit edit;
8140
60.5k
      edit.SetColumnFamily(cfd->GetID());
8141
8142
60.5k
      const auto* current = cfd->current();
8143
60.5k
      assert(current);
8144
8145
60.5k
      const auto* vstorage = current->storage_info();
8146
60.5k
      assert(vstorage);
8147
8148
484k
      for (int level = 0; level < cfd->NumberLevels(); level++) {
8149
423k
        const auto& level_files = vstorage->LevelFiles(level);
8150
8151
423k
        for (const auto& f : level_files) {
8152
68.7k
          assert(f);
8153
          // Preserves all persisted metadata, including file_open_metadata
8154
          // so live SSTs still benefit from fast_sst_open after a MANIFEST
8155
          // rotation.
8156
68.7k
          edit.AddExistingFile(level, *f, /*provenance_flags_to_set=*/0);
8157
68.7k
        }
8158
423k
      }
8159
8160
60.5k
      edit.SetCompactCursors(vstorage->GetCompactCursors());
8161
8162
60.5k
      const auto& blob_files = vstorage->GetBlobFiles();
8163
60.5k
      for (const auto& meta : blob_files) {
8164
0
        assert(meta);
8165
8166
0
        const uint64_t blob_file_number = meta->GetBlobFileNumber();
8167
8168
0
        edit.AddBlobFile(blob_file_number, meta->GetTotalBlobCount(),
8169
0
                         meta->GetTotalBlobBytes(), meta->GetChecksumMethod(),
8170
0
                         meta->GetChecksumValue());
8171
0
        if (meta->GetGarbageBlobCount() > 0) {
8172
0
          edit.AddBlobFileGarbage(blob_file_number, meta->GetGarbageBlobCount(),
8173
0
                                  meta->GetGarbageBlobBytes());
8174
0
        }
8175
0
      }
8176
8177
60.5k
      for (const uint64_t blob_file_number : vstorage->GetDroppedBlobFiles()) {
8178
0
        edit.AddDroppedBlobFile(blob_file_number);
8179
0
      }
8180
8181
60.5k
      const auto iter = curr_state.find(cfd->GetID());
8182
60.5k
      assert(iter != curr_state.end());
8183
60.5k
      uint64_t log_number = iter->second.log_number;
8184
60.5k
      edit.SetLogNumber(log_number);
8185
8186
60.5k
      if (cfd->GetID() == 0) {
8187
        // min_log_number_to_keep is for the whole db, not for specific column
8188
        // family. So it does not need to be set for every column family, just
8189
        // need to be set once. Since default CF can never be dropped, we set
8190
        // the min_log to the default CF here.
8191
44.8k
        uint64_t min_log = min_log_number_to_keep();
8192
44.8k
        if (min_log != 0) {
8193
29.2k
          edit.SetMinLogNumberToKeep(min_log);
8194
29.2k
        }
8195
44.8k
      }
8196
8197
60.5k
      const std::string& full_history_ts_low = iter->second.full_history_ts_low;
8198
60.5k
      if (!full_history_ts_low.empty()) {
8199
0
        edit.SetFullHistoryTsLow(full_history_ts_low);
8200
0
      }
8201
8202
60.5k
      edit.SetLastSequence(descriptor_last_sequence_);
8203
8204
60.5k
      const Comparator* ucmp = cfd->user_comparator();
8205
60.5k
      assert(ucmp);
8206
60.5k
      std::string record;
8207
60.5k
      if (!edit.EncodeTo(&record, ucmp->timestamp_size())) {
8208
0
        return Status::Corruption("Unable to Encode VersionEdit:" +
8209
0
                                  edit.DebugString(true));
8210
0
      }
8211
60.5k
      io_s = log->AddRecord(write_options, record);
8212
60.5k
      if (!io_s.ok()) {
8213
0
        return io_s;
8214
0
      }
8215
60.5k
    }
8216
60.5k
  }
8217
8218
44.8k
  if (options_file_manifest_state.has_committed_options_file_number) {
8219
0
    assert(log != nullptr);
8220
0
    VersionEdit edit;
8221
0
    edit.SetCommittedOptionsFileNumber(
8222
0
        options_file_manifest_state.committed_options_file_number);
8223
0
    std::string record;
8224
0
    if (!edit.EncodeTo(&record)) {
8225
0
      return Status::Corruption("Unable to Encode VersionEdit:" +
8226
0
                                edit.DebugString(true));
8227
0
    }
8228
0
    io_s = log->AddRecord(write_options, record);
8229
0
    if (!io_s.ok()) {
8230
0
      return io_s;
8231
0
    }
8232
0
  }
8233
8234
  // Record the approximate compacted manifest size so it's available at
8235
  // recovery time for TuneMaxManifestFileSize(). This record must come last
8236
  // in WriteCurrentStateToManifest for an accurate size estimate.
8237
44.8k
  {
8238
    // Include a rough estimate of this record's own size (~15 bytes for the
8239
    // VersionEdit payload + log record header).
8240
44.8k
    constexpr uint64_t kEstimatedRecordOverhead = 15;
8241
44.8k
    VersionEdit edit;
8242
44.8k
    edit.SetLastCompactedManifestFileSize(log->file()->GetFileSize() +
8243
44.8k
                                          kEstimatedRecordOverhead);
8244
44.8k
    std::string record;
8245
44.8k
    if (!edit.EncodeTo(&record)) {
8246
0
      return Status::Corruption("Unable to Encode VersionEdit:" +
8247
0
                                edit.DebugString(true));
8248
0
    }
8249
44.8k
    io_s = log->AddRecord(write_options, record);
8250
44.8k
    if (!io_s.ok()) {
8251
0
      return io_s;
8252
0
    }
8253
44.8k
  }
8254
8255
44.8k
  return Status::OK();
8256
44.8k
}
8257
8258
// TODO(aekmekji): in CompactionJob::GenSubcompactionBoundaries(), this
8259
// function is called repeatedly with consecutive pairs of slices. For example
8260
// if the slice list is [a, b, c, d] this function is called with arguments
8261
// (a,b) then (b,c) then (c,d). Knowing this, an optimization is possible
8262
// where we avoid doing binary search for the keys b and c twice and instead
8263
// somehow maintain state of where they first appear in the files.
8264
std::optional<uint64_t> VersionSet::BlobSizeAccounting::GetReferencedBlobBytes(
8265
0
    const FileMetaData& file) const {
8266
0
  if (file.referenced_blob_bytes.has_value()) {
8267
0
    return file.referenced_blob_bytes;
8268
0
  }
8269
0
  const auto captured = references.find(file.fd.GetNumber());
8270
0
  assert(captured != references.end());
8271
0
  return captured == references.end() ? std::nullopt : captured->second;
8272
0
}
8273
8274
uint64_t VersionSet::ApproximateSize(const SizeApproximationOptions& options,
8275
                                     const ReadOptions& read_options,
8276
                                     Version* v, const Slice& start,
8277
                                     const Slice& end, int start_level,
8278
                                     int end_level, TableReaderCaller caller,
8279
93.9k
                                     BlobSizeAccounting* blob_accounting) {
8280
93.9k
  const auto& icmp = v->cfd_->internal_comparator();
8281
8282
93.9k
  if (blob_accounting != nullptr) {
8283
0
    blob_accounting->referenced_blob_bytes = 0;
8284
0
    blob_accounting->unknown_sst_bytes = 0;
8285
0
  }
8286
8287
93.9k
  const auto get_referenced_blob_bytes =
8288
93.9k
      [&](const FdWithKeyRange& file) -> std::optional<uint64_t> {
8289
0
    assert(blob_accounting != nullptr);
8290
0
    assert(file.file_metadata != nullptr);
8291
0
    return blob_accounting->GetReferencedBlobBytes(*file.file_metadata);
8292
0
  };
8293
8294
93.9k
  auto account_file = [&](const FdWithKeyRange& file, uint64_t sst_bytes) {
8295
0
    if (blob_accounting == nullptr) {
8296
0
      return;
8297
0
    }
8298
0
    if (icmp.Compare(file.largest_key, start) <= 0 ||
8299
0
        icmp.Compare(file.smallest_key, end) > 0) {
8300
0
      return;
8301
0
    }
8302
0
    const std::optional<uint64_t> file_referenced_blob_bytes =
8303
0
        get_referenced_blob_bytes(file);
8304
0
    if (!file_referenced_blob_bytes.has_value()) {
8305
0
      blob_accounting->unknown_sst_bytes += sst_bytes;
8306
0
      return;
8307
0
    }
8308
0
    if (sst_bytes == 0) {
8309
0
      return;
8310
0
    }
8311
0
    const uint64_t file_size = file.fd.GetFileSize();
8312
0
    if (sst_bytes == file_size) {
8313
0
      blob_accounting->referenced_blob_bytes += *file_referenced_blob_bytes;
8314
0
    } else if (file_size > 0) {
8315
0
      blob_accounting->referenced_blob_bytes += static_cast<uint64_t>(
8316
0
          static_cast<double>(*file_referenced_blob_bytes) *
8317
0
          static_cast<double>(sst_bytes) / static_cast<double>(file_size));
8318
0
    }
8319
0
  };
8320
8321
  // pre-condition
8322
93.9k
  assert(icmp.Compare(start, end) <= 0);
8323
8324
93.9k
  uint64_t total_full_size = 0;
8325
93.9k
  const auto* vstorage = v->storage_info();
8326
93.9k
  const int num_non_empty_levels = vstorage->num_non_empty_levels();
8327
93.9k
  end_level = (end_level == -1) ? num_non_empty_levels
8328
93.9k
                                : std::min(end_level, num_non_empty_levels);
8329
93.9k
  if (end_level <= start_level) {
8330
93.9k
    return 0;
8331
93.9k
  }
8332
8333
  // Outline of the optimization that uses options.files_size_error_margin.
8334
  // When approximating the files total size that is used to store a keys
8335
  // range, we first sum up the sizes of the files that fully fall into the
8336
  // range. Then we sum up the sizes of all the files that may intersect with
8337
  // the range (this includes all files in L0 as well). Then, if
8338
  // total_intersecting_size is smaller than total_full_size *
8339
  // options.files_size_error_margin - we can infer that the intersecting
8340
  // files have a sufficiently negligible contribution to the total size, and
8341
  // we can approximate the storage required for the keys in range as just
8342
  // half of the intersecting_files_size. E.g., if the value of
8343
  // files_size_error_margin is 0.1, then the error of the approximation is
8344
  // limited to only ~10% of the total size of files that fully fall into the
8345
  // keys range. In such case, this helps to avoid a costly process of binary
8346
  // searching the intersecting files that is required only for a more precise
8347
  // calculation of the total size.
8348
8349
0
  autovector<FdWithKeyRange*, 32> first_files;
8350
0
  autovector<FdWithKeyRange*, 16> last_files;
8351
8352
  // scan all the levels
8353
0
  for (int level = start_level; level < end_level; ++level) {
8354
0
    const LevelFilesBrief& files_brief = vstorage->LevelFilesBrief(level);
8355
0
    if (files_brief.num_files == 0) {
8356
      // empty level, skip exploration
8357
0
      continue;
8358
0
    }
8359
8360
0
    if (level == 0) {
8361
      // level 0 files are not in sorted order, we need to iterate through
8362
      // the list to compute the total bytes that require scanning,
8363
      // so handle the case explicitly (similarly to first_files case)
8364
0
      for (size_t i = 0; i < files_brief.num_files; i++) {
8365
0
        first_files.push_back(&files_brief.files[i]);
8366
0
      }
8367
0
      continue;
8368
0
    }
8369
8370
0
    assert(level > 0);
8371
0
    assert(files_brief.num_files > 0);
8372
8373
    // identify the file position for start key
8374
0
    const int idx_start =
8375
0
        FindFileInRange(icmp, files_brief, start, 0,
8376
0
                        static_cast<uint32_t>(files_brief.num_files - 1));
8377
0
    assert(static_cast<size_t>(idx_start) < files_brief.num_files);
8378
8379
    // identify the file position for end key
8380
0
    int idx_end = idx_start;
8381
0
    if (icmp.Compare(files_brief.files[idx_end].largest_key, end) < 0) {
8382
0
      idx_end =
8383
0
          FindFileInRange(icmp, files_brief, end, idx_start,
8384
0
                          static_cast<uint32_t>(files_brief.num_files - 1));
8385
0
    }
8386
0
    assert(idx_end >= idx_start &&
8387
0
           static_cast<size_t>(idx_end) < files_brief.num_files);
8388
8389
    // scan all files from the starting index to the ending index
8390
    // (inferred from the sorted order)
8391
8392
    // first scan all the intermediate full files (excluding first and last)
8393
0
    for (int i = idx_start + 1; i < idx_end; ++i) {
8394
0
      uint64_t file_size = files_brief.files[i].fd.GetFileSize();
8395
      // The entire file falls into the range, so we can just take its size.
8396
0
      assert(file_size == ApproximateSize(read_options, v, files_brief.files[i],
8397
0
                                          start, end, caller));
8398
0
      total_full_size += file_size;
8399
0
      account_file(files_brief.files[i], file_size);
8400
0
    }
8401
8402
    // save the first and the last files (which may be the same file), so we
8403
    // can scan them later.
8404
0
    first_files.push_back(&files_brief.files[idx_start]);
8405
0
    if (idx_start != idx_end) {
8406
      // we need to estimate size for both files, only if they are different
8407
0
      last_files.push_back(&files_brief.files[idx_end]);
8408
0
    }
8409
0
  }
8410
8411
  // The sum of all file sizes that intersect the [start, end] keys range.
8412
0
  uint64_t total_intersecting_size = 0;
8413
0
  uint64_t intersecting_referenced_blob_bytes = 0;
8414
0
  uint64_t intersecting_unknown_sst_bytes = 0;
8415
0
  auto account_intersecting_file = [&](const FdWithKeyRange& file) {
8416
0
    const uint64_t file_size = file.fd.GetFileSize();
8417
0
    total_intersecting_size += file_size;
8418
0
    if (blob_accounting == nullptr) {
8419
0
      return;
8420
0
    }
8421
0
    if (icmp.Compare(file.largest_key, start) <= 0 ||
8422
0
        icmp.Compare(file.smallest_key, end) > 0) {
8423
0
      return;
8424
0
    }
8425
0
    const std::optional<uint64_t> file_referenced_blob_bytes =
8426
0
        get_referenced_blob_bytes(file);
8427
0
    if (file_referenced_blob_bytes.has_value()) {
8428
0
      intersecting_referenced_blob_bytes += *file_referenced_blob_bytes;
8429
0
    } else {
8430
0
      intersecting_unknown_sst_bytes += file_size;
8431
0
    }
8432
0
  };
8433
0
  for (const auto* file_ptr : first_files) {
8434
0
    account_intersecting_file(*file_ptr);
8435
0
  }
8436
0
  for (const auto* file_ptr : last_files) {
8437
0
    account_intersecting_file(*file_ptr);
8438
0
  }
8439
0
  TEST_SYNC_POINT_CALLBACK(
8440
0
      "VersionSet::ApproximateSize:IntersectingReferencedBlobBytes",
8441
0
      &intersecting_referenced_blob_bytes);
8442
8443
  // Now scan all the first & last files at each level, and estimate their
8444
  // size. If the total_intersecting_size is less than X% of the
8445
  // total_full_size - we want to approximate the result in order to avoid the
8446
  // costly binary search inside ApproximateSize. We use half of file size as
8447
  // an approximation below.
8448
8449
0
  const double margin = options.files_size_error_margin;
8450
0
  if (margin > 0 && total_intersecting_size <
8451
0
                        static_cast<uint64_t>(total_full_size * margin)) {
8452
0
    total_full_size += total_intersecting_size / 2;
8453
0
    if (blob_accounting != nullptr) {
8454
0
      blob_accounting->referenced_blob_bytes +=
8455
0
          intersecting_referenced_blob_bytes / 2;
8456
0
      blob_accounting->unknown_sst_bytes += intersecting_unknown_sst_bytes / 2;
8457
0
    }
8458
0
  } else {
8459
    // Estimate for all the first files (might also be last files), at each
8460
    // level
8461
0
    for (const auto file_ptr : first_files) {
8462
0
      const uint64_t file_size =
8463
0
          ApproximateSize(read_options, v, *file_ptr, start, end, caller);
8464
0
      total_full_size += file_size;
8465
0
      account_file(*file_ptr, file_size);
8466
0
    }
8467
8468
    // Estimate for all the last files, at each level
8469
0
    for (const auto file_ptr : last_files) {
8470
      // We could use ApproximateSize here, but calling ApproximateOffsetOf
8471
      // directly is just more efficient.
8472
0
      const uint64_t file_size =
8473
0
          ApproximateOffsetOf(read_options, v, *file_ptr, end, caller);
8474
0
      total_full_size += file_size;
8475
0
      account_file(*file_ptr, file_size);
8476
0
    }
8477
0
  }
8478
8479
0
  return total_full_size;
8480
93.9k
}
8481
8482
uint64_t VersionSet::ApproximateOffsetOf(const ReadOptions& read_options,
8483
                                         Version* v, const FdWithKeyRange& f,
8484
                                         const Slice& key,
8485
0
                                         TableReaderCaller caller) {
8486
  // pre-condition
8487
0
  assert(v);
8488
0
  const auto& icmp = v->cfd_->internal_comparator();
8489
8490
0
  uint64_t result = 0;
8491
0
  if (icmp.Compare(f.largest_key, key) <= 0) {
8492
    // Entire file is before "key", so just add the file size
8493
0
    result = f.fd.GetFileSize();
8494
0
  } else if (icmp.Compare(f.smallest_key, key) > 0) {
8495
    // Entire file is after "key", so ignore
8496
0
    result = 0;
8497
0
  } else {
8498
    // "key" falls in the range for this table.  Add the
8499
    // approximate offset of "key" within the table.
8500
0
    TableCache* table_cache = v->cfd_->table_cache();
8501
0
    const MutableCFOptions& cf_opts = v->GetMutableCFOptions();
8502
0
    if (table_cache != nullptr) {
8503
0
      result = table_cache->ApproximateOffsetOf(
8504
0
          read_options, key, *f.file_metadata, caller, icmp, cf_opts);
8505
0
    }
8506
0
  }
8507
0
  return result;
8508
0
}
8509
8510
uint64_t VersionSet::ApproximateSize(const ReadOptions& read_options,
8511
                                     Version* v, const FdWithKeyRange& f,
8512
                                     const Slice& start, const Slice& end,
8513
0
                                     TableReaderCaller caller) {
8514
  // pre-condition
8515
0
  assert(v);
8516
0
  const auto& icmp = v->cfd_->internal_comparator();
8517
0
  assert(icmp.Compare(start, end) <= 0);
8518
8519
0
  if (icmp.Compare(f.largest_key, start) <= 0 ||
8520
0
      icmp.Compare(f.smallest_key, end) > 0) {
8521
    // Entire file is before or after the start/end keys range
8522
0
    return 0;
8523
0
  }
8524
8525
0
  if (icmp.Compare(f.smallest_key, start) >= 0) {
8526
    // Start of the range is before the file start - approximate by end offset
8527
0
    return ApproximateOffsetOf(read_options, v, f, end, caller);
8528
0
  }
8529
8530
0
  if (icmp.Compare(f.largest_key, end) < 0) {
8531
    // End of the range is after the file end - approximate by subtracting
8532
    // start offset from the file size
8533
0
    uint64_t start_offset =
8534
0
        ApproximateOffsetOf(read_options, v, f, start, caller);
8535
0
    assert(f.fd.GetFileSize() >= start_offset);
8536
0
    return f.fd.GetFileSize() - start_offset;
8537
0
  }
8538
8539
  // The interval falls entirely in the range for this file.
8540
0
  TableCache* table_cache = v->cfd_->table_cache();
8541
0
  if (table_cache == nullptr) {
8542
0
    return 0;
8543
0
  }
8544
0
  const MutableCFOptions& cf_opts = v->GetMutableCFOptions();
8545
0
  return table_cache->ApproximateSize(read_options, start, end,
8546
0
                                      *f.file_metadata, caller, icmp, cf_opts);
8547
0
}
8548
8549
void VersionSet::RemoveLiveFiles(
8550
    std::vector<ObsoleteFileInfo>& sst_delete_candidates,
8551
23.4k
    std::vector<ObsoleteBlobFileInfo>& blob_delete_candidates) const {
8552
23.4k
  assert(column_family_set_);
8553
24.5k
  for (auto cfd : *column_family_set_) {
8554
24.5k
    assert(cfd);
8555
24.5k
    if (!cfd->initialized()) {
8556
0
      continue;
8557
0
    }
8558
8559
24.5k
    auto* current = cfd->current();
8560
24.5k
    bool found_current = false;
8561
8562
24.5k
    Version* const dummy_versions = cfd->dummy_versions();
8563
24.5k
    assert(dummy_versions);
8564
8565
49.3k
    for (Version* v = dummy_versions->next_; v != dummy_versions;
8566
24.7k
         v = v->next_) {
8567
24.7k
      v->RemoveLiveFiles(sst_delete_candidates, blob_delete_candidates);
8568
24.7k
      if (v == current) {
8569
24.5k
        found_current = true;
8570
24.5k
      }
8571
24.7k
    }
8572
8573
24.5k
    if (!found_current && current != nullptr) {
8574
      // Should never happen unless it is a bug.
8575
0
      assert(false);
8576
0
      current->RemoveLiveFiles(sst_delete_candidates, blob_delete_candidates);
8577
0
    }
8578
24.5k
  }
8579
23.4k
}
8580
8581
void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_table_files,
8582
89.7k
                              std::vector<uint64_t>* live_blob_files) const {
8583
89.7k
  assert(live_table_files);
8584
89.7k
  assert(live_blob_files);
8585
8586
  // pre-calculate space requirement
8587
89.7k
  size_t total_table_files = 0;
8588
89.7k
  size_t total_blob_files = 0;
8589
8590
89.7k
  assert(column_family_set_);
8591
121k
  for (auto cfd : *column_family_set_) {
8592
121k
    assert(cfd);
8593
8594
121k
    if (!cfd->initialized()) {
8595
0
      continue;
8596
0
    }
8597
8598
121k
    Version* const dummy_versions = cfd->dummy_versions();
8599
121k
    assert(dummy_versions);
8600
8601
242k
    for (Version* v = dummy_versions->next_; v != dummy_versions;
8602
121k
         v = v->next_) {
8603
121k
      assert(v);
8604
8605
121k
      const auto* vstorage = v->storage_info();
8606
121k
      assert(vstorage);
8607
8608
968k
      for (int level = 0; level < vstorage->num_levels(); ++level) {
8609
847k
        total_table_files += vstorage->LevelFiles(level).size();
8610
847k
      }
8611
8612
121k
      total_blob_files += vstorage->GetBlobFiles().size();
8613
121k
    }
8614
121k
  }
8615
8616
  // just one time extension to the right size
8617
89.7k
  live_table_files->reserve(live_table_files->size() + total_table_files);
8618
89.7k
  live_blob_files->reserve(live_blob_files->size() + total_blob_files);
8619
8620
89.7k
  assert(column_family_set_);
8621
121k
  for (auto cfd : *column_family_set_) {
8622
121k
    assert(cfd);
8623
121k
    if (!cfd->initialized()) {
8624
0
      continue;
8625
0
    }
8626
8627
121k
    auto* current = cfd->current();
8628
121k
    bool found_current = false;
8629
8630
121k
    Version* const dummy_versions = cfd->dummy_versions();
8631
121k
    assert(dummy_versions);
8632
8633
242k
    for (Version* v = dummy_versions->next_; v != dummy_versions;
8634
121k
         v = v->next_) {
8635
121k
      v->AddLiveFiles(live_table_files, live_blob_files);
8636
121k
      if (v == current) {
8637
121k
        found_current = true;
8638
121k
      }
8639
121k
    }
8640
8641
121k
    if (!found_current && current != nullptr) {
8642
      // Should never happen unless it is a bug.
8643
0
      assert(false);
8644
0
      current->AddLiveFiles(live_table_files, live_blob_files);
8645
0
    }
8646
121k
  }
8647
89.7k
}
8648
8649
InternalIterator* VersionSet::MakeInputIterator(
8650
    const ReadOptions& read_options, const Compaction* c,
8651
    RangeDelAggregator* range_del_agg,
8652
    const FileOptions& file_options_compactions,
8653
    const std::optional<const Slice>& start,
8654
2.77k
    const std::optional<const Slice>& end, bool open_ephemeral_table_reader) {
8655
2.77k
  auto cfd = c->column_family_data();
8656
  // Level-0 files have to be merged together.  For other levels,
8657
  // we will make a concatenating iterator per level.
8658
  // TODO(opt): use concatenating iterator for level-0 if there is no overlap
8659
2.77k
  const size_t space = (c->level() == 0 ? c->input_levels(0)->num_files +
8660
2.36k
                                              c->num_input_levels() - 1
8661
2.77k
                                        : c->num_input_levels());
8662
2.77k
  InternalIterator** list = new InternalIterator*[space];
8663
  // First item in the pair is a pointer to range tombstones.
8664
  // Second item is a pointer to a member of a LevelIterator,
8665
  // that will be initialized to where CompactionMergingIterator stores
8666
  // pointer to its range tombstones. This is used by LevelIterator
8667
  // to update pointer to range tombstones as it traverse different SST files.
8668
2.77k
  std::vector<std::pair<std::unique_ptr<TruncatedRangeDelIterator>,
8669
2.77k
                        std::unique_ptr<TruncatedRangeDelIterator>**>>
8670
2.77k
      range_tombstones;
8671
2.77k
  size_t num = 0;
8672
2.77k
  [[maybe_unused]] size_t num_input_files = 0;
8673
2.77k
  const TableCacheOpenOptions table_cache_open_options =
8674
2.77k
      GetCompactionTableCacheOpenOptions(open_ephemeral_table_reader);
8675
7.19k
  for (size_t which = 0; which < c->num_input_levels(); which++) {
8676
4.41k
    const LevelFilesBrief* flevel = c->input_levels(which);
8677
4.41k
    num_input_files += flevel->num_files;
8678
4.41k
    if (flevel->num_files != 0) {
8679
4.41k
      if (c->level(which) == 0) {
8680
9.52k
        for (size_t i = 0; i < flevel->num_files; i++) {
8681
7.15k
          const FileMetaData& fmd = *flevel->files[i].file_metadata;
8682
7.15k
          if (start.has_value() &&
8683
0
              cfd->user_comparator()->CompareWithoutTimestamp(
8684
0
                  *start, fmd.largest.user_key()) > 0) {
8685
0
            continue;
8686
0
          }
8687
          // We should be able to filter out the case where the end key
8688
          // equals to the end boundary, since the end key is exclusive.
8689
          // We try to be extra safe here.
8690
7.15k
          if (end.has_value() &&
8691
0
              cfd->user_comparator()->CompareWithoutTimestamp(
8692
0
                  *end, fmd.smallest.user_key()) < 0) {
8693
0
            continue;
8694
0
          }
8695
7.15k
          std::unique_ptr<TruncatedRangeDelIterator> range_tombstone_iter =
8696
7.15k
              nullptr;
8697
7.15k
          list[num++] = cfd->table_cache()->NewIterator(
8698
7.15k
              read_options, file_options_compactions,
8699
7.15k
              cfd->internal_comparator(), fmd, range_del_agg,
8700
7.15k
              c->mutable_cf_options(),
8701
7.15k
              /*table_reader_ptr=*/nullptr,
8702
7.15k
              /*file_read_hist=*/nullptr, TableReaderCaller::kCompaction,
8703
7.15k
              /*arena=*/nullptr,
8704
7.15k
              /*skip_filters=*/false,
8705
7.15k
              /*level=*/static_cast<int>(c->level(which)),
8706
7.15k
              MaxFileSizeForL0MetaPin(c->mutable_cf_options()),
8707
7.15k
              /*smallest_compaction_key=*/nullptr,
8708
7.15k
              /*largest_compaction_key=*/nullptr,
8709
7.15k
              /*allow_unprepared_value=*/false, /*seek_target=*/nullptr,
8710
7.15k
              /*prefix_filtered_table=*/nullptr,
8711
7.15k
              /*range_del_read_seqno=*/nullptr,
8712
7.15k
              /*range_del_iter=*/&range_tombstone_iter,
8713
7.15k
              /*maybe_pin_table_handle=*/false,
8714
7.15k
              /*file_open_metadata=*/nullptr, table_cache_open_options);
8715
7.15k
          range_tombstones.emplace_back(std::move(range_tombstone_iter),
8716
7.15k
                                        nullptr);
8717
7.15k
        }
8718
2.36k
      } else {
8719
        // Create concatenating iterator for the files from this level
8720
2.05k
        std::unique_ptr<TruncatedRangeDelIterator>** tombstone_iter_ptr =
8721
2.05k
            nullptr;
8722
2.05k
        list[num++] = new LevelIterator(
8723
2.05k
            cfd->table_cache(), read_options, file_options_compactions,
8724
2.05k
            cfd->internal_comparator(), flevel, c->mutable_cf_options(),
8725
2.05k
            /*no per level latency histogram=*/nullptr,
8726
2.05k
            TableReaderCaller::kCompaction, /*skip_filters=*/false,
8727
2.05k
            /*level=*/static_cast<int>(c->level(which)), range_del_agg,
8728
2.05k
            c->boundaries(which), false, &tombstone_iter_ptr,
8729
2.05k
            kMaxSequenceNumber, db_options_->statistics.get(), clock_,
8730
2.05k
            open_ephemeral_table_reader);
8731
2.05k
        range_tombstones.emplace_back(nullptr, tombstone_iter_ptr);
8732
2.05k
      }
8733
4.41k
    }
8734
4.41k
  }
8735
2.77k
  TEST_SYNC_POINT_CALLBACK(
8736
2.77k
      "VersionSet::MakeInputIterator:NewCompactionMergingIterator",
8737
2.77k
      &num_input_files);
8738
2.77k
  assert(num <= space);
8739
2.77k
  InternalIterator* result = NewCompactionMergingIterator(
8740
2.77k
      &c->column_family_data()->internal_comparator(), list,
8741
2.77k
      static_cast<int>(num), range_tombstones, /*arena=*/nullptr,
8742
2.77k
      c->column_family_data()->internal_stats());
8743
2.77k
  delete[] list;
8744
2.77k
  return result;
8745
2.77k
}
8746
8747
Status VersionSet::GetMetadataForFile(uint64_t number, int* filelevel,
8748
                                      FileMetaData** meta,
8749
0
                                      ColumnFamilyData** cfd) {
8750
0
  for (auto cfd_iter : *column_family_set_) {
8751
0
    if (!cfd_iter->initialized()) {
8752
0
      continue;
8753
0
    }
8754
0
    Version* version = cfd_iter->current();
8755
0
    const auto* vstorage = version->storage_info();
8756
0
    for (int level = 0; level < vstorage->num_levels(); level++) {
8757
0
      for (const auto& file : vstorage->LevelFiles(level)) {
8758
0
        if (file->fd.GetNumber() == number) {
8759
0
          *meta = file;
8760
0
          *filelevel = level;
8761
0
          *cfd = cfd_iter;
8762
0
          return Status::OK();
8763
0
        }
8764
0
      }
8765
0
    }
8766
0
  }
8767
0
  return Status::NotFound("File not present in any level");
8768
0
}
8769
8770
0
void VersionSet::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
8771
0
  if (!metadata) {
8772
0
    return;
8773
0
  }
8774
0
  assert(metadata);
8775
0
  size_t count = 0;
8776
0
  for (auto cfd : *column_family_set_) {
8777
0
    if (cfd->IsDropped() || !cfd->initialized()) {
8778
0
      continue;
8779
0
    }
8780
0
    for (int level = 0; level < cfd->NumberLevels(); level++) {
8781
0
      count += cfd->current()->storage_info()->LevelFiles(level).size();
8782
0
    }
8783
0
  }
8784
0
  metadata->reserve(count);
8785
0
  for (auto cfd : *column_family_set_) {
8786
0
    if (cfd->IsDropped() || !cfd->initialized()) {
8787
0
      continue;
8788
0
    }
8789
0
    for (int level = 0; level < cfd->NumberLevels(); level++) {
8790
0
      for (const auto& file :
8791
0
           cfd->current()->storage_info()->LevelFiles(level)) {
8792
0
        LiveFileMetaData filemetadata;
8793
0
        filemetadata.column_family_name = cfd->GetName();
8794
0
        uint32_t path_id = file->fd.GetPathId();
8795
0
        if (path_id < cfd->ioptions().cf_paths.size()) {
8796
0
          filemetadata.db_path = cfd->ioptions().cf_paths[path_id].path;
8797
0
        } else {
8798
0
          assert(!cfd->ioptions().cf_paths.empty());
8799
0
          filemetadata.db_path = cfd->ioptions().cf_paths.back().path;
8800
0
        }
8801
0
        filemetadata.directory = filemetadata.db_path;
8802
0
        const uint64_t file_number = file->fd.GetNumber();
8803
0
        filemetadata.name = MakeTableFileName("", file_number);
8804
0
        filemetadata.relative_filename = filemetadata.name.substr(1);
8805
0
        filemetadata.file_number = file_number;
8806
0
        filemetadata.level = level;
8807
0
        filemetadata.size = file->fd.GetFileSize();
8808
0
        filemetadata.smallestkey = file->smallest.user_key().ToString();
8809
0
        filemetadata.largestkey = file->largest.user_key().ToString();
8810
0
        filemetadata.smallest_seqno = file->fd.smallest_seqno;
8811
0
        filemetadata.largest_seqno = file->fd.largest_seqno;
8812
0
        filemetadata.num_reads_sampled =
8813
0
            file->stats.num_reads_sampled.load(std::memory_order_relaxed);
8814
0
        filemetadata.being_compacted = file->being_compacted;
8815
0
        filemetadata.num_entries = file->num_entries;
8816
0
        filemetadata.num_deletions = file->num_deletions;
8817
0
        filemetadata.oldest_blob_file_number = file->oldest_blob_file_number;
8818
0
        filemetadata.file_checksum = file->file_checksum;
8819
0
        filemetadata.file_checksum_func_name = file->file_checksum_func_name;
8820
0
        filemetadata.temperature = file->temperature;
8821
0
        filemetadata.oldest_ancestor_time = file->TryGetOldestAncestorTime();
8822
0
        filemetadata.file_creation_time = file->TryGetFileCreationTime();
8823
0
        filemetadata.db_ownership_start_time = file->db_ownership_start_time;
8824
0
        filemetadata.data_file_provenance = file->data_file_provenance;
8825
0
        filemetadata.epoch_number = file->epoch_number;
8826
0
        metadata->push_back(filemetadata);
8827
0
      }
8828
0
    }
8829
0
  }
8830
0
}
8831
8832
void VersionSet::GetObsoleteFiles(std::vector<ObsoleteFileInfo>* files,
8833
                                  std::vector<ObsoleteBlobFileInfo>* blob_files,
8834
                                  std::vector<std::string>* manifest_filenames,
8835
113k
                                  uint64_t min_pending_output) {
8836
113k
  assert(files);
8837
113k
  assert(blob_files);
8838
113k
  assert(manifest_filenames);
8839
113k
  assert(files->empty());
8840
113k
  assert(blob_files->empty());
8841
113k
  assert(manifest_filenames->empty());
8842
8843
113k
  std::vector<ObsoleteFileInfo> pending_files;
8844
113k
  for (auto& f : obsolete_files_) {
8845
11.9k
    if (f.metadata->fd.GetNumber() < min_pending_output) {
8846
11.9k
      files->emplace_back(std::move(f));
8847
11.9k
    } else {
8848
0
      pending_files.emplace_back(std::move(f));
8849
0
    }
8850
11.9k
  }
8851
113k
  obsolete_files_.swap(pending_files);
8852
8853
113k
  std::vector<ObsoleteBlobFileInfo> pending_blob_files;
8854
113k
  for (auto& blob_file : obsolete_blob_files_) {
8855
0
    if (blob_file.GetBlobFileNumber() < min_pending_output) {
8856
0
      blob_files->emplace_back(std::move(blob_file));
8857
0
    } else {
8858
0
      pending_blob_files.emplace_back(std::move(blob_file));
8859
0
    }
8860
0
  }
8861
113k
  obsolete_blob_files_.swap(pending_blob_files);
8862
8863
113k
  obsolete_manifests_.swap(*manifest_filenames);
8864
113k
}
8865
8866
0
uint64_t VersionSet::GetObsoleteSstFilesSize() const {
8867
0
  uint64_t ret = 0;
8868
0
  for (auto& f : obsolete_files_) {
8869
0
    if (f.metadata != nullptr) {
8870
0
      ret += f.metadata->fd.GetFileSize();
8871
0
    }
8872
0
  }
8873
0
  return ret;
8874
0
}
8875
8876
ColumnFamilyData* VersionSet::CreateColumnFamily(
8877
    const ColumnFamilyOptions& cf_options, const ReadOptions& read_options,
8878
87.5k
    const VersionEdit* edit, bool read_only, bool options_are_sanitized) {
8879
87.5k
  assert(edit->IsColumnFamilyAdd());
8880
  // Unchanging LSM tree implies no writes to the CF
8881
87.5k
  assert(!unchanging_ || read_only);
8882
8883
87.5k
  MutableCFOptions dummy_cf_options;
8884
87.5k
  Version* dummy_versions =
8885
87.5k
      new Version(nullptr, this, file_options_, dummy_cf_options, io_tracer_);
8886
  // Ref() dummy version once so that later we can call Unref() to delete it
8887
  // by avoiding calling "delete" explicitly (~Version is private)
8888
87.5k
  dummy_versions->Ref();
8889
87.5k
  auto new_cfd = column_family_set_->CreateColumnFamily(
8890
87.5k
      edit->GetColumnFamilyName(), edit->GetColumnFamily(), dummy_versions,
8891
87.5k
      cf_options, read_only, options_are_sanitized);
8892
8893
87.5k
  Version* v = new Version(new_cfd, this, file_options_,
8894
87.5k
                           new_cfd->GetLatestMutableCFOptions(), io_tracer_,
8895
87.5k
                           current_version_number_++);
8896
8897
87.5k
  constexpr bool update_stats = false;
8898
8899
87.5k
  v->PrepareAppend(read_options, update_stats);
8900
8901
87.5k
  AppendVersion(new_cfd, v);
8902
  // GetLatestMutableCFOptions() is safe here without mutex since the
8903
  // cfd is not available to client
8904
87.5k
  new_cfd->CreateNewMemtable(LastSequence());
8905
87.5k
  new_cfd->SetLogNumber(edit->GetLogNumber());
8906
87.5k
  return new_cfd;
8907
87.5k
}
8908
8909
0
uint64_t VersionSet::GetNumLiveVersions(Version* dummy_versions) {
8910
0
  uint64_t count = 0;
8911
0
  for (Version* v = dummy_versions->next_; v != dummy_versions; v = v->next_) {
8912
0
    count++;
8913
0
  }
8914
0
  return count;
8915
0
}
8916
8917
0
uint64_t VersionSet::GetTotalSstFilesSize(Version* dummy_versions) {
8918
0
  std::unordered_set<uint64_t> unique_files;
8919
0
  uint64_t total_files_size = 0;
8920
0
  for (Version* v = dummy_versions->next_; v != dummy_versions; v = v->next_) {
8921
0
    VersionStorageInfo* storage_info = v->storage_info();
8922
0
    for (int level = 0; level < storage_info->num_levels_; level++) {
8923
0
      for (const auto& file_meta : storage_info->LevelFiles(level)) {
8924
0
        if (unique_files.find(file_meta->fd.packed_number_and_path_id) ==
8925
0
            unique_files.end()) {
8926
0
          unique_files.insert(file_meta->fd.packed_number_and_path_id);
8927
0
          total_files_size += file_meta->fd.GetFileSize();
8928
0
        }
8929
0
      }
8930
0
    }
8931
0
  }
8932
0
  return total_files_size;
8933
0
}
8934
8935
0
uint64_t VersionSet::GetTotalBlobFileSize(Version* dummy_versions) {
8936
0
  std::unordered_set<uint64_t> unique_blob_files;
8937
8938
0
  uint64_t all_versions_blob_file_size = 0;
8939
8940
0
  for (auto* v = dummy_versions->next_; v != dummy_versions; v = v->next_) {
8941
    // iterate all the versions
8942
0
    const auto* vstorage = v->storage_info();
8943
0
    assert(vstorage);
8944
8945
0
    const auto& blob_files = vstorage->GetBlobFiles();
8946
8947
0
    for (const auto& meta : blob_files) {
8948
0
      assert(meta);
8949
8950
0
      const uint64_t blob_file_number = meta->GetBlobFileNumber();
8951
8952
0
      if (unique_blob_files.find(blob_file_number) == unique_blob_files.end()) {
8953
        // find Blob file that has not been counted
8954
0
        unique_blob_files.insert(blob_file_number);
8955
0
        all_versions_blob_file_size += meta->GetBlobFileSize();
8956
0
      }
8957
0
    }
8958
0
  }
8959
8960
0
  return all_versions_blob_file_size;
8961
0
}
8962
8963
Status VersionSet::VerifyFileMetadata(const ReadOptions& read_options,
8964
                                      ColumnFamilyData* cfd,
8965
                                      const std::string& fpath, int level,
8966
0
                                      const FileMetaData& meta) {
8967
0
  uint64_t fsize = 0;
8968
0
  Status status = fs_->GetFileSize(fpath, IOOptions(), &fsize, nullptr);
8969
0
  if (status.ok()) {
8970
0
    if (fsize != meta.fd.GetFileSize()) {
8971
0
      status = Status::Corruption("File size mismatch: " + fpath);
8972
0
    }
8973
0
  }
8974
0
  if (status.ok() && db_options_->verify_sst_unique_id_in_manifest) {
8975
0
    assert(cfd);
8976
0
    TableCache* table_cache = cfd->table_cache();
8977
0
    assert(table_cache);
8978
8979
0
    const auto& cf_opts = cfd->GetLatestMutableCFOptions();
8980
0
    size_t max_sz_for_l0_meta_pin = MaxFileSizeForL0MetaPin(cf_opts);
8981
8982
0
    const FileOptions& file_opts = file_options();
8983
8984
0
    Version* version = cfd->current();
8985
0
    assert(version);
8986
0
    VersionStorageInfo& storage_info = version->storage_info_;
8987
0
    const InternalKeyComparator* icmp = storage_info.InternalComparator();
8988
0
    assert(icmp);
8989
8990
0
    InternalStats* internal_stats = cfd->internal_stats();
8991
8992
0
    TableCache::TypedHandle* handle = nullptr;
8993
0
    TableReader* table_reader = nullptr;
8994
0
    status = table_cache->FindTable(
8995
0
        read_options, file_opts, *icmp, meta, &handle, cf_opts, &table_reader,
8996
0
        /*no_io=*/false, internal_stats->GetFileReadHist(level), false, level,
8997
0
        /*prefetch_index_and_filter_in_cache*/ false, max_sz_for_l0_meta_pin,
8998
0
        meta.temperature);
8999
0
    if (handle) {
9000
0
      table_cache->get_cache().Release(handle);
9001
0
    }
9002
0
  }
9003
0
  return status;
9004
0
}
9005
9006
ReactiveVersionSet::ReactiveVersionSet(
9007
    const std::string& dbname, const ImmutableDBOptions* imm_db_options,
9008
    const MutableDBOptions& mutable_db_options,
9009
    const FileOptions& _file_options, Cache* table_cache,
9010
    WriteBufferManager* write_buffer_manager, WriteController* write_controller,
9011
    const std::shared_ptr<IOTracer>& io_tracer, const std::string& db_id,
9012
    const std::string& db_session_id, bool defer_sst_file_opening)
9013
0
    : VersionSet(dbname, imm_db_options, mutable_db_options, _file_options,
9014
0
                 table_cache, write_buffer_manager, write_controller,
9015
0
                 /*block_cache_tracer=*/nullptr, io_tracer, db_id,
9016
0
                 db_session_id, /*daily_offpeak_time_utc*/ "",
9017
0
                 /*error_handler=*/nullptr, /*unchanging=*/false),
9018
0
      defer_sst_file_opening_(defer_sst_file_opening) {}
9019
9020
0
ReactiveVersionSet::~ReactiveVersionSet() = default;
9021
9022
Status ReactiveVersionSet::Recover(
9023
    const std::vector<ColumnFamilyDescriptor>& column_families,
9024
    std::unique_ptr<log::FragmentBufferedReader>* manifest_reader,
9025
    std::unique_ptr<log::Reader::Reporter>* manifest_reporter,
9026
0
    std::unique_ptr<Status>* manifest_reader_status) {
9027
0
  assert(manifest_reader != nullptr);
9028
0
  assert(manifest_reporter != nullptr);
9029
0
  assert(manifest_reader_status != nullptr);
9030
9031
0
  manifest_reader_status->reset(new Status());
9032
0
  manifest_reporter->reset(new LogReporter());
9033
0
  static_cast_with_check<LogReporter>(manifest_reporter->get())->status =
9034
0
      manifest_reader_status->get();
9035
0
  Status s = MaybeSwitchManifest(manifest_reporter->get(), manifest_reader);
9036
0
  if (!s.ok()) {
9037
0
    return s;
9038
0
  }
9039
0
  log::Reader* reader = manifest_reader->get();
9040
0
  assert(reader);
9041
9042
0
  manifest_tailer_.reset(new ManifestTailer(
9043
0
      column_families, const_cast<ReactiveVersionSet*>(this), io_tracer_,
9044
0
      read_options_, trust_manifest_recovery_, defer_sst_file_opening_,
9045
0
      EpochNumberRequirement::kMightMissing));
9046
9047
0
  manifest_tailer_->Iterate(*reader, manifest_reader_status->get());
9048
9049
0
  s = manifest_tailer_->status();
9050
0
  if (s.ok()) {
9051
0
    RecoverEpochNumbers();
9052
0
  }
9053
0
  return s;
9054
0
}
9055
9056
Status ReactiveVersionSet::ReadAndApply(
9057
    InstrumentedMutex* mu,
9058
    std::unique_ptr<log::FragmentBufferedReader>* manifest_reader,
9059
    Status* manifest_read_status,
9060
    std::unordered_set<ColumnFamilyData*>* cfds_changed,
9061
0
    std::vector<std::string>* files_to_delete) {
9062
0
  assert(manifest_reader != nullptr);
9063
0
  assert(cfds_changed != nullptr);
9064
0
  mu->AssertHeld();
9065
9066
0
  Status s;
9067
0
  log::Reader* reader = manifest_reader->get();
9068
0
  assert(reader);
9069
0
  s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
9070
0
  if (!s.ok()) {
9071
0
    return s;
9072
0
  }
9073
0
  manifest_tailer_->Iterate(*(manifest_reader->get()), manifest_read_status);
9074
0
  s = manifest_tailer_->status();
9075
0
  if (s.ok()) {
9076
0
    *cfds_changed = std::move(manifest_tailer_->GetUpdatedColumnFamilies());
9077
0
  }
9078
0
  if (files_to_delete) {
9079
0
    *files_to_delete = manifest_tailer_->GetAndClearIntermediateFiles();
9080
0
  }
9081
9082
0
  return s;
9083
0
}
9084
9085
uint64_t ReactiveVersionSet::GetInstalledVersionLogNumber(
9086
0
    uint32_t cf_id) const {
9087
0
  assert(manifest_tailer_ != nullptr);
9088
0
  return manifest_tailer_->GetInstalledVersionLogNumber(cf_id);
9089
0
}
9090
9091
Status ReactiveVersionSet::MaybeSwitchManifest(
9092
    log::Reader::Reporter* reporter,
9093
0
    std::unique_ptr<log::FragmentBufferedReader>* manifest_reader) {
9094
0
  assert(manifest_reader != nullptr);
9095
0
  Status s;
9096
0
  std::string manifest_path;
9097
0
  s = GetCurrentManifestPath(dbname_, fs_.get(), /*is_retry=*/false,
9098
0
                             &manifest_path, &manifest_file_number_);
9099
0
  if (!s.ok()) {
9100
0
    return s;
9101
0
  }
9102
0
  std::unique_ptr<FSSequentialFile> manifest_file;
9103
0
  if (manifest_reader->get() != nullptr &&
9104
0
      manifest_reader->get()->file()->file_name() == manifest_path) {
9105
    // CURRENT points to the same MANIFEST as before, no need to switch
9106
    // MANIFEST.
9107
0
    return s;
9108
0
  }
9109
0
  assert(nullptr == manifest_reader->get() ||
9110
0
         manifest_reader->get()->file()->file_name() != manifest_path);
9111
0
  s = fs_->FileExists(manifest_path, IOOptions(), nullptr);
9112
0
  if (s.IsNotFound()) {
9113
0
    return Status::TryAgain(
9114
0
        "The primary may have switched to a new MANIFEST and deleted the old "
9115
0
        "one.");
9116
0
  } else if (!s.ok()) {
9117
0
    return s;
9118
0
  }
9119
0
  TEST_SYNC_POINT(
9120
0
      "ReactiveVersionSet::MaybeSwitchManifest:"
9121
0
      "AfterGetCurrentManifestPath:0");
9122
0
  TEST_SYNC_POINT(
9123
0
      "ReactiveVersionSet::MaybeSwitchManifest:"
9124
0
      "AfterGetCurrentManifestPath:1");
9125
  // The primary can also delete the MANIFEST while the secondary is reading
9126
  // it. This is OK on POSIX. For other file systems, maybe create a hard link
9127
  // to MANIFEST. The hard link should be cleaned up later by the secondary.
9128
0
  s = fs_->NewSequentialFile(manifest_path,
9129
0
                             fs_->OptimizeForManifestRead(file_options_),
9130
0
                             &manifest_file, nullptr);
9131
0
  std::unique_ptr<SequentialFileReader> manifest_file_reader;
9132
0
  if (s.ok()) {
9133
0
    manifest_file_reader.reset(new SequentialFileReader(
9134
0
        std::move(manifest_file), manifest_path,
9135
0
        db_options_->log_readahead_size, io_tracer_, db_options_->listeners));
9136
0
    manifest_reader->reset(new log::FragmentBufferedReader(
9137
0
        nullptr, std::move(manifest_file_reader), reporter, true /* checksum */,
9138
0
        0 /* log_number */));
9139
0
    ROCKS_LOG_INFO(db_options_->info_log, "Switched to new manifest: %s\n",
9140
0
                   manifest_path.c_str());
9141
0
    if (manifest_tailer_) {
9142
0
      manifest_tailer_->PrepareToReadNewManifest();
9143
0
    }
9144
0
  } else if (s.IsPathNotFound()) {
9145
    // This can happen if the primary switches to a new MANIFEST after the
9146
    // secondary reads the CURRENT file but before the secondary actually
9147
    // tries to open the MANIFEST.
9148
0
    s = Status::TryAgain(
9149
0
        "The primary may have switched to a new MANIFEST and deleted the old "
9150
0
        "one.");
9151
0
  }
9152
0
  return s;
9153
0
}
9154
9155
#ifndef NDEBUG
9156
uint64_t ReactiveVersionSet::TEST_read_edits_in_atomic_group() const {
9157
  assert(manifest_tailer_);
9158
  return manifest_tailer_->GetReadBuffer().TEST_read_edits_in_atomic_group();
9159
}
9160
#endif  // !NDEBUG
9161
9162
0
std::vector<VersionEdit>& ReactiveVersionSet::replay_buffer() {
9163
  assert(manifest_tailer_);
9164
0
  return manifest_tailer_->GetReadBuffer().replay_buffer();
9165
0
}
9166
9167
}  // namespace ROCKSDB_NAMESPACE
9168
9169
// Generate the regular and coroutine versions of the functions defined in
9170
// version_set_sync_and_async.h by including it twice. Included here, after the
9171
// namespace and all definitions in this file, so the generated bodies see the
9172
// file-local types and helpers they use. The macros expand differently based on
9173
// whether WITH_COROUTINES or WITHOUT_COROUTINES is defined.
9174
// clang-format off
9175
#define WITHOUT_COROUTINES
9176
#include "db/version_set_sync_and_async.h"
9177
#undef WITHOUT_COROUTINES
9178
#define WITH_COROUTINES
9179
#include "db/version_set_sync_and_async.h"
9180
#undef WITH_COROUTINES
9181
// clang-format on