add `ldb unsafe_remove_sst_file` subcommand (#7335)

Summary:
This is adapted from https://github.com/facebook/rocksdb/issues/6678 but takes a different approach, avoiding opening a read-write DB and avoiding the `DeleteFile()` API.

First, this PR refactors how options variables are initialized in `ldb` so it can be reused in a subcommand that doesn't open a DB:

- Separated remaining option initialization logic out of `OpenDB()`. The new `PrepareOptions()` function initializes the full options state.
- Fixed an old TODO about applying the subcommand CF option overrides to the proper `ColumnFamilyOptions` object.

Second, this PR adds the `ldb unsafe_remove_sst_file` subcommand. It uses the `VersionSet`-level APIs to remove the file with the specified number.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/7335

Test Plan: played with interactive python and this file removal command. Verified openability/correct results in case of multiple column families, multiple levels, etc.

Reviewed By: pdillinger

Differential Revision: D23454575

Pulled By: ajkr

fbshipit-source-id: 039b7a8cbfc42fd123dcb25821eef51d61148afe
main
Andrew Kryczka 4 years ago committed by Facebook GitHub Bot
parent 40e97b02be
commit 5746767387
  1. 1
      HISTORY.md
  2. 4
      include/rocksdb/utilities/ldb_cmd.h
  3. 342
      tools/ldb_cmd.cc
  4. 35
      tools/ldb_cmd_impl.h
  5. 1
      tools/ldb_tool.cc

@ -14,6 +14,7 @@
### New Features ### New Features
* A new option `std::shared_ptr<FileChecksumGenFactory> file_checksum_gen_factory` is added to `BackupableDBOptions`. The default value for this option is `nullptr`. If this option is null, the default backup engine checksum function (crc32c) will be used for creating, verifying, or restoring backups. If it is not null and is set to the DB custom checksum factory, the custom checksum function used in DB will also be used for creating, verifying, or restoring backups, in addition to the default checksum function (crc32c). If it is not null and is set to a custom checksum factory different than the DB custom checksum factory (which may be null), BackupEngine will return `Status::InvalidArgument()`. * A new option `std::shared_ptr<FileChecksumGenFactory> file_checksum_gen_factory` is added to `BackupableDBOptions`. The default value for this option is `nullptr`. If this option is null, the default backup engine checksum function (crc32c) will be used for creating, verifying, or restoring backups. If it is not null and is set to the DB custom checksum factory, the custom checksum function used in DB will also be used for creating, verifying, or restoring backups, in addition to the default checksum function (crc32c). If it is not null and is set to a custom checksum factory different than the DB custom checksum factory (which may be null), BackupEngine will return `Status::InvalidArgument()`.
* A new field `std::string requested_checksum_func_name` is added to `FileChecksumGenContext`, which enables the checksum factory to create generators for a suite of different functions. * A new field `std::string requested_checksum_func_name` is added to `FileChecksumGenContext`, which enables the checksum factory to create generators for a suite of different functions.
* Added a new subcommand, `ldb unsafe_remove_sst_file`, which removes a lost or corrupt SST file from a DB's metadata. This command involves data loss and must not be used on a live DB.
### Performance Improvements ### Performance Improvements
* Reduce thread number for multiple DB instances by re-using one global thread for statistics dumping and persisting. * Reduce thread number for multiple DB instances by re-using one global thread for statistics dumping and persisting.

@ -84,7 +84,9 @@ class LDBCommand {
bool ValidateCmdLineOptions(); bool ValidateCmdLineOptions();
virtual Options PrepareOptionsForOpenDB(); virtual void PrepareOptions();
virtual void OverrideBaseOptions();
virtual void SetDBOptions(Options options) { options_ = options; } virtual void SetDBOptions(Options options) { options_ = options; }

@ -276,6 +276,10 @@ LDBCommand* LDBCommand::SelectCommand(const ParsedParams& parsed_params) {
} else if (parsed_params.cmd == ListFileRangeDeletesCommand::Name()) { } else if (parsed_params.cmd == ListFileRangeDeletesCommand::Name()) {
return new ListFileRangeDeletesCommand(parsed_params.option_map, return new ListFileRangeDeletesCommand(parsed_params.option_map,
parsed_params.flags); parsed_params.flags);
} else if (parsed_params.cmd == UnsafeRemoveSstFileCommand::Name()) {
return new UnsafeRemoveSstFileCommand(parsed_params.cmd_params,
parsed_params.option_map,
parsed_params.flags);
} }
return nullptr; return nullptr;
} }
@ -370,34 +374,7 @@ LDBCommand::LDBCommand(const std::map<std::string, std::string>& options,
} }
void LDBCommand::OpenDB() { void LDBCommand::OpenDB() {
if (!create_if_missing_ && try_load_options_) { PrepareOptions();
config_options_.env = options_.env;
Status s = LoadLatestOptions(config_options_, db_path_, &options_,
&column_families_);
if (!s.ok() && !s.IsNotFound()) {
// Option file exists but load option file error.
std::string msg = s.ToString();
exec_state_ = LDBCommandExecuteResult::Failed(msg);
db_ = nullptr;
return;
}
if (options_.env->FileExists(options_.wal_dir).IsNotFound()) {
options_.wal_dir = db_path_;
fprintf(
stderr,
"wal_dir loaded from the option file doesn't exist. Ignore it.\n");
}
// If merge operator is not set, set a string append operator. There is
// no harm doing it.
for (auto& cf_entry : column_families_) {
if (!cf_entry.options.merge_operator) {
cf_entry.options.merge_operator =
MergeOperators::CreateStringAppendOperator(':');
}
}
}
options_ = PrepareOptionsForOpenDB();
if (!exec_state_.IsNotStarted()) { if (!exec_state_.IsNotStarted()) {
return; return;
} }
@ -425,21 +402,6 @@ void LDBCommand::OpenDB() {
} }
db_ = db_ttl_; db_ = db_ttl_;
} else { } else {
if (column_families_.empty()) {
// Try to figure out column family lists
std::vector<std::string> cf_list;
st = DB::ListColumnFamilies(options_, db_path_, &cf_list);
// There is possible the DB doesn't exist yet, for "create if not
// "existing case". The failure is ignored here. We rely on DB::Open()
// to give us the correct error message for problem with opening
// existing DB.
if (st.ok() && cf_list.size() > 1) {
// Ignore single column family DB.
for (auto cf_name : cf_list) {
column_families_.emplace_back(cf_name, options_);
}
}
}
if (is_read_only_ && secondary_path_.empty()) { if (is_read_only_ && secondary_path_.empty()) {
if (column_families_.empty()) { if (column_families_.empty()) {
st = DB::OpenForReadOnly(options_, db_path_, &db_); st = DB::OpenForReadOnly(options_, db_path_, &db_);
@ -585,22 +547,8 @@ bool LDBCommand::ParseStringOption(
return false; return false;
} }
Options LDBCommand::PrepareOptionsForOpenDB() { void LDBCommand::OverrideBaseOptions() {
ColumnFamilyOptions* cf_opts; options_.create_if_missing = false;
auto column_families_iter =
std::find_if(column_families_.begin(), column_families_.end(),
[this](const ColumnFamilyDescriptor& cf_desc) {
return cf_desc.name == column_family_name_;
});
if (column_families_iter != column_families_.end()) {
cf_opts = &column_families_iter->options;
} else {
cf_opts = static_cast<ColumnFamilyOptions*>(&options_);
}
DBOptions* db_opts = static_cast<DBOptions*>(&options_);
db_opts->create_if_missing = false;
std::map<std::string, std::string>::const_iterator itr;
BlockBasedTableOptions table_options; BlockBasedTableOptions table_options;
bool use_table_options = false; bool use_table_options = false;
@ -626,35 +574,35 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
} }
} }
cf_opts->force_consistency_checks = force_consistency_checks_; options_.force_consistency_checks = force_consistency_checks_;
if (use_table_options) { if (use_table_options) {
cf_opts->table_factory.reset(NewBlockBasedTableFactory(table_options)); options_.table_factory.reset(NewBlockBasedTableFactory(table_options));
} }
itr = option_map_.find(ARG_AUTO_COMPACTION); auto itr = option_map_.find(ARG_AUTO_COMPACTION);
if (itr != option_map_.end()) { if (itr != option_map_.end()) {
cf_opts->disable_auto_compactions = !StringToBool(itr->second); options_.disable_auto_compactions = !StringToBool(itr->second);
} }
itr = option_map_.find(ARG_COMPRESSION_TYPE); itr = option_map_.find(ARG_COMPRESSION_TYPE);
if (itr != option_map_.end()) { if (itr != option_map_.end()) {
std::string comp = itr->second; std::string comp = itr->second;
if (comp == "no") { if (comp == "no") {
cf_opts->compression = kNoCompression; options_.compression = kNoCompression;
} else if (comp == "snappy") { } else if (comp == "snappy") {
cf_opts->compression = kSnappyCompression; options_.compression = kSnappyCompression;
} else if (comp == "zlib") { } else if (comp == "zlib") {
cf_opts->compression = kZlibCompression; options_.compression = kZlibCompression;
} else if (comp == "bzip2") { } else if (comp == "bzip2") {
cf_opts->compression = kBZip2Compression; options_.compression = kBZip2Compression;
} else if (comp == "lz4") { } else if (comp == "lz4") {
cf_opts->compression = kLZ4Compression; options_.compression = kLZ4Compression;
} else if (comp == "lz4hc") { } else if (comp == "lz4hc") {
cf_opts->compression = kLZ4HCCompression; options_.compression = kLZ4HCCompression;
} else if (comp == "xpress") { } else if (comp == "xpress") {
cf_opts->compression = kXpressCompression; options_.compression = kXpressCompression;
} else if (comp == "zstd") { } else if (comp == "zstd") {
cf_opts->compression = kZSTD; options_.compression = kZSTD;
} else { } else {
// Unknown compression. // Unknown compression.
exec_state_ = exec_state_ =
@ -666,7 +614,7 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
if (ParseIntOption(option_map_, ARG_COMPRESSION_MAX_DICT_BYTES, if (ParseIntOption(option_map_, ARG_COMPRESSION_MAX_DICT_BYTES,
compression_max_dict_bytes, exec_state_)) { compression_max_dict_bytes, exec_state_)) {
if (compression_max_dict_bytes >= 0) { if (compression_max_dict_bytes >= 0) {
cf_opts->compression_opts.max_dict_bytes = compression_max_dict_bytes; options_.compression_opts.max_dict_bytes = compression_max_dict_bytes;
} else { } else {
exec_state_ = LDBCommandExecuteResult::Failed( exec_state_ = LDBCommandExecuteResult::Failed(
ARG_COMPRESSION_MAX_DICT_BYTES + " must be >= 0."); ARG_COMPRESSION_MAX_DICT_BYTES + " must be >= 0.");
@ -677,7 +625,7 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
if (ParseIntOption(option_map_, ARG_DB_WRITE_BUFFER_SIZE, if (ParseIntOption(option_map_, ARG_DB_WRITE_BUFFER_SIZE,
db_write_buffer_size, exec_state_)) { db_write_buffer_size, exec_state_)) {
if (db_write_buffer_size >= 0) { if (db_write_buffer_size >= 0) {
db_opts->db_write_buffer_size = db_write_buffer_size; options_.db_write_buffer_size = db_write_buffer_size;
} else { } else {
exec_state_ = LDBCommandExecuteResult::Failed(ARG_DB_WRITE_BUFFER_SIZE + exec_state_ = LDBCommandExecuteResult::Failed(ARG_DB_WRITE_BUFFER_SIZE +
" must be >= 0."); " must be >= 0.");
@ -688,7 +636,7 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
if (ParseIntOption(option_map_, ARG_WRITE_BUFFER_SIZE, write_buffer_size, if (ParseIntOption(option_map_, ARG_WRITE_BUFFER_SIZE, write_buffer_size,
exec_state_)) { exec_state_)) {
if (write_buffer_size > 0) { if (write_buffer_size > 0) {
cf_opts->write_buffer_size = write_buffer_size; options_.write_buffer_size = write_buffer_size;
} else { } else {
exec_state_ = LDBCommandExecuteResult::Failed(ARG_WRITE_BUFFER_SIZE + exec_state_ = LDBCommandExecuteResult::Failed(ARG_WRITE_BUFFER_SIZE +
" must be > 0."); " must be > 0.");
@ -698,15 +646,15 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
int file_size; int file_size;
if (ParseIntOption(option_map_, ARG_FILE_SIZE, file_size, exec_state_)) { if (ParseIntOption(option_map_, ARG_FILE_SIZE, file_size, exec_state_)) {
if (file_size > 0) { if (file_size > 0) {
cf_opts->target_file_size_base = file_size; options_.target_file_size_base = file_size;
} else { } else {
exec_state_ = exec_state_ =
LDBCommandExecuteResult::Failed(ARG_FILE_SIZE + " must be > 0."); LDBCommandExecuteResult::Failed(ARG_FILE_SIZE + " must be > 0.");
} }
} }
if (db_opts->db_paths.size() == 0) { if (options_.db_paths.size() == 0) {
db_opts->db_paths.emplace_back(db_path_, options_.db_paths.emplace_back(db_path_,
std::numeric_limits<uint64_t>::max()); std::numeric_limits<uint64_t>::max());
} }
@ -714,18 +662,83 @@ Options LDBCommand::PrepareOptionsForOpenDB() {
if (ParseIntOption(option_map_, ARG_FIX_PREFIX_LEN, fix_prefix_len, if (ParseIntOption(option_map_, ARG_FIX_PREFIX_LEN, fix_prefix_len,
exec_state_)) { exec_state_)) {
if (fix_prefix_len > 0) { if (fix_prefix_len > 0) {
cf_opts->prefix_extractor.reset( options_.prefix_extractor.reset(
NewFixedPrefixTransform(static_cast<size_t>(fix_prefix_len))); NewFixedPrefixTransform(static_cast<size_t>(fix_prefix_len)));
} else { } else {
exec_state_ = exec_state_ =
LDBCommandExecuteResult::Failed(ARG_FIX_PREFIX_LEN + " must be > 0."); LDBCommandExecuteResult::Failed(ARG_FIX_PREFIX_LEN + " must be > 0.");
} }
} }
}
// TODO(ajkr): this return value doesn't reflect the CF options changed, so // First, initializes the options state using the OPTIONS file when enabled.
// subcommands that rely on this won't see the effect of CF-related CLI args. // Second, overrides the options according to the CLI arguments and the
// Such subcommands need to be changed to properly support CFs. // specific subcommand being run.
return options_; void LDBCommand::PrepareOptions() {
if (!create_if_missing_ && try_load_options_) {
config_options_.env = options_.env;
Status s = LoadLatestOptions(config_options_, db_path_, &options_,
&column_families_);
if (!s.ok() && !s.IsNotFound()) {
// Option file exists but load option file error.
std::string msg = s.ToString();
exec_state_ = LDBCommandExecuteResult::Failed(msg);
db_ = nullptr;
return;
}
if (options_.env->FileExists(options_.wal_dir).IsNotFound()) {
options_.wal_dir = db_path_;
fprintf(
stderr,
"wal_dir loaded from the option file doesn't exist. Ignore it.\n");
}
// If merge operator is not set, set a string append operator.
for (auto& cf_entry : column_families_) {
if (!cf_entry.options.merge_operator) {
cf_entry.options.merge_operator =
MergeOperators::CreateStringAppendOperator(':');
}
}
}
OverrideBaseOptions();
if (exec_state_.IsFailed()) {
return;
}
if (column_families_.empty()) {
// Reads the MANIFEST to figure out what column families exist. In this
// case, the option overrides from the CLI argument/specific subcommand
// apply to all column families.
std::vector<std::string> cf_list;
Status st = DB::ListColumnFamilies(options_, db_path_, &cf_list);
// It is possible the DB doesn't exist yet, for "create if not
// existing" case. The failure is ignored here. We rely on DB::Open()
// to give us the correct error message for problem with opening
// existing DB.
if (st.ok() && cf_list.size() > 1) {
// Ignore single column family DB.
for (auto cf_name : cf_list) {
column_families_.emplace_back(cf_name, options_);
}
}
} else {
// We got column families from the OPTIONS file. In this case, the option
// overrides from the CLI argument/specific subcommand only apply to the
// column family specified by `--column_family_name`.
auto column_families_iter =
std::find_if(column_families_.begin(), column_families_.end(),
[this](const ColumnFamilyDescriptor& cf_desc) {
return cf_desc.name == column_family_name_;
});
if (column_families_iter == column_families_.end()) {
exec_state_ = LDBCommandExecuteResult::Failed(
"Non-existing column family " + column_family_name_);
return;
}
column_families_iter->options = options_;
}
} }
bool LDBCommand::ParseKeyValue(const std::string& line, std::string* key, bool LDBCommand::ParseKeyValue(const std::string& line, std::string* key,
@ -964,13 +977,12 @@ void DBLoaderCommand::Help(std::string& ret) {
ret.append("\n"); ret.append("\n");
} }
Options DBLoaderCommand::PrepareOptionsForOpenDB() { void DBLoaderCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.create_if_missing = create_if_missing_; options_.create_if_missing = create_if_missing_;
if (bulk_load_) { if (bulk_load_) {
opt.PrepareForBulkLoad(); options_.PrepareForBulkLoad();
} }
return opt;
} }
void DBLoaderCommand::DoCommand() { void DBLoaderCommand::DoCommand() {
@ -1872,14 +1884,14 @@ void ReduceDBLevelsCommand::Help(std::string& ret) {
ret.append("\n"); ret.append("\n");
} }
Options ReduceDBLevelsCommand::PrepareOptionsForOpenDB() { void ReduceDBLevelsCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.num_levels = old_levels_; options_.num_levels = old_levels_;
opt.max_bytes_for_level_multiplier_additional.resize(opt.num_levels, 1); options_.max_bytes_for_level_multiplier_additional.resize(options_.num_levels,
1);
// Disable size compaction // Disable size compaction
opt.max_bytes_for_level_base = 1ULL << 50; options_.max_bytes_for_level_base = 1ULL << 50;
opt.max_bytes_for_level_multiplier = 1; options_.max_bytes_for_level_multiplier = 1;
return opt;
} }
Status ReduceDBLevelsCommand::GetOldNumOfLevels(Options& opt, Status ReduceDBLevelsCommand::GetOldNumOfLevels(Options& opt,
@ -1924,9 +1936,9 @@ void ReduceDBLevelsCommand::DoCommand() {
} }
Status st; Status st;
Options opt = PrepareOptionsForOpenDB(); PrepareOptions();
int old_level_num = -1; int old_level_num = -1;
st = GetOldNumOfLevels(opt, &old_level_num); st = GetOldNumOfLevels(options_, &old_level_num);
if (!st.ok()) { if (!st.ok()) {
exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); exec_state_ = LDBCommandExecuteResult::Failed(st.ToString());
return; return;
@ -1953,7 +1965,8 @@ void ReduceDBLevelsCommand::DoCommand() {
CloseDB(); CloseDB();
EnvOptions soptions; EnvOptions soptions;
st = VersionSet::ReduceNumberOfLevels(db_path_, &opt, soptions, new_levels_); st = VersionSet::ReduceNumberOfLevels(db_path_, &options_, soptions,
new_levels_);
if (!st.ok()) { if (!st.ok()) {
exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); exec_state_ = LDBCommandExecuteResult::Failed(st.ToString());
return; return;
@ -2020,21 +2033,19 @@ void ChangeCompactionStyleCommand::Help(std::string& ret) {
ret.append("\n"); ret.append("\n");
} }
Options ChangeCompactionStyleCommand::PrepareOptionsForOpenDB() { void ChangeCompactionStyleCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
if (old_compaction_style_ == kCompactionStyleLevel && if (old_compaction_style_ == kCompactionStyleLevel &&
new_compaction_style_ == kCompactionStyleUniversal) { new_compaction_style_ == kCompactionStyleUniversal) {
// In order to convert from level compaction to universal compaction, we // In order to convert from level compaction to universal compaction, we
// need to compact all data into a single file and move it to level 0. // need to compact all data into a single file and move it to level 0.
opt.disable_auto_compactions = true; options_.disable_auto_compactions = true;
opt.target_file_size_base = INT_MAX; options_.target_file_size_base = INT_MAX;
opt.target_file_size_multiplier = 1; options_.target_file_size_multiplier = 1;
opt.max_bytes_for_level_base = INT_MAX; options_.max_bytes_for_level_base = INT_MAX;
opt.max_bytes_for_level_multiplier = 1; options_.max_bytes_for_level_multiplier = 1;
} }
return opt;
} }
void ChangeCompactionStyleCommand::DoCommand() { void ChangeCompactionStyleCommand::DoCommand() {
@ -2481,10 +2492,9 @@ void BatchPutCommand::DoCommand() {
} }
} }
Options BatchPutCommand::PrepareOptionsForOpenDB() { void BatchPutCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.create_if_missing = create_if_missing_; options_.create_if_missing = create_if_missing_;
return opt;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -2762,10 +2772,9 @@ void PutCommand::DoCommand() {
} }
} }
Options PutCommand::PrepareOptionsForOpenDB() { void PutCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.create_if_missing = create_if_missing_; options_.create_if_missing = create_if_missing_;
return opt;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
@ -2927,10 +2936,14 @@ void RepairCommand::Help(std::string& ret) {
ret.append("\n"); ret.append("\n");
} }
void RepairCommand::OverrideBaseOptions() {
LDBCommand::OverrideBaseOptions();
options_.info_log.reset(new StderrLogger(InfoLogLevel::WARN_LEVEL));
}
void RepairCommand::DoCommand() { void RepairCommand::DoCommand() {
Options options = PrepareOptionsForOpenDB(); PrepareOptions();
options.info_log.reset(new StderrLogger(InfoLogLevel::WARN_LEVEL)); Status status = RepairDB(db_path_, options_);
Status status = RepairDB(db_path_, options);
if (status.ok()) { if (status.ok()) {
fprintf(stdout, "OK\n"); fprintf(stdout, "OK\n");
} else { } else {
@ -3273,10 +3286,9 @@ void WriteExternalSstFilesCommand::DoCommand() {
"external SST file written to " + output_sst_path_); "external SST file written to " + output_sst_path_);
} }
Options WriteExternalSstFilesCommand::PrepareOptionsForOpenDB() { void WriteExternalSstFilesCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.create_if_missing = create_if_missing_; options_.create_if_missing = create_if_missing_;
return opt;
} }
const std::string IngestExternalSstFilesCommand::ARG_MOVE_FILES = "move_files"; const std::string IngestExternalSstFilesCommand::ARG_MOVE_FILES = "move_files";
@ -3388,10 +3400,9 @@ void IngestExternalSstFilesCommand::DoCommand() {
} }
} }
Options IngestExternalSstFilesCommand::PrepareOptionsForOpenDB() { void IngestExternalSstFilesCommand::OverrideBaseOptions() {
Options opt = LDBCommand::PrepareOptionsForOpenDB(); LDBCommand::OverrideBaseOptions();
opt.create_if_missing = create_if_missing_; options_.create_if_missing = create_if_missing_;
return opt;
} }
ListFileRangeDeletesCommand::ListFileRangeDeletesCommand( ListFileRangeDeletesCommand::ListFileRangeDeletesCommand(
@ -3440,8 +3451,87 @@ void ListFileRangeDeletesCommand::DoCommand() {
TEST_SYNC_POINT_CALLBACK( TEST_SYNC_POINT_CALLBACK(
"ListFileRangeDeletesCommand::DoCommand:BeforePrint", &out_str); "ListFileRangeDeletesCommand::DoCommand:BeforePrint", &out_str);
fprintf(stdout, "%s\n", out_str.c_str()); fprintf(stdout, "%s\n", out_str.c_str());
}
}
void UnsafeRemoveSstFileCommand::Help(std::string& ret) {
ret.append(" ");
ret.append(UnsafeRemoveSstFileCommand::Name());
ret.append(" <SST file number>");
ret.append("\n");
ret.append(" MUST NOT be used on a live DB.");
ret.append("\n");
}
UnsafeRemoveSstFileCommand::UnsafeRemoveSstFileCommand(
const std::vector<std::string>& params,
const std::map<std::string, std::string>& options,
const std::vector<std::string>& flags)
: LDBCommand(options, flags, false /* is_read_only */,
BuildCmdLineOptions({})) {
if (params.size() != 1) {
exec_state_ =
LDBCommandExecuteResult::Failed("SST file number must be specified");
} else { } else {
exec_state_ = LDBCommandExecuteResult::Failed(st.ToString()); char* endptr = nullptr;
sst_file_number_ = strtoull(params.at(0).c_str(), &endptr, 10 /* base */);
if (endptr == nullptr || *endptr != '\0') {
exec_state_ = LDBCommandExecuteResult::Failed(
"Failed to parse SST file number " + params.at(0));
}
}
}
void UnsafeRemoveSstFileCommand::DoCommand() {
// Instead of opening a `DB` and calling `DeleteFile()`, this implementation
// uses the underlying `VersionSet` API to read and modify the MANIFEST. This
// allows us to use the user's real options, while not having to worry about
// the DB persisting new SST files via flush/compaction or attempting to read/
// compact files which may fail, particularly for the file we intend to remove
// (the user may want to remove an already deleted file from MANIFEST).
PrepareOptions();
if (options_.db_paths.empty()) {
// `VersionSet` expects options that have been through `SanitizeOptions()`,
// which would sanitize an empty `db_paths`.
options_.db_paths.emplace_back(db_path_, 0 /* target_size */);
}
WriteController wc(options_.delayed_write_rate);
WriteBufferManager wb(options_.db_write_buffer_size);
ImmutableDBOptions immutable_db_options(options_);
std::shared_ptr<Cache> tc(
NewLRUCache(1 << 20 /* capacity */, options_.table_cache_numshardbits));
EnvOptions sopt;
VersionSet versions(db_path_, &immutable_db_options, sopt, tc.get(), &wb, &wc,
/*block_cache_tracer=*/nullptr, /*io_tracer=*/nullptr);
Status s = versions.Recover(column_families_);
ColumnFamilyData* cfd = nullptr;
int level = -1;
if (s.ok()) {
FileMetaData* metadata = nullptr;
s = versions.GetMetadataForFile(sst_file_number_, &level, &metadata, &cfd);
}
if (s.ok()) {
VersionEdit edit;
edit.SetColumnFamily(cfd->GetID());
edit.DeleteFile(level, sst_file_number_);
// Use `mutex` to imitate a locked DB mutex when calling `LogAndApply()`.
InstrumentedMutex mutex;
mutex.Lock();
s = versions.LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(), &edit,
&mutex, nullptr /* db_directory */,
false /* new_descriptor_log */);
mutex.Unlock();
}
if (!s.ok()) {
exec_state_ = LDBCommandExecuteResult::Failed(
"failed to unsafely remove SST file: " + s.ToString());
} else {
exec_state_ = LDBCommandExecuteResult::Succeed("unsafely removed SST file");
} }
} }

@ -136,7 +136,7 @@ class DBLoaderCommand : public LDBCommand {
static void Help(std::string& ret); static void Help(std::string& ret);
virtual void DoCommand() override; virtual void DoCommand() override;
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
private: private:
bool disable_wal_; bool disable_wal_;
@ -246,7 +246,7 @@ class ReduceDBLevelsCommand : public LDBCommand {
const std::map<std::string, std::string>& options, const std::map<std::string, std::string>& options,
const std::vector<std::string>& flags); const std::vector<std::string>& flags);
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
virtual void DoCommand() override; virtual void DoCommand() override;
@ -278,7 +278,7 @@ class ChangeCompactionStyleCommand : public LDBCommand {
const std::map<std::string, std::string>& options, const std::map<std::string, std::string>& options,
const std::vector<std::string>& flags); const std::vector<std::string>& flags);
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
virtual void DoCommand() override; virtual void DoCommand() override;
@ -362,7 +362,7 @@ class BatchPutCommand : public LDBCommand {
static void Help(std::string& ret); static void Help(std::string& ret);
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
private: private:
/** /**
@ -437,7 +437,7 @@ class PutCommand : public LDBCommand {
static void Help(std::string& ret); static void Help(std::string& ret);
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
private: private:
std::string key_; std::string key_;
@ -511,6 +511,8 @@ class RepairCommand : public LDBCommand {
virtual bool NoDBOpen() override { return true; } virtual bool NoDBOpen() override { return true; }
virtual void OverrideBaseOptions() override;
static void Help(std::string& ret); static void Help(std::string& ret);
}; };
@ -568,7 +570,7 @@ class WriteExternalSstFilesCommand : public LDBCommand {
virtual bool NoDBOpen() override { return false; } virtual bool NoDBOpen() override { return false; }
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
static void Help(std::string& ret); static void Help(std::string& ret);
@ -588,7 +590,7 @@ class IngestExternalSstFilesCommand : public LDBCommand {
virtual bool NoDBOpen() override { return false; } virtual bool NoDBOpen() override { return false; }
virtual Options PrepareOptionsForOpenDB() override; virtual void OverrideBaseOptions() override;
static void Help(std::string& ret); static void Help(std::string& ret);
@ -625,4 +627,23 @@ class ListFileRangeDeletesCommand : public LDBCommand {
int max_keys_ = 1000; int max_keys_ = 1000;
}; };
// Command that removes the SST file forcibly from the manifest.
class UnsafeRemoveSstFileCommand : public LDBCommand {
public:
static std::string Name() { return "unsafe_remove_sst_file"; }
UnsafeRemoveSstFileCommand(const std::vector<std::string>& params,
const std::map<std::string, std::string>& options,
const std::vector<std::string>& flags);
static void Help(std::string& ret);
virtual void DoCommand() override;
virtual bool NoDBOpen() override { return true; }
private:
uint64_t sst_file_number_;
};
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE

@ -98,6 +98,7 @@ void LDBCommandRunner::PrintHelp(const LDBOptions& ldb_options,
CheckPointCommand::Help(ret); CheckPointCommand::Help(ret);
WriteExternalSstFilesCommand::Help(ret); WriteExternalSstFilesCommand::Help(ret);
IngestExternalSstFilesCommand::Help(ret); IngestExternalSstFilesCommand::Help(ret);
UnsafeRemoveSstFileCommand::Help(ret);
fprintf(to_stderr ? stderr : stdout, "%s\n", ret.c_str()); fprintf(to_stderr ? stderr : stdout, "%s\n", ret.c_str());
} }

Loading…
Cancel
Save