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