db_dynamic_level_test.cc 17 KB

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