]> git-server-git.apps.pok.os.sepia.ceph.com Git - rocksdb.git/commitdiff
New ldb command to convert compaction style
authorXing Jin <xjin@fb.com>
Wed, 4 Sep 2013 20:13:08 +0000 (13:13 -0700)
committerXing Jin <xjin@fb.com>
Wed, 4 Sep 2013 20:13:08 +0000 (13:13 -0700)
Summary:
Add new command "change_compaction_style" to ldb tool. For
universal->level, it shows "nothing to do". For level->universal, it
compacts all files into a single one and moves the file to level 0.

Also add check for number of files at level 1+ when opening db with
universal compaction style.

Test Plan:
'make all check'. New unit test for internal convertion function. Also manully test various
cmd like:

./ldb change_compaction_style --old_compaction_style=0
--new_compaction_style=1 --db=/tmp/leveldbtest-3088/db_test

Reviewers: haobo, dhruba

Reviewed By: haobo

CC: vamsi, emayanke
Differential Revision: https://reviews.facebook.net/D12603

db/db_impl.cc
db/db_impl.h
db/db_impl_readonly.h
db/db_test.cc
include/rocksdb/db.h
include/utilities/stackable_db.h
util/ldb_cmd.cc
util/ldb_cmd.h
util/ldb_tool.cc
utilities/ttl/db_ttl.cc
utilities/ttl/db_ttl.h

