db_dynamic_level_test.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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. // Introduction of SyncPoint effectively disabled building and running this test
  10. // in Release build.
  11. // which is a pity, it is a good test
  12. #include "db/db_test_util.h"
  13. #include "port/port.h"
  14. #include "port/stack_trace.h"
  15. #include "rocksdb/env.h"
  16. #include "util/random.h"
  17. namespace ROCKSDB_NAMESPACE {
  18. class DBTestDynamicLevel : public DBTestBase {
  19. public:
  20. DBTestDynamicLevel()
  21. : DBTestBase("db_dynamic_level_test", /*env_do_fsync=*/true) {}
  22. };
  23. TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
  24. if (!Snappy_Supported() || !LZ4_Supported()) {
  25. return;
  26. }
  27. // Use InMemoryEnv, or it would be too slow.
  28. std::unique_ptr<Env> env(NewMemEnv(env_));
  29. const int kNKeys = 1000;
  30. int keys[kNKeys];
  31. auto verify_func = [&]() {
  32. for (int i = 0; i < kNKeys; i++) {
  33. ASSERT_NE("NOT_FOUND", Get(Key(i)));
  34. ASSERT_NE("NOT_FOUND", Get(Key(kNKeys * 2 + i)));
  35. if (i < kNKeys / 10) {
  36. ASSERT_EQ("NOT_FOUND", Get(Key(kNKeys + keys[i])));
  37. } else {
  38. ASSERT_NE("NOT_FOUND", Get(Key(kNKeys + keys[i])));
  39. }
  40. }
  41. };
  42. Random rnd(301);
  43. for (int ordered_insert = 0; ordered_insert <= 1; ordered_insert++) {
  44. for (int i = 0; i < kNKeys; i++) {
  45. keys[i] = i;
  46. }
  47. if (ordered_insert == 0) {
  48. RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
  49. }
  50. for (int max_background_compactions = 1; max_background_compactions < 4;
  51. max_background_compactions += 2) {
  52. Options options;
  53. options.env = env.get();
  54. options.create_if_missing = true;
  55. options.write_buffer_size = 2048;
  56. options.max_write_buffer_number = 2;
  57. options.level0_file_num_compaction_trigger = 2;
  58. options.level0_slowdown_writes_trigger = 2;
  59. options.level0_stop_writes_trigger = 2;
  60. options.target_file_size_base = 2048;
  61. options.level_compaction_dynamic_level_bytes = true;
  62. options.max_bytes_for_level_base = 10240;
  63. options.max_bytes_for_level_multiplier = 4;
  64. options.max_background_compactions = max_background_compactions;
  65. options.num_levels = 5;
  66. options.compression_per_level.resize(3);
  67. options.compression_per_level[0] = kNoCompression;
  68. options.compression_per_level[1] = kLZ4Compression;
  69. options.compression_per_level[2] = kSnappyCompression;
  70. options.env = env_;
  71. DestroyAndReopen(options);
  72. for (int i = 0; i < kNKeys; i++) {
  73. int key = keys[i];
  74. ASSERT_OK(Put(Key(kNKeys + key), rnd.RandomString(102)));
  75. ASSERT_OK(Put(Key(key), rnd.RandomString(102)));
  76. ASSERT_OK(Put(Key(kNKeys * 2 + key), rnd.RandomString(102)));
  77. ASSERT_OK(Delete(Key(kNKeys + keys[i / 10])));
  78. env_->SleepForMicroseconds(5000);
  79. }
  80. uint64_t int_prop;
  81. ASSERT_TRUE(db_->GetIntProperty("rocksdb.background-errors", &int_prop));
  82. ASSERT_EQ(0U, int_prop);
  83. // Verify DB
  84. for (int j = 0; j < 2; j++) {
  85. verify_func();
  86. if (j == 0) {
  87. Reopen(options);
  88. }
  89. }
  90. // Test compact range works
  91. ASSERT_OK(
  92. dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  93. // All data should be in the last level.
  94. ColumnFamilyMetaData cf_meta;
  95. db_->GetColumnFamilyMetaData(&cf_meta);
  96. ASSERT_EQ(5U, cf_meta.levels.size());
  97. for (int i = 0; i < 4; i++) {
  98. ASSERT_EQ(0U, cf_meta.levels[i].files.size());
  99. }
  100. ASSERT_GT(cf_meta.levels[4U].files.size(), 0U);
  101. verify_func();
  102. Close();
  103. }
  104. }
  105. env_->SetBackgroundThreads(1, Env::LOW);
  106. env_->SetBackgroundThreads(1, Env::HIGH);
  107. }
  108. // Test specific cases in dynamic max bytes
  109. TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
  110. Random rnd(301);
  111. int kMaxKey = 1000000;
  112. Options options = CurrentOptions();
  113. options.compression = kNoCompression;
  114. options.create_if_missing = true;
  115. options.write_buffer_size = 20480;
  116. options.max_write_buffer_number = 2;
  117. options.level0_file_num_compaction_trigger = 2;
  118. options.level0_slowdown_writes_trigger = 9999;
  119. options.level0_stop_writes_trigger = 9999;
  120. options.target_file_size_base = 9102;
  121. options.level_compaction_dynamic_level_bytes = true;
  122. options.max_bytes_for_level_base = 40960;
  123. options.max_bytes_for_level_multiplier = 4;
  124. options.max_background_compactions = 2;
  125. options.num_levels = 5;
  126. options.max_compaction_bytes = 0; // Force not expanding in compactions
  127. options.db_host_id = ""; // Setting this messes up the file size calculation
  128. BlockBasedTableOptions table_options;
  129. table_options.block_size = 1024;
  130. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  131. DestroyAndReopen(options);
  132. ASSERT_OK(dbfull()->SetOptions({
  133. {"disable_auto_compactions", "true"},
  134. }));
  135. uint64_t int_prop;
  136. std::string str_prop;
  137. // Initial base level is the last level
  138. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  139. ASSERT_EQ(4U, int_prop);
  140. // Put about 28K to L0
  141. for (int i = 0; i < 70; i++) {
  142. ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
  143. rnd.RandomString(380)));
  144. }
  145. ASSERT_OK(dbfull()->SetOptions({
  146. {"disable_auto_compactions", "false"},
  147. }));
  148. ASSERT_OK(Flush());
  149. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  150. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  151. ASSERT_EQ(4U, int_prop);
  152. // Insert extra about 28K to L0. After they are compacted to L4, the base
  153. // level should be changed to L3.
  154. ASSERT_OK(dbfull()->SetOptions({
  155. {"disable_auto_compactions", "true"},
  156. }));
  157. for (int i = 0; i < 70; i++) {
  158. ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
  159. rnd.RandomString(380)));
  160. }
  161. ASSERT_OK(dbfull()->SetOptions({
  162. {"disable_auto_compactions", "false"},
  163. }));
  164. ASSERT_OK(Flush());
  165. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  166. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  167. ASSERT_EQ(3U, int_prop);
  168. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level1", &str_prop));
  169. ASSERT_EQ("0", str_prop);
  170. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level2", &str_prop));
  171. ASSERT_EQ("0", str_prop);
  172. // Write even more data while leaving the base level at L3.
  173. ASSERT_OK(dbfull()->SetOptions({
  174. {"disable_auto_compactions", "true"},
  175. }));
  176. // Write about 40K more
  177. for (int i = 0; i < 100; i++) {
  178. ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
  179. rnd.RandomString(380)));
  180. }
  181. ASSERT_OK(dbfull()->SetOptions({
  182. {"disable_auto_compactions", "false"},
  183. }));
  184. ASSERT_OK(Flush());
  185. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  186. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  187. ASSERT_EQ(3U, int_prop);
  188. // Fill up L0, and then run an (auto) L0->Lmax compaction to raise the base
  189. // level to 2.
  190. ASSERT_OK(dbfull()->SetOptions({
  191. {"disable_auto_compactions", "true"},
  192. }));
  193. // Write about 650K more.
  194. // Each file is about 11KB, with 9KB of data.
  195. for (int i = 0; i < 1300; i++) {
  196. ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
  197. rnd.RandomString(380)));
  198. }
  199. // Make sure that the compaction starts before the last bit of data is
  200. // flushed, so that the base level isn't raised to L1.
  201. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  202. {"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:0"},
  203. });
  204. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  205. ASSERT_OK(dbfull()->SetOptions({
  206. {"disable_auto_compactions", "false"},
  207. }));
  208. TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
  209. ASSERT_OK(Flush());
  210. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  211. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  212. ASSERT_EQ(2U, int_prop);
  213. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  214. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
  215. // Write more data until the base level changes to L1. There will be
  216. // a manual compaction going on at the same time.
  217. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
  218. {"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:1"},
  219. {"DynamicLevelMaxBytesBase2:2", "CompactionJob::Run():End"},
  220. {"DynamicLevelMaxBytesBase2:compact_range_finish",
  221. "FlushJob::WriteLevel0Table"},
  222. });
  223. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  224. ROCKSDB_NAMESPACE::port::Thread thread([this] {
  225. TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_start");
  226. ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  227. TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
  228. });
  229. TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:1");
  230. for (int i = 0; i < 2; i++) {
  231. ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
  232. rnd.RandomString(380)));
  233. }
  234. TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:2");
  235. ASSERT_OK(Flush());
  236. thread.join();
  237. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  238. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
  239. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  240. ASSERT_EQ(1U, int_prop);
  241. }
  242. // Test specific cases in dynamic max bytes
  243. TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
  244. Random rnd(301);
  245. int kMaxKey = 1000000;
  246. Options options = CurrentOptions();
  247. options.create_if_missing = true;
  248. options.write_buffer_size = 2048;
  249. options.max_write_buffer_number = 2;
  250. options.level0_file_num_compaction_trigger = 2;
  251. options.level0_slowdown_writes_trigger = 9999;
  252. options.level0_stop_writes_trigger = 9999;
  253. options.target_file_size_base = 2;
  254. options.level_compaction_dynamic_level_bytes = true;
  255. options.max_bytes_for_level_base = 10240;
  256. options.max_bytes_for_level_multiplier = 4;
  257. options.max_background_compactions = 1;
  258. const int kNumLevels = 5;
  259. options.num_levels = kNumLevels;
  260. options.max_compaction_bytes = 1; // Force not expanding in compactions
  261. BlockBasedTableOptions table_options;
  262. table_options.block_size = 1024;
  263. options.table_factory.reset(NewBlockBasedTableFactory(table_options));
  264. DestroyAndReopen(options);
  265. // Compact against empty DB
  266. ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  267. uint64_t int_prop;
  268. std::string str_prop;
  269. // Initial base level is the last level
  270. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  271. ASSERT_EQ(4U, int_prop);
  272. // Put about 7K to L0
  273. for (int i = 0; i < 140; i++) {
  274. ASSERT_OK(
  275. Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
  276. }
  277. ASSERT_OK(Flush());
  278. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  279. if (NumTableFilesAtLevel(0) == 0) {
  280. // Make sure level 0 is not empty
  281. ASSERT_OK(
  282. Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
  283. ASSERT_OK(Flush());
  284. }
  285. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  286. ASSERT_EQ(3U, int_prop);
  287. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level1", &str_prop));
  288. ASSERT_EQ("0", str_prop);
  289. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level2", &str_prop));
  290. ASSERT_EQ("0", str_prop);
  291. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  292. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
  293. std::set<int> output_levels;
  294. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  295. "CompactionPicker::CompactRange:Return", [&](void* arg) {
  296. Compaction* compaction = static_cast<Compaction*>(arg);
  297. output_levels.insert(compaction->output_level());
  298. });
  299. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  300. ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
  301. ASSERT_EQ(output_levels.size(), 2);
  302. ASSERT_TRUE(output_levels.find(3) != output_levels.end());
  303. ASSERT_TRUE(output_levels.find(4) != output_levels.end());
  304. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level0", &str_prop));
  305. ASSERT_EQ("0", str_prop);
  306. ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level3", &str_prop));
  307. ASSERT_EQ("0", str_prop);
  308. // Base level is still level 3.
  309. ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
  310. ASSERT_EQ(3U, int_prop);
  311. }
  312. TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBaseInc) {
  313. Options options = CurrentOptions();
  314. options.create_if_missing = true;
  315. options.write_buffer_size = 2048;
  316. options.max_write_buffer_number = 2;
  317. options.level0_file_num_compaction_trigger = 2;
  318. options.level0_slowdown_writes_trigger = 2;
  319. options.level0_stop_writes_trigger = 2;
  320. options.target_file_size_base = 2048;
  321. options.level_compaction_dynamic_level_bytes = true;
  322. options.max_bytes_for_level_base = 10240;
  323. options.max_bytes_for_level_multiplier = 4;
  324. options.max_background_compactions = 2;
  325. options.num_levels = 5;
  326. options.max_compaction_bytes = 100000000;
  327. DestroyAndReopen(options);
  328. int non_trivial = 0;
  329. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
  330. "DBImpl::BackgroundCompaction:NonTrivial",
  331. [&](void* /*arg*/) { non_trivial++; });
  332. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
  333. Random rnd(301);
  334. const int total_keys = 3000;
  335. const int random_part_size = 100;
  336. for (int i = 0; i < total_keys; i++) {
  337. std::string value = rnd.RandomString(random_part_size);
  338. PutFixed32(&value, static_cast<uint32_t>(i));
  339. ASSERT_OK(Put(Key(i), value));
  340. }
  341. ASSERT_OK(Flush());
  342. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  343. ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
  344. ASSERT_EQ(non_trivial, 0);
  345. for (int i = 0; i < total_keys; i++) {
  346. std::string value = Get(Key(i));
  347. ASSERT_EQ(DecodeFixed32(value.c_str() + random_part_size),
  348. static_cast<uint32_t>(i));
  349. }
  350. env_->SetBackgroundThreads(1, Env::LOW);
  351. env_->SetBackgroundThreads(1, Env::HIGH);
  352. }
  353. TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
  354. Random rnd(301);
  355. const int kMaxKey = 2000;
  356. Options options;
  357. options.create_if_missing = true;
  358. options.write_buffer_size = 2048;
  359. options.max_write_buffer_number = 8;
  360. options.level0_file_num_compaction_trigger = 4;
  361. options.level0_slowdown_writes_trigger = 4;
  362. options.level0_stop_writes_trigger = 8;
  363. options.target_file_size_base = 2048;
  364. options.level_compaction_dynamic_level_bytes = false;
  365. options.max_bytes_for_level_base = 10240;
  366. options.max_bytes_for_level_multiplier = 4;
  367. options.num_levels = 8;
  368. DestroyAndReopen(options);
  369. auto verify_func = [&](int num_keys, bool if_sleep) {
  370. for (int i = 0; i < num_keys; i++) {
  371. ASSERT_NE("NOT_FOUND", Get(Key(kMaxKey + i)));
  372. if (i < num_keys / 10) {
  373. ASSERT_EQ("NOT_FOUND", Get(Key(i)));
  374. } else {
  375. ASSERT_NE("NOT_FOUND", Get(Key(i)));
  376. }
  377. if (if_sleep && i % 1000 == 0) {
  378. // Without it, valgrind may choose not to give another
  379. // thread a chance to run before finishing the function,
  380. // causing the test to be extremely slow.
  381. env_->SleepForMicroseconds(1);
  382. }
  383. }
  384. };
  385. int total_keys = 1000;
  386. for (int i = 0; i < total_keys; i++) {
  387. ASSERT_OK(Put(Key(i), rnd.RandomString(102)));
  388. ASSERT_OK(Put(Key(kMaxKey + i), rnd.RandomString(102)));
  389. ASSERT_OK(Delete(Key(i / 10)));
  390. }
  391. verify_func(total_keys, false);
  392. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  393. options.level_compaction_dynamic_level_bytes = true;
  394. options.disable_auto_compactions = true;
  395. Reopen(options);
  396. verify_func(total_keys, false);
  397. std::atomic_bool compaction_finished;
  398. compaction_finished = false;
  399. // Issue manual compaction in one thread and still verify DB state
  400. // in main thread.
  401. ROCKSDB_NAMESPACE::port::Thread t([&]() {
  402. CompactRangeOptions compact_options;
  403. compact_options.change_level = true;
  404. compact_options.target_level = options.num_levels - 1;
  405. ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
  406. compaction_finished.store(true);
  407. });
  408. do {
  409. verify_func(total_keys, true);
  410. } while (!compaction_finished.load());
  411. t.join();
  412. ASSERT_OK(dbfull()->SetOptions({
  413. {"disable_auto_compactions", "false"},
  414. }));
  415. int total_keys2 = 2000;
  416. for (int i = total_keys; i < total_keys2; i++) {
  417. ASSERT_OK(Put(Key(i), rnd.RandomString(102)));
  418. ASSERT_OK(Put(Key(kMaxKey + i), rnd.RandomString(102)));
  419. ASSERT_OK(Delete(Key(i / 10)));
  420. }
  421. verify_func(total_keys2, false);
  422. ASSERT_OK(dbfull()->TEST_WaitForCompact());
  423. verify_func(total_keys2, false);
  424. // Base level is not level 1
  425. ASSERT_EQ(NumTableFilesAtLevel(1), 0);
  426. ASSERT_EQ(NumTableFilesAtLevel(2), 0);
  427. }
  428. } // namespace ROCKSDB_NAMESPACE
  429. int main(int argc, char** argv) {
  430. ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
  431. ::testing::InitGoogleTest(&argc, argv);
  432. return RUN_ALL_TESTS();
  433. }