2016-02-09 23:12:00 +00:00
|
|
|
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
|
2017-07-15 23:03:42 +00:00
|
|
|
// 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).
|
2015-07-14 02:00:30 +00:00
|
|
|
//
|
|
|
|
// 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
|
|
|
|
|
2015-10-12 20:05:42 +00:00
|
|
|
#include "db/db_test_util.h"
|
2017-02-06 22:43:55 +00:00
|
|
|
#include "port/port.h"
|
2015-07-14 02:00:30 +00:00
|
|
|
#include "port/stack_trace.h"
|
2021-09-21 15:53:03 +00:00
|
|
|
#include "rocksdb/env.h"
|
2020-07-09 21:33:42 +00:00
|
|
|
#include "util/random.h"
|
2015-07-14 02:00:30 +00:00
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
namespace ROCKSDB_NAMESPACE {
|
2015-07-14 02:00:30 +00:00
|
|
|
class DBTestDynamicLevel : public DBTestBase {
|
|
|
|
public:
|
2020-08-18 01:41:20 +00:00
|
|
|
DBTestDynamicLevel()
|
2021-07-23 15:37:27 +00:00
|
|
|
: DBTestBase("db_dynamic_level_test", /*env_do_fsync=*/true) {}
|
2015-07-14 02:00:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase) {
|
|
|
|
if (!Snappy_Supported() || !LZ4_Supported()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Use InMemoryEnv, or it would be too slow.
|
2021-09-21 15:53:03 +00:00
|
|
|
std::unique_ptr<Env> env(NewMemEnv(env_));
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
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) {
|
2020-04-20 20:21:34 +00:00
|
|
|
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
|
|
|
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.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;
|
2017-03-13 16:41:30 +00:00
|
|
|
options.env = env_;
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
|
|
|
|
for (int i = 0; i < kNKeys; i++) {
|
|
|
|
int key = keys[i];
|
2020-07-09 21:33:42 +00:00
|
|
|
ASSERT_OK(Put(Key(kNKeys + key), rnd.RandomString(102)));
|
|
|
|
ASSERT_OK(Put(Key(key), rnd.RandomString(102)));
|
|
|
|
ASSERT_OK(Put(Key(kNKeys * 2 + key), rnd.RandomString(102)));
|
2015-07-14 02:00:30 +00:00
|
|
|
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
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(
|
|
|
|
dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
2015-07-14 02:00:30 +00:00
|
|
|
// 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
|
2016-05-25 01:21:54 +00:00
|
|
|
TEST_F(DBTestDynamicLevel, DynamicLevelMaxBytesBase2) {
|
2015-07-14 02:00:30 +00:00
|
|
|
Random rnd(301);
|
|
|
|
int kMaxKey = 1000000;
|
|
|
|
|
|
|
|
Options options = CurrentOptions();
|
2018-11-13 00:40:08 +00:00
|
|
|
options.compression = kNoCompression;
|
2015-07-14 02:00:30 +00:00
|
|
|
options.create_if_missing = true;
|
2016-05-25 01:21:54 +00:00
|
|
|
options.write_buffer_size = 20480;
|
2015-07-14 02:00:30 +00:00
|
|
|
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;
|
2016-05-25 01:21:54 +00:00
|
|
|
options.target_file_size_base = 9102;
|
2015-07-14 02:00:30 +00:00
|
|
|
options.level_compaction_dynamic_level_bytes = true;
|
2016-05-25 01:21:54 +00:00
|
|
|
options.max_bytes_for_level_base = 40960;
|
2015-07-14 02:00:30 +00:00
|
|
|
options.max_bytes_for_level_multiplier = 4;
|
|
|
|
options.max_background_compactions = 2;
|
|
|
|
options.num_levels = 5;
|
2016-06-16 23:02:52 +00:00
|
|
|
options.max_compaction_bytes = 0; // Force not expanding in compactions
|
2020-10-19 18:37:05 +00:00
|
|
|
options.db_host_id = ""; // Setting this messes up the file size calculation
|
2015-07-14 02:00:30 +00:00
|
|
|
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);
|
|
|
|
|
2016-05-25 01:21:54 +00:00
|
|
|
// Put about 28K to L0
|
2015-07-14 02:00:30 +00:00
|
|
|
for (int i = 0; i < 70; i++) {
|
|
|
|
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
2020-07-09 21:33:42 +00:00
|
|
|
rnd.RandomString(380)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "false"},
|
|
|
|
}));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
|
|
|
ASSERT_EQ(4U, int_prop);
|
|
|
|
|
2018-11-13 00:40:08 +00:00
|
|
|
// Insert extra about 28K to L0. After they are compacted to L4, the base
|
|
|
|
// level should be changed to L3.
|
2015-07-14 02:00:30 +00:00
|
|
|
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))),
|
2020-07-09 21:33:42 +00:00
|
|
|
rnd.RandomString(380)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "false"},
|
|
|
|
}));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
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);
|
|
|
|
|
2018-11-13 00:40:08 +00:00
|
|
|
// Write even more data while leaving the base level at L3.
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "true"},
|
|
|
|
}));
|
2016-05-25 01:21:54 +00:00
|
|
|
// Write about 40K more
|
2015-07-14 02:00:30 +00:00
|
|
|
for (int i = 0; i < 100; i++) {
|
|
|
|
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
2020-07-09 21:33:42 +00:00
|
|
|
rnd.RandomString(380)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "false"},
|
|
|
|
}));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
|
|
|
ASSERT_EQ(3U, int_prop);
|
|
|
|
|
2018-11-13 00:40:08 +00:00
|
|
|
// Fill up L0, and then run an (auto) L0->Lmax compaction to raise the base
|
|
|
|
// level to 2.
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "true"},
|
|
|
|
}));
|
2016-06-16 23:02:52 +00:00
|
|
|
// Write about 650K more.
|
|
|
|
// Each file is about 11KB, with 9KB of data.
|
|
|
|
for (int i = 0; i < 1300; i++) {
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
2020-07-09 21:33:42 +00:00
|
|
|
rnd.RandomString(380)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
2018-11-13 00:40:08 +00:00
|
|
|
|
|
|
|
// Make sure that the compaction starts before the last bit of data is
|
|
|
|
// flushed, so that the base level isn't raised to L1.
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
2018-11-13 00:40:08 +00:00
|
|
|
{"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:0"},
|
|
|
|
});
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
2018-11-13 00:40:08 +00:00
|
|
|
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(dbfull()->SetOptions({
|
|
|
|
{"disable_auto_compactions", "false"},
|
|
|
|
}));
|
2018-11-13 00:40:08 +00:00
|
|
|
|
|
|
|
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:0");
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_TRUE(db_->GetIntProperty("rocksdb.base-level", &int_prop));
|
|
|
|
ASSERT_EQ(2U, int_prop);
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
2016-05-25 01:21:54 +00:00
|
|
|
|
2018-11-13 00:40:08 +00:00
|
|
|
// Write more data until the base level changes to L1. There will be
|
|
|
|
// a manual compaction going on at the same time.
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
|
2018-11-13 00:40:08 +00:00
|
|
|
{"CompactionJob::Run():Start", "DynamicLevelMaxBytesBase2:1"},
|
|
|
|
{"DynamicLevelMaxBytesBase2:2", "CompactionJob::Run():End"},
|
2016-05-25 01:21:54 +00:00
|
|
|
{"DynamicLevelMaxBytesBase2:compact_range_finish",
|
|
|
|
"FlushJob::WriteLevel0Table"},
|
|
|
|
});
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::port::Thread thread([this] {
|
2016-05-25 01:21:54 +00:00
|
|
|
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_start");
|
|
|
|
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
|
|
|
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:compact_range_finish");
|
|
|
|
});
|
|
|
|
|
2018-11-13 00:40:08 +00:00
|
|
|
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:1");
|
2016-05-25 01:21:54 +00:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
ASSERT_OK(Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))),
|
2020-07-09 21:33:42 +00:00
|
|
|
rnd.RandomString(380)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
2018-11-13 00:40:08 +00:00
|
|
|
TEST_SYNC_POINT("DynamicLevelMaxBytesBase2:2");
|
2016-05-25 01:21:54 +00:00
|
|
|
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
2016-05-25 01:21:54 +00:00
|
|
|
|
|
|
|
thread.join();
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
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;
|
2016-06-16 23:02:52 +00:00
|
|
|
options.max_compaction_bytes = 1; // Force not expanding in compactions
|
2015-07-14 02:00:30 +00:00
|
|
|
BlockBasedTableOptions table_options;
|
|
|
|
table_options.block_size = 1024;
|
|
|
|
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
|
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
|
|
|
|
// Compact against empty DB
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
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++) {
|
2020-07-09 21:33:42 +00:00
|
|
|
ASSERT_OK(
|
|
|
|
Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
if (NumTableFilesAtLevel(0) == 0) {
|
|
|
|
// Make sure level 0 is not empty
|
2020-07-09 21:33:42 +00:00
|
|
|
ASSERT_OK(
|
|
|
|
Put(Key(static_cast<int>(rnd.Uniform(kMaxKey))), rnd.RandomString(80)));
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
2015-07-14 02:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
std::set<int> output_levels;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
2015-07-14 02:00:30 +00:00
|
|
|
"CompactionPicker::CompactRange:Return", [&](void* arg) {
|
Prefer static_cast in place of most reinterpret_cast (#12308)
Summary:
The following are risks associated with pointer-to-pointer reinterpret_cast:
* Can produce the "wrong result" (crash or memory corruption). IIRC, in theory this can happen for any up-cast or down-cast for a non-standard-layout type, though in practice would only happen for multiple inheritance cases (where the base class pointer might be "inside" the derived object). We don't use multiple inheritance a lot, but we do.
* Can mask useful compiler errors upon code change, including converting between unrelated pointer types that you are expecting to be related, and converting between pointer and scalar types unintentionally.
I can only think of some obscure cases where static_cast could be troublesome when it compiles as a replacement:
* Going through `void*` could plausibly cause unnecessary or broken pointer arithmetic. Suppose we have
`struct Derived: public Base1, public Base2`. If we have `Derived*` -> `void*` -> `Base2*` -> `Derived*` through reinterpret casts, this could plausibly work (though technical UB) assuming the `Base2*` is not dereferenced. Changing to static cast could introduce breaking pointer arithmetic.
* Unnecessary (but safe) pointer arithmetic could arise in a case like `Derived*` -> `Base2*` -> `Derived*` where before the Base2 pointer might not have been dereferenced. This could potentially affect performance.
With some light scripting, I tried replacing pointer-to-pointer reinterpret_casts with static_cast and kept the cases that still compile. Most occurrences of reinterpret_cast have successfully been changed (except for java/ and third-party/). 294 changed, 257 remain.
A couple of related interventions included here:
* Previously Cache::Handle was not actually derived from in the implementations and just used as a `void*` stand-in with reinterpret_cast. Now there is a relationship to allow static_cast. In theory, this could introduce pointer arithmetic (as described above) but is unlikely without multiple inheritance AND non-empty Cache::Handle.
* Remove some unnecessary casts to void* as this is allowed to be implicit (for better or worse).
Most of the remaining reinterpret_casts are for converting to/from raw bytes of objects. We could consider better idioms for these patterns in follow-up work.
I wish there were a way to implement a template variant of static_cast that would only compile if no pointer arithmetic is generated, but best I can tell, this is not possible. AFAIK the best you could do is a dynamic check that the void* conversion after the static cast is unchanged.
Pull Request resolved: https://github.com/facebook/rocksdb/pull/12308
Test Plan: existing tests, CI
Reviewed By: ltamasi
Differential Revision: D53204947
Pulled By: pdillinger
fbshipit-source-id: 9de23e618263b0d5b9820f4e15966876888a16e2
2024-02-07 18:44:11 +00:00
|
|
|
Compaction* compaction = static_cast<Compaction*>(arg);
|
2015-07-14 02:00:30 +00:00
|
|
|
output_levels.insert(compaction->output_level());
|
|
|
|
});
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
|
2015-07-14 02:00:30 +00:00
|
|
|
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.max_background_compactions = 2;
|
|
|
|
options.num_levels = 5;
|
2017-06-02 21:13:59 +00:00
|
|
|
options.max_compaction_bytes = 100000000;
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
DestroyAndReopen(options);
|
|
|
|
|
|
|
|
int non_trivial = 0;
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
2015-07-14 02:00:30 +00:00
|
|
|
"DBImpl::BackgroundCompaction:NonTrivial",
|
2018-04-13 00:55:14 +00:00
|
|
|
[&](void* /*arg*/) { non_trivial++; });
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
Random rnd(301);
|
|
|
|
const int total_keys = 3000;
|
|
|
|
const int random_part_size = 100;
|
|
|
|
for (int i = 0; i < total_keys; i++) {
|
2020-07-09 21:33:42 +00:00
|
|
|
std::string value = rnd.RandomString(random_part_size);
|
2015-07-14 02:00:30 +00:00
|
|
|
PutFixed32(&value, static_cast<uint32_t>(i));
|
|
|
|
ASSERT_OK(Put(Key(i), value));
|
|
|
|
}
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(Flush());
|
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-12-13 19:42:42 +00:00
|
|
|
TEST_F(DBTestDynamicLevel, DISABLED_MigrateToDynamicLevelMaxBytesBase) {
|
2015-07-14 02:00:30 +00:00
|
|
|
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.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++) {
|
2020-07-09 21:33:42 +00:00
|
|
|
ASSERT_OK(Put(Key(i), rnd.RandomString(102)));
|
|
|
|
ASSERT_OK(Put(Key(kMaxKey + i), rnd.RandomString(102)));
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(Delete(Key(i / 10)));
|
|
|
|
}
|
|
|
|
verify_func(total_keys, false);
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
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.
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::port::Thread t([&]() {
|
2015-07-14 02:00:30 +00:00
|
|
|
CompactRangeOptions compact_options;
|
|
|
|
compact_options.change_level = true;
|
|
|
|
compact_options.target_level = options.num_levels - 1;
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(dbfull()->CompactRange(compact_options, nullptr, nullptr));
|
2015-07-14 02:00:30 +00:00
|
|
|
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++) {
|
2020-07-09 21:33:42 +00:00
|
|
|
ASSERT_OK(Put(Key(i), rnd.RandomString(102)));
|
|
|
|
ASSERT_OK(Put(Key(kMaxKey + i), rnd.RandomString(102)));
|
2015-07-14 02:00:30 +00:00
|
|
|
ASSERT_OK(Delete(Key(i / 10)));
|
|
|
|
}
|
|
|
|
|
|
|
|
verify_func(total_keys2, false);
|
2021-01-06 22:14:01 +00:00
|
|
|
ASSERT_OK(dbfull()->TEST_WaitForCompact());
|
2015-07-14 02:00:30 +00:00
|
|
|
verify_func(total_keys2, false);
|
|
|
|
|
|
|
|
// Base level is not level 1
|
|
|
|
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
|
|
|
|
ASSERT_EQ(NumTableFilesAtLevel(2), 0);
|
|
|
|
}
|
2020-02-20 20:07:53 +00:00
|
|
|
} // namespace ROCKSDB_NAMESPACE
|
2015-07-14 02:00:30 +00:00
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) {
|
2020-02-20 20:07:53 +00:00
|
|
|
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
|
2015-07-14 02:00:30 +00:00
|
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
|
|
return RUN_ALL_TESTS();
|
|
|
|
}
|