db_table_properties_test.cc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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. // 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.
  9. #include <unordered_set>
  10. #include <vector>
  11. #include "db/db_test_util.h"
  12. #include "port/stack_trace.h"
  13. #include "rocksdb/db.h"
  14. #include "rocksdb/utilities/table_properties_collectors.h"
  15. #include "test_util/testharness.h"
  16. #include "test_util/testutil.h"
  17. #ifndef ROCKSDB_LITE
  18. namespace ROCKSDB_NAMESPACE {
  19. // A helper function that ensures the table properties returned in
  20. // `GetPropertiesOfAllTablesTest` is correct.
  21. // This test assumes entries size is different for each of the tables.
  22. namespace {
  23. void VerifyTableProperties(DB* db, uint64_t expected_entries_size) {
  24. TablePropertiesCollection props;
  25. ASSERT_OK(db->GetPropertiesOfAllTables(&props));
  26. ASSERT_EQ(4U, props.size());
  27. std::unordered_set<uint64_t> unique_entries;
  28. // Indirect test
  29. uint64_t sum = 0;
  30. for (const auto& item : props) {
  31. unique_entries.insert(item.second->num_entries);
  32. sum += item.second->num_entries;
  33. }
  34. ASSERT_EQ(props.size(), unique_entries.size());
  35. ASSERT_EQ(expected_entries_size, sum);
  36. }
  37. } // namespace
  38. class DBTablePropertiesTest : public DBTestBase {
  39. public:
  40. DBTablePropertiesTest() : DBTestBase("/db_table_properties_test") {}
  41. TablePropertiesCollection TestGetPropertiesOfTablesInRange(
  42. std::vector<Range> ranges, std::size_t* num_properties = nullptr,
  43. std::size_t* num_files = nullptr);
  44. };
  45. TEST_F(DBTablePropertiesTest, GetPropertiesOfAllTablesTest) {
  46. Options options = CurrentOptions();
  47. options.level0_file_num_compaction_trigger = 8;
  48. Reopen(options);
  49. // Create 4 tables
  50. for (int table = 0; table < 4; ++table) {
  51. for (int i = 0; i < 10 + table; ++i) {
  52. db_->Put(WriteOptions(), ToString(table * 100 + i), "val");
  53. }
  54. db_->Flush(FlushOptions());
  55. }
  56. // 1. Read table properties directly from file
  57. Reopen(options);
  58. VerifyTableProperties(db_, 10 + 11 + 12 + 13);
  59. // 2. Put two tables to table cache and
  60. Reopen(options);
  61. // fetch key from 1st and 2nd table, which will internally place that table to
  62. // the table cache.
  63. for (int i = 0; i < 2; ++i) {
  64. Get(ToString(i * 100 + 0));
  65. }
  66. VerifyTableProperties(db_, 10 + 11 + 12 + 13);
  67. // 3. Put all tables to table cache
  68. Reopen(options);
  69. // fetch key from 1st and 2nd table, which will internally place that table to
  70. // the table cache.
  71. for (int i = 0; i < 4; ++i) {
  72. Get(ToString(i * 100 + 0));
  73. }
  74. VerifyTableProperties(db_, 10 + 11 + 12 + 13);
  75. }
  76. TablePropertiesCollection
  77. DBTablePropertiesTest::TestGetPropertiesOfTablesInRange(
  78. std::vector<Range> ranges, std::size_t* num_properties,
  79. std::size_t* num_files) {
  80. // Since we deref zero element in the vector it can not be empty
  81. // otherwise we pass an address to some random memory
  82. EXPECT_GT(ranges.size(), 0U);
  83. // run the query
  84. TablePropertiesCollection props;
  85. EXPECT_OK(db_->GetPropertiesOfTablesInRange(
  86. db_->DefaultColumnFamily(), &ranges[0], ranges.size(), &props));
  87. // Make sure that we've received properties for those and for those files
  88. // only which fall within requested ranges
  89. std::vector<LiveFileMetaData> vmd;
  90. db_->GetLiveFilesMetaData(&vmd);
  91. for (auto& md : vmd) {
  92. std::string fn = md.db_path + md.name;
  93. bool in_range = false;
  94. for (auto& r : ranges) {
  95. // smallestkey < limit && largestkey >= start
  96. if (r.limit.compare(md.smallestkey) >= 0 &&
  97. r.start.compare(md.largestkey) <= 0) {
  98. in_range = true;
  99. EXPECT_GT(props.count(fn), 0);
  100. }
  101. }
  102. if (!in_range) {
  103. EXPECT_EQ(props.count(fn), 0);
  104. }
  105. }
  106. if (num_properties) {
  107. *num_properties = props.size();
  108. }
  109. if (num_files) {
  110. *num_files = vmd.size();
  111. }
  112. return props;
  113. }
  114. TEST_F(DBTablePropertiesTest, GetPropertiesOfTablesInRange) {
  115. // Fixed random sead
  116. Random rnd(301);
  117. Options options;
  118. options.create_if_missing = true;
  119. options.write_buffer_size = 4096;
  120. options.max_write_buffer_number = 2;
  121. options.level0_file_num_compaction_trigger = 2;
  122. options.level0_slowdown_writes_trigger = 2;
  123. options.level0_stop_writes_trigger = 2;
  124. options.target_file_size_base = 2048;
  125. options.max_bytes_for_level_base = 40960;
  126. options.max_bytes_for_level_multiplier = 4;
  127. options.hard_pending_compaction_bytes_limit = 16 * 1024;
  128. options.num_levels = 8;
  129. options.env = env_;
  130. DestroyAndReopen(options);
  131. // build a decent LSM
  132. for (int i = 0; i < 10000; i++) {
  133. ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
  134. }
  135. Flush();
  136. dbfull()->TEST_WaitForCompact();
  137. if (NumTableFilesAtLevel(0) == 0) {
  138. ASSERT_OK(Put(test::RandomKey(&rnd, 5), RandomString(&rnd, 102)));
  139. Flush();
  140. }
  141. db_->PauseBackgroundWork();
  142. // Ensure that we have at least L0, L1 and L2
  143. ASSERT_GT(NumTableFilesAtLevel(0), 0);
  144. ASSERT_GT(NumTableFilesAtLevel(1), 0);
  145. ASSERT_GT(NumTableFilesAtLevel(2), 0);
  146. // Query the largest range
  147. std::size_t num_properties, num_files;
  148. TestGetPropertiesOfTablesInRange(
  149. {Range(test::RandomKey(&rnd, 5, test::RandomKeyType::SMALLEST),
  150. test::RandomKey(&rnd, 5, test::RandomKeyType::LARGEST))},
  151. &num_properties, &num_files);
  152. ASSERT_EQ(num_properties, num_files);
  153. // Query the empty range
  154. TestGetPropertiesOfTablesInRange(
  155. {Range(test::RandomKey(&rnd, 5, test::RandomKeyType::LARGEST),
  156. test::RandomKey(&rnd, 5, test::RandomKeyType::SMALLEST))},
  157. &num_properties, &num_files);
  158. ASSERT_GT(num_files, 0);
  159. ASSERT_EQ(num_properties, 0);
  160. // Query the middle rangee
  161. TestGetPropertiesOfTablesInRange(
  162. {Range(test::RandomKey(&rnd, 5, test::RandomKeyType::MIDDLE),
  163. test::RandomKey(&rnd, 5, test::RandomKeyType::LARGEST))},
  164. &num_properties, &num_files);
  165. ASSERT_GT(num_files, 0);
  166. ASSERT_GT(num_files, num_properties);
  167. ASSERT_GT(num_properties, 0);
  168. // Query a bunch of random ranges
  169. for (int j = 0; j < 100; j++) {
  170. // create a bunch of ranges
  171. std::vector<std::string> random_keys;
  172. // Random returns numbers with zero included
  173. // when we pass empty ranges TestGetPropertiesOfTablesInRange()
  174. // derefs random memory in the empty ranges[0]
  175. // so want to be greater than zero and even since
  176. // the below loop requires that random_keys.size() to be even.
  177. auto n = 2 * (rnd.Uniform(50) + 1);
  178. for (uint32_t i = 0; i < n; ++i) {
  179. random_keys.push_back(test::RandomKey(&rnd, 5));
  180. }
  181. ASSERT_GT(random_keys.size(), 0U);
  182. ASSERT_EQ((random_keys.size() % 2), 0U);
  183. std::vector<Range> ranges;
  184. auto it = random_keys.begin();
  185. while (it != random_keys.end()) {
  186. ranges.push_back(Range(*it, *(it + 1)));
  187. it += 2;
  188. }
  189. TestGetPropertiesOfTablesInRange(std::move(ranges));
  190. }
  191. }
  192. TEST_F(DBTablePropertiesTest, GetColumnFamilyNameProperty) {
  193. std::string kExtraCfName = "pikachu";
  194. CreateAndReopenWithCF({kExtraCfName}, CurrentOptions());
  195. // Create one table per CF, then verify it was created with the column family
  196. // name property.
  197. for (uint32_t cf = 0; cf < 2; ++cf) {
  198. Put(cf, "key", "val");
  199. Flush(cf);
  200. TablePropertiesCollection fname_to_props;
  201. ASSERT_OK(db_->GetPropertiesOfAllTables(handles_[cf], &fname_to_props));
  202. ASSERT_EQ(1U, fname_to_props.size());
  203. std::string expected_cf_name;
  204. if (cf > 0) {
  205. expected_cf_name = kExtraCfName;
  206. } else {
  207. expected_cf_name = kDefaultColumnFamilyName;
  208. }
  209. ASSERT_EQ(expected_cf_name,
  210. fname_to_props.begin()->second->column_family_name);
  211. ASSERT_EQ(cf, static_cast<uint32_t>(
  212. fname_to_props.begin()->second->column_family_id));
  213. }
  214. }
  215. TEST_F(DBTablePropertiesTest, DeletionTriggeredCompactionMarking) {
  216. int kNumKeys = 1000;
  217. int kWindowSize = 100;
  218. int kNumDelsTrigger = 90;
  219. std::shared_ptr<TablePropertiesCollectorFactory> compact_on_del =
  220. NewCompactOnDeletionCollectorFactory(kWindowSize, kNumDelsTrigger);
  221. Options opts = CurrentOptions();
  222. opts.table_properties_collector_factories.emplace_back(compact_on_del);
  223. Reopen(opts);
  224. // add an L1 file to prevent tombstones from dropping due to obsolescence
  225. // during flush
  226. Put(Key(0), "val");
  227. Flush();
  228. MoveFilesToLevel(1);
  229. for (int i = 0; i < kNumKeys; ++i) {
  230. if (i >= kNumKeys - kWindowSize &&
  231. i < kNumKeys - kWindowSize + kNumDelsTrigger) {
  232. Delete(Key(i));
  233. } else {
  234. Put(Key(i), "val");
  235. }
  236. }
  237. Flush();
  238. dbfull()->TEST_WaitForCompact();
  239. ASSERT_EQ(0, NumTableFilesAtLevel(0));
  240. ASSERT_GT(NumTableFilesAtLevel(1), 0);
  241. // Change the window size and deletion trigger and ensure new values take
  242. // effect
  243. kWindowSize = 50;
  244. kNumDelsTrigger = 40;
  245. static_cast<CompactOnDeletionCollectorFactory*>
  246. (compact_on_del.get())->SetWindowSize(kWindowSize);
  247. static_cast<CompactOnDeletionCollectorFactory*>
  248. (compact_on_del.get())->SetDeletionTrigger(kNumDelsTrigger);
  249. for (int i = 0; i < kNumKeys; ++i) {
  250. if (i >= kNumKeys - kWindowSize &&
  251. i < kNumKeys - kWindowSize + kNumDelsTrigger) {
  252. Delete(Key(i));
  253. } else {
  254. Put(Key(i), "val");
  255. }
  256. }
  257. Flush();
  258. dbfull()->TEST_WaitForCompact();
  259. ASSERT_EQ(0, NumTableFilesAtLevel(0));
  260. ASSERT_GT(NumTableFilesAtLevel(1), 0);
  261. // Change the window size to disable delete triggered compaction
  262. kWindowSize = 0;
  263. static_cast<CompactOnDeletionCollectorFactory*>
  264. (compact_on_del.get())->SetWindowSize(kWindowSize);
  265. static_cast<CompactOnDeletionCollectorFactory*>
  266. (compact_on_del.get())->SetDeletionTrigger(kNumDelsTrigger);
  267. for (int i = 0; i < kNumKeys; ++i) {
  268. if (i >= kNumKeys - kWindowSize &&
  269. i < kNumKeys - kWindowSize + kNumDelsTrigger) {
  270. Delete(Key(i));
  271. } else {
  272. Put(Key(i), "val");
  273. }
  274. }
  275. Flush();
  276. dbfull()->TEST_WaitForCompact();
  277. ASSERT_EQ(1, NumTableFilesAtLevel(0));
  278. }
  279. } // namespace ROCKSDB_NAMESPACE
  280. #endif // ROCKSDB_LITE
  281. int main(int argc, char** argv) {
  282. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  283. ::testing::InitGoogleTest(&argc, argv);
  284. return RUN_ALL_TESTS();
  285. }