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 #ifndef ROCKSDB_LITE
8 
9 #include "db/db_impl/db_impl.h"
10 #include "db/version_set.h"
11 #include "rocksdb/db.h"
12 #include "rocksdb/utilities/ldb_cmd.h"
13 #include "test_util/testharness.h"
14 #include "test_util/testutil.h"
15 #include "tools/ldb_cmd_impl.h"
16 #include "util/string_util.h"
17 
18 namespace ROCKSDB_NAMESPACE {
19 
20 class ReduceLevelTest : public testing::Test {
21 public:
ReduceLevelTest()22   ReduceLevelTest() {
23     dbname_ = test::PerThreadDBPath("db_reduce_levels_test");
24     DestroyDB(dbname_, Options());
25     db_ = nullptr;
26   }
27 
28   Status OpenDB(bool create_if_missing, int levels);
29 
Put(const std::string & k,const std::string & v)30   Status Put(const std::string& k, const std::string& v) {
31     return db_->Put(WriteOptions(), k, v);
32   }
33 
Get(const std::string & k)34   std::string Get(const std::string& k) {
35     ReadOptions options;
36     std::string result;
37     Status s = db_->Get(options, k, &result);
38     if (s.IsNotFound()) {
39       result = "NOT_FOUND";
40     } else if (!s.ok()) {
41       result = s.ToString();
42     }
43     return result;
44   }
45 
Flush()46   Status Flush() {
47     if (db_ == nullptr) {
48       return Status::InvalidArgument("DB not opened.");
49     }
50     DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_);
51     return db_impl->TEST_FlushMemTable();
52   }
53 
MoveL0FileToLevel(int level)54   void MoveL0FileToLevel(int level) {
55     DBImpl* db_impl = reinterpret_cast<DBImpl*>(db_);
56     for (int i = 0; i < level; ++i) {
57       ASSERT_OK(db_impl->TEST_CompactRange(i, nullptr, nullptr));
58     }
59   }
60 
CloseDB()61   void CloseDB() {
62     if (db_ != nullptr) {
63       delete db_;
64       db_ = nullptr;
65     }
66   }
67 
68   bool ReduceLevels(int target_level);
69 
FilesOnLevel(int level)70   int FilesOnLevel(int level) {
71     std::string property;
72     EXPECT_TRUE(db_->GetProperty(
73         "rocksdb.num-files-at-level" + NumberToString(level), &property));
74     return atoi(property.c_str());
75   }
76 
77 private:
78   std::string dbname_;
79   DB* db_;
80 };
81 
OpenDB(bool create_if_missing,int num_levels)82 Status ReduceLevelTest::OpenDB(bool create_if_missing, int num_levels) {
83   ROCKSDB_NAMESPACE::Options opt;
84   opt.num_levels = num_levels;
85   opt.create_if_missing = create_if_missing;
86   ROCKSDB_NAMESPACE::Status st =
87       ROCKSDB_NAMESPACE::DB::Open(opt, dbname_, &db_);
88   if (!st.ok()) {
89     fprintf(stderr, "Can't open the db:%s\n", st.ToString().c_str());
90   }
91   return st;
92 }
93 
ReduceLevels(int target_level)94 bool ReduceLevelTest::ReduceLevels(int target_level) {
95   std::vector<std::string> args =
96       ROCKSDB_NAMESPACE::ReduceDBLevelsCommand::PrepareArgs(
97           dbname_, target_level, false);
98   LDBCommand* level_reducer = LDBCommand::InitFromCmdLineArgs(
99       args, Options(), LDBOptions(), nullptr, LDBCommand::SelectCommand);
100   level_reducer->Run();
101   bool is_succeed = level_reducer->GetExecuteState().IsSucceed();
102   delete level_reducer;
103   return is_succeed;
104 }
105 
TEST_F(ReduceLevelTest,Last_Level)106 TEST_F(ReduceLevelTest, Last_Level) {
107   ASSERT_OK(OpenDB(true, 4));
108   ASSERT_OK(Put("aaaa", "11111"));
109   Flush();
110   MoveL0FileToLevel(3);
111   ASSERT_EQ(FilesOnLevel(3), 1);
112   CloseDB();
113 
114   ASSERT_TRUE(ReduceLevels(3));
115   ASSERT_OK(OpenDB(true, 3));
116   ASSERT_EQ(FilesOnLevel(2), 1);
117   CloseDB();
118 
119   ASSERT_TRUE(ReduceLevels(2));
120   ASSERT_OK(OpenDB(true, 2));
121   ASSERT_EQ(FilesOnLevel(1), 1);
122   CloseDB();
123 }
124 
TEST_F(ReduceLevelTest,Top_Level)125 TEST_F(ReduceLevelTest, Top_Level) {
126   ASSERT_OK(OpenDB(true, 5));
127   ASSERT_OK(Put("aaaa", "11111"));
128   Flush();
129   ASSERT_EQ(FilesOnLevel(0), 1);
130   CloseDB();
131 
132   ASSERT_TRUE(ReduceLevels(4));
133   ASSERT_OK(OpenDB(true, 4));
134   CloseDB();
135 
136   ASSERT_TRUE(ReduceLevels(3));
137   ASSERT_OK(OpenDB(true, 3));
138   CloseDB();
139 
140   ASSERT_TRUE(ReduceLevels(2));
141   ASSERT_OK(OpenDB(true, 2));
142   CloseDB();
143 }
144 
TEST_F(ReduceLevelTest,All_Levels)145 TEST_F(ReduceLevelTest, All_Levels) {
146   ASSERT_OK(OpenDB(true, 5));
147   ASSERT_OK(Put("a", "a11111"));
148   ASSERT_OK(Flush());
149   MoveL0FileToLevel(4);
150   ASSERT_EQ(FilesOnLevel(4), 1);
151   CloseDB();
152 
153   ASSERT_OK(OpenDB(true, 5));
154   ASSERT_OK(Put("b", "b11111"));
155   ASSERT_OK(Flush());
156   MoveL0FileToLevel(3);
157   ASSERT_EQ(FilesOnLevel(3), 1);
158   ASSERT_EQ(FilesOnLevel(4), 1);
159   CloseDB();
160 
161   ASSERT_OK(OpenDB(true, 5));
162   ASSERT_OK(Put("c", "c11111"));
163   ASSERT_OK(Flush());
164   MoveL0FileToLevel(2);
165   ASSERT_EQ(FilesOnLevel(2), 1);
166   ASSERT_EQ(FilesOnLevel(3), 1);
167   ASSERT_EQ(FilesOnLevel(4), 1);
168   CloseDB();
169 
170   ASSERT_OK(OpenDB(true, 5));
171   ASSERT_OK(Put("d", "d11111"));
172   ASSERT_OK(Flush());
173   MoveL0FileToLevel(1);
174   ASSERT_EQ(FilesOnLevel(1), 1);
175   ASSERT_EQ(FilesOnLevel(2), 1);
176   ASSERT_EQ(FilesOnLevel(3), 1);
177   ASSERT_EQ(FilesOnLevel(4), 1);
178   CloseDB();
179 
180   ASSERT_TRUE(ReduceLevels(4));
181   ASSERT_OK(OpenDB(true, 4));
182   ASSERT_EQ("a11111", Get("a"));
183   ASSERT_EQ("b11111", Get("b"));
184   ASSERT_EQ("c11111", Get("c"));
185   ASSERT_EQ("d11111", Get("d"));
186   CloseDB();
187 
188   ASSERT_TRUE(ReduceLevels(3));
189   ASSERT_OK(OpenDB(true, 3));
190   ASSERT_EQ("a11111", Get("a"));
191   ASSERT_EQ("b11111", Get("b"));
192   ASSERT_EQ("c11111", Get("c"));
193   ASSERT_EQ("d11111", Get("d"));
194   CloseDB();
195 
196   ASSERT_TRUE(ReduceLevels(2));
197   ASSERT_OK(OpenDB(true, 2));
198   ASSERT_EQ("a11111", Get("a"));
199   ASSERT_EQ("b11111", Get("b"));
200   ASSERT_EQ("c11111", Get("c"));
201   ASSERT_EQ("d11111", Get("d"));
202   CloseDB();
203 }
204 
205 }  // namespace ROCKSDB_NAMESPACE
206 
main(int argc,char ** argv)207 int main(int argc, char** argv) {
208   ::testing::InitGoogleTest(&argc, argv);
209   return RUN_ALL_TESTS();
210 }
211 
212 #else
213 #include <stdio.h>
214 
main(int,char **)215 int main(int /*argc*/, char** /*argv*/) {
216   fprintf(stderr, "SKIPPED as LDBCommand is not supported in ROCKSDB_LITE\n");
217   return 0;
218 }
219 
220 #endif  // !ROCKSDB_LITE
221