| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file. See the AUTHORS file for names of contributors.
- // Introduction of SyncPoint effectively disabled building and running this test
- // in Release build.
- // which is a pity, it is a good test
- #if !defined(ROCKSDB_LITE)
- #include "db/db_test_util.h"
- #include "port/port.h"
- #include "port/stack_trace.h"
- namespace ROCKSDB_NAMESPACE {
- class DBTestDynamicLevel : public DBTestBase {
- public:
- DBTestDynamicLevel() : DBTestBase("/db_dynamic_level_test") {}
- };
- TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
- if (!Snappy_Supported() || !LZ4_Supported()) {
- return;
- }
- // Use InMemoryEnv, or it would be too slow.
- std::unique_ptr<Env> env(new MockEnv(env_));
- const int kNKeys = 1000;
- int keys[kNKeys];
- auto verify_func = [&]() {
- for (int i = 0; i < kNKeys; i++) {
- ASSERT_NE("NOT_FOUND", Get(Key(i)));
- ASSERT_NE("NOT_FOUND", Get(Key(kNKeys * 2 + i)));
- if (i < kNKeys / 10) {
- ASSERT_EQ("NOT_FOUND", Get(Key(kNKeys + keys[i])));
- } else {
- ASSERT_NE("NOT_FOUND", Get(Key(kNKeys + keys[i])));
- }
- }
- };
- Random rnd(301);
- for (int ordered_insert = 0; ordered_insert <= 1; ordered_insert++) {
- for (int i = 0; i < kNKeys; i++) {
- keys[i] = i;
- }
- if (ordered_insert == 0) {
- std::random_shuffle(std::begin(keys), std::end(keys));
- }
- for (int max_background_compactions = 1; max_background_compactions < 4;
- max_background_compactions += 2) {
- Options options;
- options.env = env.get();
- options.create_if_missing = true;
- options.write_buffer_size = 2048;
- options.max_write_buffer_number = 2;
- options.level0_file_num_compaction_trigger = 2;
- options.level0_slowdown_writes_trigger = 2;
- options.level0_stop_writes_trigger = 2;
- options.target_file_size_base = 2048;
- options.level_compaction_dynamic_level_bytes = true;
- options.max_bytes_for_level_base = 10240;
- options.max_bytes_for_level_multiplier = 4;
- options.soft_rate_limit = 1.1;
- options.max_background_compactions = max_background_compactions;
- options.num_levels = 5;
- options.compression_per_level.resize(3);
- options.compression_per_level[0] = kNoCompression;
- options.compression_per_level[1] = kLZ4Compression;
- options.compression_per_level[2] = kSnappyCompression;
- options.env = env_;
- DestroyAndReopen(options);
- for (int i = 0; i < kNKeys; i++) {
- int key = keys[i];
- ASSERT_OK(Put(Key(kNKeys + key), RandomString(&rnd, 102)));
- ASSERT_OK(Put(Key(key), RandomString(&rnd, 102)));
- ASSERT_OK(Put(Key(kNKeys * 2 + key), RandomString(&rnd, 102)));
- ASSERT_OK(Delete(Key(kNKeys + keys[i / 10])));
- env_->SleepForMicroseconds(5000);
- }
- uint64_t int_prop;
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.background-errors", &int_prop));
- ASSERT_EQ(0U, int_prop);
- // Verify DB
- for (int j = 0; j < 2; j++) {
- verify_func();
- if (j == 0) {
- Reopen(options);
- }
- }
- // Test compact range works
- dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- // All data should be in the last level.
- ColumnFamilyMetaData cf_meta;
- db_->GetColumnFamilyMetaData(&cf_meta);
- ASSERT_EQ(5U, cf_meta.levels.size());
- for (int i = 0; i < 4; i++) {
- ASSERT_EQ(0U, cf_meta.levels[i].files.size());
- }
- ASSERT_GT(cf_meta.levels[4U].files.size(), 0U);
- verify_func();
- Close();
- }
- }
- env_->SetBackgroundThreads(1, Env::LOW);
- env_->SetBackgroundThreads(1, Env::HIGH);
- }
- // Test specific cases in dynamic max bytes
- TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
- Random rnd(301);
- int kMaxKey = 1000000;
- Options options = CurrentOptions();
- options.compression = kNoCompression;
- options.create_if_missing = true;
- options.write_buffer_size = 20480;
- options.max_write_buffer_number = 2;
- options.level0_file_num_compaction_trigger = 2;
- options.level0_slowdown_writes_trigger = 9999;
- options.level0_stop_writes_trigger = 9999;
- options.target_file_size_base = 9102;
- options.level_compaction_dynamic_level_bytes = true;
- options.max_bytes_for_level_base = 40960;
- options.max_bytes_for_level_multiplier = 4;
- options.max_background_compactions = 2;
- options.num_levels = 5;
- options.max_compaction_bytes = 0; // Force not expanding in compactions
- BlockBasedTableOptions table_options;
- table_options.block_size = 1024;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- DestroyAndReopen(options);
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "true"},
- }));
- uint64_t int_prop;
- std::string str_prop;
- // Initial base level is the last level
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(4U, int_prop);
- // Put about 28K to L0
- for (int i = 0; i < 70; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 380)));
- }
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "false"},
- }));
- Flush();
- dbfull()->TEST_WaitForCompact();
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(4U, int_prop);
- // Insert extra about 28K to L0. After they are compacted to L4, the base
- // level should be changed to L3.
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "true"},
- }));
- for (int i = 0; i < 70; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 380)));
- }
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "false"},
- }));
- Flush();
- dbfull()->TEST_WaitForCompact();
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(3U, int_prop);
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level1", &str_prop));
- ASSERT_EQ("0", str_prop);
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level2", &str_prop));
- ASSERT_EQ("0", str_prop);
- // Write even more data while leaving the base level at L3.
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "true"},
- }));
- // Write about 40K more
- for (int i = 0; i < 100; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 380)));
- }
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "false"},
- }));
- Flush();
- dbfull()->TEST_WaitForCompact();
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(3U, int_prop);
- // Fill up L0, and then run an (auto) L0->Lmax compaction to raise the base
- // level to 2.
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "true"},
- }));
- // Write about 650K more.
- // Each file is about 11KB, with 9KB of data.
- for (int i = 0; i < 1300; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 380)));
- }
- // Make sure that the compaction starts before the last bit of data is
- // flushed, so that the base level isn't raised to L1.
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
- {"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:0"},
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "false"},
- }));
- TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
- Flush();
- dbfull()->TEST_WaitForCompact();
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(2U, int_prop);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- // Write more data until the base level changes to L1. There will be
- // a manual compaction going on at the same time.
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
- {"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:1"},
- {"DynamicLevelMaxBytesBase2:2", "CompactionJob::Run():End"},
- {"DynamicLevelMaxBytesBase2:compact_range_finish",
- "FlushJob::WriteLevel0Table"},
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- ROCKSDB_NAMESPACE::port::Thread thread([this] {
- TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_start");
- ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
- TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
- });
- TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:1");
- for (int i = 0; i < 2; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 380)));
- }
- TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:2");
- Flush();
- thread.join();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(1U, int_prop);
- }
- // Test specific cases in dynamic max bytes
- TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesCompactRange) {
- Random rnd(301);
- int kMaxKey = 1000000;
- Options options = CurrentOptions();
- options.create_if_missing = true;
- options.write_buffer_size = 2048;
- options.max_write_buffer_number = 2;
- options.level0_file_num_compaction_trigger = 2;
- options.level0_slowdown_writes_trigger = 9999;
- options.level0_stop_writes_trigger = 9999;
- options.target_file_size_base = 2;
- options.level_compaction_dynamic_level_bytes = true;
- options.max_bytes_for_level_base = 10240;
- options.max_bytes_for_level_multiplier = 4;
- options.max_background_compactions = 1;
- const int kNumLevels = 5;
- options.num_levels = kNumLevels;
- options.max_compaction_bytes = 1; // Force not expanding in compactions
- BlockBasedTableOptions table_options;
- table_options.block_size = 1024;
- options.table_factory.reset(NewBlockBasedTableFactory(table_options));
- DestroyAndReopen(options);
- // Compact against empty DB
- dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- uint64_t int_prop;
- std::string str_prop;
- // Initial base level is the last level
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(4U, int_prop);
- // Put about 7K to L0
- for (int i = 0; i < 140; i++) {
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 80)));
- }
- Flush();
- dbfull()->TEST_WaitForCompact();
- if (NumTableFilesAtLevel(0) == 0) {
- // Make sure level 0 is not empty
- ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
- RandomString(&rnd, 80)));
- Flush();
- }
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(3U, int_prop);
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level1", &str_prop));
- ASSERT_EQ("0", str_prop);
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level2", &str_prop));
- ASSERT_EQ("0", str_prop);
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
- std::set<int> output_levels;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "CompactionPicker::CompactRange:Return", [&](void* arg) {
- Compaction* compaction = reinterpret_cast<Compaction*>(arg);
- output_levels.insert(compaction->output_level());
- });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
- ASSERT_EQ(output_levels.size(), 2);
- ASSERT_TRUE(output_levels.find(3) != output_levels.end());
- ASSERT_TRUE(output_levels.find(4) != output_levels.end());
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level0", &str_prop));
- ASSERT_EQ("0", str_prop);
- ASSERT_TRUE(db_->GetProperty("rocksdb.num-files-at-level3", &str_prop));
- ASSERT_EQ("0", str_prop);
- // Base level is still level 3.
- ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
- ASSERT_EQ(3U, int_prop);
- }
- TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBaseInc) {
- Options options = CurrentOptions();
- options.create_if_missing = true;
- options.write_buffer_size = 2048;
- options.max_write_buffer_number = 2;
- options.level0_file_num_compaction_trigger = 2;
- options.level0_slowdown_writes_trigger = 2;
- options.level0_stop_writes_trigger = 2;
- options.target_file_size_base = 2048;
- options.level_compaction_dynamic_level_bytes = true;
- options.max_bytes_for_level_base = 10240;
- options.max_bytes_for_level_multiplier = 4;
- options.soft_rate_limit = 1.1;
- options.max_background_compactions = 2;
- options.num_levels = 5;
- options.max_compaction_bytes = 100000000;
- DestroyAndReopen(options);
- int non_trivial = 0;
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
- "DBImpl::BackgroundCompaction:NonTrivial",
- [&](void* /*arg*/) { non_trivial++; });
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
- Random rnd(301);
- const int total_keys = 3000;
- const int random_part_size = 100;
- for (int i = 0; i < total_keys; i++) {
- std::string value = RandomString(&rnd, random_part_size);
- PutFixed32(&value, static_cast<uint32_t>(i));
- ASSERT_OK(Put(Key(i), value));
- }
- Flush();
- dbfull()->TEST_WaitForCompact();
- ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
- ASSERT_EQ(non_trivial, 0);
- for (int i = 0; i < total_keys; i++) {
- std::string value = Get(Key(i));
- ASSERT_EQ(DecodeFixed32(value.c_str() + random_part_size),
- static_cast<uint32_t>(i));
- }
- env_->SetBackgroundThreads(1, Env::LOW);
- env_->SetBackgroundThreads(1, Env::HIGH);
- }
- TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
- Random rnd(301);
- const int kMaxKey = 2000;
- Options options;
- options.create_if_missing = true;
- options.write_buffer_size = 2048;
- options.max_write_buffer_number = 8;
- options.level0_file_num_compaction_trigger = 4;
- options.level0_slowdown_writes_trigger = 4;
- options.level0_stop_writes_trigger = 8;
- options.target_file_size_base = 2048;
- options.level_compaction_dynamic_level_bytes = false;
- options.max_bytes_for_level_base = 10240;
- options.max_bytes_for_level_multiplier = 4;
- options.soft_rate_limit = 1.1;
- options.num_levels = 8;
- DestroyAndReopen(options);
- auto verify_func = [&](int num_keys, bool if_sleep) {
- for (int i = 0; i < num_keys; i++) {
- ASSERT_NE("NOT_FOUND", Get(Key(kMaxKey + i)));
- if (i < num_keys / 10) {
- ASSERT_EQ("NOT_FOUND", Get(Key(i)));
- } else {
- ASSERT_NE("NOT_FOUND", Get(Key(i)));
- }
- if (if_sleep && i % 1000 == 0) {
- // Without it, valgrind may choose not to give another
- // thread a chance to run before finishing the function,
- // causing the test to be extremely slow.
- env_->SleepForMicroseconds(1);
- }
- }
- };
- int total_keys = 1000;
- for (int i = 0; i < total_keys; i++) {
- ASSERT_OK(Put(Key(i), RandomString(&rnd, 102)));
- ASSERT_OK(Put(Key(kMaxKey + i), RandomString(&rnd, 102)));
- ASSERT_OK(Delete(Key(i / 10)));
- }
- verify_func(total_keys, false);
- dbfull()->TEST_WaitForCompact();
- options.level_compaction_dynamic_level_bytes = true;
- options.disable_auto_compactions = true;
- Reopen(options);
- verify_func(total_keys, false);
- std::atomic_bool compaction_finished;
- compaction_finished = false;
- // Issue manual compaction in one thread and still verify DB state
- // in main thread.
- ROCKSDB_NAMESPACE::port::Thread t([&]() {
- CompactRangeOptions compact_options;
- compact_options.change_level = true;
- compact_options.target_level = options.num_levels - 1;
- dbfull()->CompactRange(compact_options, nullptr, nullptr);
- compaction_finished.store(true);
- });
- do {
- verify_func(total_keys, true);
- } while (!compaction_finished.load());
- t.join();
- ASSERT_OK(dbfull()->SetOptions({
- {"disable_auto_compactions", "false"},
- }));
- int total_keys2 = 2000;
- for (int i = total_keys; i < total_keys2; i++) {
- ASSERT_OK(Put(Key(i), RandomString(&rnd, 102)));
- ASSERT_OK(Put(Key(kMaxKey + i), RandomString(&rnd, 102)));
- ASSERT_OK(Delete(Key(i / 10)));
- }
- verify_func(total_keys2, false);
- dbfull()->TEST_WaitForCompact();
- verify_func(total_keys2, false);
- // Base level is not level 1
- ASSERT_EQ(NumTableFilesAtLevel(1), 0);
- ASSERT_EQ(NumTableFilesAtLevel(2), 0);
- }
- } // namespace ROCKSDB_NAMESPACE
- #endif // !defined(ROCKSDB_LITE)
- int main(int argc, char** argv) {
- #if !defined(ROCKSDB_LITE)
- ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
- ::testing::InitGoogleTest(&argc, argv);
- return RUN_ALL_TESTS();
- #else
- (void) argc;
- (void) argv;
- return 0;
- #endif
- }
|