Coverage Report

Created: 2026-08-14 08:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/rocksdb/db/db_filesnapshot.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
7
#include <algorithm>
8
#include <cstdint>
9
#include <memory>
10
#include <string>
11
#include <unordered_set>
12
#include <vector>
13
14
#include "db/db_impl/db_impl.h"
15
#include "db/job_context.h"
16
#include "db/version_set.h"
17
#include "file/file_util.h"
18
#include "file/filename.h"
19
#include "logging/logging.h"
20
#include "port/port.h"
21
#include "rocksdb/db.h"
22
#include "rocksdb/env.h"
23
#include "rocksdb/metadata.h"
24
#include "rocksdb/types.h"
25
#include "rocksdb/wal_iterator.h"
26
#include "test_util/sync_point.h"
27
#include "util/file_checksum_helper.h"
28
#include "util/mutexlock.h"
29
30
namespace ROCKSDB_NAMESPACE {
31
32
0
Status DBImpl::FlushForGetLiveFiles(bool force_atomic_flush) {
33
0
  FlushOptions flush_opts;
34
0
  flush_opts.force_atomic_flush = force_atomic_flush;
35
0
  return DBImpl::FlushAllColumnFamilies(flush_opts, FlushReason::kGetLiveFiles);
36
0
}
37
38
Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
39
0
                            uint64_t* manifest_file_size, bool flush_memtable) {
40
0
  *manifest_file_size = 0;
41
42
0
  mutex_.Lock();
43
44
0
  if (flush_memtable) {
45
0
    Status status = FlushForGetLiveFiles();
46
0
    if (!status.ok()) {
47
0
      mutex_.Unlock();
48
0
      ROCKS_LOG_ERROR(immutable_db_options_.info_log, "Cannot Flush data %s\n",
49
0
                      status.ToString().c_str());
50
0
      return status;
51
0
    }
52
0
  }
53
54
  // Make a set of all of the live table and blob files
55
0
  std::vector<uint64_t> live_table_files;
56
0
  std::vector<uint64_t> live_blob_files;
57
0
  for (auto cfd : *versions_->GetColumnFamilySet()) {
58
0
    if (cfd->IsDropped()) {
59
0
      continue;
60
0
    }
61
0
    cfd->current()->AddLiveFiles(&live_table_files, &live_blob_files);
62
0
  }
63
64
0
  ret.clear();
65
0
  ret.reserve(live_table_files.size() + live_blob_files.size() +
66
0
              3);  // for CURRENT + MANIFEST + OPTIONS
67
68
  // create names of the live files. The names are not absolute
69
  // paths, instead they are relative to dbname_.
70
0
  for (const auto& table_file_number : live_table_files) {
71
0
    ret.emplace_back(MakeTableFileName("", table_file_number));
72
0
  }
73
74
0
  for (const auto& blob_file_number : live_blob_files) {
75
0
    ret.emplace_back(BlobFileName("", blob_file_number));
76
0
  }
77
78
0
  ret.emplace_back(CurrentFileName(""));
79
0
  ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
80
  // In read-only mode the OPTIONS file number is zero when no OPTIONS file
81
  // exist at all. In this cases we do not record any OPTIONS file in the live
82
  // file list.
83
0
  if (versions_->options_file_number() != 0) {
84
0
    ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
85
0
  }
86
87
  // find length of manifest file while holding the mutex lock
88
0
  *manifest_file_size = versions_->manifest_file_size();
89
90
0
  mutex_.Unlock();
91
0
  return Status::OK();
92
0
}
93
94
0
Status DBImpl::GetSortedWalFiles(VectorWalPtr& files) {
95
0
  return GetSortedWalFilesImpl(files,
96
0
                               /*need_seqnos*/ true);
97
0
}
98
99
0
Status DBImpl::GetSortedWalFilesImpl(VectorWalPtr& files, bool need_seqnos) {
100
  // Record tracked WALs as a (minimum) cross-check for directory scan
101
0
  std::vector<uint64_t> required_by_manifest;
102
103
  // If caller disabled deletions, this function should return files that are
104
  // guaranteed not to be deleted until deletions are re-enabled. We need to
105
  // wait for pending purges to finish since WalManager doesn't know which
106
  // files are going to be purged. Additional purges won't be scheduled as
107
  // long as deletions are disabled (so the below loop must terminate).
108
  // Also note that we disable deletions anyway to avoid the case where a
109
  // file is deleted in the middle of the scan, causing IO error.
110
0
  Status deletions_disabled = DisableFileDeletions();
111
0
  {
112
0
    InstrumentedMutexLock l(&mutex_);
113
0
    while (pending_purge_obsolete_files_ > 0 || bg_purge_scheduled_ > 0) {
114
0
      TEST_SYNC_POINT("DBImpl::GetSortedWalFilesImpl:WaitPurge");
115
0
      bg_cv_.Wait();
116
0
    }
117
118
    // Record tracked WALs as a (minimum) cross-check for directory scan
119
0
    const auto& manifest_wals = versions_->GetWalSet().GetWals();
120
0
    required_by_manifest.reserve(manifest_wals.size());
121
0
    for (const auto& wal : manifest_wals) {
122
0
      required_by_manifest.push_back(wal.first);
123
0
    }
124
0
  }
125
126
  // NOTE: need to include archived WALs because needed WALs might have been
127
  // archived since getting required_by_manifest set
128
0
  Status s = wal_manager_.GetSortedWalFiles(files, need_seqnos,
129
0
                                            /*include_archived*/ true);
130
131
  // DisableFileDeletions / EnableFileDeletions not supported in read-only DB
132
0
  if (deletions_disabled.ok()) {
133
0
    Status s2 = EnableFileDeletions();
134
0
    assert(s2.ok());
135
0
    s2.PermitUncheckedError();
136
0
  } else {
137
0
    assert(deletions_disabled.IsNotSupported());
138
0
  }
139
140
0
  if (s.ok()) {
141
    // Verify includes those required by manifest (one sorted list is superset
142
    // of the other)
143
0
    auto required = required_by_manifest.begin();
144
0
    auto included = files.begin();
145
146
0
    while (required != required_by_manifest.end()) {
147
0
      if (included == files.end() || *required < (*included)->LogNumber()) {
148
        // FAIL - did not find
149
0
        return Status::Corruption(
150
0
            "WAL file " + std::to_string(*required) +
151
0
            " required by manifest but not in directory list");
152
0
      }
153
0
      if (*required == (*included)->LogNumber()) {
154
0
        ++required;
155
0
        ++included;
156
0
      } else {
157
0
        assert(*required > (*included)->LogNumber());
158
0
        ++included;
159
0
      }
160
0
    }
161
0
  }
162
163
0
  if (s.ok()) {
164
0
    size_t wal_count = files.size();
165
0
    ROCKS_LOG_INFO(immutable_db_options_.info_log,
166
0
                   "Number of WAL files %" ROCKSDB_PRIszt " (%" ROCKSDB_PRIszt
167
0
                   " required by manifest)",
168
0
                   wal_count, required_by_manifest.size());
169
#ifndef NDEBUG
170
    std::ostringstream wal_names;
171
    for (const auto& wal : files) {
172
      wal_names << wal->PathName() << " ";
173
    }
174
175
    std::ostringstream wal_required_by_manifest_names;
176
    for (const auto& wal : required_by_manifest) {
177
      wal_required_by_manifest_names << wal << ".log ";
178
    }
179
180
    ROCKS_LOG_INFO(immutable_db_options_.info_log,
181
                   "Log files : %s .Log files required by manifest: %s.",
182
                   wal_names.str().c_str(),
183
                   wal_required_by_manifest_names.str().c_str());
184
#endif  // NDEBUG
185
0
  }
186
0
  return s;
187
0
}
188
189
0
Status DBImpl::GetCurrentWalFile(std::unique_ptr<WalFile>* current_wal_file) {
190
0
  uint64_t current_logfile_number;
191
0
  {
192
0
    InstrumentedMutexLock l(&mutex_);
193
0
    current_logfile_number = cur_wal_number_;
194
0
  }
195
196
0
  return wal_manager_.GetLiveWalFile(current_logfile_number, current_wal_file);
197
0
}
198
199
Status DBImpl::AppendColumnFamilyDropsToManifest(
200
    const std::string& manifest_path, uint64_t manifest_size,
201
0
    const std::vector<uint32_t>& cf_ids) {
202
0
  const WriteOptions write_options;
203
  // Snapshot manifest_preallocation_size_ under the DB mutex per VersionSet's
204
  // contract (the field is mutated by SetDBOptions under this mutex).
205
0
  uint64_t preallocation_size = 0;
206
0
  {
207
0
    InstrumentedMutexLock l(&mutex_);
208
0
    preallocation_size = versions_->manifest_preallocation_size_;
209
0
  }
210
0
  return versions_->AppendColumnFamilyDropsToManifest(
211
0
      manifest_path, manifest_size, cf_ids, write_options, preallocation_size);
212
0
}
213
214
Status DBImpl::GetLiveFilesStorageInfo(
215
    const LiveFilesStorageInfoOptions& opts,
216
0
    std::vector<LiveFileStorageInfo>* files) {
217
0
  return GetLiveFilesStorageInfoImpl(opts, /*include_cf_ids=*/{}, files,
218
0
                                     /*excluded_cf_ids=*/nullptr);
219
0
}
220
221
Status DBImpl::GetLiveFilesStorageInfoForSubsetCheckpoint(
222
    const LiveFilesStorageInfoOptions& opts,
223
    const std::vector<uint32_t>& include_cf_ids,
224
    std::vector<LiveFileStorageInfo>* files,
225
0
    std::vector<uint32_t>* excluded_cf_ids) {
226
  // Internal contract: caller must coalesce ids and always include the default
227
  // CF (it cannot be dropped, so a subset checkpoint without it would produce
228
  // an unopenable DB). CheckpointImpl enforces this before calling here.
229
0
  assert(!include_cf_ids.empty());
230
0
  assert(std::find(include_cf_ids.begin(), include_cf_ids.end(),
231
0
                   default_cf_handle_->GetID()) != include_cf_ids.end());
232
0
  assert(excluded_cf_ids != nullptr);
233
0
  return GetLiveFilesStorageInfoImpl(opts, include_cf_ids, files,
234
0
                                     excluded_cf_ids);
235
0
}
236
237
Status DBImpl::GetLiveFilesStorageInfoImpl(
238
    const LiveFilesStorageInfoOptions& opts,
239
    const std::vector<uint32_t>& include_cf_ids,
240
    std::vector<LiveFileStorageInfo>* files,
241
0
    std::vector<uint32_t>* excluded_cf_ids) {
242
  // To avoid returning partial results, only move results to files on success.
243
0
  assert(files);
244
0
  files->clear();
245
0
  std::vector<LiveFileStorageInfo> results;
246
247
0
  const bool cf_subset = !include_cf_ids.empty();
248
0
  assert(!cf_subset || excluded_cf_ids != nullptr);
249
0
  const std::unordered_set<uint32_t> include_cf_id_set(include_cf_ids.begin(),
250
0
                                                       include_cf_ids.end());
251
  // Tracks which requested ids remain to be observed as live under the DB
252
  // mutex; any id still present after the enumeration was either dropped
253
  // before or during the checkpoint (race) and must not silently disappear
254
  // from the checkpoint.
255
0
  std::unordered_set<uint32_t> unseen_include_cf_ids = include_cf_id_set;
256
257
  // NOTE: This implementation was largely migrated from Checkpoint.
258
259
0
  VectorWalPtr live_wal_files;
260
0
  bool flush_memtable = true;
261
0
  if (!immutable_db_options_.allow_2pc) {
262
0
    if (opts.wal_size_for_flush == std::numeric_limits<uint64_t>::max()) {
263
0
      flush_memtable = false;
264
0
    } else if (opts.wal_size_for_flush > 0) {
265
      // FIXME: avoid querying the filesystem for current WAL state
266
      // If the outstanding WAL files are small, we skip the flush.
267
      // Don't take archived log size into account when calculating wal
268
      // size for flush, and don't need to verify consistency with manifest
269
      // here & now.
270
0
      Status wal_s = wal_manager_.GetSortedWalFiles(live_wal_files,
271
0
                                                    /* need_seqnos */ false,
272
0
                                                    /*include_archived*/ false);
273
274
0
      if (!wal_s.ok()) {
275
0
        return wal_s;
276
0
      }
277
278
      // Don't flush column families if total log size is smaller than
279
      // log_size_for_flush. We copy the log files instead.
280
      // We may be able to cover 2PC case too.
281
0
      uint64_t total_wal_size = 0;
282
0
      for (auto& wal : live_wal_files) {
283
0
        assert(wal->Type() == kAliveLogFile);
284
0
        total_wal_size += wal->SizeFileBytes();
285
0
      }
286
0
      if (total_wal_size < opts.wal_size_for_flush) {
287
0
        flush_memtable = false;
288
0
      }
289
0
      live_wal_files.clear();
290
0
    }
291
0
  }
292
293
  // This is a modified version of GetLiveFiles, to get access to more
294
  // metadata.
295
0
  mutex_.Lock();
296
0
  bool wal_locked = false;
297
0
  const bool needs_blob_direct_write_flush =
298
0
      HasInFlightBlobDirectWriteFilesWithLockHeld();
299
0
  if (needs_blob_direct_write_flush && !flush_memtable) {
300
0
    mutex_.Unlock();
301
0
    return Status::NotSupported(
302
0
        "Blob direct write requires flushing active blob files before "
303
0
        "capturing live files. Retry with flush enabled.");
304
0
  }
305
0
  if (flush_memtable) {
306
0
    wal_locked = !lock_wal_owner_thread_id_counts_.empty();
307
0
    if (wal_locked) {
308
0
      if (needs_blob_direct_write_flush) {
309
0
        mutex_.Unlock();
310
0
        return Status::NotSupported(
311
0
            "Blob direct write requires flushing active blob files before "
312
0
            "capturing live files. Retry with WAL unlocked.");
313
0
      }
314
0
    }
315
0
    if (wal_locked && !has_unpersisted_data_.load(std::memory_order_relaxed)) {
316
0
      ROCKS_LOG_INFO(
317
0
          immutable_db_options_.info_log,
318
0
          "Skipping FlushForGetLiveFiles while WAL is locked and all data is "
319
0
          "already persisted");
320
0
    } else {
321
0
      Status status = FlushForGetLiveFiles(opts.atomic_flush);
322
0
      if (!status.ok()) {
323
0
        mutex_.Unlock();
324
0
        ROCKS_LOG_ERROR(immutable_db_options_.info_log,
325
0
                        "Cannot Flush data %s\n", status.ToString().c_str());
326
0
        return status;
327
0
      }
328
0
    }
329
0
  }
330
331
  // Make a set of all of the live table and blob files
332
0
  for (auto cfd : *versions_->GetColumnFamilySet()) {
333
0
    if (cfd->IsDropped()) {
334
0
      continue;
335
0
    }
336
0
    if (cf_subset &&
337
0
        include_cf_id_set.find(cfd->GetID()) == include_cf_id_set.end()) {
338
0
      excluded_cf_ids->push_back(cfd->GetID());
339
0
      continue;
340
0
    }
341
0
    if (cf_subset) {
342
0
      unseen_include_cf_ids.erase(cfd->GetID());
343
0
    }
344
0
    VersionStorageInfo& vsi = *cfd->current()->storage_info();
345
0
    auto& cf_paths = cfd->ioptions().cf_paths;
346
347
0
    auto GetDir = [&](size_t path_id) {
348
      // Matching TableFileName() behavior
349
0
      if (path_id >= cf_paths.size()) {
350
0
        assert(false);
351
0
        return cf_paths.back().path;
352
0
      } else {
353
0
        return cf_paths[path_id].path;
354
0
      }
355
0
    };
356
357
0
    for (int level = 0; level < vsi.num_levels(); ++level) {
358
0
      const auto& level_files = vsi.LevelFiles(level);
359
0
      for (const auto& meta : level_files) {
360
0
        assert(meta);
361
362
0
        results.emplace_back();
363
0
        LiveFileStorageInfo& info = results.back();
364
365
0
        info.relative_filename = MakeTableFileName(meta->fd.GetNumber());
366
0
        info.directory = GetDir(meta->fd.GetPathId());
367
0
        info.file_number = meta->fd.GetNumber();
368
0
        info.file_type = kTableFile;
369
0
        info.size = meta->fd.GetFileSize();
370
0
        if (opts.include_checksum_info) {
371
0
          info.file_checksum_func_name = meta->file_checksum_func_name;
372
0
          info.file_checksum = meta->file_checksum;
373
0
          if (info.file_checksum_func_name.empty()) {
374
0
            info.file_checksum_func_name = kUnknownFileChecksumFuncName;
375
0
            info.file_checksum = kUnknownFileChecksum;
376
0
          }
377
0
        }
378
0
        info.temperature = meta->temperature;
379
0
      }
380
0
    }
381
0
    const auto& blob_files = vsi.GetBlobFiles();
382
0
    for (const auto& meta : blob_files) {
383
0
      assert(meta);
384
385
0
      results.emplace_back();
386
0
      LiveFileStorageInfo& info = results.back();
387
388
0
      info.relative_filename = BlobFileName(meta->GetBlobFileNumber());
389
0
      info.directory = GetDir(/* path_id */ 0);
390
0
      info.file_number = meta->GetBlobFileNumber();
391
0
      info.file_type = kBlobFile;
392
0
      info.size = meta->GetBlobFileSize();
393
0
      if (opts.include_checksum_info) {
394
0
        info.file_checksum_func_name = meta->GetChecksumMethod();
395
0
        info.file_checksum = meta->GetChecksumValue();
396
0
        if (info.file_checksum_func_name.empty()) {
397
0
          info.file_checksum_func_name = kUnknownFileChecksumFuncName;
398
0
          info.file_checksum = kUnknownFileChecksum;
399
0
        }
400
0
      }
401
      // TODO?: info.temperature
402
0
    }
403
0
  }
404
405
  // Release-build validation: every id the subset-checkpoint caller asked for
406
  // must have been observed as live under the mutex. If any is missing, it was
407
  // dropped before we ran (stale handle) or lost a race with a concurrent
408
  // drop; either way the checkpoint would silently omit it, violating the
409
  // caller's contract that the checkpoint contains the requested CFs.
410
0
  if (cf_subset && !unseen_include_cf_ids.empty()) {
411
0
    mutex_.Unlock();
412
0
    return Status::InvalidArgument(
413
0
        "Requested column family id was not live at checkpoint time");
414
0
  }
415
416
  // Capture some final info before releasing mutex
417
0
  const uint64_t manifest_number = versions_->manifest_file_number();
418
0
  const uint64_t manifest_size = versions_->manifest_file_size();
419
0
  const uint64_t options_number = versions_->options_file_number();
420
0
  const uint64_t options_size = versions_->options_file_size_;
421
0
  const uint64_t min_log_num = MinLogNumberToKeep();
422
  // Ensure consistency with manifest for track_and_verify_wals_in_manifest
423
0
  const uint64_t max_log_num = cur_wal_number_;
424
425
0
  mutex_.Unlock();
426
427
0
  std::string manifest_fname = DescriptorFileName(manifest_number);
428
0
  {  // MANIFEST
429
0
    results.emplace_back();
430
0
    LiveFileStorageInfo& info = results.back();
431
432
0
    info.relative_filename = manifest_fname;
433
0
    info.directory = GetName();
434
0
    info.file_number = manifest_number;
435
0
    info.file_type = kDescriptorFile;
436
0
    info.size = manifest_size;
437
0
    info.trim_to_size = true;
438
0
    if (opts.include_checksum_info) {
439
0
      info.file_checksum_func_name = kUnknownFileChecksumFuncName;
440
0
      info.file_checksum = kUnknownFileChecksum;
441
0
    }
442
0
  }
443
444
0
  {  // CURRENT
445
0
    results.emplace_back();
446
0
    LiveFileStorageInfo& info = results.back();
447
448
0
    info.relative_filename = kCurrentFileName;
449
0
    info.directory = GetName();
450
0
    info.file_type = kCurrentFile;
451
    // CURRENT could be replaced so we have to record the contents as needed.
452
0
    info.replacement_contents = manifest_fname + "\n";
453
0
    info.size = manifest_fname.size() + 1;
454
0
    if (opts.include_checksum_info) {
455
0
      info.file_checksum_func_name = kUnknownFileChecksumFuncName;
456
0
      info.file_checksum = kUnknownFileChecksum;
457
0
    }
458
0
  }
459
460
  // In read-only mode the OPTIONS file number is zero when no OPTIONS file
461
  // exist at all. In this cases we do not record any OPTIONS file in the live
462
  // file list.
463
0
  if (options_number != 0) {
464
0
    results.emplace_back();
465
0
    LiveFileStorageInfo& info = results.back();
466
467
0
    info.relative_filename = OptionsFileName(options_number);
468
0
    info.directory = GetName();
469
0
    info.file_number = options_number;
470
0
    info.file_type = kOptionsFile;
471
0
    info.size = options_size;
472
0
    if (opts.include_checksum_info) {
473
0
      info.file_checksum_func_name = kUnknownFileChecksumFuncName;
474
0
      info.file_checksum = kUnknownFileChecksum;
475
0
    }
476
0
  }
477
478
  // Some legacy testing stuff  TODO: carefully clean up obsolete parts
479
0
  TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:FlushDone");
480
481
0
  TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles1");
482
0
  TEST_SYNC_POINT("CheckpointImpl::CreateCheckpoint:SavedLiveFiles2");
483
484
  // FlushWAL is required to ensure we can physically copy everything
485
  // logically written to the WAL. (Sync not strictly required for
486
  // active WAL to be copied rather than hard linked, even when
487
  // Checkpoint guarantees that the copied-to file is sync-ed. Plus we can't
488
  // help track_and_verify_wals_in_manifest after manifest_size is
489
  // already determined.)
490
0
  Status s = FlushWAL(/*sync=*/false);
491
0
  if (s.IsNotSupported()) {  // read-only DB or similar
492
0
    s = Status::OK();
493
0
  }
494
495
0
  TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive1");
496
0
  TEST_SYNC_POINT("CheckpointImpl::CreateCustomCheckpoint:AfterGetLive2");
497
498
  // Even after WAL flush, there could be multiple WALs that are not
499
  // fully synced. Although the output DB of a Checkpoint or Backup needs
500
  // to be fully synced on return, we don't strictly need to sync this
501
  // DB (the input DB). If we allow Checkpoint to hard link an inactive
502
  // WAL that isn't fully synced, that could result in an insufficiently
503
  // sync-ed Checkpoint. Here we get the set of WALs that are potentially
504
  // unsynced or still being written to, to prevent them from being hard
505
  // linked. Enforcing max_log_num from above ensures any new WALs after
506
  // GetOpenWalSizes() and before GetSortedWalFiles() are not included in
507
  // the results.
508
  // NOTE: we might still hard link a file that is open for writing, even
509
  // if we don't do any more writes to it.
510
  //
511
  // In a step toward reducing unnecessary file metadata queries, we also
512
  // get and use our known flushed sizes for those WALs.
513
  // FIXME: eventually we should not be using filesystem queries at all for
514
  // the required set of WAL files.
515
  //
516
  // However for recycled log files, we just copy the whole file,
517
  // for better or worse.
518
  //
519
0
  std::map<uint64_t, uint64_t> open_wal_number_to_size;
520
0
  bool recycling_log_files = immutable_db_options_.recycle_log_file_num > 0;
521
0
  if (s.ok() && !recycling_log_files) {
522
0
    s = GetOpenWalSizes(open_wal_number_to_size);
523
0
  }
524
525
  // [old comment] If we have more than one column family, we also need to get
526
  // WAL files.
527
0
  if (s.ok()) {
528
    // FIXME: avoid querying the filesystem for current WAL state
529
0
    s = GetSortedWalFilesImpl(live_wal_files,
530
0
                              /* need_seqnos */ false);
531
0
  }
532
0
  if (!s.ok()) {
533
0
    return s;
534
0
  }
535
536
0
  size_t wal_count = live_wal_files.size();
537
  // Link WAL files. Copy exact size of last one because it is the only one
538
  // that has changes after the last flush.
539
0
  auto wal_dir = immutable_db_options_.GetWalDir();
540
0
  for (size_t i = 0; s.ok() && i < wal_count; ++i) {
541
0
    if ((live_wal_files[i]->Type() == kAliveLogFile) &&
542
0
        (!flush_memtable || live_wal_files[i]->LogNumber() >= min_log_num) &&
543
0
        live_wal_files[i]->LogNumber() <= max_log_num) {
544
0
      results.emplace_back();
545
0
      LiveFileStorageInfo& info = results.back();
546
0
      auto f = live_wal_files[i]->PathName();
547
0
      assert(!f.empty() && f[0] == '/');
548
0
      info.relative_filename = f.substr(1);
549
0
      info.directory = wal_dir;
550
0
      info.file_number = live_wal_files[i]->LogNumber();
551
0
      info.file_type = kWalFile;
552
0
      if (recycling_log_files) {
553
0
        info.size = live_wal_files[i]->SizeFileBytes();
554
        // Recyclable WAL files must be copied instead of hard linked
555
0
        info.trim_to_size = true;
556
0
      } else {
557
0
        auto it = open_wal_number_to_size.find(info.file_number);
558
0
        if (it == open_wal_number_to_size.end()) {
559
          // Known fully synced and no future writes (in part from
560
          // max_log_num check). Ok to hard link
561
0
          info.size = live_wal_files[i]->SizeFileBytes();
562
0
          assert(!info.trim_to_size);
563
0
        } else {
564
          // Marked as (possibly) still open -> use our known flushed size
565
          // and force file copy instead of hard link
566
0
          info.size = it->second;
567
0
          info.trim_to_size = true;
568
          // FIXME: this is needed as long as db_stress uses
569
          // SetReadUnsyncedData(false), because it will only be able to
570
          // copy the synced portion of the WAL, which under
571
          // SetReadUnsyncedData(false) is given by the reported file size.
572
0
          info.size = std::min(info.size, live_wal_files[i]->SizeFileBytes());
573
0
        }
574
0
      }
575
0
      if (opts.include_checksum_info) {
576
0
        info.file_checksum_func_name = kUnknownFileChecksumFuncName;
577
0
        info.file_checksum = kUnknownFileChecksum;
578
0
      }
579
0
    }
580
0
  }
581
582
0
  if (s.ok()) {
583
    // Only move results to output on success.
584
0
    *files = std::move(results);
585
0
  }
586
0
  return s;
587
0
}
588
589
}  // namespace ROCKSDB_NAMESPACE