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).
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.
10 // Repairer does best effort recovery to recover as much data as possible after
11 // a disaster without compromising consistency. It does not guarantee bringing
12 // the database to a time consistent state.
14 // Repair process is broken into 4 phases:
16 // (b) Convert logs to tables
17 // (c) Extract metadata
18 // (d) Write Descriptor
22 // The repairer goes through all the files in the directory, and classifies them
23 // based on their file name. Any file that cannot be identified by name will be
26 // (b) Convert logs to table
28 // Every log file that is active is replayed. All sections of the file where the
29 // checksum does not match is skipped over. We intentionally give preference to
32 // (c) Extract metadata
34 // We scan every table to compute
35 // (1) smallest/largest for the table
36 // (2) largest sequence number in the table
37 // (3) oldest blob file referred to by the table (if applicable)
39 // If we are unable to scan the file, then we ignore the table.
41 // (d) Write Descriptor
43 // We generate descriptor contents:
44 // - log number is set to zero
45 // - next-file-number is set to 1 + largest file number we found
46 // - last-sequence-number is set to largest sequence# found across
47 // all tables (see 2c)
48 // - compaction pointers are cleared
49 // - every table file is added at level 0
51 // Possible optimization 1:
52 // (a) Compute total size and use to pick appropriate max-level M
53 // (b) Sort tables by largest sequence# in the table
54 // (c) For each table: if it overlaps earlier table, place in level-0,
55 // else place in level-M.
56 // (d) We can provide options for time consistent recovery and unsafe recovery
57 // (ignore checksum failure when applicable)
58 // Possible optimization 2:
59 // Store per-table metadata (smallest, largest, largest-seq#, ...)
60 // in the table's meta section to speed up ScanTable.
65 #include "db/builder.h"
66 #include "db/db_impl/db_impl.h"
67 #include "db/dbformat.h"
68 #include "db/log_reader.h"
69 #include "db/log_writer.h"
70 #include "db/memtable.h"
71 #include "db/table_cache.h"
72 #include "db/version_edit.h"
73 #include "db/write_batch_internal.h"
74 #include "env/composite_env_wrapper.h"
75 #include "file/filename.h"
76 #include "file/writable_file_writer.h"
77 #include "options/cf_options.h"
78 #include "rocksdb/comparator.h"
79 #include "rocksdb/db.h"
80 #include "rocksdb/env.h"
81 #include "rocksdb/options.h"
82 #include "rocksdb/write_buffer_manager.h"
83 #include "table/scoped_arena_iterator.h"
84 #include "util/string_util.h"
86 namespace ROCKSDB_NAMESPACE
{
92 Repairer(const std::string
& dbname
, const DBOptions
& db_options
,
93 const std::vector
<ColumnFamilyDescriptor
>& column_families
,
94 const ColumnFamilyOptions
& default_cf_opts
,
95 const ColumnFamilyOptions
& unknown_cf_opts
, bool create_unknown_cfs
)
99 db_options_(SanitizeOptions(dbname_
, db_options
)),
100 immutable_db_options_(ImmutableDBOptions(db_options_
)),
101 icmp_(default_cf_opts
.comparator
),
103 SanitizeOptions(immutable_db_options_
, default_cf_opts
)),
105 ImmutableCFOptions(immutable_db_options_
, default_cf_opts_
)),
107 SanitizeOptions(immutable_db_options_
, unknown_cf_opts
)),
108 create_unknown_cfs_(create_unknown_cfs
),
110 // TableCache can be small since we expect each table to be opened
112 NewLRUCache(10, db_options_
.table_cache_numshardbits
)),
113 table_cache_(new TableCache(
114 default_cf_iopts_
, env_options_
, raw_table_cache_
.get(),
115 /*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr)),
116 wb_(db_options_
.db_write_buffer_size
),
117 wc_(db_options_
.delayed_write_rate
),
118 vset_(dbname_
, &immutable_db_options_
, env_options_
,
119 raw_table_cache_
.get(), &wb_
, &wc_
,
120 /*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr),
121 next_file_number_(1),
124 for (const auto& cfd
: column_families
) {
125 cf_name_to_opts_
[cfd
.name
] = cfd
.options
;
129 const ColumnFamilyOptions
* GetColumnFamilyOptions(
130 const std::string
& cf_name
) {
131 if (cf_name_to_opts_
.find(cf_name
) == cf_name_to_opts_
.end()) {
132 if (create_unknown_cfs_
) {
133 return &unknown_cf_opts_
;
137 return &cf_name_to_opts_
[cf_name
];
140 // Adds a column family to the VersionSet with cf_options_ and updates
142 Status
AddColumnFamily(const std::string
& cf_name
, uint32_t cf_id
) {
143 const auto* cf_opts
= GetColumnFamilyOptions(cf_name
);
144 if (cf_opts
== nullptr) {
145 return Status::Corruption("Encountered unknown column family with name=" +
146 cf_name
+ ", id=" + ToString(cf_id
));
148 Options
opts(db_options_
, *cf_opts
);
149 MutableCFOptions
mut_cf_opts(opts
);
152 edit
.SetComparatorName(opts
.comparator
->Name());
153 edit
.SetLogNumber(0);
154 edit
.SetColumnFamily(cf_id
);
155 ColumnFamilyData
* cfd
;
157 edit
.AddColumnFamily(cf_name
);
160 Status status
= vset_
.LogAndApply(cfd
, mut_cf_opts
, &edit
, &mutex_
,
161 nullptr /* db_directory */,
162 false /* new_descriptor_log */, cf_opts
);
168 Status s
= Status::OK();
170 if (db_lock_
!= nullptr) {
171 s
= env_
->UnlockFile(db_lock_
);
179 ~Repairer() { Close().PermitUncheckedError(); }
182 Status status
= env_
->LockFile(LockFileName(dbname_
), &db_lock_
);
186 status
= FindFiles();
187 DBImpl
* db_impl
= nullptr;
189 // Discard older manifests and start a fresh one
190 for (size_t i
= 0; i
< manifests_
.size(); i
++) {
191 ArchiveFile(dbname_
+ "/" + manifests_
[i
]);
193 // Just create a DBImpl temporarily so we can reuse NewDB()
194 db_impl
= new DBImpl(db_options_
, dbname_
);
195 // Also use this temp DBImpl to get a session id
196 status
= db_impl
->GetDbSessionId(db_session_id_
);
199 status
= db_impl
->NewDB(/*new_filenames=*/nullptr);
204 // Recover using the fresh manifest created by NewDB()
206 vset_
.Recover({{kDefaultColumnFamilyName
, default_cf_opts_
}}, false);
209 // Need to scan existing SST files first so the column families are
210 // created before we process WAL files
213 // ExtractMetaData() uses table_fds_ to know which SST files' metadata to
214 // extract -- we need to clear it here since metadata for existing SST
215 // files has been extracted already
217 ConvertLogFilesToTables();
219 status
= AddTables();
223 for (size_t i
= 0; i
< tables_
.size(); i
++) {
224 bytes
+= tables_
[i
].meta
.fd
.GetFileSize();
226 ROCKS_LOG_WARN(db_options_
.info_log
,
227 "**** Repaired rocksdb %s; "
228 "recovered %" ROCKSDB_PRIszt
" files; %" PRIu64
230 "Some data may have been lost. "
232 dbname_
.c_str(), tables_
.size(), bytes
);
240 uint32_t column_family_id
;
241 std::string column_family_name
;
244 std::string
const dbname_
;
245 std::string db_session_id_
;
247 const EnvOptions env_options_
;
248 const DBOptions db_options_
;
249 const ImmutableDBOptions immutable_db_options_
;
250 const InternalKeyComparator icmp_
;
251 const ColumnFamilyOptions default_cf_opts_
;
252 const ImmutableCFOptions default_cf_iopts_
; // table_cache_ holds reference
253 const ColumnFamilyOptions unknown_cf_opts_
;
254 const bool create_unknown_cfs_
;
255 std::shared_ptr
<Cache
> raw_table_cache_
;
256 std::unique_ptr
<TableCache
> table_cache_
;
257 WriteBufferManager wb_
;
260 std::unordered_map
<std::string
, ColumnFamilyOptions
> cf_name_to_opts_
;
261 InstrumentedMutex mutex_
;
263 std::vector
<std::string
> manifests_
;
264 std::vector
<FileDescriptor
> table_fds_
;
265 std::vector
<uint64_t> logs_
;
266 std::vector
<TableInfo
> tables_
;
267 uint64_t next_file_number_
;
268 // Lock over the persistent DB state. Non-nullptr iff successfully
274 std::vector
<std::string
> filenames
;
275 bool found_file
= false;
276 std::vector
<std::string
> to_search_paths
;
278 for (size_t path_id
= 0; path_id
< db_options_
.db_paths
.size(); path_id
++) {
279 to_search_paths
.push_back(db_options_
.db_paths
[path_id
].path
);
282 // search wal_dir if user uses a customize wal_dir
284 Status status
= env_
->AreFilesSame(db_options_
.wal_dir
, dbname_
, &same
);
285 if (status
.IsNotSupported()) {
286 same
= db_options_
.wal_dir
== dbname_
;
287 status
= Status::OK();
288 } else if (!status
.ok()) {
293 to_search_paths
.push_back(db_options_
.wal_dir
);
296 for (size_t path_id
= 0; path_id
< to_search_paths
.size(); path_id
++) {
297 status
= env_
->GetChildren(to_search_paths
[path_id
], &filenames
);
301 if (!filenames
.empty()) {
307 for (size_t i
= 0; i
< filenames
.size(); i
++) {
308 if (ParseFileName(filenames
[i
], &number
, &type
)) {
309 if (type
== kDescriptorFile
) {
310 manifests_
.push_back(filenames
[i
]);
312 if (number
+ 1 > next_file_number_
) {
313 next_file_number_
= number
+ 1;
315 if (type
== kWalFile
) {
316 logs_
.push_back(number
);
317 } else if (type
== kTableFile
) {
318 table_fds_
.emplace_back(number
, static_cast<uint32_t>(path_id
),
321 // Ignore other files
328 return Status::Corruption(dbname_
, "repair found no files");
333 void ConvertLogFilesToTables() {
334 for (size_t i
= 0; i
< logs_
.size(); i
++) {
335 // we should use LogFileName(wal_dir, logs_[i]) here. user might uses wal_dir option.
336 std::string logname
= LogFileName(db_options_
.wal_dir
, logs_
[i
]);
337 Status status
= ConvertLogToTable(logs_
[i
]);
339 ROCKS_LOG_WARN(db_options_
.info_log
,
340 "Log #%" PRIu64
": ignoring conversion error: %s",
341 logs_
[i
], status
.ToString().c_str());
343 ArchiveFile(logname
);
347 Status
ConvertLogToTable(uint64_t log
) {
348 struct LogReporter
: public log::Reader::Reporter
{
350 std::shared_ptr
<Logger
> info_log
;
352 void Corruption(size_t bytes
, const Status
& s
) override
{
353 // We print error messages for corruption, but continue repairing.
354 ROCKS_LOG_ERROR(info_log
, "Log #%" PRIu64
": dropping %d bytes; %s",
355 lognum
, static_cast<int>(bytes
), s
.ToString().c_str());
360 std::string logname
= LogFileName(db_options_
.wal_dir
, log
);
361 std::unique_ptr
<SequentialFile
> lfile
;
362 Status status
= env_
->NewSequentialFile(
363 logname
, &lfile
, env_
->OptimizeForLogRead(env_options_
));
367 std::unique_ptr
<SequentialFileReader
> lfile_reader(new SequentialFileReader(
368 NewLegacySequentialFileWrapper(lfile
), logname
));
370 // Create the log reader.
371 LogReporter reporter
;
373 reporter
.info_log
= db_options_
.info_log
;
374 reporter
.lognum
= log
;
375 // We intentionally make log::Reader do checksumming so that
376 // corruptions cause entire commits to be skipped instead of
377 // propagating bad information (like overly large sequence
379 log::Reader
reader(db_options_
.info_log
, std::move(lfile_reader
), &reporter
,
380 true /*enable checksum*/, log
);
382 // Initialize per-column family memtables
383 for (auto* cfd
: *vset_
.GetColumnFamilySet()) {
384 cfd
->CreateNewMemtable(*cfd
->GetLatestMutableCFOptions(),
387 auto cf_mems
= new ColumnFamilyMemTablesImpl(vset_
.GetColumnFamilySet());
389 // Read all the records and add to a memtable
394 while (reader
.ReadRecord(&record
, &scratch
)) {
395 if (record
.size() < WriteBatchInternal::kHeader
) {
397 record
.size(), Status::Corruption("log record too small"));
400 Status record_status
= WriteBatchInternal::SetContents(&batch
, record
);
401 if (record_status
.ok()) {
403 WriteBatchInternal::InsertInto(&batch
, cf_mems
, nullptr, nullptr);
405 if (record_status
.ok()) {
406 counter
+= WriteBatchInternal::Count(&batch
);
408 ROCKS_LOG_WARN(db_options_
.info_log
, "Log #%" PRIu64
": ignoring %s",
409 log
, record_status
.ToString().c_str());
413 // Dump a table for each column family with entries in this log file.
414 for (auto* cfd
: *vset_
.GetColumnFamilySet()) {
415 // Do not record a version edit for this conversion to a Table
416 // since ExtractMetaData() will also generate edits.
417 MemTable
* mem
= cfd
->mem();
418 if (mem
->IsEmpty()) {
423 meta
.fd
= FileDescriptor(next_file_number_
++, 0, 0);
425 ro
.total_order_seek
= true;
427 ScopedArenaIterator
iter(mem
->NewIterator(ro
, &arena
));
428 int64_t _current_time
= 0;
429 status
= env_
->GetCurrentTime(&_current_time
); // ignore error
430 const uint64_t current_time
= static_cast<uint64_t>(_current_time
);
431 SnapshotChecker
* snapshot_checker
= DisableGCSnapshotChecker::Instance();
433 auto write_hint
= cfd
->CalculateSSTWriteHint(0);
434 std::vector
<std::unique_ptr
<FragmentedRangeTombstoneIterator
>>
436 auto range_del_iter
=
437 mem
->NewRangeTombstoneIterator(ro
, kMaxSequenceNumber
);
438 if (range_del_iter
!= nullptr) {
439 range_del_iters
.emplace_back(range_del_iter
);
442 LegacyFileSystemWrapper
fs(env_
);
445 dbname_
, /* versions */ nullptr, immutable_db_options_
,
446 *cfd
->ioptions(), *cfd
->GetLatestMutableCFOptions(), env_options_
,
447 table_cache_
.get(), iter
.get(), std::move(range_del_iters
), &meta
,
448 nullptr /* blob_file_additions */, cfd
->internal_comparator(),
449 cfd
->int_tbl_prop_collector_factories(), cfd
->GetID(), cfd
->GetName(),
450 {}, kMaxSequenceNumber
, snapshot_checker
, kNoCompression
,
451 0 /* sample_for_compression */, CompressionOptions(), false,
452 nullptr /* internal_stats */, TableFileCreationReason::kRecovery
,
453 &io_s
, nullptr /*IOTracer*/, nullptr /* event_logger */,
454 0 /* job_id */, Env::IO_HIGH
, nullptr /* table_properties */,
455 -1 /* level */, current_time
, 0 /* oldest_key_time */, write_hint
,
456 0 /* file_creation_time */, "DB Repairer" /* db_id */,
458 ROCKS_LOG_INFO(db_options_
.info_log
,
459 "Log #%" PRIu64
": %d ops saved to Table #%" PRIu64
" %s",
460 log
, counter
, meta
.fd
.GetNumber(),
461 status
.ToString().c_str());
463 if (meta
.fd
.GetFileSize() > 0) {
464 table_fds_
.push_back(meta
.fd
);
474 void ExtractMetaData() {
475 for (size_t i
= 0; i
< table_fds_
.size(); i
++) {
477 t
.meta
.fd
= table_fds_
[i
];
478 Status status
= ScanTable(&t
);
480 std::string fname
= TableFileName(
481 db_options_
.db_paths
, t
.meta
.fd
.GetNumber(), t
.meta
.fd
.GetPathId());
482 char file_num_buf
[kFormatFileNumberBufSize
];
483 FormatFileNumber(t
.meta
.fd
.GetNumber(), t
.meta
.fd
.GetPathId(),
484 file_num_buf
, sizeof(file_num_buf
));
485 ROCKS_LOG_WARN(db_options_
.info_log
, "Table #%s: ignoring %s",
486 file_num_buf
, status
.ToString().c_str());
489 tables_
.push_back(t
);
494 Status
ScanTable(TableInfo
* t
) {
495 std::string fname
= TableFileName(
496 db_options_
.db_paths
, t
->meta
.fd
.GetNumber(), t
->meta
.fd
.GetPathId());
499 Status status
= env_
->GetFileSize(fname
, &file_size
);
500 t
->meta
.fd
= FileDescriptor(t
->meta
.fd
.GetNumber(), t
->meta
.fd
.GetPathId(),
502 std::shared_ptr
<const TableProperties
> props
;
504 status
= table_cache_
->GetTableProperties(env_options_
, icmp_
, t
->meta
.fd
,
508 t
->column_family_id
= static_cast<uint32_t>(props
->column_family_id
);
509 if (t
->column_family_id
==
510 TablePropertiesCollectorFactory::Context::kUnknownColumnFamily
) {
512 db_options_
.info_log
,
514 ": column family unknown (probably due to legacy format); "
515 "adding to default column family id 0.",
516 t
->meta
.fd
.GetNumber());
517 t
->column_family_id
= 0;
520 if (vset_
.GetColumnFamilySet()->GetColumnFamily(t
->column_family_id
) ==
523 AddColumnFamily(props
->column_family_name
, t
->column_family_id
);
525 t
->meta
.oldest_ancester_time
= props
->creation_time
;
527 ColumnFamilyData
* cfd
= nullptr;
529 cfd
= vset_
.GetColumnFamilySet()->GetColumnFamily(t
->column_family_id
);
530 if (cfd
->GetName() != props
->column_family_name
) {
532 db_options_
.info_log
,
534 ": inconsistent column family name '%s'; expected '%s' for column "
535 "family id %" PRIu32
".",
536 t
->meta
.fd
.GetNumber(), props
->column_family_name
.c_str(),
537 cfd
->GetName().c_str(), t
->column_family_id
);
538 status
= Status::Corruption(dbname_
, "inconsistent column family name");
543 ropts
.total_order_seek
= true;
544 InternalIterator
* iter
= table_cache_
->NewIterator(
545 ropts
, env_options_
, cfd
->internal_comparator(), t
->meta
,
546 nullptr /* range_del_agg */,
547 cfd
->GetLatestMutableCFOptions()->prefix_extractor
.get(),
548 /*table_reader_ptr=*/nullptr, /*file_read_hist=*/nullptr,
549 TableReaderCaller::kRepair
, /*arena=*/nullptr, /*skip_filters=*/false,
550 /*level=*/-1, /*max_file_size_for_l0_meta_pin=*/0,
551 /*smallest_compaction_key=*/nullptr,
552 /*largest_compaction_key=*/nullptr,
553 /*allow_unprepared_value=*/false);
554 ParsedInternalKey parsed
;
555 for (iter
->SeekToFirst(); iter
->Valid(); iter
->Next()) {
556 Slice key
= iter
->key();
558 ParseInternalKey(key
, &parsed
, db_options_
.allow_data_in_errors
);
559 if (!pik_status
.ok()) {
560 ROCKS_LOG_ERROR(db_options_
.info_log
,
561 "Table #%" PRIu64
": unparsable key - %s",
562 t
->meta
.fd
.GetNumber(), pik_status
.getState());
568 t
->meta
.UpdateBoundaries(key
, iter
->value(), parsed
.sequence
,
571 if (!iter
->status().ok()) {
572 status
= iter
->status();
576 ROCKS_LOG_INFO(db_options_
.info_log
, "Table #%" PRIu64
": %d entries %s",
577 t
->meta
.fd
.GetNumber(), counter
,
578 status
.ToString().c_str());
584 std::unordered_map
<uint32_t, std::vector
<const TableInfo
*>> cf_id_to_tables
;
585 SequenceNumber max_sequence
= 0;
586 for (size_t i
= 0; i
< tables_
.size(); i
++) {
587 cf_id_to_tables
[tables_
[i
].column_family_id
].push_back(&tables_
[i
]);
588 if (max_sequence
< tables_
[i
].meta
.fd
.largest_seqno
) {
589 max_sequence
= tables_
[i
].meta
.fd
.largest_seqno
;
592 vset_
.SetLastAllocatedSequence(max_sequence
);
593 vset_
.SetLastPublishedSequence(max_sequence
);
594 vset_
.SetLastSequence(max_sequence
);
596 for (const auto& cf_id_and_tables
: cf_id_to_tables
) {
598 vset_
.GetColumnFamilySet()->GetColumnFamily(cf_id_and_tables
.first
);
600 edit
.SetComparatorName(cfd
->user_comparator()->Name());
601 edit
.SetLogNumber(0);
602 edit
.SetNextFile(next_file_number_
);
603 edit
.SetColumnFamily(cfd
->GetID());
605 // TODO(opt): separate out into multiple levels
606 for (const auto* table
: cf_id_and_tables
.second
) {
608 0, table
->meta
.fd
.GetNumber(), table
->meta
.fd
.GetPathId(),
609 table
->meta
.fd
.GetFileSize(), table
->meta
.smallest
,
610 table
->meta
.largest
, table
->meta
.fd
.smallest_seqno
,
611 table
->meta
.fd
.largest_seqno
, table
->meta
.marked_for_compaction
,
612 table
->meta
.oldest_blob_file_number
,
613 table
->meta
.oldest_ancester_time
, table
->meta
.file_creation_time
,
614 table
->meta
.file_checksum
, table
->meta
.file_checksum_func_name
);
616 assert(next_file_number_
> 0);
617 vset_
.MarkFileNumberUsed(next_file_number_
- 1);
619 Status status
= vset_
.LogAndApply(
620 cfd
, *cfd
->GetLatestMutableCFOptions(), &edit
, &mutex_
,
621 nullptr /* db_directory */, false /* new_descriptor_log */);
630 void ArchiveFile(const std::string
& fname
) {
631 // Move into another directory. E.g., for
635 const char* slash
= strrchr(fname
.c_str(), '/');
637 if (slash
!= nullptr) {
638 new_dir
.assign(fname
.data(), slash
- fname
.data());
640 new_dir
.append("/lost");
641 env_
->CreateDir(new_dir
).PermitUncheckedError(); // Ignore error
642 std::string new_file
= new_dir
;
643 new_file
.append("/");
644 new_file
.append((slash
== nullptr) ? fname
.c_str() : slash
+ 1);
645 Status s
= env_
->RenameFile(fname
, new_file
);
646 ROCKS_LOG_INFO(db_options_
.info_log
, "Archiving %s: %s\n", fname
.c_str(),
647 s
.ToString().c_str());
651 Status
GetDefaultCFOptions(
652 const std::vector
<ColumnFamilyDescriptor
>& column_families
,
653 ColumnFamilyOptions
* res
) {
654 assert(res
!= nullptr);
655 auto iter
= std::find_if(column_families
.begin(), column_families
.end(),
656 [](const ColumnFamilyDescriptor
& cfd
) {
657 return cfd
.name
== kDefaultColumnFamilyName
;
659 if (iter
== column_families
.end()) {
660 return Status::InvalidArgument(
661 "column_families", "Must contain entry for default column family");
663 *res
= iter
->options
;
666 } // anonymous namespace
668 Status
RepairDB(const std::string
& dbname
, const DBOptions
& db_options
,
669 const std::vector
<ColumnFamilyDescriptor
>& column_families
671 ColumnFamilyOptions default_cf_opts
;
672 Status status
= GetDefaultCFOptions(column_families
, &default_cf_opts
);
677 Repairer
repairer(dbname
, db_options
, column_families
, default_cf_opts
,
678 ColumnFamilyOptions() /* unknown_cf_opts */,
679 false /* create_unknown_cfs */);
680 status
= repairer
.Run();
682 status
= repairer
.Close();
687 Status
RepairDB(const std::string
& dbname
, const DBOptions
& db_options
,
688 const std::vector
<ColumnFamilyDescriptor
>& column_families
,
689 const ColumnFamilyOptions
& unknown_cf_opts
) {
690 ColumnFamilyOptions default_cf_opts
;
691 Status status
= GetDefaultCFOptions(column_families
, &default_cf_opts
);
696 Repairer
repairer(dbname
, db_options
, column_families
, default_cf_opts
,
697 unknown_cf_opts
, true /* create_unknown_cfs */);
698 status
= repairer
.Run();
700 status
= repairer
.Close();
705 Status
RepairDB(const std::string
& dbname
, const Options
& options
) {
706 Options
opts(options
);
707 DBOptions
db_options(opts
);
708 ColumnFamilyOptions
cf_options(opts
);
710 Repairer
repairer(dbname
, db_options
,
711 {}, cf_options
/* default_cf_opts */,
712 cf_options
/* unknown_cf_opts */,
713 true /* create_unknown_cfs */);
714 Status status
= repairer
.Run();
716 status
= repairer
.Close();
721 } // namespace ROCKSDB_NAMESPACE
723 #endif // ROCKSDB_LITE