index 6ed88da43ae0036c63edc7500cc403349d5c294d..d222323d78534507098f63caf69df8b7bf49dfb8 100644 (file)
@@ -946,7 +946,7 @@ Status DBImpl::CompactMemTable(bool* madeProgress) {
 }
 
 void DBImpl::CompactRange(const Slice* begin, const Slice* end,
-                          bool reduce_level) {
+                          bool reduce_level, int target_level) {
   int max_level_with_files = 1;
   {
     MutexLock l(&mutex_);
@@ -963,7 +963,7 @@ void DBImpl::CompactRange(const Slice* begin, const Slice* end,
   }
 
   if (reduce_level) {
-    ReFitLevel(max_level_with_files);
+    ReFitLevel(max_level_with_files, target_level);
   }
 }
 
@@ -983,7 +983,7 @@ int DBImpl::FindMinimumEmptyLevelFitting(int level) {
   return minimum_level;
 }
 
-void DBImpl::ReFitLevel(int level) {
+void DBImpl::ReFitLevel(int level, int target_level) {
   assert(level < NumberLevels());
 
   MutexLock l(&mutex_);
@@ -1005,7 +1005,10 @@ void DBImpl::ReFitLevel(int level) {
   }
 
   // move to a smaller level
-  int to_level = FindMinimumEmptyLevelFitting(level);
+  int to_level = target_level;
+  if (target_level < 0) {
+    to_level = FindMinimumEmptyLevelFitting(level);
+  }
 
   assert(to_level <= level);
 
@@ -3107,6 +3110,20 @@ Status DB::Open(const Options& options, const std::string& dbname, DB** dbptr) {
     }
   }
   impl->mutex_.Unlock();
+
+  if (options.compaction_style == kCompactionStyleUniversal) {
+    std::string property;
+    int num_files;
+    for (int i = 1; i < impl->NumberLevels(); i++) {
+      num_files = impl->versions_->NumLevelFiles(i);
+      if (num_files > 0) {
+        s = Status::InvalidArgument("Not all files are at level 0. Cannot "
+          "open with universal compaction style.");
+        break;
+      }
+    }
+  }
+
   if (s.ok()) {
     *dbptr = impl;
   } else {
index 6f4b5db42688141918e5159cbd56ff38c36dcabb..d7e7f1d415ff241a8f40df27936c8003083b87fc 100644 (file)
@@ -64,7 +64,7 @@ class DBImpl : public DB {
   virtual bool GetProperty(const Slice& property, std::string* value);
   virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes);
   virtual void CompactRange(const Slice* begin, const Slice* end,
-                            bool reduce_level = false);
+                            bool reduce_level = false, int target_level = -1);
   virtual int NumberLevels();
   virtual int MaxMemCompactionLevel();
   virtual int Level0StopWriteTrigger();
@@ -241,9 +241,10 @@ class DBImpl : public DB {
   // input level. Return the input level, if such level could not be found.
   int FindMinimumEmptyLevelFitting(int level);
 
-  // Move the files in the input level to the minimum level that could hold
-  // the data set.
-  void ReFitLevel(int level);
+  // Move the files in the input level to the target level.
+  // If target_level < 0, automatically calculate the minimum level that could
+  // hold the data set.
+  void ReFitLevel(int level, int target_level = -1);
 
   // Constant after construction
   const InternalFilterPolicy internal_filter_policy_;
index 4f81d43d957c8ee9dc33e94af032b70c46cba492..b3ade0b6be2348b1ac80803b6fa5cd075662b936 100644 (file)
@@ -51,7 +51,7 @@ public:
    return Status::NotSupported("Not supported operation in read only mode.");
  }
  virtual void CompactRange(const Slice* begin, const Slice* end,
-                           bool reduce_level = false) {
+                           bool reduce_level = false, int target_level = -1) {
  }
  virtual Status DisableFileDeletions() {
    return Status::NotSupported("Not supported operation in read only mode.");
index 04f148c687335d7c9b6808e50070bb4d23f09f91..fce858d6c7f88c34d9741fba2581dc2a9d87fd59 100644 (file)
@@ -1620,6 +1620,100 @@ TEST(DBTest, UniversalCompactionTrigger) {
   }
 }
 
+TEST(DBTest, ConvertCompactionStyle) {
+  Random rnd(301);
+  int max_key_level_insert = 200;
+  int max_key_universal_insert = 600;
+
+  // Stage 1: generate a db with level compaction
+  Options options = CurrentOptions();
+  options.write_buffer_size = 100<<10; //100KB
+  options.num_levels = 4;
+  options.level0_file_num_compaction_trigger = 3;
+  options.max_bytes_for_level_base = 500<<10; // 500KB
+  options.max_bytes_for_level_multiplier = 1;
+  options.target_file_size_base = 200<<10; // 200KB
+  options.target_file_size_multiplier = 1;
+  Reopen(&options);
+
+  for (int i = 0; i <= max_key_level_insert; i++) {
+    // each value is 10K
+    ASSERT_OK(Put(Key(i), RandomString(&rnd, 10000)));
+  }
+  dbfull()->Flush(FlushOptions());
+  dbfull()->TEST_WaitForCompact();
+
+  ASSERT_GT(TotalTableFiles(), 1);
+  int non_level0_num_files = 0;
+  for (int i = 1; i < dbfull()->NumberLevels(); i++) {
+    non_level0_num_files += NumTableFilesAtLevel(i);
+  }
+  ASSERT_GT(non_level0_num_files, 0);
+
+  // Stage 2: reopen with universal compaction - should fail
+  options = CurrentOptions();
+  options.compaction_style = kCompactionStyleUniversal;
+  Status s = TryReopen(&options);
+  ASSERT_TRUE(s.IsInvalidArgument());
+
+  // Stage 3: compact into a single file and move the file to level 0
+  options = CurrentOptions();
+  options.disable_auto_compactions = true;
+  options.target_file_size_base = INT_MAX;
+  options.target_file_size_multiplier = 1;
+  options.max_bytes_for_level_base = INT_MAX;
+  options.max_bytes_for_level_multiplier = 1;
+  Reopen(&options);
+
+  dbfull()->CompactRange(nullptr, nullptr,
+                         true /* reduce level */,
+                         0    /* reduce to level 0 */);
+
+  for (int i = 0; i < dbfull()->NumberLevels(); i++) {
+    int num = NumTableFilesAtLevel(i);
+    if (i == 0) {
+      ASSERT_EQ(num, 1);
+    } else {
+      ASSERT_EQ(num, 0);
+    }
+  }
+
+  // Stage 4: re-open in universal compaction style and do some db operations
+  options = CurrentOptions();
+  options.compaction_style = kCompactionStyleUniversal;
+  options.write_buffer_size = 100<<10; //100KB
+  options.level0_file_num_compaction_trigger = 3;
+  Reopen(&options);
+
+  for (int i = max_key_level_insert / 2; i <= max_key_universal_insert; i++) {
+    ASSERT_OK(Put(Key(i), RandomString(&rnd, 10000)));
+  }
+  dbfull()->Flush(FlushOptions());
+  dbfull()->TEST_WaitForCompact();
+
+  for (int i = 1; i < dbfull()->NumberLevels(); i++) {
+    ASSERT_EQ(NumTableFilesAtLevel(i), 0);
+  }
+
+  // verify keys inserted in both level compaction style and universal
+  // compaction style
+  std::string keys_in_db;
+  Iterator* iter = dbfull()->NewIterator(ReadOptions());
+  for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
+    keys_in_db.append(iter->key().ToString());
+    keys_in_db.push_back(',');
+  }
+  delete iter;
+
+  std::string expected_keys;
+  for (int i = 0; i <= max_key_universal_insert; i++) {
+    expected_keys.append(Key(i));
+    expected_keys.push_back(',');
+  }
+
+  ASSERT_EQ(keys_in_db, expected_keys);
+}
+
 void MinLevelHelper(DBTest* self, Options& options) {
   Random rnd(301);
 
@@ -3530,7 +3624,7 @@ class ModelDB: public DB {
     }
   }
   virtual void CompactRange(const Slice* start, const Slice* end,
-                            bool reduce_level ) {
+                            bool reduce_level, int target_level) {
   }
 
   virtual int NumberLevels()
index d68772e4c18db8bf016a67ea47d24e3041fa8440..1809a0cce99ef38df3f4092d3859e2dd36014001 100644 (file)
@@ -209,9 +209,10 @@ class DB {
   // after compaction is reduced, that level might not be appropriate for
   // hosting all the files. In this case, client could set reduce_level
   // to true, to move the files back to the minimum level capable of holding
-  // the data set.
+  // the data set or a given level (specified by non-negative target_level).
   virtual void CompactRange(const Slice* begin, const Slice* end,
-                            bool reduce_level = false) = 0;
+                            bool reduce_level = false,
+                            int target_level = -1) = 0;
 
   // Number of levels used for this DB.
   virtual int NumberLevels() = 0;
index 6cfdf6a8d7ef42b2196698507515a006c596c477..aa6e6c556c6ad942a8bbc9d58d46a8ed83bac25e 100644 (file)
@@ -104,8 +104,9 @@ class StackableDB : public DB {
   }
 
   virtual void CompactRange(const Slice* begin, const Slice* end,
-                            bool reduce_level = false) override {
-    return sdb_->CompactRange(begin, end, reduce_level);
+                            bool reduce_level = false,
+                            int target_level = -1) override {
+    return sdb_->CompactRange(begin, end, reduce_level, target_level);
   }
 
   virtual int NumberLevels() override {
index 31e0d8ca2f510e37c2552f694f639dc7ed683c70..95286d3fe770242987717ae468983eefcf8463b3 100644 (file)
@@ -143,6 +143,8 @@ LDBCommand* LDBCommand::SelectCommand(
     return new WALDumperCommand(cmdParams, option_map, flags);
   } else if (cmd == ReduceDBLevelsCommand::Name()) {
     return new ReduceDBLevelsCommand(cmdParams, option_map, flags);
+  } else if (cmd == ChangeCompactionStyleCommand::Name()) {
+    return new ChangeCompactionStyleCommand(cmdParams, option_map, flags);
   } else if (cmd == DBDumperCommand::Name()) {
     return new DBDumperCommand(cmdParams, option_map, flags);
   } else if (cmd == DBLoaderCommand::Name()) {
@@ -996,6 +998,138 @@ void ReduceDBLevelsCommand::DoCommand() {
   }
 }
 
+const string ChangeCompactionStyleCommand::ARG_OLD_COMPACTION_STYLE =
+  "old_compaction_style";
+const string ChangeCompactionStyleCommand::ARG_NEW_COMPACTION_STYLE =
+  "new_compaction_style";
+
+ChangeCompactionStyleCommand::ChangeCompactionStyleCommand(
+      const vector<string>& params, const map<string, string>& options,
+      const vector<string>& flags) :
+    LDBCommand(options, flags, false,
+               BuildCmdLineOptions({ARG_OLD_COMPACTION_STYLE,
+                                    ARG_NEW_COMPACTION_STYLE})),
+    old_compaction_style_(-1),
+    new_compaction_style_(-1) {
+
+  ParseIntOption(option_map_, ARG_OLD_COMPACTION_STYLE, old_compaction_style_,
+    exec_state_);
+  if (old_compaction_style_ != kCompactionStyleLevel &&
+     old_compaction_style_ != kCompactionStyleUniversal) {
+    exec_state_ = LDBCommandExecuteResult::FAILED(
+      "Use --" + ARG_OLD_COMPACTION_STYLE + " to specify old compaction " +
+      "style. Check ldb help for proper compaction style value.\n");
+    return;
+  }
+
+  ParseIntOption(option_map_, ARG_NEW_COMPACTION_STYLE, new_compaction_style_,
+    exec_state_);
+  if (new_compaction_style_ != kCompactionStyleLevel &&
+     new_compaction_style_ != kCompactionStyleUniversal) {
+    exec_state_ = LDBCommandExecuteResult::FAILED(
+      "Use --" + ARG_NEW_COMPACTION_STYLE + " to specify new compaction " +
+      "style. Check ldb help for proper compaction style value.\n");
+    return;
+  }
+
+  if (new_compaction_style_ == old_compaction_style_) {
+    exec_state_ = LDBCommandExecuteResult::FAILED(
+      "Old compaction style is the same as new compaction style. "
+      "Nothing to do.\n");
+    return;
+  }
+
+  if (old_compaction_style_ == kCompactionStyleUniversal &&
+      new_compaction_style_ == kCompactionStyleLevel) {
+    exec_state_ = LDBCommandExecuteResult::FAILED(
+      "Convert from universal compaction to level compaction. "
+      "Nothing to do.\n");
+    return;
+  }
+}
+
+void ChangeCompactionStyleCommand::Help(string& ret) {
+  ret.append("  ");
+  ret.append(ChangeCompactionStyleCommand::Name());
+  ret.append(" --" + ARG_OLD_COMPACTION_STYLE + "=<Old compaction style: 0 " +
+             "for level compaction, 1 for universal compaction>");
+  ret.append(" --" + ARG_NEW_COMPACTION_STYLE + "=<New compaction style: 0 " +
+             "for level compaction, 1 for universal compaction>");
+  ret.append("\n");
+}
+
+Options ChangeCompactionStyleCommand::PrepareOptionsForOpenDB() {
+  Options opt = LDBCommand::PrepareOptionsForOpenDB();
+
+  if (old_compaction_style_ == kCompactionStyleLevel &&
+      new_compaction_style_ == kCompactionStyleUniversal) {
+    // 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.
+    opt.disable_auto_compactions = true;
+    opt.target_file_size_base = INT_MAX;
+    opt.target_file_size_multiplier = 1;
+    opt.max_bytes_for_level_base = INT_MAX;
+    opt.max_bytes_for_level_multiplier = 1;
+  }
+
+  return opt;
+}
+
+void ChangeCompactionStyleCommand::DoCommand() {
+  // print db stats before we have made any change
+  std::string property;
+  std::string files_per_level;
+  for (int i = 0; i < db_->NumberLevels(); i++) {
+    db_->GetProperty("leveldb.num-files-at-level" + NumberToString(i),
+                     &property);
+
+    // format print string
+    char buf[100];
+    snprintf(buf, sizeof(buf), "%s%s", (i ? "," : ""), property.c_str());
+    files_per_level += buf;
+  }
+  fprintf(stdout, "files per level before compaction: %s\n",
+          files_per_level.c_str());
+
+  // manual compact into a single file and move the file to level 0
+  db_->CompactRange(nullptr, nullptr,
+                    true /* reduce level */,
+                    0    /* reduce to level 0 */);
+
+  // verify compaction result
+  files_per_level = "";
+  int num_files = 0;
+  for (int i = 0; i < db_->NumberLevels(); i++) {
+    db_->GetProperty("leveldb.num-files-at-level" + NumberToString(i),
+                     &property);
+
+    // format print string
+    char buf[100];
+    snprintf(buf, sizeof(buf), "%s%s", (i ? "," : ""), property.c_str());
+    files_per_level += buf;
+
+    num_files = atoi(property.c_str());
+
+    // level 0 should have only 1 file
+    if (i == 0 && num_files != 1) {
+      exec_state_ = LDBCommandExecuteResult::FAILED("Number of db files at "
+        "level 0 after compaction is " + std::to_string(num_files) +
+        ", not 1.\n");
+      return;
+    }
+    // other levels should have no file
+    if (i > 0 && num_files != 0) {
+      exec_state_ = LDBCommandExecuteResult::FAILED("Number of db files at "
+        "level " + std::to_string(i) + " after compaction is " +
+        std::to_string(num_files) + ", not 0.\n");
+      return;
+    }
+  }
+
+  fprintf(stdout, "files per level after compaction: %s\n",
+          files_per_level.c_str());
+}
+
 class InMemoryHandler : public WriteBatch::Handler {
  public:
 
@@ -1050,8 +1184,8 @@ void WALDumperCommand::Help(string& ret) {
   ret.append("  ");
   ret.append(WALDumperCommand::Name());
   ret.append(" --" + ARG_WAL_FILE + "=<write_ahead_log_file_path>");
-  ret.append(" --[" + ARG_PRINT_HEADER + "] ");
-  ret.append(" --[ " + ARG_PRINT_VALUE + "] ");
+  ret.append(" [--" + ARG_PRINT_HEADER + "] ");
+  ret.append(" [--" + ARG_PRINT_VALUE + "] ");
   ret.append("\n");
 }
 
index f590924d488bb93129a0ddadf5231deb815ab19f..baf367e8da12b04021fab9d13708683f546910e0 100644 (file)
@@ -509,6 +509,27 @@ private:
   Status GetOldNumOfLevels(Options& opt, int* levels);
 };
 
+class ChangeCompactionStyleCommand : public LDBCommand {
+public:
+  static string Name() { return "change_compaction_style"; }
+
+  ChangeCompactionStyleCommand(const vector<string>& params,
+      const map<string, string>& options, const vector<string>& flags);
+
+  virtual Options PrepareOptionsForOpenDB();
+
+  virtual void DoCommand();
+
+  static void Help(string& msg);
+
+private:
+  int old_compaction_style_;
+  int new_compaction_style_;
+
+  static const string ARG_OLD_COMPACTION_STYLE;
+  static const string ARG_NEW_COMPACTION_STYLE;
+};
+
 class WALDumperCommand : public LDBCommand {
 public:
   static string Name() { return "dump_wal"; }
index 64c7c5dd6769053e49df950841b50a02d4abf77d..d04ecd972df16b342bdcfe5ec69b9d5ba1618b4b 100644 (file)
@@ -56,6 +56,7 @@ public:
     WALDumperCommand::Help(ret);
     CompactorCommand::Help(ret);
     ReduceDBLevelsCommand::Help(ret);
+    ChangeCompactionStyleCommand::Help(ret);
     DBDumperCommand::Help(ret);
     DBLoaderCommand::Help(ret);
     ManifestDumpCommand::Help(ret);
index 8f5536c7deb2588db8fc4fe8192314961eddda8c..9e6181f93117304d8adfcbd6d39fe1f1c349934a 100644 (file)
@@ -236,8 +236,8 @@ void DBWithTTL::GetApproximateSizes(const Range* r, int n, uint64_t* sizes) {
 }
 
 void DBWithTTL::CompactRange(const Slice* begin, const Slice* end,
-                             bool reduce_level) {
-  db_->CompactRange(begin, end, reduce_level);
+                             bool reduce_level, int target_level) {
+  db_->CompactRange(begin, end, reduce_level, target_level);
 }
 
 int DBWithTTL::NumberLevels() {
index e9442bc0bf2dcf2229fcd63f1530418944396357..f11f220cd6f2cd65a604cce80adb17b34c37d9df 100644 (file)
@@ -61,7 +61,7 @@ class DBWithTTL : public StackableDB {
   virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes);
 
   virtual void CompactRange(const Slice* begin, const Slice* end,
-                            bool reduce_level = false);
+                            bool reduce_level = false, int target_level = -1);
 
   virtual int NumberLevels();