#include <tuple>
#include "compaction/compaction_picker_universal.h"
#include "db/blob/blob_index.h"
#include "db/db_test_util.h"
#include "db/dbformat.h"
#include "env/mock_env.h"
#include "port/port.h"
#include "port/stack_trace.h"
#include "rocksdb/advanced_options.h"
#include "rocksdb/concurrent_task_limiter.h"
#include "rocksdb/experimental.h"
#include "rocksdb/iostats_context.h"
#include "rocksdb/sst_file_writer.h"
#include "test_util/mock_time_env.h"
#include "test_util/sync_point.h"
#include "test_util/testutil.h"
#include "util/concurrent_task_limiter_impl.h"
#include "util/random.h"
#include "utilities/fault_injection_env.h"
#include "utilities/fault_injection_fs.h"
namespace ROCKSDB_NAMESPACE {
class CompactionStatsCollector : public EventListener {
public:
CompactionStatsCollector()
: compaction_completed_(
static_cast<int>(CompactionReason::kNumOfReasons)) {
for (auto& v : compaction_completed_) {
v.store(0);
}
}
~CompactionStatsCollector() override = default;
void OnCompactionCompleted(DB* ,
const CompactionJobInfo& info) override {
int k = static_cast<int>(info.compaction_reason);
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
assert(k >= 0 && k < num_of_reasons);
compaction_completed_[k]++;
}
void OnExternalFileIngested(
DB* , const ExternalFileIngestionInfo& ) override {
int k = static_cast<int>(CompactionReason::kExternalSstIngestion);
compaction_completed_[k]++;
}
void OnFlushCompleted(DB* , const FlushJobInfo& ) override {
int k = static_cast<int>(CompactionReason::kFlush);
compaction_completed_[k]++;
}
int NumberOfCompactions(CompactionReason reason) const {
int num_of_reasons = static_cast<int>(CompactionReason::kNumOfReasons);
int k = static_cast<int>(reason);
assert(k >= 0 && k < num_of_reasons);
return compaction_completed_.at(k).load();
}
private:
std::vector<std::atomic<int>> compaction_completed_;
};
class DeletionTriggeredCompactionWithMinFileSizeTestListener
: public EventListener {
public:
explicit DeletionTriggeredCompactionWithMinFileSizeTestListener(
uint64_t min_file_size)
: min_file_size_(min_file_size) {}
void OnCompactionBegin(DB* db, const CompactionJobInfo& ci) override {
ASSERT_EQ(ci.compaction_reason,
CompactionReason::kFilesMarkedForCompaction);
auto env = db->GetEnv();
const std::vector<DbPath>& db_paths = db->GetOptions().db_paths;
for (const auto& file : ci.input_file_infos) {
uint64_t file_size = GetSstFileSize(env, db_paths, file.file_number);
ASSERT_GE(file_size, min_file_size_);
}
}
private:
static uint64_t GetSstFileSize(Env* env, const std::vector<DbPath>& db_paths,
uint64_t file_number) {
uint32_t path_id = 0; std::string sst_file_name = TableFileName(db_paths, file_number, path_id);
uint64_t file_size = 0;
Status s = env->GetFileSize(sst_file_name, &file_size);
if (!s.ok()) {
return 0;
}
return file_size;
}
uint64_t min_file_size_;
};
class DBCompactionTest : public DBTestBase {
public:
DBCompactionTest()
: DBTestBase("db_compaction_test", false) {}
protected:
void VerifyCompactionStats(ColumnFamilyData& cfd,
const CompactionStatsCollector& collector) {
#ifndef NDEBUG
InternalStats* internal_stats_ptr = cfd.internal_stats();
ASSERT_NE(internal_stats_ptr, nullptr);
const std::vector<InternalStats::CompactionStats>& comp_stats =
internal_stats_ptr->TEST_GetCompactionStats();
const int num_of_reasons =
static_cast<int>(CompactionReason::kNumOfReasons);
std::vector<int> counts(num_of_reasons, 0);
for (const auto& stat : comp_stats) {
int sum = 0;
for (int i = 0; i < num_of_reasons; i++) {
counts[i] += stat.counts[i];
sum += stat.counts[i];
}
ASSERT_EQ(sum, stat.count);
}
for (int i = 0; i < num_of_reasons; i++) {
ASSERT_EQ(collector.NumberOfCompactions(static_cast<CompactionReason>(i)),
counts[i]);
}
#endif
}
};
class DBCompactionTestWithParam
: public DBTestBase,
public testing::WithParamInterface<std::tuple<uint32_t, bool>> {
public:
DBCompactionTestWithParam()
: DBTestBase("db_compaction_test", false) {
max_subcompactions_ = std::get<0>(GetParam());
exclusive_manual_compaction_ = std::get<1>(GetParam());
}
class TrivialMoveEventListener : public EventListener {
public:
explicit TrivialMoveEventListener(size_t expected_trivially_moved_files)
: expected_trivially_moved_files_(expected_trivially_moved_files) {}
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
ASSERT_EQ(ci.stats.num_input_files_trivially_moved,
expected_trivially_moved_files_);
}
private:
size_t expected_trivially_moved_files_ = 0;
};
static void SetUpTestCase() {}
static void TearDownTestCase() {}
uint32_t max_subcompactions_;
bool exclusive_manual_compaction_;
};
class DBCompactionTestWithBottommostParam
: public DBTestBase,
public testing::WithParamInterface<
std::tuple<BottommostLevelCompaction, bool>> {
public:
DBCompactionTestWithBottommostParam()
: DBTestBase("db_compaction_test", false) {
bottommost_level_compaction_ = std::get<0>(GetParam());
}
BottommostLevelCompaction bottommost_level_compaction_;
};
class DBCompactionDirectIOTest : public DBCompactionTest,
public ::testing::WithParamInterface<bool> {
public:
DBCompactionDirectIOTest() : DBCompactionTest() {}
};
class DBCompactionWaitForCompactTest
: public DBTestBase,
public testing::WithParamInterface<
std::tuple<bool, bool, bool, std::chrono::microseconds>> {
public:
DBCompactionWaitForCompactTest()
: DBTestBase("db_compaction_test", false) {
abort_on_pause_ = std::get<0>(GetParam());
flush_ = std::get<1>(GetParam());
close_db_ = std::get<2>(GetParam());
timeout_ = std::get<3>(GetParam());
}
bool abort_on_pause_;
bool flush_;
bool close_db_;
std::chrono::microseconds timeout_;
Options options_;
WaitForCompactOptions wait_for_compact_options_;
void SetUp() override {
const int kNumKeysPerFile = 4;
const int kNumFiles = 2;
options_ = CurrentOptions();
options_.level0_file_num_compaction_trigger = kNumFiles + 1;
wait_for_compact_options_ = WaitForCompactOptions();
wait_for_compact_options_.abort_on_pause = abort_on_pause_;
wait_for_compact_options_.flush = flush_;
wait_for_compact_options_.close_db = close_db_;
wait_for_compact_options_.timeout = timeout_;
DestroyAndReopen(options_);
Random rnd(301);
for (int i = 0; i < kNumFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(100 )));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2", FilesPerLevel());
}
};
class ChangeLevelConflictsWithAuto
: public DBCompactionTest,
public ::testing::WithParamInterface<bool> {
public:
ChangeLevelConflictsWithAuto() : DBCompactionTest() {}
};
class RoundRobinSubcompactionsAgainstPressureToken
: public DBCompactionTest,
public ::testing::WithParamInterface<bool> {
public:
RoundRobinSubcompactionsAgainstPressureToken() {
grab_pressure_token_ = GetParam();
}
bool grab_pressure_token_;
};
class RoundRobinSubcompactionsAgainstResources
: public DBCompactionTest,
public ::testing::WithParamInterface<std::tuple<int, int>> {
public:
RoundRobinSubcompactionsAgainstResources() {
total_low_pri_threads_ = std::get<0>(GetParam());
max_compaction_limits_ = std::get<1>(GetParam());
}
int total_low_pri_threads_;
int max_compaction_limits_;
};
namespace {
class FlushedFileCollector : public EventListener {
public:
FlushedFileCollector() = default;
~FlushedFileCollector() override = default;
void OnFlushCompleted(DB* , const FlushJobInfo& info) override {
std::lock_guard<std::mutex> lock(mutex_);
flushed_files_.push_back(info.file_path);
}
std::vector<std::string> GetFlushedFiles() {
std::lock_guard<std::mutex> lock(mutex_);
std::vector<std::string> result;
for (const auto& fname : flushed_files_) {
result.push_back(fname);
}
return result;
}
void ClearFlushedFiles() { flushed_files_.clear(); }
private:
std::vector<std::string> flushed_files_;
std::mutex mutex_;
};
class SstStatsCollector : public EventListener {
public:
SstStatsCollector() : num_ssts_creation_started_(0) {}
void OnTableFileCreationStarted(
const TableFileCreationBriefInfo& ) override {
++num_ssts_creation_started_;
}
int num_ssts_creation_started() { return num_ssts_creation_started_; }
private:
std::atomic<int> num_ssts_creation_started_;
};
static const int kCDTValueSize = 1000;
static const int kCDTKeysPerBuffer = 4;
static const int kCDTNumLevels = 8;
Options DeletionTriggerOptions(Options options) {
options.compression = kNoCompression;
options.write_buffer_size = kCDTKeysPerBuffer * (kCDTValueSize + 24);
options.min_write_buffer_number_to_merge = 1;
options.max_write_buffer_size_to_maintain = 0;
options.num_levels = kCDTNumLevels;
options.level0_file_num_compaction_trigger = 1;
options.target_file_size_base = options.write_buffer_size * 2;
options.target_file_size_multiplier = 2;
options.max_bytes_for_level_base =
options.target_file_size_base * options.target_file_size_multiplier;
options.max_bytes_for_level_multiplier = 2;
options.disable_auto_compactions = false;
options.compaction_options_universal.max_size_amplification_percent = 100;
return options;
}
bool HaveOverlappingKeyRanges(const Comparator* c, const SstFileMetaData& a,
const SstFileMetaData& b) {
if (c->CompareWithoutTimestamp(a.smallestkey, b.smallestkey) >= 0) {
if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
return true;
}
} else if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
return true;
}
if (c->CompareWithoutTimestamp(a.largestkey, b.largestkey) <= 0) {
if (c->CompareWithoutTimestamp(a.largestkey, b.smallestkey) >= 0) {
return true;
}
} else if (c->CompareWithoutTimestamp(a.smallestkey, b.largestkey) <= 0) {
return true;
}
return false;
}
void GetOverlappingFileNumbersForLevelCompaction(
const ColumnFamilyMetaData& cf_meta, const Comparator* comparator,
int min_level, int max_level, const SstFileMetaData* input_file_meta,
std::set<std::string>* overlapping_file_names) {
std::set<const SstFileMetaData*> overlapping_files;
overlapping_files.insert(input_file_meta);
for (int m = min_level; m <= max_level; ++m) {
for (auto& file : cf_meta.levels[m].files) {
for (auto* included_file : overlapping_files) {
if (HaveOverlappingKeyRanges(comparator, *included_file, file)) {
overlapping_files.insert(&file);
overlapping_file_names->insert(file.name);
break;
}
}
}
}
}
void VerifyCompactionResult(
const ColumnFamilyMetaData& cf_meta,
const std::set<std::string>& overlapping_file_numbers) {
#ifndef NDEBUG
for (auto& level : cf_meta.levels) {
for (auto& file : level.files) {
assert(overlapping_file_numbers.find(file.name) ==
overlapping_file_numbers.end());
}
}
#endif
}
const SstFileMetaData* PickFileRandomly(const ColumnFamilyMetaData& cf_meta,
Random* rand, int* level = nullptr) {
auto file_id = rand->Uniform(static_cast<int>(cf_meta.file_count)) + 1;
for (auto& level_meta : cf_meta.levels) {
if (file_id <= level_meta.files.size()) {
if (level != nullptr) {
*level = level_meta.level;
}
auto result = rand->Uniform(file_id);
return &(level_meta.files[result]);
}
file_id -= static_cast<uint32_t>(level_meta.files.size());
}
assert(false);
return nullptr;
}
}
#if !defined(ROCKSDB_VALGRIND_RUN) || defined(ROCKSDB_FULL_VALGRIND_RUN)
TEST_P(DBCompactionTestWithParam, CompactionDeletionTrigger) {
for (int tid = 0; tid < 3; ++tid) {
uint64_t db_size[2];
Options options = DeletionTriggerOptions(CurrentOptions());
options.max_subcompactions = max_subcompactions_;
if (tid == 1) {
options.skip_stats_update_on_db_open = true;
} else if (tid == 2) {
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 1;
}
DestroyAndReopen(options);
Random rnd(301);
const int kTestSize = kCDTKeysPerBuffer * 1024;
std::vector<std::string> values;
for (int k = 0; k < kTestSize; ++k) {
values.push_back(rnd.RandomString(kCDTValueSize));
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[0]));
for (int k = 0; k < kTestSize; ++k) {
ASSERT_OK(Delete(Key(k)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
if (options.compaction_style == kCompactionStyleUniversal) {
ASSERT_EQ(0, db_size[1]);
} else {
ASSERT_GT(db_size[0] / 2, db_size[1]);
}
}
}
#endif TEST_F(DBCompactionTest, UniversalReduceFileLockingRepickNothing) {
const int kFileNumCompactionTrigger = 3;
Options options = CurrentOptions();
options.compaction_options_universal.reduce_file_locking = true;
options.max_background_jobs = 3;
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
options.num_levels = 3;
options.compaction_style = kCompactionStyleUniversal;
options.level0_file_num_compaction_trigger = kFileNumCompactionTrigger;
options.compaction_options_universal.max_size_amplification_percent = 1;
DestroyAndReopen(options);
auto pressure_token =
dbfull()->TEST_write_controler().GetCompactionPressureToken();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{ {"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
"LowPriCompaction"},
{"DBImpl::BackgroundCompaction:NonTrivial",
"DBImpl::BGWorkBottomCompaction"}});
bool bottom_pri_compaction_attempt_repick = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction():AfterPickCompactionBottomPri",
[&](void* arg) {
bottom_pri_compaction_attempt_repick = true;
Compaction* c = static_cast<Compaction*>(arg);
assert(c == nullptr);
});
SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < kFileNumCompactionTrigger; ++i) {
if (i == 0) {
ASSERT_OK(Put("file_locked_for_bottom_pri_compaction", "value"));
} else {
ASSERT_OK(
Put("file_not_locked_for_bottom_pri_compaction" + std::to_string(i),
"value"));
}
ASSERT_OK(Flush());
}
TEST_SYNC_POINT("LowPriCompaction");
ASSERT_OK(Put("a_new_file_to_pick_for_low_pri_compaction", "value"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(bottom_pri_compaction_attempt_repick);
}
TEST_F(DBCompactionTest, SkipStatsUpdateTest) {
Options options = DeletionTriggerOptions(CurrentOptions());
options.disable_auto_compactions = true;
options.env = env_;
DestroyAndReopen(options);
Random rnd(301);
const int kTestSize = kCDTKeysPerBuffer * 512;
std::vector<std::string> values;
for (int k = 0; k < kTestSize; ++k) {
values.push_back(rnd.RandomString(kCDTValueSize));
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(Flush());
Close();
int update_acc_stats_called = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionStorageInfo::UpdateAccumulatedStats",
[&](void* ) { ++update_acc_stats_called; });
SyncPoint::GetInstance()->EnableProcessing();
options.skip_stats_update_on_db_open = true;
options.max_open_files = 20;
Reopen(options);
ASSERT_EQ(update_acc_stats_called, 0);
options.skip_stats_update_on_db_open = false;
Reopen(options);
ASSERT_GT(update_acc_stats_called, 0);
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, TestTableReaderForCompaction) {
Options options = CurrentOptions();
options.env = env_;
options.max_open_files = 20;
options.level0_file_num_compaction_trigger = 3;
options.table_cache_numshardbits = 2;
DestroyAndReopen(options);
Random rnd(301);
int num_table_cache_lookup = 0;
int num_new_table_reader = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"TableCache::FindTable:0", [&](void* arg) {
assert(arg != nullptr);
bool no_io = *(static_cast<bool*>(arg));
if (!no_io) {
num_table_cache_lookup++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"TableCache::GetTableReader:0",
[&](void* ) { num_new_table_reader++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int k = 0; k < options.level0_file_num_compaction_trigger; ++k) {
ASSERT_OK(Put(Key(k), Key(k)));
ASSERT_OK(Put(Key(10 - k), "bar"));
if (k < options.level0_file_num_compaction_trigger - 1) {
num_table_cache_lookup = 0;
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
int old_num_table_cache_lookup = num_table_cache_lookup;
ASSERT_GE(num_table_cache_lookup, 1);
ASSERT_EQ(num_new_table_reader, 1);
num_table_cache_lookup = 0;
num_new_table_reader = 0;
ASSERT_EQ(Key(k), Get(Key(k)));
ASSERT_EQ(old_num_table_cache_lookup + num_table_cache_lookup, 2);
ASSERT_EQ(num_new_table_reader, 0);
}
}
num_table_cache_lookup = 0;
num_new_table_reader = 0;
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_GE(num_table_cache_lookup, 2);
int old_num_table_cache_lookup2 = num_table_cache_lookup;
ASSERT_EQ(num_new_table_reader, 2);
num_table_cache_lookup = 0;
num_new_table_reader = 0;
ASSERT_EQ(Key(1), Get(Key(1)));
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 5);
ASSERT_EQ(num_new_table_reader, 0);
num_table_cache_lookup = 0;
num_new_table_reader = 0;
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_GE(num_table_cache_lookup, 1);
old_num_table_cache_lookup2 = num_table_cache_lookup;
ASSERT_EQ(num_new_table_reader, 1);
num_table_cache_lookup = 0;
num_new_table_reader = 0;
ASSERT_EQ(Key(1), Get(Key(1)));
ASSERT_EQ(num_table_cache_lookup + old_num_table_cache_lookup2, 3);
ASSERT_EQ(num_new_table_reader, 0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBCompactionTestWithParam, CompactionDeletionTriggerReopen) {
for (int tid = 0; tid < 2; ++tid) {
uint64_t db_size[3];
Options options = DeletionTriggerOptions(CurrentOptions());
options.max_subcompactions = max_subcompactions_;
if (tid == 1) {
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 1;
}
DestroyAndReopen(options);
Random rnd(301);
const int kTestSize = kCDTKeysPerBuffer * 512;
std::vector<std::string> values;
for (int k = 0; k < kTestSize; ++k) {
values.push_back(rnd.RandomString(kCDTValueSize));
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[0]));
Close();
options.create_if_missing = false;
options.disable_auto_compactions = true;
Reopen(options);
for (int k = 0; k < kTestSize; ++k) {
ASSERT_OK(Delete(Key(k)));
}
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
Close();
ASSERT_LE(db_size[0], db_size[1]);
options.disable_auto_compactions = false;
Reopen(options);
for (int k = 0; k < kTestSize / 10; ++k) {
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[2]));
ASSERT_GT(3 * db_size[0] / 5, db_size[2]);
}
}
TEST_F(DBCompactionTest, CompactRangeBottomPri) {
ASSERT_OK(Put(Key(50), ""));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(100), ""));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(200), ""));
ASSERT_OK(Flush());
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,3", FilesPerLevel(0));
ASSERT_OK(Put(Key(1), ""));
ASSERT_OK(Put(Key(199), ""));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(2), ""));
ASSERT_OK(Put(Key(199), ""));
ASSERT_OK(Flush());
ASSERT_EQ("2,0,3", FilesPerLevel(0));
int low_pri_count = 0;
int bottom_pri_count = 0;
SyncPoint::GetInstance()->SetCallBack(
"ThreadPoolImpl::Impl::BGThread:BeforeRun", [&](void* arg) {
Env::Priority* pri = static_cast<Env::Priority*>(arg);
if (low_pri_count == 0 && bottom_pri_count == 0) {
ASSERT_EQ(Env::Priority::LOW, *pri);
}
if (*pri == Env::Priority::LOW) {
low_pri_count++;
} else {
bottom_pri_count++;
}
});
SyncPoint::GetInstance()->EnableProcessing();
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ(1, low_pri_count);
ASSERT_EQ(1, bottom_pri_count);
ASSERT_EQ("0,0,2", FilesPerLevel(0));
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ(1, low_pri_count);
ASSERT_EQ(2, bottom_pri_count);
env_->SetBackgroundThreads(0, Env::Priority::BOTTOM);
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ(2, low_pri_count);
ASSERT_EQ(2, bottom_pri_count);
SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, DisableStatsUpdateReopen) {
uint64_t db_size[3];
for (int test = 0; test < 2; ++test) {
Options options = DeletionTriggerOptions(CurrentOptions());
options.skip_stats_update_on_db_open = (test == 0);
env_->random_read_counter_.Reset();
DestroyAndReopen(options);
Random rnd(301);
const int kTestSize = kCDTKeysPerBuffer * 512;
std::vector<std::string> values;
for (int k = 0; k < kTestSize; ++k) {
values.push_back(rnd.RandomString(kCDTValueSize));
ASSERT_OK(Put(Key(k), values[k]));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(3 );
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[0]));
Close();
options.create_if_missing = false;
options.disable_auto_compactions = true;
env_->random_read_counter_.Reset();
Reopen(options);
for (int k = 0; k < kTestSize; ++k) {
ASSERT_OK(Delete(Key(k)));
}
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[1]));
Close();
ASSERT_LE(db_size[0], db_size[1]);
options.disable_auto_compactions = false;
Reopen(options);
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Size(Key(0), Key(kTestSize - 1), &db_size[2]));
if (options.skip_stats_update_on_db_open) {
ASSERT_LE(db_size[0], db_size[2]);
} else {
ASSERT_GT(db_size[0] / 2, db_size[2]);
}
}
}
TEST_P(DBCompactionTestWithParam, CompactionTrigger) {
const int kNumKeysPerFile = 100;
Options options = CurrentOptions();
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.num_levels = 3;
options.level0_file_num_compaction_trigger = 3;
options.max_subcompactions = max_subcompactions_;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeysPerFile));
CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301);
for (int num = 0; num < options.level0_file_num_compaction_trigger - 1;
num++) {
std::vector<std::string> values;
for (int i = 0; i < kNumKeysPerFile; i++) {
values.push_back(rnd.RandomString(990));
ASSERT_OK(Put(1, Key(i), values[i]));
}
ASSERT_OK(Put(1, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
ASSERT_EQ(NumTableFilesAtLevel(0, 1), num + 1);
}
std::vector<std::string> values;
for (int i = 0; i < kNumKeysPerFile; i++) {
values.push_back(rnd.RandomString(990));
ASSERT_OK(Put(1, Key(i), values[i]));
}
ASSERT_OK(Put(1, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
ASSERT_EQ(NumTableFilesAtLevel(1, 1), 1);
}
TEST_F(DBCompactionTest, BGCompactionsAllowed) {
const int kNumKeysPerFile = 100;
Options options = CurrentOptions();
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.num_levels = 3;
options.level0_file_num_compaction_trigger = 2;
options.level0_slowdown_writes_trigger = 20;
options.soft_pending_compaction_bytes_limit = 1 << 30; options.max_background_compactions = 3;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeysPerFile));
CreateAndReopenWithCF({"one", "two", "three"}, options);
Random rnd(301);
for (int cf = 0; cf < 4; cf++) {
ASSERT_OK(Put(cf, Key(0), rnd.RandomString(102400)));
ASSERT_OK(Flush(cf));
MoveFilesToLevel(2, cf);
ASSERT_OK(Put(cf, Key(0), ""));
ASSERT_OK(Flush(cf));
MoveFilesToLevel(1, cf);
}
const size_t kTotalTasks = 4;
env_->SetBackgroundThreads(4, Env::LOW);
test::SleepingBackgroundTask sleeping_tasks[kTotalTasks];
for (size_t i = 0; i < kTotalTasks; i++) {
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_tasks[i], Env::Priority::LOW);
sleeping_tasks[i].WaitUntilSleeping();
}
for (int cf = 0; cf < 4; cf++) {
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(cf, Key(i), ""));
}
ASSERT_OK(Put(cf, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
ASSERT_EQ(NumTableFilesAtLevel(0, cf), num + 1);
}
}
ASSERT_EQ(1u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(2, Key(i), ""));
}
ASSERT_OK(Put(2, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[2]));
ASSERT_EQ(options.level0_file_num_compaction_trigger + num + 1,
NumTableFilesAtLevel(0, 2));
}
ASSERT_EQ(3U, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
for (size_t i = 0; i < kTotalTasks; i++) {
sleeping_tasks[i].WakeUp();
sleeping_tasks[i].WaitUntilDone();
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (size_t i = 0; i < kTotalTasks; i++) {
sleeping_tasks[i].Reset();
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_tasks[i], Env::Priority::LOW);
sleeping_tasks[i].WaitUntilSleeping();
}
for (int cf = 0; cf < 4; cf++) {
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(cf, Key(i), ""));
}
ASSERT_OK(Put(cf, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
ASSERT_EQ(NumTableFilesAtLevel(0, cf), num + 1);
}
}
ASSERT_EQ(1U, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
for (size_t i = 0; i < kTotalTasks; i++) {
sleeping_tasks[i].WakeUp();
sleeping_tasks[i].WaitUntilDone();
}
}
TEST_P(DBCompactionTestWithParam, CompactionsGenerateMultipleFiles) {
Options options = CurrentOptions();
options.write_buffer_size = 100000000; options.max_subcompactions = max_subcompactions_;
CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301);
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
std::vector<std::string> values;
for (int i = 0; i < 80; i++) {
values.push_back(rnd.RandomString(100000));
ASSERT_OK(Put(1, Key(i), values[i]));
}
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, handles_[1],
true ));
ASSERT_EQ(NumTableFilesAtLevel(0, 1), 0);
ASSERT_GT(NumTableFilesAtLevel(1, 1), 1);
for (int i = 0; i < 80; i++) {
ASSERT_EQ(Get(1, Key(i)), values[i]);
}
}
TEST_F(DBCompactionTest, MinorCompactionsHappen) {
do {
Options options = CurrentOptions();
options.write_buffer_size = 10000;
CreateAndReopenWithCF({"pikachu"}, options);
const int N = 500;
int starting_num_tables = TotalTableFiles(1);
for (int i = 0; i < N; i++) {
ASSERT_OK(Put(1, Key(i), Key(i) + std::string(1000, 'v')));
}
int ending_num_tables = TotalTableFiles(1);
ASSERT_GT(ending_num_tables, starting_num_tables);
for (int i = 0; i < N; i++) {
ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(1, Key(i)));
}
ReopenWithColumnFamilies({"default", "pikachu"}, options);
for (int i = 0; i < N; i++) {
ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(1, Key(i)));
}
} while (ChangeCompactOptions());
}
TEST_F(DBCompactionTest, UserKeyCrossFile1) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
DestroyAndReopen(options);
ASSERT_OK(Put("4", "A"));
ASSERT_OK(Put("3", "A"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(Put("2", "A"));
ASSERT_OK(Delete("3"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ("NOT_FOUND", Get("3"));
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ("NOT_FOUND", Get("3"));
for (int i = 0; i < 3; i++) {
ASSERT_OK(Put("2", "B"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("NOT_FOUND", Get("3"));
}
TEST_F(DBCompactionTest, UserKeyCrossFile2) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
DestroyAndReopen(options);
ASSERT_OK(Put("4", "A"));
ASSERT_OK(Put("3", "A"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(Put("2", "A"));
ASSERT_OK(SingleDelete("3"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ("NOT_FOUND", Get("3"));
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ("NOT_FOUND", Get("3"));
for (int i = 0; i < 3; i++) {
ASSERT_OK(Put("2", "B"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("NOT_FOUND", Get("3"));
}
TEST_F(DBCompactionTest, CompactionSstPartitioner) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
std::shared_ptr<SstPartitionerFactory> factory(
NewSstPartitionerFixedPrefixFactory(4));
options.sst_partitioner_factory = factory;
DestroyAndReopen(options);
ASSERT_OK(Put("aaaa1", "A"));
ASSERT_OK(Put("bbbb1", "B"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(Put("aaaa1", "A2"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
std::vector<LiveFileMetaData> files;
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(2, files.size());
ASSERT_EQ("A2", Get("aaaa1"));
ASSERT_EQ("B", Get("bbbb1"));
}
TEST_F(DBCompactionTest, CompactionSstPartitionWithManualCompaction) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
DestroyAndReopen(options);
ASSERT_OK(Put("000015", "A"));
ASSERT_OK(Put("000025", "B"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(Put("000015", "A2"));
ASSERT_OK(Put("000025", "B2"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
CompactRangeOptions compact_options;
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
std::vector<LiveFileMetaData> files;
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(1, files.size());
std::shared_ptr<SstPartitionerFactory> factory(
NewSstPartitionerFixedPrefixFactory(5));
options.sst_partitioner_factory = factory;
Reopen(options);
Slice from("000017");
Slice to("000019");
ASSERT_OK(dbfull()->CompactRange(compact_options, &from, &to));
files.clear();
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(1, files.size());
ASSERT_EQ("A2", Get("000015"));
ASSERT_EQ("B2", Get("000025"));
from = Slice("000019");
to = Slice("000020");
ASSERT_OK(dbfull()->CompactRange(compact_options, &from, &to));
files.clear();
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(2, files.size());
ASSERT_EQ("A2", Get("000015"));
ASSERT_EQ("B2", Get("000025"));
}
TEST_F(DBCompactionTest, CompactionSstPartitionerNonTrivial) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 1;
std::shared_ptr<SstPartitionerFactory> factory(
NewSstPartitionerFixedPrefixFactory(4));
options.sst_partitioner_factory = factory;
DestroyAndReopen(options);
ASSERT_OK(Put("aaaa1", "A"));
ASSERT_OK(Put("bbbb1", "B"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
std::vector<LiveFileMetaData> files;
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(2, files.size());
ASSERT_EQ("A", Get("aaaa1"));
ASSERT_EQ("B", Get("bbbb1"));
}
TEST_F(DBCompactionTest, ZeroSeqIdCompaction) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
FlushedFileCollector* collector = new FlushedFileCollector();
options.listeners.emplace_back(collector);
CompactionOptions compact_opt;
compact_opt.compression = kNoCompression;
compact_opt.output_file_size_limit = 4096;
const size_t key_len =
static_cast<size_t>(compact_opt.output_file_size_limit) / 5;
DestroyAndReopen(options);
std::vector<const Snapshot*> snaps;
for (auto& key : {"1", "2", "3", "3", "3", "3"}) {
ASSERT_OK(Put(key, std::string(key_len, 'A')));
snaps.push_back(dbfull()->GetSnapshot());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
for (auto& key : {"3", "4", "5", "6", "7", "8"}) {
ASSERT_OK(Put(key, std::string(key_len, 'A')));
snaps.push_back(dbfull()->GetSnapshot());
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(
dbfull()->CompactFiles(compact_opt, collector->GetFlushedFiles(), 1));
for (auto snap : snaps) {
dbfull()->ReleaseSnapshot(snap);
}
for (int i = 0; i < options.level0_file_num_compaction_trigger; i++) {
ASSERT_OK(Put("2", std::string(1, 'A')));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(Put("", ""));
}
TEST_F(DBCompactionTest, ManualCompactionUnknownOutputSize) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
DestroyAndReopen(options);
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < options.level0_file_num_compaction_trigger; j++) {
ASSERT_OK(Put(std::to_string(2 * i), std::string(1, 'A')));
ASSERT_OK(Put(std::to_string(2 * i + 1), std::string(1, 'A')));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
ASSERT_EQ(NumTableFilesAtLevel(1, 0), 2);
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "3"}}));
ColumnFamilyMetaData cf_meta;
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
ASSERT_EQ(2, cf_meta.levels[1].files.size());
std::vector<std::string> input_filenames;
for (const auto& sst_file : cf_meta.levels[1].files) {
input_filenames.push_back(sst_file.name);
}
CompactionOptions compact_opt;
compact_opt.compression = kNoCompression;
ASSERT_OK(dbfull()->CompactFiles(compact_opt, input_filenames, 1));
}
TEST_F(DBCompactionTest, RecoverDuringMemtableCompaction) {
do {
Options options = CurrentOptions();
options.env = env_;
CreateAndReopenWithCF({"pikachu"}, options);
ASSERT_OK(Put(1, "foo", "v1")); ASSERT_OK(Put(1, "big1", std::string(10000000, 'x'))); ASSERT_OK(Put(1, "big2", std::string(1000, 'y'))); ASSERT_OK(Put(1, "bar", "v2"));
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_EQ("v1", Get(1, "foo"));
ASSERT_EQ("v2", Get(1, "bar"));
ASSERT_EQ(std::string(10000000, 'x'), Get(1, "big1"));
ASSERT_EQ(std::string(1000, 'y'), Get(1, "big2"));
} while (ChangeOptions());
}
TEST_F(DBCompactionTest, CompactionWithDeletionsAndMinFileSize) {
const uint64_t kMinFileSize = 32 * 1024; const int kDeletionTriggerCount = 50;
const int kInitialKeyCount = 100;
const int kAdditionalKeyCount = 50;
const int kValueSize = 1024;
const int kSmallValueSize = 512;
const int kSeed = 301;
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 1024 * 1024; options.level0_file_num_compaction_trigger = 100;
options.table_properties_collector_factories = {
NewCompactOnDeletionCollectorFactory(
kInitialKeyCount , kDeletionTriggerCount,
0.5 , kMinFileSize)};
auto listener =
new DeletionTriggeredCompactionWithMinFileSizeTestListener(kMinFileSize);
options.listeners.emplace_back(listener);
DestroyAndReopen(options);
Random rnd(kSeed);
for (int i = 0; i < kInitialKeyCount; i++) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
std::vector<LiveFileMetaData> initial_metadata;
db_->GetLiveFilesMetaData(&initial_metadata);
ASSERT_EQ(initial_metadata.size(), 1);
ASSERT_OK(Put("small_file_key1", rnd.RandomString(kSmallValueSize)));
ASSERT_OK(Put("small_file_key2", rnd.RandomString(kSmallValueSize)));
ASSERT_OK(Flush());
ASSERT_OK(Delete("small_file_key1"));
ASSERT_OK(Flush());
for (int i = 0; i < kDeletionTriggerCount; i++) {
ASSERT_OK(Delete(Key(i)));
}
for (int i = kInitialKeyCount; i < kInitialKeyCount + kAdditionalKeyCount;
i++) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 2); ASSERT_EQ(NumTableFilesAtLevel(1), 1);
for (int i = 0; i < kDeletionTriggerCount; i++) {
std::string value;
ASSERT_TRUE(db_->Get(ReadOptions(), Key(i), &value).IsNotFound());
}
for (int i = kDeletionTriggerCount; i < kInitialKeyCount; i++) {
std::string value;
ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value));
ASSERT_EQ(value.size(), kValueSize);
}
for (int i = kInitialKeyCount; i < kInitialKeyCount + kAdditionalKeyCount;
i++) {
std::string value;
ASSERT_OK(db_->Get(ReadOptions(), Key(i), &value));
ASSERT_EQ(value.size(), kValueSize);
}
}
TEST_P(DBCompactionTestWithParam, TrivialMoveOneFile) {
int32_t trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.write_buffer_size = 100000000;
TrivialMoveEventListener* trivial_move_listener =
new TrivialMoveEventListener(1 );
options.listeners.emplace_back(trivial_move_listener);
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
int32_t num_keys = 80;
int32_t value_size = 100 * 1024;
Random rnd(301);
std::vector<std::string> values;
for (int i = 0; i < num_keys; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(Key(i), values[i]));
}
Reopen(options);
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 1); ASSERT_EQ(NumTableFilesAtLevel(1, 0), 0);
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
ASSERT_EQ(metadata.size(), 1U);
LiveFileMetaData level0_file = metadata[0];
CompactRangeOptions cro;
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0); ASSERT_EQ(NumTableFilesAtLevel(1, 0), 1);
metadata.clear();
db_->GetLiveFilesMetaData(&metadata);
ASSERT_EQ(metadata.size(), 1U);
ASSERT_EQ(metadata[0].name , level0_file.name);
ASSERT_EQ(metadata[0].size , level0_file.size);
for (int i = 0; i < num_keys; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
ASSERT_EQ(trivial_move, 1);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_P(DBCompactionTestWithParam, TrivialMoveNonOverlappingFiles) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.disable_auto_compactions = true;
TrivialMoveEventListener* trivial_move_listener =
new TrivialMoveEventListener(8 );
options.listeners.emplace_back(trivial_move_listener);
options.write_buffer_size = 10 * 1024 * 1024;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
std::vector<std::pair<int32_t, int32_t>> ranges = {
{100, 199}, {300, 399}, {0, 99}, {200, 299},
{600, 699}, {400, 499}, {500, 550}, {551, 599},
};
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
values[j] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(j), values[j]));
}
ASSERT_OK(Flush());
}
int32_t level0_files = NumTableFilesAtLevel(0, 0);
ASSERT_EQ(level0_files, ranges.size()); ASSERT_EQ(NumTableFilesAtLevel(1, 0), 0);
CompactRangeOptions cro;
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
ASSERT_EQ(NumTableFilesAtLevel(1, 0) , level0_files);
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
ASSERT_EQ(Get(Key(j)), values[j]);
}
}
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
trivial_move = 0;
non_trivial_move = 0;
values.clear();
options.listeners.clear();
TrivialMoveEventListener* trivial_move_listener2 =
new TrivialMoveEventListener(0 );
options.listeners.emplace_back(trivial_move_listener2);
DestroyAndReopen(options);
ranges = {
{100, 199},
{300, 399},
{0, 99},
{200, 299},
{600, 699},
{400, 499},
{500, 560}, {551, 599},
};
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
values[j] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(j), values[j]));
}
ASSERT_OK(Flush());
}
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
ASSERT_EQ(Get(Key(j)), values[j]);
}
}
ASSERT_EQ(trivial_move, 0);
ASSERT_EQ(non_trivial_move, 1);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_P(DBCompactionTestWithParam, TrivialMoveTargetLevel) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.disable_auto_compactions = true;
TrivialMoveEventListener* trivial_move_listener1 =
new TrivialMoveEventListener(2 );
options.listeners.emplace_back(trivial_move_listener1);
options.write_buffer_size = 10 * 1024 * 1024;
options.num_levels = 7;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (int32_t i = 0; i <= 300; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
for (int32_t i = 600; i <= 700; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("2", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 6;
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
for (int32_t i = 0; i <= 300; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
for (int32_t i = 600; i <= 700; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
}
TEST_P(DBCompactionTestWithParam, PartialOverlappingL0) {
class SubCompactionEventListener : public EventListener {
public:
void OnSubcompactionCompleted(const SubcompactionJobInfo&) override {
sub_compaction_finished_++;
}
std::atomic<int> sub_compaction_finished_{0};
};
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.write_buffer_size = 10 * 1024 * 1024;
options.max_subcompactions = max_subcompactions_;
SubCompactionEventListener* listener = new SubCompactionEventListener();
options.listeners.emplace_back(listener);
DestroyAndReopen(options);
ASSERT_OK(Put("key", ""));
ASSERT_OK(Put("kez", ""));
ASSERT_OK(Flush());
ASSERT_OK(Put("key", ""));
ASSERT_OK(Put("kez", ""));
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
std::vector<std::pair<int32_t, int32_t>> ranges = {
{100, 199}, {198, 399}, {397, 600}, {598, 800}, {799, 900}, {895, 999},
};
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
values[j] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(j), values[j]));
}
ASSERT_OK(Flush());
}
int32_t level0_files = NumTableFilesAtLevel(0, 0);
ASSERT_EQ(level0_files, ranges.size()); ASSERT_EQ(NumTableFilesAtLevel(1, 0), 1);
listener->sub_compaction_finished_ = 0;
ASSERT_OK(db_->EnableAutoCompaction({db_->DefaultColumnFamily()}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
if (max_subcompactions_ > 3) {
ASSERT_GT(listener->sub_compaction_finished_.load(), 2);
}
ASSERT_EQ(NumTableFilesAtLevel(0, 0), 0);
ASSERT_GT(NumTableFilesAtLevel(1, 0), 1);
for (size_t i = 0; i < ranges.size(); i++) {
for (int32_t j = ranges[i].first; j <= ranges[i].second; j++) {
ASSERT_EQ(Get(Key(j)), values[j]);
}
}
}
TEST_P(DBCompactionTestWithParam, ManualCompactionPartial) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
bool first = true;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBCompaction::ManualPartial:4", "DBCompaction::ManualPartial:1"},
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:2"},
{"DBCompaction::ManualPartial:5", "DBCompaction::ManualPartial:3"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* ) {
if (first) {
first = false;
TEST_SYNC_POINT("DBCompaction::ManualPartial:4");
TEST_SYNC_POINT("DBCompaction::ManualPartial:3");
} else { TEST_SYNC_POINT("DBCompaction::ManualPartial:2");
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.write_buffer_size = 10 * 1024 * 1024;
options.num_levels = 7;
options.max_subcompactions = max_subcompactions_;
options.level0_file_num_compaction_trigger = 3;
options.max_background_compactions = 3;
options.target_file_size_base = 1 << 23;
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (int32_t i = 0; i < 100; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
for (int32_t i = 100; i < 300; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("2", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 6;
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
for (int32_t i = 0; i < 200; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1,0,0,0,0,0,2", FilesPerLevel(0));
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, false));
ASSERT_OK(dbfull()->TEST_CompactRange(1, nullptr, nullptr, nullptr, false));
ASSERT_OK(dbfull()->TEST_CompactRange(2, nullptr, nullptr, nullptr, false));
ASSERT_OK(dbfull()->TEST_CompactRange(3, nullptr, nullptr, nullptr, false));
ASSERT_OK(dbfull()->TEST_CompactRange(4, nullptr, nullptr, nullptr, false));
ASSERT_EQ("0,0,0,0,0,1,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 6);
ASSERT_EQ(non_trivial_move, 0);
ROCKSDB_NAMESPACE::port::Thread threads([&] {
compact_options.change_level = false;
compact_options.exclusive_manual_compaction = false;
std::string begin_string = Key(0);
std::string end_string = Key(199);
Slice begin(begin_string);
Slice end(end_string);
ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
});
TEST_SYNC_POINT("DBCompaction::ManualPartial:1");
for (int32_t i = 300; i <= 400; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
for (int32_t i = 400; i <= 500; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
for (int32_t i = 500; i <= 600; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("3,0,0,0,0,1,2", FilesPerLevel(0));
TEST_SYNC_POINT("DBCompaction::ManualPartial:5");
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1,0,0,0,0,1", FilesPerLevel(0));
threads.join();
for (int32_t i = 0; i < 600; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
}
TEST_F(DBCompactionTest, DISABLED_ManualPartialFill) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
bool first = true;
bool second = true;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBCompaction::PartialFill:4", "DBCompaction::PartialFill:1"},
{"DBCompaction::PartialFill:2", "DBCompaction::PartialFill:3"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun", [&](void* ) {
if (first) {
TEST_SYNC_POINT("DBCompaction::PartialFill:4");
first = false;
TEST_SYNC_POINT("DBCompaction::PartialFill:3");
} else if (second) {
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.write_buffer_size = 10 * 1024 * 1024;
options.max_bytes_for_level_multiplier = 2;
options.num_levels = 4;
options.level0_file_num_compaction_trigger = 3;
options.max_background_compactions = 3;
DestroyAndReopen(options);
auto stop_token =
dbfull()->TEST_write_controler().GetCompactionPressureToken();
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (int32_t i = 0; i < 100; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
for (int32_t i = 100; i < 300; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("2", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 2;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
for (int32_t i = 0; i < 200; i++) {
values[i] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1,0,2", FilesPerLevel(0));
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr, false));
ASSERT_EQ("0,1,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 2);
ASSERT_EQ(non_trivial_move, 0);
ROCKSDB_NAMESPACE::port::Thread threads([&] {
compact_options.change_level = false;
compact_options.exclusive_manual_compaction = false;
std::string begin_string = Key(0);
std::string end_string = Key(199);
Slice begin(begin_string);
Slice end(end_string);
ASSERT_OK(db_->CompactRange(compact_options, &begin, &end));
});
TEST_SYNC_POINT("DBCompaction::PartialFill:1");
for (int32_t i = 0; i <= 5; i++) {
for (int32_t j = 300; j < 4300; j++) {
if (j == 2300) {
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
values[j] = rnd.RandomString(value_size);
ASSERT_OK(Put(Key(j), values[j]));
}
}
uint64_t target_size = 4 * options.max_bytes_for_level_base;
for (int32_t i = 1; i < options.num_levels; i++) {
ASSERT_LE(SizeAtLevel(i), target_size);
target_size = static_cast<uint64_t>(target_size *
options.max_bytes_for_level_multiplier);
}
TEST_SYNC_POINT("DBCompaction::PartialFill:2");
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
threads.join();
for (int32_t i = 0; i < 4300; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
}
TEST_F(DBCompactionTest, ManualCompactionWithUnorderedWrite) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WriteImpl:UnorderedWriteAfterWriteWAL",
"DBCompactionTest::ManualCompactionWithUnorderedWrite:WaitWriteWAL"},
{"DBImpl::WaitForPendingWrites:BeforeBlock",
"DBImpl::WriteImpl:BeforeUnorderedWriteMemtable"}});
Options options = CurrentOptions();
options.unordered_write = true;
DestroyAndReopen(options);
ASSERT_OK(Put("foo", "v1"));
ASSERT_OK(Flush());
ASSERT_OK(Put("bar", "v1"));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
port::Thread writer([&]() { ASSERT_OK(Put("foo", "v2")); });
TEST_SYNC_POINT(
"DBCompactionTest::ManualCompactionWithUnorderedWrite:WaitWriteWAL");
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
writer.join();
ASSERT_EQ(Get("foo"), "v2");
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
Reopen(options);
ASSERT_EQ(Get("foo"), "v2");
}
class DBDeleteFileRangeTest : public DBTestBase,
public testing::WithParamInterface<bool> {
public:
DBDeleteFileRangeTest()
: DBTestBase("db_delete_file_range_test", true) {}
void SetUp() override { enable_udt_ = GetParam(); }
protected:
void PutKeyValue(const Slice& key, const Slice& value) {
if (enable_udt_) {
EXPECT_OK(db_->Put(WriteOptions(), key, min_ts_, value));
} else {
EXPECT_OK(Put(key, value));
}
}
std::string GetValue(const std::string& key) {
ReadOptions roptions;
std::string result;
if (enable_udt_) {
roptions.timestamp = &min_ts_;
}
Status s = db_->Get(roptions, key, &result);
EXPECT_TRUE(s.ok());
return result;
}
Status MaybeGetValue(const std::string& key, std::string* result) {
ReadOptions roptions;
if (enable_udt_) {
roptions.timestamp = &min_ts_;
}
Status s = db_->Get(roptions, key, result);
EXPECT_TRUE(s.IsNotFound() || s.ok());
return s;
}
bool enable_udt_ = false;
Slice min_ts_ = MinU64Ts();
};
TEST_P(DBDeleteFileRangeTest, DeleteFileRange) {
Options options = CurrentOptions();
options.write_buffer_size = 10 * 1024 * 1024;
options.max_bytes_for_level_multiplier = 2;
options.num_levels = 4;
options.level0_file_num_compaction_trigger = 3;
options.max_background_compactions = 3;
if (enable_udt_) {
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
}
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (int32_t i = 0; i < 100; i++) {
values[i] = rnd.RandomString(value_size);
PutKeyValue(Key(i), values[i]);
}
ASSERT_OK(Flush());
for (int32_t i = 100; i < 300; i++) {
values[i] = rnd.RandomString(value_size);
PutKeyValue(Key(i), values[i]);
}
ASSERT_OK(Flush());
ASSERT_EQ("2", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 2;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,2", FilesPerLevel(0));
for (int32_t i = 0; i < 200; i++) {
values[i] = rnd.RandomString(value_size);
PutKeyValue(Key(i), values[i]);
}
ASSERT_OK(Flush());
for (int32_t i = 0; i <= 5; i++) {
for (int32_t j = 300; j < 4300; j++) {
if (j == 2300) {
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
values[j] = rnd.RandomString(value_size);
PutKeyValue(Key(j), values[j]);
}
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
uint64_t target_size = 4 * options.max_bytes_for_level_base;
for (int32_t i = 1; i < options.num_levels; i++) {
ASSERT_LE(SizeAtLevel(i), target_size);
target_size = static_cast<uint64_t>(target_size *
options.max_bytes_for_level_multiplier);
}
const size_t old_num_files = CountFiles();
std::string begin_string = Key(1000);
std::string end_string = Key(2000);
Slice begin(begin_string);
Slice end(end_string);
ASSERT_OK(
DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), &begin, &end));
int32_t deleted_count = 0;
for (int32_t i = 0; i < 4300; i++) {
if (i < 1000 || i > 2000) {
ASSERT_EQ(GetValue(Key(i)), values[i]);
} else {
std::string result;
Status s = MaybeGetValue(Key(i), &result);
ASSERT_TRUE(s.IsNotFound() || s.ok());
if (s.IsNotFound()) {
deleted_count++;
}
}
}
ASSERT_GT(deleted_count, 0);
begin_string = Key(5000);
end_string = Key(6000);
Slice begin1(begin_string);
Slice end1(end_string);
ASSERT_OK(DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), &begin1,
&end1));
compact_options.change_level = true;
compact_options.target_level = 1;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), nullptr,
nullptr));
int32_t deleted_count2 = 0;
for (int32_t i = 0; i < 4300; i++) {
ReadOptions roptions;
std::string result;
ASSERT_TRUE(MaybeGetValue(Key(i), &result).IsNotFound());
deleted_count2++;
}
ASSERT_GT(deleted_count2, deleted_count);
const size_t new_num_files = CountFiles();
ASSERT_GT(old_num_files, new_num_files);
}
TEST_P(DBDeleteFileRangeTest, DeleteFilesInRanges) {
Options options = CurrentOptions();
options.write_buffer_size = 10 * 1024 * 1024;
options.max_bytes_for_level_multiplier = 2;
options.num_levels = 4;
options.max_background_compactions = 3;
options.disable_auto_compactions = true;
if (enable_udt_) {
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
}
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::map<int32_t, std::string> values;
for (auto i = 0; i < 10; i++) {
for (auto j = 0; j < 100; j++) {
auto k = i * 100 + j;
values[k] = rnd.RandomString(value_size);
PutKeyValue(Key(k), values[k]);
}
ASSERT_OK(Flush());
}
ASSERT_EQ("10", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 2;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,10", FilesPerLevel(0));
for (auto i = 0; i < 10; i += 2) {
for (auto j = 0; j < 100; j++) {
auto k = i * 100 + j;
PutKeyValue(Key(k), values[k]);
}
ASSERT_OK(Flush());
}
ASSERT_EQ("5,0,10", FilesPerLevel(0));
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr));
ASSERT_EQ("0,5,10", FilesPerLevel(0));
{
auto begin_str1 = Key(0), end_str1 = Key(100);
auto begin_str2 = Key(100), end_str2 = Key(200);
auto begin_str3 = Key(200), end_str3 = Key(299);
std::vector<RangeOpt> ranges;
ranges.emplace_back(begin_str1, end_str1);
ranges.emplace_back(begin_str2, end_str2);
ranges.emplace_back(begin_str3, end_str3);
ASSERT_OK(DeleteFilesInRanges(db_.get(), db_->DefaultColumnFamily(),
ranges.data(), ranges.size()));
ASSERT_EQ("0,3,7", FilesPerLevel(0));
for (auto i = 0; i < 300; i++) {
std::string result;
auto s = MaybeGetValue(Key(i), &result);
ASSERT_TRUE(s.IsNotFound());
}
for (auto i = 300; i < 1000; i++) {
ASSERT_EQ(GetValue(Key(i)), values[i]);
}
}
{
auto begin_str1 = Key(600), end_str1 = Key(800);
auto begin_str2 = Key(700), end_str2 = Key(900);
auto begin_str3 = Key(800), end_str3 = Key(999);
Slice begin1(begin_str1), end1(end_str1);
Slice begin2(begin_str2), end2(end_str2);
Slice begin3(begin_str3), end3(end_str3);
std::vector<RangePtr> ranges;
ranges.emplace_back(&begin1, &end1);
ranges.emplace_back(&begin2, &end2);
ranges.emplace_back(&begin3, &end3);
ASSERT_OK(DeleteFilesInRanges(db_.get(), db_->DefaultColumnFamily(),
ranges.data(), ranges.size(), false));
ASSERT_EQ("0,1,4", FilesPerLevel(0));
for (auto i = 600; i < 900; i++) {
std::string result;
auto s = MaybeGetValue(Key(i), &result);
ASSERT_TRUE(s.IsNotFound());
}
for (auto i = 300; i < 600; i++) {
ASSERT_EQ(GetValue(Key(i)), values[i]);
}
for (auto i = 900; i < 1000; i++) {
ASSERT_EQ(GetValue(Key(i)), values[i]);
}
}
{
RangeOpt range;
ASSERT_OK(
DeleteFilesInRanges(db_.get(), db_->DefaultColumnFamily(), &range, 1));
ASSERT_EQ("", FilesPerLevel(0));
for (auto i = 0; i < 1000; i++) {
std::string result;
auto s = MaybeGetValue(Key(i), &result);
ASSERT_TRUE(s.IsNotFound());
}
}
}
TEST_P(DBDeleteFileRangeTest, DeleteFileRangeFileEndpointsOverlapBug) {
const int kNumL0Files = 2;
const int kValSize = 8 << 10; Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
options.target_file_size_base = 1 << 10; if (enable_udt_) {
options.comparator = test::BytewiseComparatorWithU64TsWrapper();
}
DestroyAndReopen(options);
const Snapshot* snapshot = nullptr;
Random rnd(301);
std::string vals[kNumL0Files];
for (int i = 0; i < kNumL0Files; ++i) {
vals[i] = rnd.RandomString(kValSize);
PutKeyValue(Key(i), vals[i]);
PutKeyValue(Key(i + 1), vals[i]);
ASSERT_OK(Flush());
if (i == 0) {
snapshot = db_->GetSnapshot();
}
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
std::string begin_str = Key(0), end_str = Key(1);
Slice begin = begin_str, end = end_str;
ASSERT_OK(
DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), &begin, &end));
ASSERT_EQ(vals[1], GetValue(Key(1)));
db_->ReleaseSnapshot(snapshot);
}
INSTANTIATE_TEST_CASE_P(DBDeleteFileRangeTest, DBDeleteFileRangeTest,
::testing::Bool());
TEST_P(DBCompactionTestWithParam, TrivialMoveToLastLevelWithFiles) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.write_buffer_size = 100000000;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::vector<std::string> values;
for (int i = 0; i < 100; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 3;
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
for (int i = 100; i < 200; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(Key(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1,0,0,1", FilesPerLevel(0));
CompactRangeOptions cro;
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ("0,0,0,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 4);
ASSERT_EQ(non_trivial_move, 0);
for (int i = 0; i < 200; i++) {
ASSERT_EQ(Get(Key(i)), values[i]);
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_P(DBCompactionTestWithParam, LevelCompactionThirdPath) {
Options options = CurrentOptions();
options.db_paths.emplace_back(dbname_, 500 * 1024);
options.db_paths.emplace_back(dbname_ + "_2", 4 * 1024 * 1024);
options.db_paths.emplace_back(dbname_ + "_3", 1024 * 1024 * 1024);
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 4;
options.max_bytes_for_level_base = 400 * 1024;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
Random rnd(301);
int key_idx = 0;
for (int num = 0; num < 3; num++) {
GenerateNewFile(&rnd, &key_idx);
}
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ(3, GetSstFileCount(options.db_paths[1].path));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4", FilesPerLevel(0));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,1", FilesPerLevel(0));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,2", FilesPerLevel(0));
ASSERT_EQ(2, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,3", FilesPerLevel(0));
ASSERT_EQ(3, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,4", FilesPerLevel(0));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,5", FilesPerLevel(0));
ASSERT_EQ(5, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,6", FilesPerLevel(0));
ASSERT_EQ(6, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,7", FilesPerLevel(0));
ASSERT_EQ(7, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,8", FilesPerLevel(0));
ASSERT_EQ(8, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(4, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Reopen(options);
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Destroy(options);
}
TEST_P(DBCompactionTestWithParam, LevelCompactionPathUse) {
Options options = CurrentOptions();
options.db_paths.emplace_back(dbname_, 500 * 1024);
options.db_paths.emplace_back(dbname_ + "_2", 4 * 1024 * 1024);
options.db_paths.emplace_back(dbname_ + "_3", 1024 * 1024 * 1024);
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 4;
options.max_bytes_for_level_base = 400 * 1024;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
Random rnd(301);
int key_idx = 0;
for (int num = 0; num < 3; num++) {
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
}
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1", FilesPerLevel(0));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("0,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("0,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("0,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("0,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1", FilesPerLevel(0));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[2].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(dbname_));
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Reopen(options);
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Destroy(options);
}
TEST_P(DBCompactionTestWithParam, LevelCompactionCFPathUse) {
Options options = CurrentOptions();
options.db_paths.emplace_back(dbname_, 500 * 1024);
options.db_paths.emplace_back(dbname_ + "_2", 4 * 1024 * 1024);
options.db_paths.emplace_back(dbname_ + "_3", 1024 * 1024 * 1024);
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 4;
options.max_bytes_for_level_base = 400 * 1024;
options.max_subcompactions = max_subcompactions_;
std::vector<Options> option_vector;
option_vector.emplace_back(options);
ColumnFamilyOptions cf_opt1(options), cf_opt2(options);
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1", 500 * 1024);
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_2", 4 * 1024 * 1024);
cf_opt1.cf_paths.emplace_back(dbname_ + "cf1_3", 1024 * 1024 * 1024);
option_vector.emplace_back(DBOptions(options), cf_opt1);
CreateColumnFamilies({"one"}, option_vector[1]);
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2", 500 * 1024);
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_2", 4 * 1024 * 1024);
cf_opt2.cf_paths.emplace_back(dbname_ + "cf2_3", 1024 * 1024 * 1024);
option_vector.emplace_back(DBOptions(options), cf_opt2);
CreateColumnFamilies({"two"}, option_vector[2]);
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
Random rnd(301);
int key_idx = 0;
int key_idx1 = 0;
int key_idx2 = 0;
auto generate_file = [&]() {
GenerateNewFile(0, &rnd, &key_idx);
GenerateNewFile(1, &rnd, &key_idx1);
GenerateNewFile(2, &rnd, &key_idx2);
};
auto check_sstfilecount = [&](int path_id, int expected) {
ASSERT_EQ(expected, GetSstFileCount(options.db_paths[path_id].path));
ASSERT_EQ(expected, GetSstFileCount(cf_opt1.cf_paths[path_id].path));
ASSERT_EQ(expected, GetSstFileCount(cf_opt2.cf_paths[path_id].path));
};
auto check_filesperlevel = [&](const std::string& expected) {
ASSERT_EQ(expected, FilesPerLevel(0));
ASSERT_EQ(expected, FilesPerLevel(1));
ASSERT_EQ(expected, FilesPerLevel(2));
};
auto check_getvalues = [&]() {
for (int i = 0; i < key_idx; i++) {
auto v = Get(0, Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
for (int i = 0; i < key_idx1; i++) {
auto v = Get(1, Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
for (int i = 0; i < key_idx2; i++) {
auto v = Get(2, Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
};
for (int num = 0; num < 3; num++) {
generate_file();
}
check_sstfilecount(0, 1);
check_sstfilecount(1, 2);
generate_file();
check_sstfilecount(1, 3);
generate_file();
check_filesperlevel("1,4");
check_sstfilecount(1, 4);
check_sstfilecount(0, 1);
generate_file();
check_filesperlevel("1,4,1");
check_sstfilecount(2, 1);
check_sstfilecount(1, 4);
check_sstfilecount(0, 1);
generate_file();
check_filesperlevel("1,4,2");
check_sstfilecount(2, 2);
check_sstfilecount(1, 4);
check_sstfilecount(0, 1);
check_getvalues();
{ std::vector<LiveFileStorageInfo> new_infos;
LiveFilesStorageInfoOptions lfsio;
lfsio.wal_size_for_flush = UINT64_MAX; ASSERT_OK(db_->GetLiveFilesStorageInfo(lfsio, &new_infos));
std::unordered_map<std::string, int> live_sst_by_dir;
for (auto& info : new_infos) {
if (info.file_type == kTableFile) {
live_sst_by_dir[info.directory]++;
uint64_t size;
ASSERT_OK(env_->GetFileSize(
info.directory + "/" + info.relative_filename, &size));
ASSERT_EQ(info.size, size);
}
}
ASSERT_EQ(3U * 3U, live_sst_by_dir.size());
for (auto& paths : {options.db_paths, cf_opt1.cf_paths, cf_opt2.cf_paths}) {
ASSERT_EQ(1, live_sst_by_dir[paths[0].path]);
ASSERT_EQ(4, live_sst_by_dir[paths[1].path]);
ASSERT_EQ(2, live_sst_by_dir[paths[2].path]);
}
}
ReopenWithColumnFamilies({"default", "one", "two"}, option_vector);
check_getvalues();
Destroy(options, true);
}
TEST_P(DBCompactionTestWithParam, ConvertCompactionStyle) {
Random rnd(301);
int max_key_level_insert = 200;
int max_key_universal_insert = 600;
Options options = CurrentOptions();
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.num_levels = 4;
options.level0_file_num_compaction_trigger = 3;
options.max_bytes_for_level_base = 500 << 10; options.max_bytes_for_level_multiplier = 1;
options.target_file_size_base = 200 << 10; options.target_file_size_multiplier = 1;
options.max_subcompactions = max_subcompactions_;
CreateAndReopenWithCF({"pikachu"}, options);
for (int i = 0; i <= max_key_level_insert; i++) {
ASSERT_OK(Put(1, Key(i), rnd.RandomString(10000)));
}
ASSERT_OK(Flush(1));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_GT(TotalTableFiles(1, 4), 1);
int non_level0_num_files = 0;
for (int i = 1; i < options.num_levels; i++) {
non_level0_num_files += NumTableFilesAtLevel(i, 1);
}
ASSERT_GT(non_level0_num_files, 0);
options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 1;
options = CurrentOptions(options);
Status s = TryReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_TRUE(s.IsInvalidArgument());
options = CurrentOptions();
options.disable_auto_compactions = true;
options.target_file_size_base = INT_MAX;
options.target_file_size_multiplier = 1;
options.max_bytes_for_level_base = INT_MAX;
options.max_bytes_for_level_multiplier = 1;
options.num_levels = 4;
options = CurrentOptions(options);
ReopenWithColumnFamilies({"default", "pikachu"}, options);
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 0;
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kForce;
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(
dbfull()->CompactRange(compact_options, handles_[1], nullptr, nullptr));
ASSERT_EQ("1", FilesPerLevel(1));
options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 4;
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 3;
options = CurrentOptions(options);
ReopenWithColumnFamilies({"default", "pikachu"}, options);
options.num_levels = 1;
ReopenWithColumnFamilies({"default", "pikachu"}, options);
for (int i = max_key_level_insert / 2; i <= max_key_universal_insert; i++) {
ASSERT_OK(Put(1, Key(i), rnd.RandomString(10000)));
}
ASSERT_OK(dbfull()->Flush(FlushOptions()));
ASSERT_OK(Flush(1));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (int i = 1; i < options.num_levels; i++) {
ASSERT_EQ(NumTableFilesAtLevel(i, 1), 0);
}
std::string keys_in_db;
Iterator* iter = dbfull()->NewIterator(ReadOptions(), handles_[1]);
ASSERT_OK(iter->status());
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
keys_in_db.append(iter->key().ToString());
keys_in_db.push_back(',');
}
ASSERT_OK(iter->status());
delete iter;
std::string expected_keys;
for (int i = 0; i <= max_key_universal_insert; i++) {
expected_keys.append(Key(i));
expected_keys.push_back(',');
}
ASSERT_EQ(keys_in_db, expected_keys);
}
TEST_F(DBCompactionTest, L0_CompactionBug_Issue44_a) {
do {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "b", "v"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Delete(1, "b"));
ASSERT_OK(Delete(1, "a"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Delete(1, "a"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "a", "v"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_EQ("(a->v)", Contents(1));
env_->SleepForMicroseconds(1000000); ASSERT_EQ("(a->v)", Contents(1));
} while (ChangeCompactOptions());
}
TEST_F(DBCompactionTest, L0_CompactionBug_Issue44_b) {
do {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "", ""));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Delete(1, "e"));
ASSERT_OK(Put(1, "", ""));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "c", "cv"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "", ""));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "", ""));
env_->SleepForMicroseconds(1000000); ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "d", "dv"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "", ""));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_OK(Delete(1, "d"));
ASSERT_OK(Delete(1, "b"));
ReopenWithColumnFamilies({"default", "pikachu"}, CurrentOptions());
ASSERT_EQ("(->)(c->cv)", Contents(1));
env_->SleepForMicroseconds(1000000); ASSERT_EQ("(->)(c->cv)", Contents(1));
} while (ChangeCompactOptions());
}
TEST_F(DBCompactionTest, ManualAutoRace) {
const int kNumL0FilesTrigger = 4;
for (auto compaction_style :
{kCompactionStyleLevel, kCompactionStyleUniversal}) {
Env::Default()->SetBackgroundThreads(
compaction_style == kCompactionStyleUniversal ? 2 : 0,
Env::Priority::BOTTOM);
for (auto universal_reduce_file_locking : {false, true}) {
if (compaction_style != kCompactionStyleUniversal &&
universal_reduce_file_locking) {
continue;
}
Options options = CurrentOptions();
options.num_levels = 3;
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
options.compaction_style = compaction_style;
options.compaction_options_universal.reduce_file_locking =
universal_reduce_file_locking;
DestroyAndReopen(options);
CreateAndReopenWithCF({"exclusive_manual_compaction_cf"}, options);
if (compaction_style == kCompactionStyleLevel ||
!universal_reduce_file_locking) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BGWorkCompaction", "DBCompactionTest::ManualAutoRace:1"},
{"DBImpl::RunManualCompaction:WaitScheduled",
"BackgroundCallCompaction:0"}});
} else {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction:ForwardToBottomPriPool",
"DBCompactionTest::ManualAutoRace:1"},
{"DBImpl::RunManualCompaction:WaitScheduled",
"BackgroundCallCompaction:0:BottomPri"}});
}
bool encounter_conflict = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction()::Conflict",
[&](void* ) { encounter_conflict = true; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(1, "foo", ""));
ASSERT_OK(Put(1, "bar", ""));
ASSERT_OK(Flush(1));
ASSERT_OK(Put(1, "foo", ""));
ASSERT_OK(Put(1, "bar", ""));
for (int i = 0; i < kNumL0FilesTrigger; ++i) {
ASSERT_OK(Put("foo", ""));
ASSERT_OK(Put("bar", ""));
ASSERT_OK(Flush());
}
TEST_SYNC_POINT("DBCompactionTest::ManualAutoRace:1");
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
ASSERT_OK(dbfull()->CompactRange(cro, handles_[1], nullptr, nullptr));
ASSERT_EQ(compaction_style == kCompactionStyleLevel ? "0,1" : "0,0,1",
FilesPerLevel(1));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(encounter_conflict);
ASSERT_EQ(compaction_style == kCompactionStyleLevel ? "0,1" : "0,0,1",
FilesPerLevel(0));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
}
}
TEST_P(DBCompactionTestWithParam, ManualCompaction) {
Options options = CurrentOptions();
options.max_subcompactions = max_subcompactions_;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
CreateAndReopenWithCF({"pikachu"}, options);
for (int iter = 0; iter < 2; ++iter) {
MakeTables(3, "p", "q", 1);
ASSERT_EQ("1,1,1", FilesPerLevel(1));
Compact(1, "", "c");
ASSERT_EQ("1,1,1", FilesPerLevel(1));
Compact(1, "r", "z");
ASSERT_EQ("1,1,1", FilesPerLevel(1));
Compact(1, "p", "q");
ASSERT_EQ("0,0,1", FilesPerLevel(1));
MakeTables(3, "c", "e", 1);
ASSERT_EQ("1,1,2", FilesPerLevel(1));
Compact(1, "b", "f");
ASSERT_EQ("0,0,2", FilesPerLevel(1));
MakeTables(1, "a", "z", 1);
ASSERT_EQ("1,0,2", FilesPerLevel(1));
uint64_t prev_block_cache_add =
options.statistics->getTickerCount(BLOCK_CACHE_ADD);
CompactRangeOptions cro;
cro.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(db_->CompactRange(cro, handles_[1], nullptr, nullptr));
ASSERT_EQ(prev_block_cache_add,
options.statistics->getTickerCount(BLOCK_CACHE_ADD));
ASSERT_EQ("0,0,1", FilesPerLevel(1));
if (iter == 0) {
options = CurrentOptions();
options.num_levels = 3;
options.create_if_missing = true;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
DestroyAndReopen(options);
CreateAndReopenWithCF({"pikachu"}, options);
}
}
}
TEST_P(DBCompactionTestWithParam, ManualLevelCompactionOutputPathId) {
Options options = CurrentOptions();
options.db_paths.emplace_back(dbname_ + "_2", 2 * 10485760);
options.db_paths.emplace_back(dbname_ + "_3", 100 * 10485760);
options.db_paths.emplace_back(dbname_ + "_4", 120 * 10485760);
options.max_subcompactions = max_subcompactions_;
CreateAndReopenWithCF({"pikachu"}, options);
for (int iter = 0; iter < 2; ++iter) {
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put(1, "p", "begin"));
ASSERT_OK(Put(1, "q", "end"));
ASSERT_OK(Flush(1));
}
ASSERT_EQ("3", FilesPerLevel(1));
ASSERT_EQ(3, GetSstFileCount(options.db_paths[0].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
Compact(1, "", "c");
ASSERT_EQ("3", FilesPerLevel(1));
Compact(1, "r", "z");
ASSERT_EQ("3", FilesPerLevel(1));
Compact(1, "p", "q", 1);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1", FilesPerLevel(1));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[0].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put(1, "c", "begin"));
ASSERT_OK(Put(1, "e", "end"));
ASSERT_OK(Flush(1));
}
ASSERT_EQ("3,1", FilesPerLevel(1));
Compact(1, "b", "f", 1);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,2", FilesPerLevel(1));
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[0].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
ASSERT_OK(Put(1, "a", "begin"));
ASSERT_OK(Put(1, "z", "end"));
ASSERT_OK(Flush(1));
ASSERT_EQ("1,2", FilesPerLevel(1));
ASSERT_EQ(2, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[0].path));
CompactRangeOptions compact_options;
compact_options.target_path_id = 1;
compact_options.exclusive_manual_compaction = exclusive_manual_compaction_;
ASSERT_OK(
db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1", FilesPerLevel(1));
ASSERT_EQ(1, GetSstFileCount(options.db_paths[1].path));
ASSERT_EQ(0, GetSstFileCount(options.db_paths[0].path));
ASSERT_EQ(0, GetSstFileCount(dbname_));
if (iter == 0) {
DestroyAndReopen(options);
options = CurrentOptions();
options.db_paths.emplace_back(dbname_ + "_2", 2 * 10485760);
options.db_paths.emplace_back(dbname_ + "_3", 100 * 10485760);
options.db_paths.emplace_back(dbname_ + "_4", 120 * 10485760);
options.max_background_flushes = 1;
options.num_levels = 3;
options.create_if_missing = true;
CreateAndReopenWithCF({"pikachu"}, options);
}
}
}
TEST_F(DBCompactionTest, FilesDeletedAfterCompaction) {
do {
CreateAndReopenWithCF({"pikachu"}, CurrentOptions());
ASSERT_OK(Put(1, "foo", "v2"));
Compact(1, "a", "z");
const size_t num_files = CountLiveFiles();
for (int i = 0; i < 10; i++) {
ASSERT_OK(Put(1, "foo", "v2"));
Compact(1, "a", "z");
}
ASSERT_EQ(CountLiveFiles(), num_files);
} while (ChangeCompactOptions());
}
TEST_P(DBCompactionTestWithParam, DISABLED_CompactFilesOnLevelCompaction) {
const int kTestKeySize = 16;
const int kTestValueSize = 984;
const int kEntrySize = kTestKeySize + kTestValueSize;
const int kEntriesPerBuffer = 100;
Options options;
options.create_if_missing = true;
options.write_buffer_size = kEntrySize * kEntriesPerBuffer;
options.compaction_style = kCompactionStyleLevel;
options.target_file_size_base = options.write_buffer_size;
options.max_bytes_for_level_base = options.target_file_size_base * 2;
options.level0_stop_writes_trigger = 2;
options.max_bytes_for_level_multiplier = 2;
options.compression = kNoCompression;
options.max_subcompactions = max_subcompactions_;
options = CurrentOptions(options);
CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(301);
for (int key = 64 * kEntriesPerBuffer; key >= 0; --key) {
ASSERT_OK(Put(1, std::to_string(key), rnd.RandomString(kTestValueSize)));
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[1]));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ColumnFamilyMetaData cf_meta;
dbfull()->GetColumnFamilyMetaData(handles_[1], &cf_meta);
int output_level = static_cast<int>(cf_meta.levels.size()) - 1;
for (int file_picked = 5; file_picked > 0; --file_picked) {
std::set<std::string> overlapping_file_names;
std::vector<std::string> compaction_input_file_names;
for (int f = 0; f < file_picked; ++f) {
int level = 0;
auto file_meta = PickFileRandomly(cf_meta, &rnd, &level);
compaction_input_file_names.push_back(file_meta->name);
GetOverlappingFileNumbersForLevelCompaction(
cf_meta, options.comparator, level, output_level, file_meta,
&overlapping_file_names);
}
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), handles_[1],
compaction_input_file_names,
output_level));
dbfull()->GetColumnFamilyMetaData(handles_[1], &cf_meta);
VerifyCompactionResult(cf_meta, overlapping_file_names);
}
for (int key = 64 * kEntriesPerBuffer; key >= 0; --key) {
ASSERT_NE(Get(1, std::to_string(key)), "NOT_FOUND");
}
}
TEST_P(DBCompactionTestWithParam, PartialCompactionFailure) {
Options options;
const int kKeySize = 16;
const int kKvSize = 1000;
const int kKeysPerBuffer = 100;
const int kNumL1Files = 5;
options.create_if_missing = true;
options.write_buffer_size = kKeysPerBuffer * kKvSize;
options.max_write_buffer_number = 2;
options.target_file_size_base =
options.write_buffer_size * (options.max_write_buffer_number - 1);
options.level0_file_num_compaction_trigger = kNumL1Files;
options.max_bytes_for_level_base =
options.level0_file_num_compaction_trigger *
options.target_file_size_base;
options.max_bytes_for_level_multiplier = 2;
options.compression = kNoCompression;
options.max_subcompactions = max_subcompactions_;
env_->SetBackgroundThreads(1, Env::HIGH);
env_->SetBackgroundThreads(1, Env::LOW);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
options.env = env_;
DestroyAndReopen(options);
const int kNumInsertedKeys = options.level0_file_num_compaction_trigger *
(options.max_write_buffer_number - 1) *
kKeysPerBuffer;
Random rnd(301);
std::vector<std::string> keys;
std::vector<std::string> values;
for (int k = 0; k < kNumInsertedKeys; ++k) {
keys.emplace_back(rnd.RandomString(kKeySize));
values.emplace_back(rnd.RandomString(kKvSize - kKeySize));
ASSERT_OK(Put(Slice(keys[k]), Slice(values[k])));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_FlushMemTable(true));
ASSERT_GE(NumTableFilesAtLevel(0),
options.level0_file_num_compaction_trigger);
auto previous_num_level0_files = NumTableFilesAtLevel(0);
env_->non_writable_count_ = 1;
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
ASSERT_TRUE(!dbfull()->TEST_WaitForCompact().ok());
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(0), previous_num_level0_files);
for (int k = 0; k < kNumInsertedKeys; ++k) {
ASSERT_EQ(values[k], Get(keys[k]));
}
env_->non_writable_count_ = 0;
Reopen(options);
for (int k = 0; k < kNumInsertedKeys; ++k) {
ASSERT_EQ(values[k], Get(keys[k]));
}
}
TEST_P(DBCompactionTestWithParam, DeleteMovedFileAfterCompaction) {
for (int iter = 0; iter < 2; ++iter) {
Options options = CurrentOptions();
options.env = env_;
if (iter == 1) {
options.delete_obsolete_files_period_micros = 0;
}
options.create_if_missing = true;
options.level0_file_num_compaction_trigger =
2; OnFileDeletionListener* listener = new OnFileDeletionListener();
options.listeners.emplace_back(listener);
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 100; ++j) {
ASSERT_OK(Put(Key(i * 50 + j), rnd.RandomString(10 * 1024)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1", FilesPerLevel(0));
test::SleepingBackgroundTask sleeping_task;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task,
Env::Priority::LOW);
options.max_bytes_for_level_base = 1024 * 1024; Reopen(options);
std::unique_ptr<Iterator> iterator(db_->NewIterator(ReadOptions()));
ASSERT_EQ("0,1", FilesPerLevel(0));
sleeping_task.WakeUp();
sleeping_task.WaitUntilDone();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,0,1", FilesPerLevel(0));
std::vector<LiveFileMetaData> metadata;
db_->GetLiveFilesMetaData(&metadata);
ASSERT_EQ(metadata.size(), 1U);
auto moved_file_name = metadata[0].name;
for (int i = 0; i < 2; ++i) {
for (int j = 0; j < 100; ++j) {
ASSERT_OK(Put(Key(i * 50 + j + 100), rnd.RandomString(10 * 1024)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,0,2", FilesPerLevel(0));
ASSERT_OK(env_->FileExists(dbname_ + moved_file_name));
listener->SetExpectedFileName(dbname_ + moved_file_name);
ASSERT_OK(iterator->status());
iterator.reset();
ASSERT_NOK(env_->FileExists(dbname_ + moved_file_name));
listener->VerifyMatchedCount(1);
}
}
TEST_P(DBCompactionTestWithParam, CompressLevelCompaction) {
if (!Zlib_Supported()) {
return;
}
Options options = CurrentOptions();
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 110 << 10; options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 4;
options.max_bytes_for_level_base = 400 * 1024;
options.max_subcompactions = max_subcompactions_;
options.compression_per_level = {kNoCompression, kNoCompression,
kZlibCompression};
int matches = 0, didnt_match = 0, trivial_move = 0, non_trivial = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"Compaction::InputCompressionMatchesOutput:Matches",
[&](void* ) { matches++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"Compaction::InputCompressionMatchesOutput:DidntMatch",
[&](void* ) { didnt_match++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Reopen(options);
Random rnd(301);
int key_idx = 0;
for (int num = 0; num < 3; num++) {
GenerateNewFile(&rnd, &key_idx);
}
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ(4, GetSstFileCount(dbname_));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,1", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,2", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,3", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,4", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,5", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,6", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,7", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,4,8", FilesPerLevel(0));
ASSERT_EQ(matches, 12);
const int kCallsToInputCompressionMatch = 2;
ASSERT_EQ(didnt_match, 8 * kCallsToInputCompressionMatch);
ASSERT_EQ(trivial_move, 12);
ASSERT_EQ(non_trivial, 8);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Reopen(options);
for (int i = 0; i < key_idx; i++) {
auto v = Get(Key(i));
ASSERT_NE(v, "NOT_FOUND");
ASSERT_TRUE(v.size() == 1 || v.size() == 990);
}
Destroy(options);
}
TEST_F(DBCompactionTest, SanitizeCompactionOptionsTest) {
Options options = CurrentOptions();
options.max_background_compactions = 5;
options.soft_pending_compaction_bytes_limit = 0;
options.hard_pending_compaction_bytes_limit = 100;
options.create_if_missing = true;
DestroyAndReopen(options);
ASSERT_EQ(100, db_->GetOptions().soft_pending_compaction_bytes_limit);
options.max_background_compactions = 3;
options.soft_pending_compaction_bytes_limit = 200;
options.hard_pending_compaction_bytes_limit = 150;
DestroyAndReopen(options);
ASSERT_EQ(150, db_->GetOptions().soft_pending_compaction_bytes_limit);
}
TEST_F(DBCompactionTest, SuggestCompactRangeNoTwoLevel0Compactions) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.write_buffer_size = 110 << 10;
options.arena_block_size = 4 << 10;
options.level0_file_num_compaction_trigger = 4;
options.num_levels = 4;
options.compression = kNoCompression;
options.max_bytes_for_level_base = 450 << 10;
options.target_file_size_base = 98 << 10;
options.max_write_buffer_number = 2;
options.max_background_compactions = 2;
DestroyAndReopen(options);
Random rnd(301);
for (int num = 0; num < 10; num++) {
GenerateNewRandomFile(&rnd);
}
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"CompactionJob::Run():Start",
"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:1"},
{"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:2",
"CompactionJob::Run():End"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int num = 0; num < options.level0_file_num_compaction_trigger + 1;
num++) {
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(Flush());
}
TEST_SYNC_POINT(
"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:1");
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_OK(experimental::SuggestCompactRange(db_.get(), nullptr, nullptr));
for (int num = 0; num < options.level0_file_num_compaction_trigger + 1;
num++) {
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(Flush());
}
TEST_SYNC_POINT(
"DBCompactionTest::SuggestCompactRangeNoTwoLevel0Compactions:2");
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
INSTANTIATE_TEST_CASE_P(
DBCompactionWaitForCompactTest, DBCompactionWaitForCompactTest,
::testing::Combine(
testing::Bool() , testing::Bool() ,
testing::Bool() ,
testing::Values(
std::chrono::microseconds::zero(),
std::chrono::microseconds{
60 * 60 *
1000000ULL} )));
TEST_P(DBCompactionWaitForCompactTest,
WaitForCompactWaitsOnCompactionToFinish) {
int compaction_finished = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::Run():EndStatusSet", [&](void* arg) {
auto status = static_cast<Status*>(arg);
if (status->ok()) {
compaction_finished++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule", [&](void* arg) {
auto unscheduled_flushes = *static_cast<int*>(arg);
ASSERT_GT(unscheduled_flushes, 0);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish",
"DBImpl::MaybeScheduleFlushOrCompaction:BeforeSchedule"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(123);
GenerateNewRandomFile(&rnd, true);
ASSERT_EQ(0, compaction_finished);
Close();
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactWaitsOnCompactionToFinish");
ASSERT_EQ(0, compaction_finished);
Reopen(options_);
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
ASSERT_GT(compaction_finished, 0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactAbortOnPause) {
Random rnd(123);
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->PauseBackgroundWork());
if (!abort_on_pause_) {
ASSERT_OK(dbfull()->ContinueBackgroundWork());
}
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
if (abort_on_pause_) {
ASSERT_NOK(s);
ASSERT_TRUE(s.IsAborted());
} else {
ASSERT_OK(s);
}
}
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactShutdownWhileWaiting) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
{"CompactionJob::Run():Start",
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:0"},
{"DBImpl::WaitForCompact:StartWaiting",
"DBCompactionTest::WaitForCompactShutdownWhileWaiting:1"},
{"DBImpl::~DBImpl:WaitJob", "CompactionJob::Run():End"},
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(123);
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(Flush());
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:0");
auto waiting_for_compaction_thread = port::Thread([this]() {
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
ASSERT_NOK(s);
ASSERT_TRUE(s.IsShutdownInProgress());
});
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactShutdownWhileWaiting:1");
auto closing_thread = port::Thread([this]() { ASSERT_OK(db_->Close()); });
waiting_for_compaction_thread.join();
closing_thread.join();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactWithOptionToFlush) {
int compaction_finished = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:AfterCompaction",
[&](void*) { compaction_finished++; });
int flush_finished = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FlushJob::End", [&](void*) { flush_finished++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(0), "some random string"));
ASSERT_EQ(0, compaction_finished);
ASSERT_EQ(0, flush_finished);
ASSERT_EQ("2", FilesPerLevel());
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
ASSERT_EQ(flush_, compaction_finished);
ASSERT_EQ(flush_, flush_finished);
if (!close_db_) {
std::string expected_files_per_level = flush_ ? "1,2" : "2";
ASSERT_EQ(expected_files_per_level, FilesPerLevel());
}
compaction_finished = 0;
flush_finished = 0;
if (!close_db_) {
Close();
}
Reopen(options_);
ASSERT_EQ(0, flush_finished);
if (flush_) {
ASSERT_EQ(0, compaction_finished);
} else {
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
ASSERT_EQ(1, compaction_finished);
}
if (!close_db_) {
ASSERT_EQ("1,2", FilesPerLevel());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBCompactionWaitForCompactTest,
WaitForCompactWithOptionToFlushAndCloseDB) {
std::atomic_int compaction_finished = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:Finish",
[&](void*) { compaction_finished++; });
std::atomic_int flush_finished = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"FlushJob::End", [&](void*) { flush_finished++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_FALSE(options_.avoid_flush_during_shutdown);
WriteOptions write_without_wal;
write_without_wal.disableWAL = true;
ASSERT_OK(Put(Key(0), "some random string", write_without_wal));
ASSERT_EQ(0, compaction_finished);
ASSERT_EQ(0, flush_finished);
ASSERT_EQ("2", FilesPerLevel());
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
int expected_flush_count = flush_ || close_db_;
ASSERT_EQ(expected_flush_count, flush_finished);
if (!close_db_) {
ASSERT_EQ(expected_flush_count, compaction_finished);
Close();
}
flush_finished = 0;
Reopen(options_);
ASSERT_EQ(0, flush_finished);
if (compaction_finished == 0) {
ASSERT_OK(dbfull()->WaitForCompact(wait_for_compact_options_));
}
ASSERT_EQ(1, compaction_finished);
if (!close_db_) {
ASSERT_EQ("1,2", FilesPerLevel());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_P(DBCompactionWaitForCompactTest, WaitForCompactToTimeout) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBCompactionTest::WaitForCompactToTimeout",
"CompactionJob::Run():Start"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(123);
GenerateNewRandomFile(&rnd, true);
ASSERT_OK(Flush());
if (wait_for_compact_options_.timeout.count()) {
wait_for_compact_options_.timeout = std::chrono::microseconds{1000};
} else {
TEST_SYNC_POINT("DBCompactionTest::WaitForCompactToTimeout");
}
Status s = dbfull()->WaitForCompact(wait_for_compact_options_);
if (wait_for_compact_options_.timeout.count()) {
ASSERT_NOK(s);
ASSERT_TRUE(s.IsTimedOut());
} else {
ASSERT_OK(s);
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
static std::string ShortKey(int i) {
assert(i < 10000);
char buf[100];
snprintf(buf, sizeof(buf), "key%04d", i);
return std::string(buf);
}
TEST_P(DBCompactionTestWithParam, ForceBottommostLevelCompaction) {
int32_t trivial_move = 0;
int32_t non_trivial_move = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial",
[&](void* ) { non_trivial_move++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
class ShortKeyComparator : public Comparator {
int Compare(const ROCKSDB_NAMESPACE::Slice& a,
const ROCKSDB_NAMESPACE::Slice& b) const override {
assert(a.size() <= 8);
assert(b.size() <= 8);
return BytewiseComparator()->Compare(a, b);
}
const char* Name() const override { return "ShortKeyComparator"; }
void FindShortestSeparator(
std::string* start,
const ROCKSDB_NAMESPACE::Slice& limit) const override {
return BytewiseComparator()->FindShortestSeparator(start, limit);
}
void FindShortSuccessor(std::string* key) const override {
return BytewiseComparator()->FindShortSuccessor(key);
}
} short_key_cmp;
Options options = CurrentOptions();
options.target_file_size_base = 100000000;
options.write_buffer_size = 100000000;
options.max_subcompactions = max_subcompactions_;
options.comparator = &short_key_cmp;
DestroyAndReopen(options);
int32_t value_size = 10 * 1024;
Random rnd(301);
std::vector<std::string> values;
for (int i = 0; i < 100; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1", FilesPerLevel(0));
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 3;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 1);
ASSERT_EQ(non_trivial_move, 0);
for (int i = 100; i < 200; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1,0,0,1", FilesPerLevel(0));
compact_options = CompactRangeOptions();
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,1", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 4);
ASSERT_EQ(non_trivial_move, 1);
for (int i = 200; i < 300; i++) {
values.push_back(rnd.RandomString(value_size));
ASSERT_OK(Put(ShortKey(i), values[i]));
}
ASSERT_OK(Flush());
ASSERT_EQ("1,0,0,1", FilesPerLevel(0));
trivial_move = 0;
non_trivial_move = 0;
compact_options = CompactRangeOptions();
compact_options.bottommost_level_compaction =
BottommostLevelCompaction::kSkip;
ASSERT_OK(db_->CompactRange(compact_options, nullptr, nullptr));
ASSERT_EQ("0,0,0,2", FilesPerLevel(0));
ASSERT_EQ(trivial_move, 3);
ASSERT_EQ(non_trivial_move, 0);
for (int i = 0; i < 300; i++) {
ASSERT_EQ(Get(ShortKey(i)), values[i]);
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_P(DBCompactionTestWithParam, IntraL0Compaction) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level0_file_num_compaction_trigger = 5;
options.max_background_compactions = 2;
options.max_subcompactions = max_subcompactions_;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.write_buffer_size = 2 << 20;
BlockBasedTableOptions table_options;
table_options.block_cache = NewLRUCache(64 << 20); table_options.cache_index_and_filter_blocks = true;
table_options.pin_l0_filter_and_index_blocks_in_cache = true;
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
DestroyAndReopen(options);
const size_t kValueSize = 1 << 20;
Random rnd(301);
std::string value(rnd.RandomString(kValueSize));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"LevelCompactionPicker::PickCompaction:Return",
"DBCompactionTest::IntraL0Compaction:L0ToL1Ready"},
{"LevelCompactionPicker::PickCompactionBySize:0",
"CompactionJob::Run():Start"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 10; ++i) {
ASSERT_OK(Put(Key(0), "")); if (i == 5) {
TEST_SYNC_POINT("DBCompactionTest::IntraL0Compaction:L0ToL1Ready");
ASSERT_OK(Put(Key(i + 1), value + value));
} else {
ASSERT_OK(Put(Key(i + 1), value));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
ASSERT_GE(level_to_files.size(), 2); ASSERT_EQ(2, level_to_files[0].size());
ASSERT_GT(level_to_files[1].size(), 0);
for (int i = 0; i < 2; ++i) {
ASSERT_GE(level_to_files[0][i].fd.file_size, 1 << 21);
}
uint64_t prev_index_misses =
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS);
table_options.block_cache->EraseUnRefEntries();
ASSERT_EQ("", Get(Key(0)));
ASSERT_EQ(prev_index_misses + 1,
TestGetTickerCount(options, BLOCK_CACHE_INDEX_MISS));
}
TEST_P(DBCompactionTestWithParam, IntraL0CompactionDoesNotObsoleteDeletions) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level0_file_num_compaction_trigger = 5;
options.max_background_compactions = 2;
options.max_subcompactions = max_subcompactions_;
DestroyAndReopen(options);
const size_t kValueSize = 1 << 20;
Random rnd(301);
std::string value(rnd.RandomString(kValueSize));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"LevelCompactionPicker::PickCompaction:Return",
"DBCompactionTest::IntraL0CompactionDoesNotObsoleteDeletions:"
"L0ToL1Ready"},
{"LevelCompactionPicker::PickCompactionBySize:0",
"CompactionJob::Run():Start"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 10; ++i) {
if (i < 5) {
ASSERT_OK(Put(Key(0), ""));
} else {
ASSERT_OK(Delete(Key(0)));
}
if (i == 5) {
TEST_SYNC_POINT(
"DBCompactionTest::IntraL0CompactionDoesNotObsoleteDeletions:"
"L0ToL1Ready");
}
ASSERT_OK(Put(Key(i + 1), value));
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
ASSERT_GE(level_to_files.size(), 2); ASSERT_EQ(1, level_to_files[0].size());
ASSERT_GT(level_to_files[1].size(), 0);
ASSERT_GE(level_to_files[0][0].fd.file_size, 1 << 22);
ReadOptions roptions;
std::string result;
ASSERT_TRUE(db_->Get(roptions, Key(0), &result).IsNotFound());
}
TEST_P(DBCompactionTestWithParam, FullCompactionInBottomPriThreadPool) {
const int kNumFilesTrigger = 3;
Env::Default()->SetBackgroundThreads(1, Env::Priority::BOTTOM);
for (auto compaction_style :
{kCompactionStyleLevel, kCompactionStyleUniversal}) {
for (auto universal_reduce_file_locking : {false, true}) {
if (compaction_style != kCompactionStyleUniversal &&
universal_reduce_file_locking) {
continue;
}
Options options = CurrentOptions();
options.compaction_style = compaction_style;
if (compaction_style == kCompactionStyleLevel) {
options.level_compaction_dynamic_level_bytes = true;
} else {
options.compaction_options_universal.reduce_file_locking =
universal_reduce_file_locking;
options.compaction_options_universal.max_size_amplification_percent =
110;
}
options.num_levels = 4;
options.write_buffer_size = 100 << 10; options.target_file_size_base = 32 << 10; options.level0_file_num_compaction_trigger = kNumFilesTrigger;
DestroyAndReopen(options);
int num_bottom_pri_compactions = 0;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BGWorkBottomCompaction",
[&](void* ) { ++num_bottom_pri_compactions; });
SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int num = 0; num < kNumFilesTrigger; num++) {
ASSERT_EQ(NumSortedRuns(), num);
int key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(1, num_bottom_pri_compactions);
ASSERT_EQ(NumSortedRuns(), 1);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
Env::Default()->SetBackgroundThreads(0, Env::Priority::BOTTOM);
}
TEST_F(DBCompactionTest, CancelCompactionWaitingOnRunningConflict) {
const int kNumSortedRuns = 4;
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.level0_file_num_compaction_trigger = kNumSortedRuns;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
Reopen(options);
test::SleepingBackgroundTask auto_compaction_sleeping_task;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::Run():Start",
[&](void* ) { auto_compaction_sleeping_task.DoSleep(); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int i = 0; i < kNumSortedRuns; ++i) {
int key_idx = 0;
GenerateNewFile(&rnd, &key_idx,
(i == kNumSortedRuns - 1) ? true : false );
}
auto_compaction_sleeping_task.WaitUntilSleeping();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"ColumnFamilyData::CompactRange:Return",
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
"PreDisableManualCompaction"}});
auto manual_compaction_thread = port::Thread([this]() {
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::CancelCompactionWaitingOnRunningConflict:"
"PreDisableManualCompaction");
db_->DisableManualCompaction();
manual_compaction_thread.join();
}
TEST_F(DBCompactionTest, CancelCompactionWaitingOnScheduledConflict) {
const int kNumSortedRuns = 4;
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.disable_auto_compactions = true;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
Reopen(options);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
Random rnd(301);
for (int i = 0; i < kNumSortedRuns; ++i) {
int key_idx = 0;
GenerateNewFile(&rnd, &key_idx, false );
}
std::atomic<int> num_compact_range_calls{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"ColumnFamilyData::CompactRange:Return",
[&](void* ) { num_compact_range_calls++; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
const int kNumManualCompactions = 2;
port::Thread manual_compaction_threads[kNumManualCompactions];
for (int i = 0; i < kNumManualCompactions; i++) {
manual_compaction_threads[i] = port::Thread([this]() {
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr)
.IsIncomplete());
});
}
while (num_compact_range_calls < kNumManualCompactions) {
}
db_->DisableManualCompaction();
for (int i = 0; i < kNumManualCompactions; i++) {
manual_compaction_threads[i].join();
}
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
}
TEST_F(DBCompactionTest, OptimizedDeletionObsoleting) {
const int kNumL0Files = 4;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
DestroyAndReopen(options);
for (int level = 2; level >= 1; --level) {
for (int i = 0; i < 2; ++i) {
ASSERT_OK(Put(Key(2 * i + 1), "val"));
ASSERT_OK(Flush());
}
MoveFilesToLevel(level);
ASSERT_EQ(2, NumTableFilesAtLevel(level));
}
for (int i = 0; i < kNumL0Files; ++i) {
ASSERT_OK(Put(Key(0), "val")); ASSERT_OK(Delete(Key(i + 1)));
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (int i = 0; i < kNumL0Files; ++i) {
std::string value;
ASSERT_TRUE(db_->Get(ReadOptions(), Key(i + 1), &value).IsNotFound());
}
ASSERT_EQ(2, options.statistics->getTickerCount(
COMPACTION_OPTIMIZED_DEL_DROP_OBSOLETE));
ASSERT_EQ(2,
options.statistics->getTickerCount(COMPACTION_KEY_DROP_OBSOLETE));
}
TEST_F(DBCompactionTest, CompactFilesPendingL0Bug) {
const int kNumL0Files = 5;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files - 1;
options.max_background_compactions = 2;
DestroyAndReopen(options);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"LevelCompactionPicker::PickCompaction:Return",
"DBCompactionTest::CompactFilesPendingL0Bug:Picked"},
{"DBCompactionTest::CompactFilesPendingL0Bug:ManualCompacted",
"DBImpl::BackgroundCompaction:NonTrivial:AfterRun"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
auto schedule_multi_compaction_token =
dbfull()->TEST_write_controler().GetCompactionPressureToken();
for (int i = 0; i < kNumL0Files - 1; ++i) {
ASSERT_OK(Put(Key(0), "val")); ASSERT_OK(Put(Key(i + 1), "val"));
ASSERT_OK(Flush());
}
TEST_SYNC_POINT("DBCompactionTest::CompactFilesPendingL0Bug:Picked");
ASSERT_OK(Put(Key(kNumL0Files), "val"));
ASSERT_OK(Flush());
ColumnFamilyMetaData cf_meta;
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
ASSERT_EQ(kNumL0Files, cf_meta.levels[0].files.size());
std::vector<std::string> input_filenames;
input_filenames.push_back(cf_meta.levels[0].files.front().name);
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), input_filenames,
0 ));
TEST_SYNC_POINT("DBCompactionTest::CompactFilesPendingL0Bug:ManualCompacted");
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, CompactFilesOverlapInL0Bug) {
ASSERT_OK(Put(Key(0), "old_val"));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(0), "new_val"));
ASSERT_OK(Flush());
ColumnFamilyMetaData cf_meta;
dbfull()->GetColumnFamilyMetaData(dbfull()->DefaultColumnFamily(), &cf_meta);
ASSERT_GE(cf_meta.levels.size(), 2);
ASSERT_EQ(2, cf_meta.levels[0].files.size());
std::vector<std::string> input_filenames;
input_filenames.push_back(cf_meta.levels[0].files.front().name);
ASSERT_OK(dbfull()->CompactFiles(CompactionOptions(), input_filenames,
1 ));
ASSERT_EQ("new_val", Get(Key(0)));
}
TEST_F(DBCompactionTest, DeleteFilesInRangeConflictWithCompaction) {
Options options = CurrentOptions();
DestroyAndReopen(options);
const Snapshot* snapshot = nullptr;
const int kMaxKey = 10;
for (int i = 0; i < kMaxKey; i++) {
ASSERT_OK(Put(Key(i), Key(i)));
ASSERT_OK(Delete(Key(i)));
if (!snapshot) {
snapshot = db_->GetSnapshot();
}
}
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_OK(Put(Key(kMaxKey), Key(kMaxKey)));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
SyncPoint::GetInstance()->LoadDependency(
{{"VersionSet::LogAndApply:WriteManifestStart",
"BackgroundCallCompaction:0"},
{"DBImpl::BackgroundCompaction:Finish",
"VersionSet::LogAndApply:WriteManifestDone"}});
SyncPoint::GetInstance()->EnableProcessing();
db_->ReleaseSnapshot(snapshot);
std::string begin_string = Key(0);
std::string end_string = Key(kMaxKey + 1);
Slice begin(begin_string);
Slice end(end_string);
ASSERT_OK(
DeleteFilesInRange(db_.get(), db_->DefaultColumnFamily(), &begin, &end));
SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, CompactBottomLevelFilesWithDeletions) {
const int kNumKeysPerFile = 1024;
const int kNumLevelFiles = 4;
const int kValueSize = 128;
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level0_file_num_compaction_trigger = kNumLevelFiles;
options.target_file_size_base = 120 * kNumKeysPerFile * kValueSize / 100;
CreateAndReopenWithCF({"one"}, options);
Random rnd(301);
const Snapshot* snapshot = nullptr;
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
if (i == kNumLevelFiles - 1) {
snapshot = db_->GetSnapshot();
for (int j = 0; j < kNumLevelFiles * kNumKeysPerFile; j += 2) {
ASSERT_OK(Delete(Key(j)));
}
}
ASSERT_OK(Flush());
if (i < kNumLevelFiles - 1) {
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
}
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
db_->GetLiveFilesMetaData(&pre_release_metadata);
ASSERT_OK(Put(Key(0), "val"));
ASSERT_NE(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->compaction_reason() ==
CompactionReason::kBottommostFiles);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
db_->ReleaseSnapshot(snapshot);
ASSERT_EQ(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
db_->GetLiveFilesMetaData(&post_release_metadata);
ASSERT_EQ(pre_release_metadata.size(), post_release_metadata.size());
for (size_t i = 0; i < pre_release_metadata.size(); ++i) {
const auto& pre_file = pre_release_metadata[i];
const auto& post_file = post_release_metadata[i];
ASSERT_EQ(1, pre_file.level);
ASSERT_EQ(1, post_file.level);
ASSERT_LT(post_file.size, pre_file.size);
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, DelayCompactBottomLevelFilesWithDeletions) {
Options options = CurrentOptions();
env_->SetMockSleep();
options.bottommost_file_compaction_delay = 3600;
DestroyAndReopen(options);
CreateColumnFamilies({"one"}, options);
const int kNumKey = 100;
const int kValLen = 100;
Random rnd(301);
for (int i = 0; i < kNumKey; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValLen)));
}
const Snapshot* snapshot = db_->GetSnapshot();
for (int i = 0; i < kNumKey; i += 2) {
ASSERT_OK(Delete(Key(i)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_EQ(1, NumTableFilesAtLevel(1));
std::vector<LiveFileMetaData> pre_release_metadata;
db_->GetLiveFilesMetaData(&pre_release_metadata);
ASSERT_EQ(1, pre_release_metadata.size());
std::atomic_int compaction_count = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->compaction_reason() ==
CompactionReason::kBottommostFiles);
compaction_count++;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(0), "val"));
ASSERT_NE(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
db_->ReleaseSnapshot(snapshot);
ASSERT_EQ(kMaxSequenceNumber, dbfull()->bottommost_files_mark_threshold_);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, compaction_count);
env_->MockSleepForSeconds(3600);
ASSERT_OK(Flush());
ASSERT_EQ(1, NumTableFilesAtLevel(0));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(1, compaction_count);
std::vector<LiveFileMetaData> post_release_metadata;
db_->GetLiveFilesMetaData(&post_release_metadata);
ASSERT_EQ(2, post_release_metadata.size());
const auto& pre_file = pre_release_metadata[0];
const auto& post_file = post_release_metadata[0].level == 0
? post_release_metadata[1]
: post_release_metadata[0];
ASSERT_EQ(1, pre_file.level);
ASSERT_EQ(1, post_file.level);
ASSERT_LT(post_file.size, pre_file.size);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, NoCompactBottomLevelFilesWithDeletions) {
const int kNumKeysPerFile = 1024;
const int kNumLevelFiles = 4;
const int kValueSize = 128;
Options options = CurrentOptions();
options.compression = kNoCompression;
options.disable_auto_compactions = true;
options.level0_file_num_compaction_trigger = kNumLevelFiles;
options.target_file_size_base = 120 * kNumKeysPerFile * kValueSize / 100;
Reopen(options);
Random rnd(301);
const Snapshot* snapshot = nullptr;
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
if (i == kNumLevelFiles - 1) {
snapshot = db_->GetSnapshot();
for (int j = 0; j < kNumLevelFiles * kNumKeysPerFile; j += 2) {
ASSERT_OK(Delete(Key(j)));
}
}
ASSERT_OK(Flush());
if (i < kNumLevelFiles - 1) {
ASSERT_EQ(i + 1, NumTableFilesAtLevel(0));
}
}
ASSERT_OK(dbfull()->TEST_CompactRange(0, nullptr, nullptr, nullptr));
ASSERT_EQ(kNumLevelFiles, NumTableFilesAtLevel(1));
std::vector<LiveFileMetaData> pre_release_metadata, post_release_metadata;
db_->GetLiveFilesMetaData(&pre_release_metadata);
ASSERT_OK(Put(Key(0), "val"));
std::atomic<int> num_compactions{0};
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:Start",
[&](void* ) { num_compactions.fetch_add(1); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
db_->ReleaseSnapshot(snapshot);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, num_compactions);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
db_->GetLiveFilesMetaData(&post_release_metadata);
ASSERT_EQ(pre_release_metadata.size(), post_release_metadata.size());
for (size_t i = 0; i < pre_release_metadata.size(); ++i) {
const auto& pre_file = pre_release_metadata[i];
const auto& post_file = post_release_metadata[i];
ASSERT_EQ(1, pre_file.level);
ASSERT_EQ(1, post_file.level);
ASSERT_EQ(post_file.size, pre_file.size);
}
}
TEST_F(DBCompactionTest, RoundRobinTtlCompactionNormal) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level0_file_num_compaction_trigger = 20;
options.ttl = 24 * 60 * 60; options.compaction_pri = kRoundRobin;
env_->now_cpu_count_.store(0);
env_->SetMockSleep();
options.env = env_;
int small_seconds = 1;
std::atomic_int ttl_compactions{0};
std::atomic_int round_robin_ttl_compactions{0};
std::atomic_int other_compactions{0};
SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kTtl) {
ttl_compactions++;
} else if (compaction_reason == CompactionReason::kRoundRobinTtl) {
round_robin_ttl_compactions++;
} else {
other_compactions++;
}
});
SyncPoint::GetInstance()->EnableProcessing();
DestroyAndReopen(options);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 100; j++) {
ASSERT_OK(Put(Key(i * 100 + j), "value" + std::to_string(i * 100 + j)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(60 * 60); }
MoveFilesToLevel(6);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 200; j++) {
ASSERT_OK(Put(Key(i * 200 + j), "value" + std::to_string(i * 200 + j)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(60 * 60);
}
MoveFilesToLevel(5);
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 300; j++) {
ASSERT_OK(Put(Key(i * 300 + j), "value" + std::to_string(i * 300 + j)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(60 * 60);
}
MoveFilesToLevel(4);
ASSERT_EQ("0,0,0,0,3,5,10", FilesPerLevel());
env_->MockSleepForSeconds(16 * 60 * 60 + small_seconds++);
ASSERT_OK(Put(Key(4), "value" + std::to_string(1)));
ASSERT_OK(Put(Key(5), "value" + std::to_string(1)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(1, round_robin_ttl_compactions);
round_robin_ttl_compactions = 0;
env_->MockSleepForSeconds(2 * 60 * 60 + small_seconds++);
ASSERT_OK(Put(Key(4), "value" + std::to_string(2)));
ASSERT_OK(Put(Key(5), "value" + std::to_string(2)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, round_robin_ttl_compactions);
round_robin_ttl_compactions = 0;
env_->MockSleepForSeconds(4 * 60 * 60 + small_seconds++);
ASSERT_OK(Put(Key(6), "value" + std::to_string(3)));
ASSERT_OK(Put(Key(7), "value" + std::to_string(3)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(1, NumTableFilesAtLevel(4));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
ASSERT_GT(round_robin_ttl_compactions, 0);
round_robin_ttl_compactions = 0;
ASSERT_EQ(0, ttl_compactions);
env_->MockSleepForSeconds(19 * 60 * 60 + small_seconds++);
ASSERT_OK(Put(Key(6), "value" + std::to_string(4)));
ASSERT_OK(Put(Key(7), "value" + std::to_string(4)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_GT(ttl_compactions, 0);
ttl_compactions = 0;
ASSERT_GT(round_robin_ttl_compactions, 0);
round_robin_ttl_compactions = 0;
env_->MockSleepForSeconds(24 * 60 * 60);
ASSERT_OK(Put(Key(6), "value" + std::to_string(4)));
ASSERT_OK(Put(Key(7), "value" + std::to_string(4)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
ASSERT_GT(ttl_compactions, 0);
ttl_compactions = 0;
ASSERT_GT(round_robin_ttl_compactions, 0);
round_robin_ttl_compactions = 0;
ASSERT_EQ(0, other_compactions);
}
TEST_F(DBCompactionTest, RoundRobinTtlCompactionUnsortedTime) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level0_file_num_compaction_trigger = 20;
options.ttl = 24 * 60 * 60; options.compaction_pri = kRoundRobin;
env_->now_cpu_count_.store(0);
env_->SetMockSleep();
options.env = env_;
std::atomic_int ttl_compactions{0};
std::atomic_int round_robin_ttl_compactions{0};
std::atomic_int other_compactions{0};
SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kTtl) {
ttl_compactions++;
} else if (compaction_reason == CompactionReason::kRoundRobinTtl) {
round_robin_ttl_compactions++;
} else {
other_compactions++;
}
});
SyncPoint::GetInstance()->EnableProcessing();
DestroyAndReopen(options);
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 100; j++) {
ASSERT_OK(Put(Key(i * 100 + j), "value" + std::to_string(i * 100 + j)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(60 * 60); }
MoveFilesToLevel(6);
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 200; j++) {
ASSERT_OK(Put(Key(i * 200 + j), "value" + std::to_string(i * 200 + j)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(60 * 60); }
MoveFilesToLevel(5);
ASSERT_EQ("0,0,0,0,0,5,10", FilesPerLevel());
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(cfd, nullptr);
Version* const current = cfd->current();
ASSERT_NE(current, nullptr);
VersionStorageInfo* storage_info = current->storage_info();
ASSERT_NE(storage_info, nullptr);
const InternalKey split_cursor = InternalKey(Key(600), 100000, kTypeValue);
storage_info->AddCursorForOneLevel(5, split_cursor);
env_->MockSleepForSeconds(19 * 60 * 60 + 1);
ASSERT_OK(Put(Key(6), "value" + std::to_string(4)));
ASSERT_OK(Put(Key(7), "value" + std::to_string(4)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(2, NumTableFilesAtLevel(5));
ASSERT_EQ(3, round_robin_ttl_compactions);
ASSERT_EQ(0, ttl_compactions);
ASSERT_EQ(0, other_compactions);
}
TEST_F(DBCompactionTest, LevelCompactExpiredTtlFiles) {
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 2;
const int kValueSize = 1024;
Options options = CurrentOptions();
options.compression = kNoCompression;
options.ttl = 24 * 60 * 60; options.max_open_files = -1;
env_->SetMockSleep();
options.env = env_;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(3);
ASSERT_EQ("0,0,0,2", FilesPerLevel());
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Delete(Key(i * kNumKeysPerFile + j)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2,0,0,2", FilesPerLevel());
MoveFilesToLevel(1);
ASSERT_EQ("0,2,0,2", FilesPerLevel());
env_->MockSleepForSeconds(36 * 60 * 60); ASSERT_EQ("0,2,0,2", FilesPerLevel());
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1", FilesPerLevel());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
DestroyAndReopen(options);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(3);
ASSERT_EQ("0,0,0,2", FilesPerLevel());
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Delete(Key(i * kNumKeysPerFile + j)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2,0,0,2", FilesPerLevel());
MoveFilesToLevel(1);
ASSERT_EQ("0,2,0,2", FilesPerLevel());
env_->MockSleepForSeconds(12 * 60 * 60);
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,2,0,2", FilesPerLevel());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->compaction_reason() == CompactionReason::kTtl);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(dbfull()->SetOptions({{"ttl", "36000"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1", FilesPerLevel());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, LevelTtlCompactionOutputCuttingIteractingWithOther) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.ttl = 24 * 60 * 60; options.max_open_files = -1;
options.compaction_pri = kMinOverlappingRatio;
env_->SetMockSleep();
options.env = env_;
options.target_file_size_base = 4 << 10;
options.disable_auto_compactions = true;
DestroyAndReopen(options);
Random rnd(301);
ASSERT_OK(Put(Key(3), rnd.RandomString(1 << 10)));
ASSERT_OK(Put(Key(0), rnd.RandomString(1 << 10)));
ASSERT_OK(Flush());
MoveFilesToLevel(6);
ASSERT_OK(Put(Key(2), rnd.RandomString(4 << 10)));
ASSERT_OK(Put(Key(3), rnd.RandomString(4 << 10)));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(Put(Key(0), rnd.RandomString(4 << 10)));
ASSERT_OK(Put(Key(1), rnd.RandomString(4 << 10)));
ASSERT_OK(Put(Key(3), rnd.RandomString(4 << 10)));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_EQ("0,1,1,0,0,0,1", FilesPerLevel());
env_->MockSleepForSeconds(36 * 60 * 60);
CompactRangeOptions compact_range_opts;
ASSERT_OK(dbfull()->RunManualCompaction(
static_cast_with_check<ColumnFamilyHandleImpl>(db_->DefaultColumnFamily())
->cfd(),
1 , 2 , compact_range_opts,
nullptr , nullptr , true ,
true ,
std::numeric_limits<uint64_t>::max() ,
"" ));
ASSERT_EQ("0,0,2,0,0,0,1", FilesPerLevel());
}
TEST_F(DBCompactionTest, LevelTtlCascadingCompactions) {
env_->SetMockSleep();
const int kValueSize = 100;
for (bool if_restart : {false, true}) {
for (bool if_open_all_files : {false, true}) {
Options options = CurrentOptions();
options.compression = kNoCompression;
options.ttl = 24 * 60 * 60; if (if_open_all_files) {
options.max_open_files = -1;
} else {
options.max_open_files = 20;
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
int* max_open_files = static_cast<int*>(arg);
*max_open_files = 2;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionEdit::EncodeTo:VarintOldestAncesterTime", [&](void* arg) {
if (if_restart && if_open_all_files) {
std::string* encoded_field = static_cast<std::string*>(arg);
*encoded_field = "";
PutVarint64(encoded_field, 0);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
options.env = env_;
DestroyAndReopen(options);
int ttl_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kTtl) {
ttl_compactions++;
}
});
Random rnd(301);
for (int i = 1; i <= 100; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
uint64_t oldest_time = level_to_files[0][0].oldest_ancester_time;
env_->MockSleepForSeconds(1 * 60 * 60);
for (int i = 101; i <= 200; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(6);
ASSERT_EQ("0,0,0,0,0,0,2", FilesPerLevel());
env_->MockSleepForSeconds(1 * 60 * 60);
for (int i = 1; i <= 50; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(1 * 60 * 60);
for (int i = 51; i <= 150; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(4);
ASSERT_EQ("0,0,0,0,2,0,2", FilesPerLevel());
env_->MockSleepForSeconds(1 * 60 * 60);
for (int i = 26; i <= 75; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(1);
ASSERT_EQ("0,1,0,0,2,0,2", FilesPerLevel());
env_->MockSleepForSeconds(25 * 60 * 60);
ASSERT_OK(Put(Key(1), "1"));
if (if_restart) {
Reopen(options);
} else {
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
ASSERT_EQ(5, ttl_compactions);
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
ASSERT_EQ(oldest_time, level_to_files[6][0].oldest_ancester_time);
env_->MockSleepForSeconds(25 * 60 * 60);
ASSERT_OK(Put(Key(2), "1"));
if (if_restart) {
Reopen(options);
} else {
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,0,0,0,0,0,1", FilesPerLevel());
ASSERT_GE(ttl_compactions, 6);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
}
TEST_F(DBCompactionTest, LevelPeriodicCompaction) {
env_->SetMockSleep();
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 2;
const int kValueSize = 100;
for (bool if_restart : {false, true}) {
for (bool if_open_all_files : {false, true}) {
Options options = CurrentOptions();
options.periodic_compaction_seconds = 48 * 60 * 60; if (if_open_all_files) {
options.max_open_files = -1; } else {
options.max_open_files = 20;
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"SanitizeOptions::AfterChangeMaxOpenFiles", [&](void* arg) {
int* max_open_files = static_cast<int*>(arg);
*max_open_files = 0;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionEdit::EncodeTo:VarintFileCreationTime", [&](void* arg) {
if (if_restart && if_open_all_files) {
std::string* encoded_field = static_cast<std::string*>(arg);
*encoded_field = "";
PutVarint64(encoded_field, 0);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
options.env = env_;
DestroyAndReopen(options);
int periodic_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
periodic_compactions++;
}
});
Random rnd(301);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
env_->MockSleepForSeconds(50 * 60 * 60);
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("3", FilesPerLevel());
ASSERT_EQ(2, periodic_compactions);
MoveFilesToLevel(1);
ASSERT_EQ("0,3", FilesPerLevel());
env_->MockSleepForSeconds(50 * 60 * 60);
ASSERT_OK(Put("b", "2"));
if (if_restart) {
Reopen(options);
} else {
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,3", FilesPerLevel());
ASSERT_EQ(5, periodic_compactions);
env_->MockSleepForSeconds(50 * 60 * 60);
ASSERT_OK(Put("c", "3"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2,3", FilesPerLevel());
ASSERT_EQ(9, periodic_compactions);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
}
TEST_F(DBCompactionTest, LevelPeriodicCompactionOffpeak) {
constexpr int kNumKeysPerFile = 32;
constexpr int kNumLevelFiles = 2;
constexpr int kValueSize = 100;
constexpr int kSecondsPerDay = 86400;
constexpr int kSecondsPerHour = 3600;
constexpr int kSecondsPerMinute = 60;
for (bool if_restart : {false, true}) {
SCOPED_TRACE("if_restart=" + std::to_string(if_restart));
Options options = CurrentOptions();
options.ttl = 0;
options.periodic_compaction_seconds = 5 * kSecondsPerDay; ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionEdit::EncodeTo:VarintFileCreationTime", [&](void* arg) {
if (if_restart) {
std::string* encoded_field = static_cast<std::string*>(arg);
*encoded_field = "";
PutVarint64(encoded_field, 0);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(test::RandomSeed());
int days = rnd.Uniform(100);
int periodic_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
periodic_compactions++;
}
});
int now_hour = 0;
int now_minute = 15;
auto mock_clock = std::make_shared<MockSystemClock>(env_->GetSystemClock());
auto mock_env = std::make_unique<CompositeEnvWrapper>(env_, mock_clock);
options.env = mock_env.get();
mock_clock->SetCurrentTime(days * kSecondsPerDay +
now_hour * kSecondsPerHour +
now_minute * kSecondsPerMinute);
options.daily_offpeak_time_utc = "00:30-04:30";
Reopen(options);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
mock_clock->MockSleepForSeconds(1 * kSecondsPerHour);
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("3", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
MoveFilesToLevel(1);
ASSERT_EQ("0,3", FilesPerLevel());
mock_clock->MockSleepForSeconds(4 * kSecondsPerDay);
ASSERT_OK(Put("b", "2"));
if (if_restart) {
Reopen(options);
} else {
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,3", FilesPerLevel());
ASSERT_EQ(2, periodic_compactions);
Destroy(options);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
TEST_F(DBCompactionTest, LevelPeriodicCompactionWithOldDB) {
const int kNumKeysPerFile = 32;
const int kNumFiles = 4;
const int kValueSize = 100;
Options options = CurrentOptions();
env_->SetMockSleep();
options.env = env_;
DestroyAndReopen(options);
int periodic_compactions = 0;
bool set_file_creation_time_to_zero = true;
bool set_creation_time_to_zero = true;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
periodic_compactions++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PropertyBlockBuilder::AddTableProperty:Start", [&](void* arg) {
TableProperties* props = static_cast<TableProperties*>(arg);
if (set_file_creation_time_to_zero) {
props->file_creation_time = 0;
}
if (set_creation_time_to_zero) {
props->creation_time = 0;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int i = 0; i < kNumFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
if (i == 1) {
MoveFilesToLevel(2);
set_creation_time_to_zero = false;
}
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2,0,2", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
Close();
set_file_creation_time_to_zero = false;
env_->MockSleepForSeconds(2 * 24 * 60 * 60);
options.periodic_compaction_seconds = 1 * 24 * 60 * 60;
Reopen(options);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2,0,2", FilesPerLevel());
ASSERT_EQ(kNumFiles, periodic_compactions);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, LevelPeriodicAndTtlCompaction) {
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 2;
const int kValueSize = 100;
Options options = CurrentOptions();
options.ttl = 10 * 60 * 60; options.periodic_compaction_seconds = 48 * 60 * 60; options.max_open_files = -1; env_->SetMockSleep();
options.env = env_;
DestroyAndReopen(options);
int periodic_compactions = 0;
int ttl_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
periodic_compactions++;
} else if (compaction_reason == CompactionReason::kTtl) {
ttl_compactions++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(3);
ASSERT_EQ("0,0,0,2", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
ASSERT_EQ(0, ttl_compactions);
env_->MockSleepForSeconds(50 * 60 * 60);
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,0,0,2", FilesPerLevel());
ASSERT_EQ(2, periodic_compactions);
ASSERT_EQ(0, ttl_compactions);
env_->MockSleepForSeconds(11 * 60 * 60);
ASSERT_OK(Put("b", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,0,0,3", FilesPerLevel());
ASSERT_EQ(2, periodic_compactions);
ASSERT_EQ(3, ttl_compactions);
env_->MockSleepForSeconds(50 * 60 * 60);
ASSERT_OK(Put("c", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("1,0,0,4", FilesPerLevel());
ASSERT_EQ(6, periodic_compactions);
ASSERT_EQ(6, ttl_compactions);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, LevelTtlBooster) {
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 3;
const int kValueSize = 1000;
Options options = CurrentOptions();
options.ttl = 10 * 60 * 60; options.periodic_compaction_seconds = 480 * 60 * 60; options.level0_file_num_compaction_trigger = 2;
options.max_bytes_for_level_base = 5 * uint64_t{kNumKeysPerFile * kValueSize};
options.max_open_files = -1; options.compaction_pri = CompactionPri::kMinOverlappingRatio;
env_->SetMockSleep();
options.env = env_;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
MoveFilesToLevel(2);
ASSERT_EQ("0,0,3", FilesPerLevel());
for (int i = 0; i < 2; i++) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Put(Key(2 * j + i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
ASSERT_EQ("0,1,3", FilesPerLevel());
env_->MockSleepForSeconds(8 * 60 * 60);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(Put(Key(kNumKeysPerFile * 2 + 2 * j + i),
rnd.RandomString(kValueSize * 2)));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "1"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1,2", FilesPerLevel());
ASSERT_OK(
dbfull()->SetOptions({{"level0_file_num_compaction_trigger", "2"}}));
ASSERT_GT(SizeAtLevel(1), kNumKeysPerFile * 4 * kValueSize);
}
TEST_F(DBCompactionTest, LevelPeriodicCompactionWithCompactionFilters) {
class TestCompactionFilter : public CompactionFilter {
const char* Name() const override { return "TestCompactionFilter"; }
};
class TestCompactionFilterFactory : public CompactionFilterFactory {
const char* Name() const override { return "TestCompactionFilterFactory"; }
std::unique_ptr<CompactionFilter> CreateCompactionFilter(
const CompactionFilter::Context& ) override {
return std::unique_ptr<CompactionFilter>(new TestCompactionFilter());
}
};
const int kNumKeysPerFile = 32;
const int kNumLevelFiles = 2;
const int kValueSize = 100;
Random rnd(301);
Options options = CurrentOptions();
TestCompactionFilter test_compaction_filter;
env_->SetMockSleep();
options.env = env_;
enum CompactionFilterType {
kUseCompactionFilter,
kUseCompactionFilterFactory
};
for (CompactionFilterType comp_filter_type :
{kUseCompactionFilter, kUseCompactionFilterFactory}) {
ASSERT_EQ(std::numeric_limits<uint64_t>::max() - 1,
options.periodic_compaction_seconds);
if (comp_filter_type == kUseCompactionFilter) {
options.compaction_filter = &test_compaction_filter;
options.compaction_filter_factory.reset();
} else if (comp_filter_type == kUseCompactionFilterFactory) {
options.compaction_filter = nullptr;
options.compaction_filter_factory.reset(
new TestCompactionFilterFactory());
}
DestroyAndReopen(options);
ASSERT_EQ(30 * 24 * 60 * 60,
dbfull()->GetOptions().periodic_compaction_seconds);
int periodic_compactions = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
auto compaction_reason = compaction->compaction_reason();
if (compaction_reason == CompactionReason::kPeriodicCompaction) {
periodic_compactions++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < kNumLevelFiles; ++i) {
for (int j = 0; j < kNumKeysPerFile; ++j) {
ASSERT_OK(
Put(Key(i * kNumKeysPerFile + j), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("2", FilesPerLevel());
ASSERT_EQ(0, periodic_compactions);
env_->MockSleepForSeconds(31 * 24 * 60 * 60);
ASSERT_OK(Put("a", "1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("3", FilesPerLevel());
ASSERT_EQ(2, periodic_compactions);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
TEST_F(DBCompactionTest, CompactRangeDelayedByL0FileCount) {
const int kNumL0FilesTrigger = 4;
const int kNumL0FilesLimit = 8;
for (int i = 0; i < 2; ++i) {
Options options = CurrentOptions();
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
if (i == 0) {
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
} else {
options.level0_file_num_compaction_trigger = kNumL0FilesLimit;
}
Reopen(options);
if (i == 0) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
"CompactionJob::Run():End"}});
} else {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:StallWaitDone",
"CompactionJob::Run():End"}});
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int j = 0; j < kNumL0FilesLimit - 1; ++j) {
for (int k = 0; k < 2; ++k) {
ASSERT_OK(Put(Key(k), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
auto manual_compaction_thread = port::Thread([this]() {
CompactRangeOptions cro;
cro.allow_write_stall = false;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
});
manual_compaction_thread.join();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(0, NumTableFilesAtLevel(0));
ASSERT_GT(NumTableFilesAtLevel(1), 0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
TEST_F(DBCompactionTest, CompactRangeDelayedByImmMemTableCount) {
const int kNumImmMemTableLimit = 8;
for (int i = 0; i < 2; ++i) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.max_write_buffer_number = kNumImmMemTableLimit + 1;
if (i == 1) {
options.min_write_buffer_number_to_merge = kNumImmMemTableLimit;
}
Reopen(options);
if (i == 0) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
"FlushJob::WriteLevel0Table"}});
} else {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:StallWaitDone",
"FlushJob::WriteLevel0Table"}});
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int j = 0; j < kNumImmMemTableLimit - 1; ++j) {
ASSERT_OK(Put(Key(0), rnd.RandomString(1024)));
FlushOptions flush_opts;
flush_opts.wait = false;
flush_opts.allow_write_stall = true;
ASSERT_OK(dbfull()->Flush(flush_opts));
}
auto manual_compaction_thread = port::Thread([this]() {
Random compact_rnd(301);
ASSERT_OK(Put(Key(0), compact_rnd.RandomString(1024)));
CompactRangeOptions cro;
cro.allow_write_stall = false;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
});
manual_compaction_thread.join();
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ(0, NumTableFilesAtLevel(0));
ASSERT_GT(NumTableFilesAtLevel(1), 0);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
TEST_F(DBCompactionTest, CompactRangeShutdownWhileDelayed) {
const int kNumL0FilesTrigger = 4;
const int kNumL0FilesLimit = 8;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
for (int i = 0; i < 2; ++i) {
CreateAndReopenWithCF({"one"}, options);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PreShutdown"},
{"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual",
"CompactionJob::Run():End"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
for (int j = 0; j < kNumL0FilesLimit - 1; ++j) {
for (int k = 0; k < 2; ++k) {
ASSERT_OK(Put(1, Key(k), rnd.RandomString(1024)));
}
ASSERT_OK(Flush(1));
}
auto manual_compaction_thread = port::Thread([this, i]() {
CompactRangeOptions cro;
cro.allow_write_stall = false;
if (i == 0) {
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
.IsColumnFamilyDropped());
} else {
ASSERT_TRUE(db_->CompactRange(cro, handles_[1], nullptr, nullptr)
.IsShutdownInProgress());
}
});
TEST_SYNC_POINT(
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PreShutdown");
if (i == 0) {
ASSERT_OK(db_->DropColumnFamily(handles_[1]));
} else {
dbfull()->CancelAllBackgroundWork(false );
}
manual_compaction_thread.join();
TEST_SYNC_POINT(
"DBCompactionTest::CompactRangeShutdownWhileDelayed:PostManual");
if (i == 0) {
ASSERT_OK(dbfull()->TEST_WaitForCompact());
} else {
ASSERT_NOK(dbfull()->TEST_WaitForCompact());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
}
TEST_F(DBCompactionTest, CompactRangeSkipFlushAfterDelay) {
const int kNumL0FilesTrigger = 4;
const int kNumL0FilesLimit = 8;
Options options = CurrentOptions();
options.level0_slowdown_writes_trigger = kNumL0FilesLimit;
options.level0_file_num_compaction_trigger = kNumL0FilesTrigger;
Reopen(options);
Random rnd(301);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::WaitUntilFlushWouldNotStallWrites:StallWait",
"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush"},
{"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush",
"DBImpl::FlushMemTable:StallWaitDone"},
{"DBImpl::FlushMemTable:StallWaitDone", "CompactionJob::Run():End"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
FlushOptions flush_opts;
flush_opts.allow_write_stall = true;
for (int i = 0; i < kNumL0FilesLimit - 1; ++i) {
for (int j = 0; j < 2; ++j) {
ASSERT_OK(Put(Key(j), rnd.RandomString(1024)));
}
ASSERT_OK(dbfull()->Flush(flush_opts));
}
auto manual_compaction_thread = port::Thread([this]() {
CompactRangeOptions cro;
cro.allow_write_stall = false;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
});
TEST_SYNC_POINT("DBCompactionTest::CompactRangeSkipFlushAfterDelay:PreFlush");
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
ASSERT_OK(dbfull()->Flush(flush_opts));
ASSERT_OK(Put(std::to_string(0), rnd.RandomString(1024)));
TEST_SYNC_POINT(
"DBCompactionTest::CompactRangeSkipFlushAfterDelay:PostFlush");
manual_compaction_thread.join();
std::string num_keys_in_memtable;
ASSERT_TRUE(db_->GetProperty(DB::Properties::kNumEntriesActiveMemTable,
&num_keys_in_memtable));
ASSERT_EQ(std::to_string(1), num_keys_in_memtable);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, CompactRangeFlushOverlappingMemtable) {
const int kNumEndpointKeys = 5;
std::string keys[kNumEndpointKeys] = {"a", "b", "c", "d", "e"};
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
for (int i = 0; i <= kNumEndpointKeys; ++i) {
Slice begin;
Slice* begin_ptr;
if (i == 0) {
begin_ptr = nullptr;
} else {
begin = keys[i - 1];
begin_ptr = &begin;
}
for (int j = std::max(0, i - 1); j <= kNumEndpointKeys; ++j) {
Slice end;
Slice* end_ptr;
if (j == kNumEndpointKeys) {
end_ptr = nullptr;
} else {
end = keys[j];
end_ptr = &end;
}
ASSERT_OK(Put("b", "val"));
ASSERT_OK(Put("d", "val"));
CompactRangeOptions compact_range_opts;
ASSERT_OK(db_->CompactRange(compact_range_opts, begin_ptr, end_ptr));
uint64_t get_prop_tmp, num_memtable_entries = 0;
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesImmMemTables,
&get_prop_tmp));
num_memtable_entries += get_prop_tmp;
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumEntriesActiveMemTable,
&get_prop_tmp));
num_memtable_entries += get_prop_tmp;
if (begin_ptr == nullptr || end_ptr == nullptr ||
(i <= 4 && j >= 1 && (begin != "c" || end != "c"))) {
ASSERT_EQ(0, num_memtable_entries);
} else {
ASSERT_EQ(2, num_memtable_entries);
ASSERT_OK(db_->Flush(FlushOptions()));
}
}
}
}
TEST_F(DBCompactionTest, CompactionStatsTest) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
CompactionStatsCollector* collector = new CompactionStatsCollector();
options.listeners.emplace_back(collector);
DestroyAndReopen(options);
uint64_t num_running_compactions = 0;
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
ASSERT_EQ(num_running_compactions, 0);
uint64_t num_running_compaction_sorted_runs = 0;
ASSERT_TRUE(
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
&num_running_compaction_sorted_runs));
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
std::atomic<bool> sorted_runs_count_incremented = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionMergingIterator::UpdateInternalStats",
[&](void*) { sorted_runs_count_incremented = true; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ColumnFamilyHandleImpl* cfh =
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
ColumnFamilyData* cfd = cfh->cfd();
VerifyCompactionStats(*cfd, *collector);
ASSERT_TRUE(db_->GetIntProperty(DB::Properties::kNumRunningCompactions,
&num_running_compactions));
ASSERT_EQ(num_running_compactions, 0);
ASSERT_TRUE(
db_->GetIntProperty(DB::Properties::kNumRunningCompactionSortedRuns,
&num_running_compaction_sorted_runs));
ASSERT_EQ(num_running_compaction_sorted_runs, 0);
ASSERT_TRUE(sorted_runs_count_incremented);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, SubcompactionEvent) {
class SubCompactionEventListener : public EventListener {
public:
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
InstrumentedMutexLock l(&mutex_);
ASSERT_EQ(running_compactions_.find(ci.job_id),
running_compactions_.end());
running_compactions_.emplace(ci.job_id, std::unordered_set<int>());
if (expected_num_l0_files_pre_compaction_ != -1) {
ASSERT_EQ(expected_num_l0_files_pre_compaction_, ci.num_l0_files);
}
}
void OnCompactionCompleted(DB* ,
const CompactionJobInfo& ci) override {
InstrumentedMutexLock l(&mutex_);
auto it = running_compactions_.find(ci.job_id);
ASSERT_NE(it, running_compactions_.end());
ASSERT_EQ(it->second.size(), 0);
running_compactions_.erase(it);
if (expected_num_l0_files_post_compaction_ != -1) {
ASSERT_EQ(expected_num_l0_files_post_compaction_, ci.num_l0_files);
}
}
void OnSubcompactionBegin(const SubcompactionJobInfo& si) override {
InstrumentedMutexLock l(&mutex_);
auto it = running_compactions_.find(si.job_id);
ASSERT_NE(it, running_compactions_.end());
auto r = it->second.insert(si.subcompaction_job_id);
ASSERT_TRUE(r.second); total_subcompaction_cnt_++;
}
void OnSubcompactionCompleted(const SubcompactionJobInfo& si) override {
InstrumentedMutexLock l(&mutex_);
auto it = running_compactions_.find(si.job_id);
ASSERT_NE(it, running_compactions_.end());
auto r = it->second.erase(si.subcompaction_job_id);
ASSERT_EQ(r, 1);
}
size_t GetRunningCompactionCount() {
InstrumentedMutexLock l(&mutex_);
return running_compactions_.size();
}
size_t GetTotalSubcompactionCount() {
InstrumentedMutexLock l(&mutex_);
return total_subcompaction_cnt_;
}
void SetExpectedNumL0FilesPreCompaction(int num) {
expected_num_l0_files_pre_compaction_ = num;
}
void SetExpectedNumL0FilesPostCompaction(int num) {
expected_num_l0_files_post_compaction_ = num;
}
void ResetExpectedNumL0Files() {
SetExpectedNumL0FilesPreCompaction(-1);
SetExpectedNumL0FilesPostCompaction(-1);
}
private:
InstrumentedMutex mutex_;
std::unordered_map<int, std::unordered_set<int>> running_compactions_;
size_t total_subcompaction_cnt_ = 0;
int expected_num_l0_files_pre_compaction_ = -1;
int expected_num_l0_files_post_compaction_ = -1;
};
Options options = CurrentOptions();
options.target_file_size_base = 1024;
options.level0_file_num_compaction_trigger = 10;
auto* listener = new SubCompactionEventListener();
options.listeners.emplace_back(listener);
DestroyAndReopen(options);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 10; j++) {
int key_id = i * 10 + j;
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(2);
ASSERT_EQ(FilesPerLevel(), "0,0,4");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 10; j++) {
int key_id = i * 20 + j * 2;
ASSERT_OK(Put(Key(key_id), "value" + std::to_string(key_id)));
}
ASSERT_OK(Flush());
}
listener->SetExpectedNumL0FilesPreCompaction(2 );
listener->SetExpectedNumL0FilesPostCompaction(0 );
MoveFilesToLevel(1);
ASSERT_EQ(FilesPerLevel(), "0,2,4");
listener->ResetExpectedNumL0Files();
CompactRangeOptions comp_opts;
comp_opts.max_subcompactions = 4;
listener->SetExpectedNumL0FilesPreCompaction(0 );
Status s = dbfull()->CompactRange(comp_opts, nullptr, nullptr);
ASSERT_OK(s);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(listener->GetRunningCompactionCount(), 0);
ASSERT_GT(listener->GetTotalSubcompactionCount(), 0);
listener->ResetExpectedNumL0Files();
}
TEST_F(DBCompactionTest, CompactFilesOutputRangeConflict) {
Options options = CurrentOptions();
FlushedFileCollector* collector = new FlushedFileCollector();
options.listeners.emplace_back(collector);
Reopen(options);
for (int level = 3; level >= 2; --level) {
ASSERT_OK(Put("a", "val"));
ASSERT_OK(Put("b", "val"));
ASSERT_OK(Flush());
ASSERT_OK(Put("c", "val"));
ASSERT_OK(Put("d", "val"));
ASSERT_OK(Flush());
MoveFilesToLevel(level);
}
ASSERT_OK(Put("ba", "val"));
ASSERT_OK(Put("bz", "val"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
SyncPoint::GetInstance()->LoadDependency({
{"CompactFilesImpl:0",
"DBCompactionTest::CompactFilesOutputRangeConflict:Thread2Begin"},
{"DBCompactionTest::CompactFilesOutputRangeConflict:Thread2End",
"CompactFilesImpl:1"},
});
SyncPoint::GetInstance()->EnableProcessing();
auto bg_thread = port::Thread([&]() {
std::vector<std::string> filenames = collector->GetFlushedFiles();
filenames.pop_back();
ASSERT_OK(db_->CompactFiles(CompactionOptions(), filenames,
3 ));
});
TEST_SYNC_POINT(
"DBCompactionTest::CompactFilesOutputRangeConflict:Thread2Begin");
std::string filename = collector->GetFlushedFiles().back();
ASSERT_FALSE(
db_->CompactFiles(CompactionOptions(), {filename}, 3 )
.ok());
TEST_SYNC_POINT(
"DBCompactionTest::CompactFilesOutputRangeConflict:Thread2End");
bg_thread.join();
}
TEST_F(DBCompactionTest, CompactionHasEmptyOutput) {
Options options = CurrentOptions();
SstStatsCollector* collector = new SstStatsCollector();
options.level0_file_num_compaction_trigger = 2;
options.listeners.emplace_back(collector);
Reopen(options);
ASSERT_OK(Put("a", "val"));
ASSERT_OK(Put("b", "val"));
ASSERT_OK(Flush());
ASSERT_OK(Delete("a"));
ASSERT_OK(Delete("b"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(2, collector->num_ssts_creation_started());
}
TEST_F(DBCompactionTest, CompactionLimiter) {
const int kNumKeysPerFile = 10;
const int kMaxBackgroundThreads = 64;
struct CompactionLimiter {
std::string name;
int limit_tasks;
int max_tasks;
int tasks;
std::shared_ptr<ConcurrentTaskLimiter> limiter;
};
std::vector<CompactionLimiter> limiter_settings;
limiter_settings.push_back({"limiter_1", 1, 0, 0, nullptr});
limiter_settings.push_back({"limiter_2", 2, 0, 0, nullptr});
limiter_settings.push_back({"limiter_3", 3, 0, 0, nullptr});
for (auto& ls : limiter_settings) {
ls.limiter.reset(NewConcurrentTaskLimiter(ls.name, ls.limit_tasks));
}
std::shared_ptr<ConcurrentTaskLimiter> unique_limiter(
NewConcurrentTaskLimiter("unique_limiter", -1));
const char* cf_names[] = {"default", "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "a", "b", "c", "d", "e", "f"};
const unsigned int cf_count = sizeof cf_names / sizeof cf_names[0];
std::unordered_map<std::string, CompactionLimiter*> cf_to_limiter;
Options options = CurrentOptions();
options.write_buffer_size = 110 * 1024; options.arena_block_size = 4096;
options.num_levels = 3;
options.level0_file_num_compaction_trigger = 4;
options.level0_slowdown_writes_trigger = 64;
options.level0_stop_writes_trigger = 64;
options.max_background_jobs = kMaxBackgroundThreads; options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeysPerFile));
options.max_write_buffer_number = 10; DestroyAndReopen(options);
std::vector<Options> option_vector;
option_vector.reserve(cf_count);
for (unsigned int cf = 0; cf < cf_count; cf++) {
ColumnFamilyOptions cf_opt(options);
if (cf == 0) {
cf_opt.compaction_thread_limiter = nullptr;
} else if (cf == 1) {
unique_limiter->SetMaxOutstandingTask(-1);
cf_opt.compaction_thread_limiter = unique_limiter;
} else {
auto& ls = limiter_settings[cf % 3];
cf_opt.compaction_thread_limiter = ls.limiter;
cf_to_limiter[cf_names[cf]] = &ls;
}
option_vector.emplace_back(DBOptions(options), cf_opt);
}
for (unsigned int cf = 1; cf < cf_count; cf++) {
CreateColumnFamilies({cf_names[cf]}, option_vector[cf]);
}
ReopenWithColumnFamilies(
std::vector<std::string>(cf_names, cf_names + cf_count), option_vector);
port::Mutex mutex;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:BeforeCompaction", [&](void* arg) {
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
auto iter = cf_to_limiter.find(cf_name);
if (iter != cf_to_limiter.end()) {
MutexLock l(&mutex);
ASSERT_GE(iter->second->limit_tasks, ++iter->second->tasks);
iter->second->max_tasks =
std::max(iter->second->max_tasks, iter->second->limit_tasks);
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:AfterCompaction", [&](void* arg) {
const auto& cf_name = static_cast<ColumnFamilyData*>(arg)->GetName();
auto iter = cf_to_limiter.find(cf_name);
if (iter != cf_to_limiter.end()) {
MutexLock l(&mutex);
ASSERT_GE(--iter->second->tasks, 0);
}
});
std::vector<std::string> pending_compaction_cfs;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"EnqueuePendingCompaction::cfd", [&](void* arg) {
const std::string& cf_name =
static_cast<ColumnFamilyData*>(arg)->GetName();
pending_compaction_cfs.emplace_back(cf_name);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
const size_t kTotalFlushTasks = kMaxBackgroundThreads / 4;
const size_t kTotalCompactTasks = kMaxBackgroundThreads - kTotalFlushTasks;
env_->SetBackgroundThreads((int)kTotalFlushTasks, Env::HIGH);
env_->SetBackgroundThreads((int)kTotalCompactTasks, Env::LOW);
test::SleepingBackgroundTask sleeping_compact_tasks[kTotalCompactTasks];
for (size_t i = 0; i < kTotalCompactTasks; i++) {
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_compact_tasks[i], Env::LOW);
sleeping_compact_tasks[i].WaitUntilSleeping();
}
int keyIndex = 0;
for (int n = 0; n < options.level0_file_num_compaction_trigger; n++) {
for (unsigned int cf = 0; cf < cf_count; cf++) {
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(cf, Key(i), ""));
}
ASSERT_OK(Put(cf, "", ""));
}
for (unsigned int cf = 0; cf < cf_count; cf++) {
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
}
}
for (unsigned int cf = 0; cf < cf_count; cf++) {
ASSERT_EQ(NumTableFilesAtLevel(0, cf),
options.level0_file_num_compaction_trigger);
}
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(0, Key(i), ""));
}
ASSERT_OK(Put(0, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[0]));
ASSERT_EQ(options.level0_file_num_compaction_trigger + num + 1,
NumTableFilesAtLevel(0, 0));
}
unsigned int tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
for (int i = 0; i < 10000 && tp_len < cf_count; i++) {
env_->SleepForMicroseconds(1000);
tp_len = env_->GetThreadPoolQueueLen(Env::LOW);
}
ASSERT_EQ(cf_count, tp_len);
for (size_t i = 0; i < kTotalCompactTasks; i++) {
sleeping_compact_tasks[i].WakeUp();
sleeping_compact_tasks[i].WaitUntilDone();
}
for (unsigned int cf = 0; cf < cf_count; cf++) {
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf]));
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (auto& ls : limiter_settings) {
ASSERT_EQ(ls.limit_tasks, ls.max_tasks);
ASSERT_EQ(0, ls.limiter->GetOutstandingTask());
}
int cf_test = 1;
unique_limiter->SetMaxOutstandingTask(0);
for (int i = 0; i < kNumKeysPerFile; i++) {
ASSERT_OK(Put(cf_test, Key(keyIndex++), ""));
}
ASSERT_OK(Put(cf_test, "", ""));
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable(handles_[cf_test]));
ASSERT_EQ(1, NumTableFilesAtLevel(0, cf_test));
Compact(cf_test, Key(0), Key(keyIndex));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithParam, DBCompactionTestWithParam,
::testing::Values(std::make_tuple(1, true),
std::make_tuple(1, false),
std::make_tuple(4, true),
std::make_tuple(4, false)));
TEST_P(DBCompactionDirectIOTest, DirectIO) {
Options options = CurrentOptions();
Destroy(options);
options.create_if_missing = true;
options.disable_auto_compactions = true;
options.use_direct_io_for_flush_and_compaction = GetParam();
options.env = MockEnv::Create(Env::Default());
Reopen(options);
SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::OpenCompactionOutputFile", [&](void* arg) {
bool* use_direct_writes = static_cast<bool*>(arg);
ASSERT_EQ(*use_direct_writes,
options.use_direct_io_for_flush_and_compaction);
});
SyncPoint::GetInstance()->EnableProcessing();
CreateAndReopenWithCF({"pikachu"}, options);
MakeTables(3, "p", "q", 1);
ASSERT_EQ("1,1,1", FilesPerLevel(1));
Compact(1, "p", "q");
ASSERT_EQ(false, options.use_direct_reads);
ASSERT_EQ("0,0,1", FilesPerLevel(1));
Destroy(options);
delete options.env;
}
INSTANTIATE_TEST_CASE_P(DBCompactionDirectIOTest, DBCompactionDirectIOTest,
testing::Bool());
class CompactionPriTest : public DBTestBase,
public testing::WithParamInterface<uint32_t> {
public:
CompactionPriTest()
: DBTestBase("compaction_pri_test", false) {
compaction_pri_ = GetParam();
}
static void SetUpTestCase() {}
static void TearDownTestCase() {}
uint32_t compaction_pri_;
};
TEST_P(CompactionPriTest, Test) {
Options options = CurrentOptions();
options.write_buffer_size = 16 * 1024;
options.compaction_pri = static_cast<CompactionPri>(compaction_pri_);
options.hard_pending_compaction_bytes_limit = 256 * 1024;
options.max_bytes_for_level_base = 64 * 1024;
options.max_bytes_for_level_multiplier = 4;
options.compression = kNoCompression;
DestroyAndReopen(options);
Random rnd(301);
const int kNKeys = 5000;
int keys[kNKeys];
for (int i = 0; i < kNKeys; i++) {
keys[i] = i;
}
RandomShuffle(std::begin(keys), std::end(keys), rnd.Next());
for (int i = 0; i < kNKeys; i++) {
ASSERT_OK(Put(Key(keys[i]), rnd.RandomString(102)));
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (int i = 0; i < kNKeys; i++) {
ASSERT_NE("NOT_FOUND", Get(Key(i)));
}
}
INSTANTIATE_TEST_CASE_P(
CompactionPriTest, CompactionPriTest,
::testing::Values(CompactionPri::kByCompensatedSize,
CompactionPri::kOldestLargestSeqFirst,
CompactionPri::kOldestSmallestSeqFirst,
CompactionPri::kMinOverlappingRatio,
CompactionPri::kRoundRobin));
TEST_F(DBCompactionTest, PersistRoundRobinCompactCursor) {
Options options = CurrentOptions();
options.write_buffer_size = 16 * 1024;
options.max_bytes_for_level_base = 128 * 1024;
options.target_file_size_base = 64 * 1024;
options.level0_file_num_compaction_trigger = 4;
options.compaction_pri = CompactionPri::kRoundRobin;
options.max_bytes_for_level_multiplier = 4;
options.num_levels = 3;
options.compression = kNoCompression;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 16; j++) {
ASSERT_OK(Put(rnd.RandomString(24), rnd.RandomString(1000)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(cfd, nullptr);
Version* const current = cfd->current();
ASSERT_NE(current, nullptr);
const VersionStorageInfo* const storage_info = current->storage_info();
ASSERT_NE(storage_info, nullptr);
const std::vector<InternalKey> compact_cursors =
storage_info->GetCompactCursors();
Reopen(options);
VersionSet* const reopened_versions = dbfull()->GetVersionSet();
assert(reopened_versions);
ColumnFamilyData* const reopened_cfd =
reopened_versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(reopened_cfd, nullptr);
Version* const reopened_current = reopened_cfd->current();
ASSERT_NE(reopened_current, nullptr);
const VersionStorageInfo* const reopened_storage_info =
reopened_current->storage_info();
ASSERT_NE(reopened_storage_info, nullptr);
const std::vector<InternalKey> reopened_compact_cursors =
reopened_storage_info->GetCompactCursors();
const auto icmp = reopened_storage_info->InternalComparator();
ASSERT_EQ(compact_cursors.size(), reopened_compact_cursors.size());
for (size_t i = 0; i < compact_cursors.size(); i++) {
if (compact_cursors[i].Valid()) {
ASSERT_EQ(0,
icmp->Compare(compact_cursors[i], reopened_compact_cursors[i]));
} else {
ASSERT_TRUE(!reopened_compact_cursors[i].Valid());
}
}
}
TEST_P(RoundRobinSubcompactionsAgainstPressureToken,
DISABLED_PressureTokenTest) {
const int kKeysPerBuffer = 100;
const int kNumSubcompactions = 2;
const int kFilesPerLevel = 50;
SyncPoint::GetInstance()->LoadDependency({
{"ThreadPoolImpl::BGThread::Start:th1", "WaitForThreadAvailable"},
});
SyncPoint::GetInstance()->EnableProcessing();
env_->SetBackgroundThreads(kNumSubcompactions, Env::LOW);
TEST_SYNC_POINT("WaitForThreadAvailable");
SyncPoint::GetInstance()->DisableProcessing();
Options options = CurrentOptions();
options.num_levels = 3;
options.max_bytes_for_level_multiplier = 2;
options.level0_file_num_compaction_trigger = 4;
options.target_file_size_base = kKeysPerBuffer * 1024;
options.compaction_pri = CompactionPri::kRoundRobin;
options.max_bytes_for_level_base =
(kFilesPerLevel - 10) * kKeysPerBuffer * 1024;
options.disable_auto_compactions = true;
options.max_subcompactions = 1;
options.max_background_compactions = kNumSubcompactions;
options.max_compaction_bytes = 100000000;
DestroyAndReopen(options);
Random rnd(301);
for (int lvl = 2; lvl > 0; lvl--) {
for (int i = 0; i < kFilesPerLevel; i++) {
for (int j = 0; j < kKeysPerBuffer; j++) {
ASSERT_OK(Put(Key(2 * i * kKeysPerBuffer + 2 * j + (lvl - 1)),
rnd.RandomString(1010)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(lvl);
ASSERT_EQ(kFilesPerLevel, NumTableFilesAtLevel(lvl, 0));
}
bool compaction_num_input_file_verified = false;
SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
if (!compaction_num_input_file_verified) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_GT(compaction->num_input_files(0), 1);
compaction_num_input_file_verified = true;
}
});
bool num_planned_subcompactions_verified = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::GenSubcompactionBoundaries:0", [&](void* arg) {
if (!num_planned_subcompactions_verified) {
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
if (grab_pressure_token_) {
ASSERT_EQ(num_planned_subcompactions, kNumSubcompactions);
} else {
ASSERT_EQ(num_planned_subcompactions, 1);
}
num_planned_subcompactions_verified = true;
}
});
SyncPoint::GetInstance()->LoadDependency(
{{"RoundRobinSubcompactionsAgainstPressureToken:0",
"BackgroundCallCompaction:0"},
{"CompactionJob::AcquireSubcompactionResources:0",
"RoundRobinSubcompactionsAgainstPressureToken:1"},
{"RoundRobinSubcompactionsAgainstPressureToken:2",
"CompactionJob::AcquireSubcompactionResources:1"}});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstPressureToken:0");
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstPressureToken:1");
std::unique_ptr<WriteControllerToken> pressure_token;
if (grab_pressure_token_) {
pressure_token =
dbfull()->TEST_write_controler().GetCompactionPressureToken();
}
TEST_SYNC_POINT("RoundRobinSubcompactionsAgainstPressureToken:2");
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(num_planned_subcompactions_verified);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
INSTANTIATE_TEST_CASE_P(RoundRobinSubcompactionsAgainstPressureToken,
RoundRobinSubcompactionsAgainstPressureToken,
testing::Bool());
TEST_P(RoundRobinSubcompactionsAgainstResources,
DISABLED_SubcompactionsUsingResources) {
const int kKeysPerBuffer = 200;
Options options = CurrentOptions();
options.num_levels = 4;
options.level0_file_num_compaction_trigger = 3;
options.target_file_size_base = kKeysPerBuffer * 1024;
options.compaction_pri = CompactionPri::kRoundRobin;
options.max_bytes_for_level_base = 30 * kKeysPerBuffer * 1024;
options.disable_auto_compactions = true;
options.max_subcompactions = 1;
options.max_background_compactions = max_compaction_limits_;
options.max_compaction_bytes = std::numeric_limits<uint64_t>::max();
DestroyAndReopen(options);
env_->SetBackgroundThreads(total_low_pri_threads_, Env::LOW);
Random rnd(301);
const std::vector<int> files_per_level = {0, 40, 100};
for (int lvl = 2; lvl > 0; lvl--) {
for (int i = 0; i < files_per_level[lvl]; i++) {
for (int j = 0; j < kKeysPerBuffer; j++) {
ASSERT_OK(Put(Key(2 * i * kKeysPerBuffer + 2 * j + (lvl - 1)),
rnd.RandomString(1010)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(lvl);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(files_per_level[lvl], NumTableFilesAtLevel(lvl, 0));
}
bool num_planned_subcompactions_verified = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::GenSubcompactionBoundaries:0", [&](void* arg) {
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
auto actual_reserved_threads =
num_planned_subcompactions - options.max_subcompactions;
auto expected_reserved_threads =
std::min(total_low_pri_threads_, max_compaction_limits_) - 1;
ASSERT_EQ(actual_reserved_threads, expected_reserved_threads);
num_planned_subcompactions_verified = true;
});
int acquire_count = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::AcquireSubcompactionResources:0",
[&](void* ) { acquire_count++; });
int release_count = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::ReleaseSubcompactionResources",
[&](void* ) { release_count++; });
SyncPoint::GetInstance()->EnableProcessing();
auto pressure_token =
dbfull()->TEST_write_controler().GetCompactionPressureToken();
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(num_planned_subcompactions_verified);
ASSERT_EQ(acquire_count, release_count);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
INSTANTIATE_TEST_CASE_P(RoundRobinSubcompactionsAgainstResources,
RoundRobinSubcompactionsAgainstResources,
::testing::Values(std::make_tuple(1, 5),
std::make_tuple(5, 1),
std::make_tuple(10, 5),
std::make_tuple(5, 10),
std::make_tuple(10, 10)));
TEST_P(DBCompactionTestWithParam, RoundRobinWithoutAdditionalResources) {
const int kKeysPerBuffer = 200;
Options options = CurrentOptions();
options.num_levels = 4;
options.level0_file_num_compaction_trigger = 3;
options.target_file_size_base = kKeysPerBuffer * 1024;
options.compaction_pri = CompactionPri::kRoundRobin;
options.max_bytes_for_level_base = 30 * kKeysPerBuffer * 1024;
options.disable_auto_compactions = true;
options.max_subcompactions = max_subcompactions_;
options.max_background_compactions = 1;
options.max_compaction_bytes = 100000000;
DestroyAndReopen(options);
env_->SetBackgroundThreads(1, Env::LOW);
Random rnd(301);
const std::vector<int> files_per_level = {0, 33, 100};
for (int lvl = 2; lvl > 0; lvl--) {
for (int i = 0; i < files_per_level[lvl]; i++) {
for (int j = 0; j < kKeysPerBuffer; j++) {
ASSERT_OK(Put(Key(2 * i * kKeysPerBuffer + 2 * j + (lvl - 1)),
rnd.RandomString(1010)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(lvl);
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(files_per_level[lvl], NumTableFilesAtLevel(lvl, 0));
}
bool num_planned_subcompactions_verified = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::GenSubcompactionBoundaries:0", [&](void* arg) {
uint64_t num_planned_subcompactions = *(static_cast<uint64_t*>(arg));
ASSERT_EQ(num_planned_subcompactions, options.max_subcompactions);
num_planned_subcompactions_verified = true;
});
SyncPoint::GetInstance()->LoadDependency(
{{"DBCompactionTest::RoundRobinWithoutAdditionalResources:0",
"BackgroundCallCompaction:0"}});
SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(dbfull()->EnableAutoCompaction({dbfull()->DefaultColumnFamily()}));
TEST_SYNC_POINT("DBCompactionTest::RoundRobinWithoutAdditionalResources:0");
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(num_planned_subcompactions_verified);
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, RoundRobinCutOutputAtCompactCursor) {
Options options = CurrentOptions();
options.num_levels = 3;
options.compression = kNoCompression;
options.write_buffer_size = 4 * 1024;
options.max_bytes_for_level_base = 64 * 1024;
options.max_bytes_for_level_multiplier = 4;
options.level0_file_num_compaction_trigger = 4;
options.compaction_pri = CompactionPri::kRoundRobin;
DestroyAndReopen(options);
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(cfd, nullptr);
Version* const current = cfd->current();
ASSERT_NE(current, nullptr);
VersionStorageInfo* storage_info = current->storage_info();
ASSERT_NE(storage_info, nullptr);
const InternalKey split_cursor = InternalKey(Key(600), 100, kTypeValue);
storage_info->AddCursorForOneLevel(2, split_cursor);
Random rnd(301);
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
ASSERT_OK(Put(Key(j * 2 + i * 100), rnd.RandomString(102)));
}
}
for (int i = 0; i < 50; i++) {
for (int j = 0; j < 50; j++) {
ASSERT_OK(Put(Key(j * 2 + 1 + i * 100), rnd.RandomString(1014)));
}
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
const auto icmp = cfd->current()->storage_info()->InternalComparator();
for (const auto& file : level_to_files[2]) {
ASSERT_TRUE(
icmp->Compare(file.smallest.Encode(), split_cursor.Encode()) >= 0 ||
icmp->Compare(file.largest.Encode(), split_cursor.Encode()) < 0);
}
}
class NoopMergeOperator : public MergeOperator {
public:
NoopMergeOperator() = default;
bool FullMergeV2(const MergeOperationInput& ,
MergeOperationOutput* merge_out) const override {
std::string val("bar");
merge_out->new_value = val;
return true;
}
const char* Name() const override { return "Noop"; }
};
TEST_F(DBCompactionTest, PartialManualCompaction) {
Options opts = CurrentOptions();
opts.num_levels = 3;
opts.level0_file_num_compaction_trigger = 10;
opts.compression = kNoCompression;
opts.merge_operator.reset(new NoopMergeOperator());
opts.target_file_size_base = 10240;
DestroyAndReopen(opts);
Random rnd(301);
for (auto i = 0; i < 8; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(Merge("foo", rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(2);
std::string prop;
EXPECT_TRUE(dbfull()->GetProperty(DB::Properties::kLiveSstFilesSize, &prop));
uint64_t max_compaction_bytes = atoi(prop.c_str()) / 2;
ASSERT_OK(dbfull()->SetOptions(
{{"max_compaction_bytes", std::to_string(max_compaction_bytes)}}));
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
TEST_F(DBCompactionTest, ConcurrentFIFOPickingSameFileBug) {
Options opts = CurrentOptions();
opts.compaction_style = CompactionStyle::kCompactionStyleLevel;
opts.num_levels = 3;
opts.disable_auto_compactions = true;
opts.max_background_jobs = 3;
DestroyAndReopen(opts);
ASSERT_OK(Put("k1", "v1"));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
Options opts_new(opts);
opts_new.compaction_style = CompactionStyle::kCompactionStyleFIFO;
opts_new.max_open_files = -1;
opts_new.compaction_options_fifo.max_table_files_size = 1;
Reopen(opts_new);
const CompactRangeOptions cro;
const Slice begin_key("k1");
const Slice end_key("k2");
std::unique_ptr<port::Thread> concurrent_compaction;
bool within_first_compaction = true;
SyncPoint::GetInstance()->SetCallBack(
"VersionSet::LogAndApply:WriteManifestStart", [&](void* ) {
if (!within_first_compaction) {
return;
}
within_first_compaction = false;
SyncPoint::GetInstance()->LoadDependency({
{"DBImpl::BackgroundCompaction:BeforeCompaction",
"VersionSet::LogAndApply:WriteManifest"},
});
concurrent_compaction.reset(new port::Thread([&]() {
Status s = db_->CompactRange(cro, &begin_key, &end_key);
ASSERT_OK(s);
}));
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(cro, &begin_key, &end_key);
SyncPoint::GetInstance()->DisableProcessing();
ASSERT_OK(s);
concurrent_compaction->join();
}
TEST_F(DBCompactionTest, ManualCompactionFailsInReadOnlyMode) {
const int kNumL0Files = 4;
std::unique_ptr<FaultInjectionTestEnv> mock_env(
new FaultInjectionTestEnv(env_));
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
opts.env = mock_env.get();
DestroyAndReopen(opts);
Random rnd(301);
for (int i = 0; i < kNumL0Files; ++i) {
ASSERT_OK(Put("key1", rnd.RandomString(1024)));
ASSERT_OK(Put("key2", rnd.RandomString(1024)));
ASSERT_OK(Flush());
}
ASSERT_EQ(kNumL0Files, NumTableFilesAtLevel(0));
mock_env->SetFilesystemActive(false);
ASSERT_NOK(Put("key3", rnd.RandomString(1024)));
CompactRangeOptions cro;
cro.exclusive_manual_compaction = false;
Slice begin_key("key1");
Slice end_key("key2");
ASSERT_NOK(dbfull()->CompactRange(cro, &begin_key, &end_key));
ASSERT_NOK(dbfull()->CompactRange(cro, &begin_key, &end_key));
Close();
}
TEST_F(DBCompactionTest, ManualCompactionBottomLevelOptimized) {
Options opts = CurrentOptions();
opts.num_levels = 3;
opts.level0_file_num_compaction_trigger = 5;
opts.compression = kNoCompression;
opts.merge_operator.reset(new NoopMergeOperator());
opts.target_file_size_base = 1024;
opts.max_bytes_for_level_multiplier = 2;
opts.disable_auto_compactions = true;
DestroyAndReopen(opts);
ColumnFamilyHandleImpl* cfh =
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
ColumnFamilyData* cfd = cfh->cfd();
InternalStats* internal_stats_ptr = cfd->internal_stats();
ASSERT_NE(internal_stats_ptr, nullptr);
Random rnd(301);
for (auto i = 0; i < 8; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(2);
for (auto i = 0; i < 8; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(
Put("bar" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
const std::vector<InternalStats::CompactionStats>& comp_stats =
internal_stats_ptr->TEST_GetCompactionStats();
int num = comp_stats[2].num_input_files_in_output_level;
ASSERT_EQ(num, 0);
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
const std::vector<InternalStats::CompactionStats>& comp_stats2 =
internal_stats_ptr->TEST_GetCompactionStats();
num = comp_stats2[2].num_input_files_in_output_level;
ASSERT_EQ(num, 0);
}
TEST_F(DBCompactionTest, ManualCompactionMax) {
uint64_t l1_avg_size = 0, l2_avg_size = 0;
auto generate_sst_func = [&]() {
Random rnd(301);
for (auto i = 0; i < 100; i++) {
for (auto j = 0; j < 10; j++) {
ASSERT_OK(Put(Key(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(2);
for (auto i = 0; i < 10; i++) {
for (auto j = 0; j < 10; j++) {
ASSERT_OK(Put(Key(i * 100 + j * 10), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(1);
std::vector<std::vector<FileMetaData>> level_to_files;
dbfull()->TEST_GetFilesMetaData(dbfull()->DefaultColumnFamily(),
&level_to_files);
uint64_t total = 0;
for (const auto& file : level_to_files[1]) {
total += file.compensated_file_size;
}
l1_avg_size = total / level_to_files[1].size();
total = 0;
for (const auto& file : level_to_files[2]) {
total += file.compensated_file_size;
}
l2_avg_size = total / level_to_files[2].size();
};
std::atomic_int num_compactions(0);
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BGWorkCompaction", [&](void* ) { ++num_compactions; });
SyncPoint::GetInstance()->EnableProcessing();
Options opts = CurrentOptions();
opts.disable_auto_compactions = true;
DestroyAndReopen(opts);
generate_sst_func();
num_compactions.store(0);
CompactRangeOptions cro;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_TRUE(num_compactions.load() == 1);
int num_split = 5;
DestroyAndReopen(opts);
generate_sst_func();
uint64_t total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
opts.max_compaction_bytes = total_size / num_split + l2_avg_size * 10;
opts.target_file_size_base = total_size / num_split;
Reopen(opts);
num_compactions.store(0);
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_TRUE(num_compactions.load() == num_split);
opts.max_compaction_bytes = l1_avg_size / 2;
opts.target_file_size_base = l1_avg_size / 2;
DestroyAndReopen(opts);
generate_sst_func();
num_compactions.store(0);
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_TRUE(num_compactions.load() > 10);
num_split = 2;
opts.max_compaction_bytes = 0;
DestroyAndReopen(opts);
generate_sst_func();
total_size = (l1_avg_size * 10) + (l2_avg_size * 100);
Status s = db_->SetOptions(
{{"max_compaction_bytes",
std::to_string(total_size / num_split + 10 * l2_avg_size)},
{"target_file_size_base", std::to_string(total_size / num_split)}});
ASSERT_OK(s);
num_compactions.store(0);
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_TRUE(num_compactions.load() == num_split);
}
TEST_F(DBCompactionTest, CompactionDuringShutdown) {
Options opts = CurrentOptions();
opts.level0_file_num_compaction_trigger = 2;
opts.disable_auto_compactions = true;
DestroyAndReopen(opts);
ColumnFamilyHandleImpl* cfh =
static_cast<ColumnFamilyHandleImpl*>(dbfull()->DefaultColumnFamily());
ColumnFamilyData* cfd = cfh->cfd();
InternalStats* internal_stats_ptr = cfd->internal_stats();
ASSERT_NE(internal_stats_ptr, nullptr);
Random rnd(301);
for (auto i = 0; i < 2; ++i) {
for (auto j = 0; j < 10; ++j) {
ASSERT_OK(
Put("foo" + std::to_string(i * 10 + j), rnd.RandomString(1024)));
}
ASSERT_OK(Flush());
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:NonTrivial:BeforeRun",
[&](void* ) { dbfull()->shutting_down_.store(true); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Status s = dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.ok() || s.IsShutdownInProgress());
ASSERT_OK(dbfull()->error_handler_.GetBGError());
}
TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
const int kNumKeysPerFile = 100;
Options options = CurrentOptions();
std::string sst_files_dir = dbname_ + "/sst_files/";
ASSERT_OK(DestroyDir(env_, sst_files_dir));
ASSERT_OK(env_->CreateDir(sst_files_dir));
SstFileWriter sst_writer(EnvOptions(), options);
const std::string sst_file_path = sst_files_dir + "test.sst";
ASSERT_OK(sst_writer.Open(sst_file_path));
ASSERT_OK(sst_writer.Put(Key(kNumKeysPerFile - 1), "value"));
ASSERT_OK(sst_writer.Finish());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
SyncPoint::GetInstance()->LoadDependency({
{"DBImpl::IngestExternalFile:AfterIncIngestFileCounter",
"BackgroundCallCompaction:0"},
});
SyncPoint::GetInstance()->EnableProcessing();
options.write_buffer_size = 110 << 10; options.level0_file_num_compaction_trigger =
options.level0_stop_writes_trigger;
options.max_subcompactions = max_subcompactions_;
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(kNumKeysPerFile));
DestroyAndReopen(options);
Random rnd(301);
for (int i = 0; i != options.level0_file_num_compaction_trigger; ++i) {
for (int j = 0; j != kNumKeysPerFile; ++j) {
ASSERT_OK(Put(Key(j), rnd.RandomString(990)));
}
if (i > 0) {
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ(NumTableFilesAtLevel(0 , 0 ), i);
}
}
port::Thread ingestion_thr([&]() {
IngestExternalFileOptions ifo;
Status s = db_->IngestExternalFile({sst_file_path}, ifo);
ASSERT_OK(s);
});
ingestion_thr.join();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
Close();
}
class DBCompactionTestWithOngoingFileIngestionParam
: public DBCompactionTest,
public testing::WithParamInterface<std::string> {
public:
DBCompactionTestWithOngoingFileIngestionParam() : DBCompactionTest() {
compaction_path_to_test_ = GetParam();
}
void SetupOptions() {
options_ = CurrentOptions();
options_.create_if_missing = true;
if (compaction_path_to_test_ == "RefitLevelCompactRange") {
options_.num_levels = 7;
} else {
options_.num_levels = 3;
}
options_.compaction_style = CompactionStyle::kCompactionStyleLevel;
if (compaction_path_to_test_ == "AutoCompaction") {
options_.disable_auto_compactions = false;
options_.level0_file_num_compaction_trigger = 1;
} else {
options_.disable_auto_compactions = true;
}
}
void PauseCompactionThread() {
sleeping_task_.reset(new test::SleepingBackgroundTask());
env_->SetBackgroundThreads(1, Env::LOW);
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
sleeping_task_.get(), Env::Priority::LOW);
sleeping_task_->WaitUntilSleeping();
}
void ResumeCompactionThread() {
if (sleeping_task_) {
sleeping_task_->WakeUp();
sleeping_task_->WaitUntilDone();
}
}
void SetupFilesToForceFutureFilesIngestedToCertainLevel() {
SstFileWriter sst_file_writer(EnvOptions(), options_);
std::string dummy = dbname_ + "/dummy.sst";
ASSERT_OK(sst_file_writer.Open(dummy));
ASSERT_OK(sst_file_writer.Put("k2", "dummy"));
ASSERT_OK(sst_file_writer.Finish());
ASSERT_OK(db_->IngestExternalFile({dummy}, IngestExternalFileOptions()));
ASSERT_EQ("0,0,1", FilesPerLevel(0));
}
void SetupSyncPoints() {
if (compaction_path_to_test_ == "AutoCompaction") {
SyncPoint::GetInstance()->SetCallBack(
"ExternalSstFileIngestionJob::Run", [&](void*) {
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
"VersionSet::LogAndApply:WriteManifest"}});
});
} else if (compaction_path_to_test_ == "NonRefitLevelCompactRange") {
SyncPoint::GetInstance()->SetCallBack(
"ExternalSstFileIngestionJob::Run", [&](void*) {
SyncPoint::GetInstance()->LoadDependency(
{{"ColumnFamilyData::CompactRange:Return",
"VersionSet::LogAndApply:WriteManifest"}});
});
} else if (compaction_path_to_test_ == "RefitLevelCompactRange") {
SyncPoint::GetInstance()->SetCallBack(
"ExternalSstFileIngestionJob::Run", [&](void*) {
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::CompactRange:PostRefitLevel",
"VersionSet::LogAndApply:WriteManifest"}});
});
} else if (compaction_path_to_test_ == "CompactFiles") {
SyncPoint::GetInstance()->SetCallBack(
"ExternalSstFileIngestionJob::Run", [&](void*) {
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::CompactFilesImpl::"
"PostSanitizeAndConvertCompactionInputFiles",
"VersionSet::LogAndApply:WriteManifest"}});
});
} else {
assert(false);
}
SyncPoint::GetInstance()->LoadDependency(
{{"ExternalSstFileIngestionJob::Run", "PreCompaction"}});
SyncPoint::GetInstance()->EnableProcessing();
}
void RunCompactionOverlappedWithFileIngestion() {
if (compaction_path_to_test_ == "AutoCompaction") {
TEST_SYNC_POINT("PreCompaction");
ResumeCompactionThread();
Status s = dbfull()->TEST_WaitForCompact();
EXPECT_OK(s);
} else if (compaction_path_to_test_ == "NonRefitLevelCompactRange") {
CompactRangeOptions cro;
cro.change_level = false;
std::string start_key = "k1";
Slice start(start_key);
std::string end_key = "k4";
Slice end(end_key);
TEST_SYNC_POINT("PreCompaction");
Status s = dbfull()->CompactRange(cro, &start, &end);
EXPECT_OK(s);
} else if (compaction_path_to_test_ == "RefitLevelCompactRange") {
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 5;
std::string start_key = "k1";
Slice start(start_key);
std::string end_key = "k4";
Slice end(end_key);
TEST_SYNC_POINT("PreCompaction");
Status s = dbfull()->CompactRange(cro, &start, &end);
EXPECT_TRUE(s.IsNotSupported());
EXPECT_TRUE(s.ToString().find("some ongoing compaction's output") !=
std::string::npos);
} else if (compaction_path_to_test_ == "CompactFiles") {
ColumnFamilyMetaData cf_meta_data;
db_->GetColumnFamilyMetaData(&cf_meta_data);
ASSERT_EQ(cf_meta_data.levels[0].files.size(), 1);
std::vector<std::string> input_files;
for (const auto& file : cf_meta_data.levels[0].files) {
input_files.push_back(file.name);
}
TEST_SYNC_POINT("PreCompaction");
Status s = db_->CompactFiles(CompactionOptions(), input_files, 1);
EXPECT_TRUE(s.IsAborted());
EXPECT_TRUE(
s.ToString().find(
"A running compaction is writing to the same output level") !=
std::string::npos);
} else {
assert(false);
}
}
void DisableSyncPoints() {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
protected:
std::string compaction_path_to_test_;
Options options_;
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
};
INSTANTIATE_TEST_CASE_P(DBCompactionTestWithOngoingFileIngestionParam,
DBCompactionTestWithOngoingFileIngestionParam,
::testing::Values("AutoCompaction",
"NonRefitLevelCompactRange",
"RefitLevelCompactRange",
"CompactFiles"));
TEST_P(DBCompactionTestWithOngoingFileIngestionParam, RangeConflictCheck) {
SetupOptions();
DestroyAndReopen(options_);
if (compaction_path_to_test_ == "AutoCompaction") {
PauseCompactionThread();
}
if (compaction_path_to_test_ != "RefitLevelCompactRange") {
SetupFilesToForceFutureFilesIngestedToCertainLevel();
}
ASSERT_OK(Put("k1", "v"));
ASSERT_OK(Put("k4", "v"));
ASSERT_OK(Flush());
if (compaction_path_to_test_ == "RefitLevelCompactRange") {
MoveFilesToLevel(6 );
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel(0));
} else {
ASSERT_EQ("1,0,1", FilesPerLevel(0));
}
SetupSyncPoints();
port::Thread thread1([&] {
SstFileWriter sst_file_writer(EnvOptions(), options_);
std::string s2 = dbname_ + "/ingested_s2.sst";
ASSERT_OK(sst_file_writer.Open(s2));
ASSERT_OK(sst_file_writer.Put("k2", "v2"));
ASSERT_OK(sst_file_writer.Put("k3", "v2"));
ASSERT_OK(sst_file_writer.Finish());
ASSERT_OK(db_->IngestExternalFile({s2}, IngestExternalFileOptions()));
});
port::Thread thread2([&] { RunCompactionOverlappedWithFileIngestion(); });
thread1.join();
thread2.join();
DisableSyncPoints();
}
TEST_F(DBCompactionTest, ConsistencyFailTest) {
Options options = CurrentOptions();
options.force_consistency_checks = true;
DestroyAndReopen(options);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionBuilder::CheckConsistency0", [&](void* arg) {
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
FileMetaData* temp = *(p->first);
*(p->first) = *(p->second);
*(p->second) = temp;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int k = 0; k < 2; ++k) {
ASSERT_OK(Put("foo", "bar"));
Status s = Flush();
if (k < 1) {
ASSERT_OK(s);
} else {
ASSERT_TRUE(s.IsCorruption());
}
}
ASSERT_NOK(Put("foo", "bar"));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, ConsistencyFailTest2) {
Options options = CurrentOptions();
options.force_consistency_checks = true;
options.target_file_size_base = 1000;
options.level0_file_num_compaction_trigger = 2;
BlockBasedTableOptions bbto;
bbto.block_size = 400; options.table_factory.reset(NewBlockBasedTableFactory(bbto));
DestroyAndReopen(options);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"VersionBuilder::CheckConsistency1", [&](void* arg) {
auto p = static_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
FileMetaData* temp = *(p->first);
*(p->first) = *(p->second);
*(p->second) = temp;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Random rnd(301);
std::string value = rnd.RandomString(1000);
ASSERT_OK(Put("foo1", value));
ASSERT_OK(Put("z", ""));
ASSERT_OK(Flush());
ASSERT_OK(Put("foo2", value));
ASSERT_OK(Put("z", ""));
Status s = Flush();
ASSERT_TRUE(s.ok() || s.IsCorruption());
ASSERT_NOK(dbfull()->TEST_WaitForCompact());
ASSERT_NOK(Put("foo", "bar"));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
void IngestOneKeyValue(DBImpl* db, const std::string& key,
const std::string& value, const Options& options) {
ExternalSstFileInfo info;
std::string f = test::PerThreadDBPath("sst_file" + key);
EnvOptions env;
ROCKSDB_NAMESPACE::SstFileWriter writer(env, options);
auto s = writer.Open(f);
ASSERT_OK(s);
ASSERT_OK(writer.Put(key, value));
ASSERT_OK(writer.Finish(&info));
IngestExternalFileOptions ingest_opt;
ASSERT_OK(db->IngestExternalFile({info.file_path}, ingest_opt));
}
class DBCompactionTestL0FilesMisorderCorruption : public DBCompactionTest {
public:
DBCompactionTestL0FilesMisorderCorruption() : DBCompactionTest() {}
void SetupOptions(const CompactionStyle compaciton_style,
const std::string& compaction_path_to_test = "") {
options_ = CurrentOptions();
options_.create_if_missing = true;
options_.compression = kNoCompression;
options_.force_consistency_checks = true;
options_.compaction_style = compaciton_style;
if (compaciton_style == CompactionStyle::kCompactionStyleLevel) {
options_.num_levels = 7;
options_.level0_file_num_compaction_trigger = 5;
options_.max_background_compactions = 2;
options_.write_buffer_size = 2 << 20;
options_.max_write_buffer_number = 6;
} else if (compaciton_style == CompactionStyle::kCompactionStyleUniversal) {
options_.num_levels = 1;
options_.level0_file_num_compaction_trigger = 5;
CompactionOptionsUniversal universal_options;
if (compaction_path_to_test == "PickCompactionToReduceSizeAmp") {
universal_options.max_size_amplification_percent = 50;
} else if (compaction_path_to_test ==
"PickCompactionToReduceSortedRuns") {
universal_options.max_size_amplification_percent = 400;
} else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
universal_options.max_size_amplification_percent = 400;
universal_options.min_merge_width = 6;
}
options_.compaction_options_universal = universal_options;
} else if (compaciton_style == CompactionStyle::kCompactionStyleFIFO) {
options_.max_open_files = -1;
options_.num_levels = 1;
options_.level0_file_num_compaction_trigger = 3;
CompactionOptionsFIFO fifo_options;
if (compaction_path_to_test == "PickCostBasedIntraL0Compaction" ||
compaction_path_to_test == "CompactRange") {
fifo_options.allow_compaction = true;
} else if (compaction_path_to_test == "CompactFile") {
fifo_options.allow_compaction = false;
}
options_.compaction_options_fifo = fifo_options;
}
if (compaction_path_to_test == "CompactFile" ||
compaction_path_to_test == "CompactRange") {
options_.disable_auto_compactions = true;
} else {
options_.disable_auto_compactions = false;
}
}
void Destroy(const Options& options) {
if (snapshot_) {
assert(db_);
db_->ReleaseSnapshot(snapshot_);
snapshot_ = nullptr;
}
DBTestBase::Destroy(options);
}
void Reopen(const Options& options) {
DBTestBase::Reopen(options);
if (options.compaction_style != CompactionStyle::kCompactionStyleLevel) {
assert(snapshot_ == nullptr);
snapshot_ = db_->GetSnapshot();
}
}
void DestroyAndReopen(Options& options) {
Destroy(options);
Reopen(options);
}
void PauseCompactionThread() {
sleeping_task_.reset(new test::SleepingBackgroundTask());
env_->SetBackgroundThreads(1, Env::LOW);
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
sleeping_task_.get(), Env::Priority::LOW);
sleeping_task_->WaitUntilSleeping();
}
void ResumeCompactionThread() {
if (sleeping_task_) {
sleeping_task_->WakeUp();
sleeping_task_->WaitUntilDone();
}
}
void AddFilesMarkedForPeriodicCompaction(const size_t num_files) {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
assert(cfd);
Version* const current = cfd->current();
assert(current);
VersionStorageInfo* const storage_info = current->storage_info();
assert(storage_info);
const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
assert(level0_files.size() == num_files);
for (FileMetaData* f : level0_files) {
storage_info->TEST_AddFileMarkedForPeriodicCompaction(0, f);
}
}
void AddFilesMarkedForCompaction(const size_t num_files) {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
assert(cfd);
Version* const current = cfd->current();
assert(current);
VersionStorageInfo* const storage_info = current->storage_info();
assert(storage_info);
const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
assert(level0_files.size() == num_files);
for (FileMetaData* f : level0_files) {
storage_info->TEST_AddFileMarkedForCompaction(0, f);
}
}
void SetupSyncPoints(const std::string& compaction_path_to_test) {
compaction_path_sync_point_called_.store(false);
if (compaction_path_to_test == "PickCostBasedIntraL0Compaction" &&
options_.compaction_style == CompactionStyle::kCompactionStyleLevel) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PostPickFileToCompact", [&](void* arg) {
bool* picked_file_to_compact = (bool*)arg;
*picked_file_to_compact = false;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PickCostBasedIntraL0Compaction", [&](void* ) {
compaction_path_sync_point_called_.store(true);
});
} else if (compaction_path_to_test == "PickPeriodicCompaction") {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PostPickPeriodicCompaction", [&](void* compaction_arg) {
Compaction* compaction = (Compaction*)compaction_arg;
if (compaction != nullptr) {
compaction_path_sync_point_called_.store(true);
}
});
} else if (compaction_path_to_test == "PickCompactionToReduceSizeAmp") {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PickCompactionToReduceSizeAmpReturnNonnullptr", [&](void* ) {
compaction_path_sync_point_called_.store(true);
});
} else if (compaction_path_to_test == "PickCompactionToReduceSortedRuns") {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PickCompactionToReduceSortedRunsReturnNonnullptr",
[&](void* ) {
compaction_path_sync_point_called_.store(true);
});
} else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
assert(options_.compaction_style ==
CompactionStyle::kCompactionStyleUniversal);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PickDeleteTriggeredCompactionReturnNonnullptr", [&](void* ) {
compaction_path_sync_point_called_.store(true);
});
} else if ((compaction_path_to_test == "PickCostBasedIntraL0Compaction" ||
compaction_path_to_test == "CompactRange") &&
options_.compaction_style ==
CompactionStyle::kCompactionStyleFIFO) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PickCostBasedIntraL0Compaction", [&](void* ) {
compaction_path_sync_point_called_.store(true);
});
}
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
}
bool SyncPointsCalled() { return compaction_path_sync_point_called_.load(); }
void DisableSyncPoints() {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
SequenceNumber GetLatestL0FileLargestSeqnoHelper() {
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
assert(cfd);
Version* const current = cfd->current();
assert(current);
VersionStorageInfo* const storage_info = current->storage_info();
assert(storage_info);
const std::vector<FileMetaData*> level0_files = storage_info->LevelFiles(0);
assert(level0_files.size() >= 1);
uint64_t latest_file_num = 0;
uint64_t latest_file_largest_seqno = 0;
for (FileMetaData* f : level0_files) {
if (f->fd.GetNumber() > latest_file_num) {
latest_file_num = f->fd.GetNumber();
latest_file_largest_seqno = f->fd.largest_seqno;
}
}
return latest_file_largest_seqno;
}
protected:
Options options_;
private:
const Snapshot* snapshot_ = nullptr;
std::atomic<bool> compaction_path_sync_point_called_;
std::shared_ptr<test::SleepingBackgroundTask> sleeping_task_;
};
TEST_F(DBCompactionTest, CompactFilesSupportKeyPlacementRangeConflict) {
Options options;
options.create_if_missing = true;
options.compaction_style = kCompactionStyleUniversal;
options.num_levels = 3;
DestroyAndReopen(options);
ASSERT_OK(Put("k1", "v"));
ASSERT_OK(Put("k5", "v"));
ASSERT_OK(Flush());
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ("0,0,1", FilesPerLevel());
ASSERT_OK(Put("k3", "v"));
ASSERT_OK(Flush());
ASSERT_OK(Put("k4", "v"));
ASSERT_OK(Flush());
ASSERT_OK(experimental::PromoteL0(db_.get(), db_->DefaultColumnFamily(), 1));
ASSERT_EQ("0,2,1", FilesPerLevel());
ASSERT_OK(Put("k2", "v"));
ASSERT_OK(Flush());
ASSERT_EQ("1,2,1", FilesPerLevel());
Close();
options.preclude_last_level_data_seconds = 1;
Reopen(options);
ColumnFamilyMetaData cf_meta_data;
db_->GetColumnFamilyMetaData(&cf_meta_data);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactFilesImpl:0", [&](void* ) {
std::vector<std::string> c2_input_files;
c2_input_files.push_back(cf_meta_data.levels[1].files[1].name);
c2_input_files.push_back(cf_meta_data.levels[2].files[0].name);
Status s = db_->CompactFiles(CompactionOptions(), c2_input_files,
2 );
ASSERT_TRUE(s.IsAborted());
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
std::vector<std::string> c1_input_files;
c1_input_files.push_back(cf_meta_data.levels[0].files[0].name);
c1_input_files.push_back(cf_meta_data.levels[1].files[0].name);
Status s = db_->CompactFiles(CompactionOptions(), c1_input_files,
1 );
ASSERT_OK(s);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
FlushAfterIntraL0LevelCompactionWithIngestedFile) {
SetupOptions(CompactionStyle::kCompactionStyleLevel, "");
DestroyAndReopen(options_);
for (int i = 0; i < 10; ++i) {
ASSERT_OK(Put(Key(i), "")); }
ASSERT_OK(Flush());
Compact("", Key(99));
ASSERT_EQ(0, NumTableFilesAtLevel(0));
PauseCompactionThread();
for (int i = 0; i < 6; ++i) {
if (i % 2 == 0) {
IngestOneKeyValue(dbfull(), Key(i), "old", options_);
} else {
ASSERT_OK(Put(Key(i), "old"));
ASSERT_OK(Flush());
}
}
ASSERT_EQ(6, NumTableFilesAtLevel(0));
for (int i = 0; i < 6; ++i) {
ASSERT_OK(Put(Key(i), "new"));
}
ASSERT_EQ(6, NumTableFilesAtLevel(0));
for (int i = 6; i < 7; ++i) {
ASSERT_EQ(i, NumTableFilesAtLevel(0));
IngestOneKeyValue(dbfull(), Key(i), "new", options_);
}
SetupSyncPoints("PickCostBasedIntraL0Compaction");
ResumeCompactionThread();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(SyncPointsCalled());
DisableSyncPoints();
ASSERT_EQ(1, NumTableFilesAtLevel(0));
SequenceNumber compact_output_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_OK(Flush());
ASSERT_EQ(2, NumTableFilesAtLevel(0));
SequenceNumber flushed_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
for (int i = 0; i < 6; ++i) {
ASSERT_EQ("new", Get(Key(i)));
}
for (int i = 6; i < 7; ++i) {
ASSERT_EQ("new", Get(Key(i)));
}
}
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
FlushAfterIntraL0UniversalCompactionWithIngestedFile) {
for (const std::string compaction_path_to_test :
{"PickPeriodicCompaction", "PickCompactionToReduceSizeAmp",
"PickCompactionToReduceSortedRuns", "PickDeleteTriggeredCompaction"}) {
SetupOptions(CompactionStyle::kCompactionStyleUniversal,
compaction_path_to_test);
DestroyAndReopen(options_);
PauseCompactionThread();
ASSERT_OK(Put("k1", "old"));
ASSERT_OK(Put("k3", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(1, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k4", "old"));
ASSERT_OK(Put("k5", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(2, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k6", "old"));
ASSERT_OK(Put("k7", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(3, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k1", "new")); ASSERT_OK(Put("k2", "new"));
IngestOneKeyValue(dbfull(), "k8", "dummy", options_);
IngestOneKeyValue(dbfull(), "k9", "dummy", options_);
ASSERT_EQ(5, NumTableFilesAtLevel(0));
if (compaction_path_to_test == "PickPeriodicCompaction") {
AddFilesMarkedForPeriodicCompaction(5);
} else if (compaction_path_to_test == "PickDeleteTriggeredCompaction") {
AddFilesMarkedForCompaction(5);
}
SetupSyncPoints(compaction_path_to_test);
ResumeCompactionThread();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(SyncPointsCalled())
<< "failed for compaction path to test: " << compaction_path_to_test;
DisableSyncPoints();
ASSERT_EQ(1, NumTableFilesAtLevel(0))
<< "failed for compaction path to test: " << compaction_path_to_test;
SequenceNumber compact_output_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_OK(Flush()) << "failed for compaction path to test: "
<< compaction_path_to_test;
ASSERT_EQ(2, NumTableFilesAtLevel(0))
<< "failed for compaction path to test: " << compaction_path_to_test;
SequenceNumber flushed_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno)
<< "failed for compaction path to test: " << compaction_path_to_test;
EXPECT_EQ(Get("k1"), "new")
<< "failed for compaction path to test: " << compaction_path_to_test;
}
Destroy(options_);
}
TEST_F(DBCompactionTestL0FilesMisorderCorruption,
FlushAfterIntraL0FIFOCompactionWithIngestedFile) {
for (const std::string compaction_path_to_test :
{"PickCostBasedIntraL0Compaction"}) {
SetupOptions(CompactionStyle::kCompactionStyleFIFO,
compaction_path_to_test);
DestroyAndReopen(options_);
ASSERT_OK(Put("k1", "old"));
ASSERT_OK(Put("k3", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(1, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k1", "new")); ASSERT_OK(Put("k2", "new"));
PauseCompactionThread();
IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
ASSERT_EQ(3, NumTableFilesAtLevel(0));
SetupSyncPoints(compaction_path_to_test);
ResumeCompactionThread();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(SyncPointsCalled())
<< "failed for compaction path to test: " << compaction_path_to_test;
DisableSyncPoints();
ASSERT_EQ(1, NumTableFilesAtLevel(0))
<< "failed for compaction path to test: " << compaction_path_to_test;
SequenceNumber compact_output_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_OK(Flush()) << "failed for compaction path to test: "
<< compaction_path_to_test;
ASSERT_EQ(2, NumTableFilesAtLevel(0))
<< "failed for compaction path to test: " << compaction_path_to_test;
SequenceNumber flushed_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno)
<< "failed for compaction path to test: " << compaction_path_to_test;
EXPECT_EQ(Get("k1"), "new")
<< "failed for compaction path to test: " << compaction_path_to_test;
}
Destroy(options_);
}
class DBCompactionTestL0FilesMisorderCorruptionWithParam
: public DBCompactionTestL0FilesMisorderCorruption,
public testing::WithParamInterface<CompactionStyle> {
public:
DBCompactionTestL0FilesMisorderCorruptionWithParam()
: DBCompactionTestL0FilesMisorderCorruption() {}
};
INSTANTIATE_TEST_CASE_P(
DBCompactionTestL0FilesMisorderCorruptionWithParam,
DBCompactionTestL0FilesMisorderCorruptionWithParam,
::testing::Values(CompactionStyle::kCompactionStyleUniversal,
CompactionStyle::kCompactionStyleFIFO));
TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
FlushAfterIntraL0CompactFileWithIngestedFile) {
SetupOptions(GetParam(), "CompactFile");
DestroyAndReopen(options_);
ASSERT_OK(Put("k1", "old"));
ASSERT_OK(Put("k3", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(1, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k1", "new")); ASSERT_OK(Put("k2", "new"));
IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
ASSERT_EQ(3, NumTableFilesAtLevel(0));
ColumnFamilyMetaData cf_meta_data;
db_->GetColumnFamilyMetaData(&cf_meta_data);
ASSERT_EQ(cf_meta_data.levels[0].files.size(), 3);
std::vector<std::string> input_files;
for (const auto& file : cf_meta_data.levels[0].files) {
input_files.push_back(file.name);
}
ASSERT_EQ(input_files.size(), 3);
Status s = db_->CompactFiles(CompactionOptions(), input_files, 0);
ASSERT_OK(s);
ASSERT_EQ(1, NumTableFilesAtLevel(0));
SequenceNumber compact_output_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_OK(Flush());
ASSERT_EQ(2, NumTableFilesAtLevel(0));
SequenceNumber flushed_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
EXPECT_EQ(Get("k1"), "new");
Destroy(options_);
}
TEST_P(DBCompactionTestL0FilesMisorderCorruptionWithParam,
FlushAfterIntraL0CompactRangeWithIngestedFile) {
SetupOptions(GetParam(), "CompactRange");
DestroyAndReopen(options_);
ASSERT_OK(Put("k1", "old"));
ASSERT_OK(Put("k3", "old"));
ASSERT_OK(Flush());
ASSERT_EQ(1, NumTableFilesAtLevel(0));
ASSERT_OK(Put("k1", "new")); ASSERT_OK(Put("k2", "new"));
IngestOneKeyValue(dbfull(), "k4", "dummy", options_);
IngestOneKeyValue(dbfull(), "k5", "dummy", options_);
ASSERT_EQ(3, NumTableFilesAtLevel(0));
if (options_.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
SetupSyncPoints("CompactRange");
}
Slice start("k3"), end("k5");
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &start, &end));
if (options_.compaction_style == CompactionStyle::kCompactionStyleFIFO) {
ASSERT_TRUE(SyncPointsCalled());
DisableSyncPoints();
}
ASSERT_EQ(1, NumTableFilesAtLevel(0));
SequenceNumber compact_output_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_OK(Flush());
ASSERT_EQ(2, NumTableFilesAtLevel(0));
SequenceNumber flushed_file_largest_seqno =
GetLatestL0FileLargestSeqnoHelper();
ASSERT_TRUE(flushed_file_largest_seqno < compact_output_file_largest_seqno);
EXPECT_EQ(Get("k1"), "new");
Destroy(options_);
}
TEST_F(DBCompactionTest, SingleLevelUniveresal) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleUniversal;
options.disable_auto_compactions = true;
options.num_levels = 1;
DestroyAndReopen(options);
Random rnd(31);
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 50; ++j) {
ASSERT_OK(Put(Key(i * 100 + j), rnd.RandomString(50)));
}
ASSERT_OK(Flush());
}
ASSERT_EQ(NumTableFilesAtLevel(0), 10);
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
}
TEST_F(DBCompactionTest, SingleOverlappingNonL0BottommostManualCompaction) {
constexpr int kSstNum = 10;
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.num_levels = 7;
for (auto b : {BottommostLevelCompaction::kForce,
BottommostLevelCompaction::kForceOptimized}) {
DestroyAndReopen(options);
for (int i = 0; i < kSstNum; i++) {
for (int j = 1; j < UCHAR_MAX; j++) {
auto key = std::string(kSstNum, '\0');
key[kSstNum - i] += static_cast<char>(j);
ASSERT_OK(Put(key, std::string(i % 1000, 'A')));
}
ASSERT_OK(Flush());
}
MoveFilesToLevel(4);
ASSERT_EQ(NumTableFilesAtLevel(4), kSstNum);
CompactRangeOptions cro;
cro.bottommost_level_compaction = b;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_EQ(NumTableFilesAtLevel(4), 1);
}
}
TEST_P(DBCompactionTestWithBottommostParam, SequenceKeysManualCompaction) {
constexpr int kSstNum = 10;
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.num_levels = 7;
const bool dynamic_level = std::get<1>(GetParam());
options.level_compaction_dynamic_level_bytes = dynamic_level;
DestroyAndReopen(options);
for (int i = 0; i < kSstNum; i++) {
for (int j = 1; j < UCHAR_MAX; j++) {
auto key = std::string(kSstNum, '\0');
key[kSstNum - i] += static_cast<char>(j);
ASSERT_OK(Put(key, std::string(i % 1000, 'A')));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
ASSERT_EQ(std::to_string(kSstNum), FilesPerLevel(0));
auto cro = CompactRangeOptions();
cro.bottommost_level_compaction = bottommost_level_compaction_;
bool trivial_moved = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TrivialMove",
[&](void* ) { trivial_moved = true; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_TRUE(trivial_moved);
if (bottommost_level_compaction_ == BottommostLevelCompaction::kForce ||
bottommost_level_compaction_ ==
BottommostLevelCompaction::kForceOptimized) {
if (dynamic_level) {
ASSERT_EQ("0,0,0,0,0,0,1", FilesPerLevel(0));
} else {
ASSERT_EQ("0,1", FilesPerLevel(0));
}
} else {
if (dynamic_level) {
ASSERT_EQ("0,0,0,0,0,0," + std::to_string(kSstNum), FilesPerLevel(0));
} else {
ASSERT_EQ("0," + std::to_string(kSstNum), FilesPerLevel(0));
}
}
}
INSTANTIATE_TEST_CASE_P(
DBCompactionTestWithBottommostParam, DBCompactionTestWithBottommostParam,
::testing::Combine(
::testing::Values(BottommostLevelCompaction::kSkip,
BottommostLevelCompaction::kIfHaveCompactionFilter,
BottommostLevelCompaction::kForce,
BottommostLevelCompaction::kForceOptimized),
::testing::Bool()));
TEST_F(DBCompactionTest, UpdateLevelSubCompactionTest) {
Options options = CurrentOptions();
options.max_subcompactions = 10;
options.target_file_size_base = 1 << 10; DestroyAndReopen(options);
bool has_compaction = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->max_subcompactions() == 10);
has_compaction = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_TRUE(dbfull()->GetDBOptions().max_subcompactions == 10);
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(has_compaction);
has_compaction = false;
ASSERT_OK(dbfull()->SetDBOptions({{"max_subcompactions", "2"}}));
ASSERT_TRUE(dbfull()->GetDBOptions().max_subcompactions == 2);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->max_subcompactions() == 2);
has_compaction = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(has_compaction);
}
TEST_F(DBCompactionTest, UpdateUniversalSubCompactionTest) {
Options options = CurrentOptions();
options.max_subcompactions = 10;
options.compaction_style = kCompactionStyleUniversal;
options.target_file_size_base = 1 << 10; DestroyAndReopen(options);
bool has_compaction = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->max_subcompactions() == 10);
has_compaction = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(has_compaction);
has_compaction = false;
ASSERT_OK(dbfull()->SetDBOptions({{"max_subcompactions", "2"}}));
ASSERT_TRUE(dbfull()->GetDBOptions().max_subcompactions == 2);
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"UniversalCompactionBuilder::PickCompaction:Return", [&](void* arg) {
Compaction* compaction = static_cast<Compaction*>(arg);
ASSERT_TRUE(compaction->max_subcompactions() == 2);
has_compaction = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 32; i++) {
for (int j = 0; j < 5000; j++) {
ASSERT_OK(Put(std::to_string(j), std::string(1, 'A')));
}
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(has_compaction);
}
TEST_P(ChangeLevelConflictsWithAuto, TestConflict) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
Reopen(options);
ASSERT_OK(Put("foo", "v1"));
ASSERT_OK(Put("bar", "v1"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForFlushMemTable());
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,1", FilesPerLevel(0));
SyncPoint::GetInstance()->LoadDependency({
{
"DBImpl::CompactRange:BeforeRefit:1",
"AutoCompactionFinished1",
},
{
"AutoCompactionFinished2",
"DBImpl::CompactRange:BeforeRefit:2",
},
});
SyncPoint::GetInstance()->EnableProcessing();
std::thread auto_comp([&] {
TEST_SYNC_POINT("AutoCompactionFinished1");
ASSERT_OK(Put("bar", "v2"));
ASSERT_OK(Put("foo", "v2"));
ASSERT_OK(Flush());
ASSERT_OK(Put("bar", "v3"));
ASSERT_OK(Put("foo", "v3"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
TEST_SYNC_POINT("AutoCompactionFinished2");
});
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = GetParam() ? 1 : 0;
ASSERT_NOK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
auto_comp.join();
SyncPoint::GetInstance()->DisableProcessing();
}
INSTANTIATE_TEST_CASE_P(ChangeLevelConflictsWithAuto,
ChangeLevelConflictsWithAuto, testing::Bool());
TEST_F(DBCompactionTest, ChangeLevelCompactRangeConflictsWithManual) {
Options options = CurrentOptions();
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
Reopen(options);
Random rnd(301);
int key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,2", FilesPerLevel(0));
GenerateNewFile(&rnd, &key_idx);
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1,2", FilesPerLevel(0));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
{
"DBImpl::RunManualCompaction()::1",
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
"PutFG",
},
{
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
"FlushedFG",
"DBImpl::RunManualCompaction()::2",
},
{
"DBImpl::CompactRange:PreRefitLevel",
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
"CompactFG",
},
{
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
"CompactedFG",
"DBImpl::CompactRange:PostRefitLevel",
},
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ROCKSDB_NAMESPACE::port::Thread refit_level_thread([&] {
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
});
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:PutFG");
ASSERT_OK(Put(Key(1), "val"));
ASSERT_OK(Flush());
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:FlushedFG");
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:CompactFG");
ASSERT_TRUE(dbfull()
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
.IsIncomplete());
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelCompactRangeConflictsWithManual:"
"CompactedFG");
refit_level_thread.join();
}
TEST_F(DBCompactionTest, ChangeLevelErrorPathTest) {
Options options = CurrentOptions();
options.memtable_factory.reset(
test::NewSpecialSkipListFactory(KNumKeysByGenerateNewFile - 1));
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
Reopen(options);
ASSERT_EQ("", FilesPerLevel(0));
Random rnd(301);
int key_idx = 0;
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1", FilesPerLevel(0));
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,2", FilesPerLevel(0));
auto start_idx = key_idx;
GenerateNewFile(&rnd, &key_idx);
GenerateNewFile(&rnd, &key_idx);
ASSERT_EQ("1,1,2", FilesPerLevel(0));
MoveFilesToLevel(1);
ASSERT_EQ("0,2,2", FilesPerLevel(0));
{
std::string begin_string = Key(0);
std::string end_string = Key(start_idx - 1);
Slice begin(begin_string);
Slice end(end_string);
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_NOK(dbfull()->CompactRange(cro, &begin, &end));
}
ASSERT_EQ("0,2,2", FilesPerLevel(0));
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,5", FilesPerLevel(0));
}
TEST_F(DBCompactionTest, CompactionWithBlob) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char second_key[] = "second_key";
constexpr char first_value[] = "first_value";
constexpr char second_value[] = "second_value";
constexpr char third_value[] = "third_value";
ASSERT_OK(Put(first_key, first_value));
ASSERT_OK(Put(second_key, first_value));
ASSERT_OK(Flush());
ASSERT_OK(Put(first_key, second_value));
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
ASSERT_OK(Put(first_key, third_value));
ASSERT_OK(Put(second_key, third_value));
ASSERT_OK(Flush());
options.enable_blob_files = true;
Reopen(options);
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
ASSERT_EQ(Get(first_key), third_value);
ASSERT_EQ(Get(second_key), third_value);
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(cfd, nullptr);
Version* const current = cfd->current();
ASSERT_NE(current, nullptr);
const VersionStorageInfo* const storage_info = current->storage_info();
ASSERT_NE(storage_info, nullptr);
const auto& l1_files = storage_info->LevelFiles(1);
ASSERT_EQ(l1_files.size(), 1);
const FileMetaData* const table_file = l1_files[0];
ASSERT_NE(table_file, nullptr);
const auto& blob_files = storage_info->GetBlobFiles();
ASSERT_EQ(blob_files.size(), 1);
const auto& blob_file = blob_files.front();
ASSERT_NE(blob_file, nullptr);
ASSERT_EQ(table_file->smallest.user_key(), first_key);
ASSERT_EQ(table_file->largest.user_key(), second_key);
ASSERT_EQ(table_file->oldest_blob_file_number,
blob_file->GetBlobFileNumber());
ASSERT_EQ(blob_file->GetTotalBlobCount(), 2);
const InternalStats* const internal_stats = cfd->internal_stats();
ASSERT_NE(internal_stats, nullptr);
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
ASSERT_GE(compaction_stats.size(), 2);
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
ASSERT_EQ(compaction_stats[1].bytes_written, table_file->fd.GetFileSize());
ASSERT_EQ(compaction_stats[1].bytes_written_blob,
blob_file->GetTotalBlobBytes());
ASSERT_EQ(compaction_stats[1].num_output_files, 1);
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 1);
}
class DBCompactionTestBlobError
: public DBCompactionTest,
public testing::WithParamInterface<std::string> {
public:
DBCompactionTestBlobError() : sync_point_(GetParam()) {}
std::string sync_point_;
};
INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobError, DBCompactionTestBlobError,
::testing::ValuesIn(std::vector<std::string>{
"BlobFileBuilder::WriteBlobToFile:AddRecord",
"BlobFileBuilder::WriteBlobToFile:AppendFooter"}));
TEST_P(DBCompactionTestBlobError, CompactionError) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char second_key[] = "second_key";
constexpr char first_value[] = "first_value";
constexpr char second_value[] = "second_value";
constexpr char third_value[] = "third_value";
ASSERT_OK(Put(first_key, first_value));
ASSERT_OK(Put(second_key, first_value));
ASSERT_OK(Flush());
ASSERT_OK(Put(first_key, second_value));
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
ASSERT_OK(Put(first_key, third_value));
ASSERT_OK(Put(second_key, third_value));
ASSERT_OK(Flush());
options.enable_blob_files = true;
Reopen(options);
SyncPoint::GetInstance()->SetCallBack(sync_point_, [this](void* arg) {
Status* const s = static_cast<Status*>(arg);
assert(s);
(*s) = Status::IOError(sync_point_);
});
SyncPoint::GetInstance()->EnableProcessing();
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_TRUE(db_->CompactRange(CompactRangeOptions(), begin, end).IsIOError());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
ASSERT_NE(cfd, nullptr);
Version* const current = cfd->current();
ASSERT_NE(current, nullptr);
const VersionStorageInfo* const storage_info = current->storage_info();
ASSERT_NE(storage_info, nullptr);
const auto& l1_files = storage_info->LevelFiles(1);
ASSERT_TRUE(l1_files.empty());
const auto& blob_files = storage_info->GetBlobFiles();
ASSERT_TRUE(blob_files.empty());
const InternalStats* const internal_stats = cfd->internal_stats();
ASSERT_NE(internal_stats, nullptr);
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
ASSERT_GE(compaction_stats.size(), 2);
if (sync_point_ == "BlobFileBuilder::WriteBlobToFile:AddRecord") {
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
ASSERT_EQ(compaction_stats[1].bytes_written, 0);
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
ASSERT_EQ(compaction_stats[1].num_output_files, 0);
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 0);
} else {
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
ASSERT_GT(compaction_stats[1].bytes_written, 0);
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
ASSERT_EQ(compaction_stats[1].num_output_files, 1);
ASSERT_EQ(compaction_stats[1].num_output_files_blob, 0);
}
}
class DBCompactionTestBlobGC
: public DBCompactionTest,
public testing::WithParamInterface<std::tuple<double, bool>> {
public:
DBCompactionTestBlobGC()
: blob_gc_age_cutoff_(std::get<0>(GetParam())),
updated_enable_blob_files_(std::get<1>(GetParam())) {}
double blob_gc_age_cutoff_;
bool updated_enable_blob_files_;
};
INSTANTIATE_TEST_CASE_P(DBCompactionTestBlobGC, DBCompactionTestBlobGC,
::testing::Combine(::testing::Values(0.0, 0.5, 1.0),
::testing::Bool()));
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGCOverrides) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.enable_blob_files = true;
options.blob_file_size = 32; options.enable_blob_garbage_collection = true;
options.blob_garbage_collection_age_cutoff = 0;
DestroyAndReopen(options);
for (int i = 0; i < 128; i += 2) {
ASSERT_OK(Put("key" + std::to_string(i), "value" + std::to_string(i)));
ASSERT_OK(
Put("key" + std::to_string(i + 1), "value" + std::to_string(i + 1)));
ASSERT_OK(Flush());
}
std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
ASSERT_EQ(original_blob_files.size(), 128);
ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
CompactRangeOptions cro;
cro.blob_garbage_collection_policy = BlobGarbageCollectionPolicy::kForce;
cro.blob_garbage_collection_age_cutoff = blob_gc_age_cutoff_;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
{
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
assert(versions->GetColumnFamilySet());
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
assert(cfd);
const InternalStats* const internal_stats = cfd->internal_stats();
assert(internal_stats);
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
ASSERT_GE(compaction_stats.size(), 2);
ASSERT_GE(compaction_stats[1].bytes_read_blob, 0);
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
}
const size_t cutoff_index = static_cast<size_t>(
cro.blob_garbage_collection_age_cutoff * original_blob_files.size());
const size_t expected_num_files = original_blob_files.size() - cutoff_index;
const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
ASSERT_EQ(new_blob_files.size(), expected_num_files);
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
}
for (size_t i = 0; i < 128; ++i) {
ASSERT_EQ(Get("key" + std::to_string(i)), "value" + std::to_string(i));
}
}
TEST_P(DBCompactionTestBlobGC, CompactionWithBlobGC) {
Options options = CurrentOptions();
options.disable_auto_compactions = true;
options.enable_blob_files = true;
options.blob_file_size = 32; options.enable_blob_garbage_collection = true;
options.blob_garbage_collection_age_cutoff = blob_gc_age_cutoff_;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char first_value[] = "first_value";
constexpr char second_key[] = "second_key";
constexpr char second_value[] = "second_value";
ASSERT_OK(Put(first_key, first_value));
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
constexpr char third_key[] = "third_key";
constexpr char third_value[] = "third_value";
constexpr char fourth_key[] = "fourth_key";
constexpr char fourth_value[] = "fourth_value";
ASSERT_OK(Put(third_key, third_value));
ASSERT_OK(Put(fourth_key, fourth_value));
ASSERT_OK(Flush());
const std::vector<uint64_t> original_blob_files = GetBlobFileNumbers();
ASSERT_EQ(original_blob_files.size(), 4);
const size_t cutoff_index = static_cast<size_t>(
options.blob_garbage_collection_age_cutoff * original_blob_files.size());
size_t expected_number_of_files = original_blob_files.size();
if (!updated_enable_blob_files_) {
ASSERT_OK(db_->SetOptions({{"enable_blob_files", "false"}}));
expected_number_of_files -= cutoff_index;
}
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), begin, end));
ASSERT_EQ(Get(first_key), first_value);
ASSERT_EQ(Get(second_key), second_value);
ASSERT_EQ(Get(third_key), third_value);
ASSERT_EQ(Get(fourth_key), fourth_value);
const std::vector<uint64_t> new_blob_files = GetBlobFileNumbers();
ASSERT_EQ(new_blob_files.size(), expected_number_of_files);
for (size_t i = cutoff_index; i < original_blob_files.size(); ++i) {
ASSERT_EQ(new_blob_files[i - cutoff_index], original_blob_files[i]);
}
VersionSet* const versions = dbfull()->GetVersionSet();
assert(versions);
assert(versions->GetColumnFamilySet());
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
assert(cfd);
const InternalStats* const internal_stats = cfd->internal_stats();
assert(internal_stats);
const auto& compaction_stats = internal_stats->TEST_GetCompactionStats();
ASSERT_GE(compaction_stats.size(), 2);
if (blob_gc_age_cutoff_ > 0.0) {
ASSERT_GT(compaction_stats[1].bytes_read_blob, 0);
if (updated_enable_blob_files_) {
ASSERT_GT(compaction_stats[1].bytes_written_blob, 0);
ASSERT_EQ(compaction_stats[1].bytes_read_blob,
compaction_stats[1].bytes_written_blob);
} else {
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
}
} else {
ASSERT_EQ(compaction_stats[1].bytes_read_blob, 0);
ASSERT_EQ(compaction_stats[1].bytes_written_blob, 0);
}
}
TEST_F(DBCompactionTest, CompactionWithBlobGCError_CorruptIndex) {
Options options;
options.env = env_;
options.disable_auto_compactions = true;
options.enable_blob_files = true;
options.enable_blob_garbage_collection = true;
options.blob_garbage_collection_age_cutoff = 1.0;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char first_value[] = "first_value";
ASSERT_OK(Put(first_key, first_value));
constexpr char second_key[] = "second_key";
constexpr char second_value[] = "second_value";
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
constexpr char third_key[] = "third_key";
constexpr char third_value[] = "third_value";
ASSERT_OK(Put(third_key, third_value));
constexpr char fourth_key[] = "fourth_key";
constexpr char fourth_value[] = "fourth_value";
ASSERT_OK(Put(fourth_key, fourth_value));
ASSERT_OK(Flush());
SyncPoint::GetInstance()->SetCallBack(
"CompactionIterator::GarbageCollectBlobIfNeeded::TamperWithBlobIndex",
[](void* arg) {
Slice* const blob_index = static_cast<Slice*>(arg);
assert(blob_index);
assert(!blob_index->empty());
blob_index->remove_prefix(1);
});
SyncPoint::GetInstance()->EnableProcessing();
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_TRUE(
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, CompactionWithBlobGCError_InlinedTTLIndex) {
constexpr uint64_t min_blob_size = 10;
Options options;
options.env = env_;
options.disable_auto_compactions = true;
options.enable_blob_files = true;
options.min_blob_size = min_blob_size;
options.enable_blob_garbage_collection = true;
options.blob_garbage_collection_age_cutoff = 1.0;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char first_value[] = "first_value";
ASSERT_OK(Put(first_key, first_value));
constexpr char second_key[] = "second_key";
constexpr char second_value[] = "second_value";
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
constexpr char third_key[] = "third_key";
constexpr char third_value[] = "third_value";
ASSERT_OK(Put(third_key, third_value));
constexpr char fourth_key[] = "fourth_key";
constexpr char blob[] = "short";
static_assert(sizeof(short) - 1 < min_blob_size,
"Blob too long to be inlined");
std::string blob_index;
constexpr uint64_t expiration = 1234567890;
BlobIndex::EncodeInlinedTTL(&blob_index, expiration, blob);
WriteBatch batch;
ASSERT_OK(
WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key, blob_index));
ASSERT_OK(db_->Write(WriteOptions(), &batch));
ASSERT_OK(Flush());
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_TRUE(
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
}
TEST_F(DBCompactionTest, CompactionWithBlobGCError_IndexWithInvalidFileNumber) {
Options options;
options.env = env_;
options.disable_auto_compactions = true;
options.enable_blob_files = true;
options.enable_blob_garbage_collection = true;
options.blob_garbage_collection_age_cutoff = 1.0;
Reopen(options);
constexpr char first_key[] = "first_key";
constexpr char first_value[] = "first_value";
ASSERT_OK(Put(first_key, first_value));
constexpr char second_key[] = "second_key";
constexpr char second_value[] = "second_value";
ASSERT_OK(Put(second_key, second_value));
ASSERT_OK(Flush());
constexpr char third_key[] = "third_key";
constexpr char third_value[] = "third_value";
ASSERT_OK(Put(third_key, third_value));
constexpr char fourth_key[] = "fourth_key";
std::string blob_index;
constexpr uint64_t blob_file_number = 1000;
constexpr uint64_t offset = 1234;
constexpr uint64_t size = 5678;
BlobIndex::EncodeBlob(&blob_index, blob_file_number, offset, size,
kNoCompression);
WriteBatch batch;
ASSERT_OK(
WriteBatchInternal::PutBlobIndex(&batch, 0, fourth_key, blob_index));
ASSERT_OK(db_->Write(WriteOptions(), &batch));
ASSERT_OK(Flush());
constexpr Slice* begin = nullptr;
constexpr Slice* end = nullptr;
ASSERT_TRUE(
db_->CompactRange(CompactRangeOptions(), begin, end).IsCorruption());
}
TEST_F(DBCompactionTest, CompactionWithChecksumHandoff1) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
std::shared_ptr<FaultInjectionTestFS> fault_fs(
new FaultInjectionTestFS(FileSystem::Default()));
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
options.env = fault_fs_env.get();
options.create_if_missing = true;
options.checksum_handoff_file_types.Add(FileType::kTableFile);
Status s;
Reopen(options);
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
Destroy(options);
Reopen(options);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0", [&](void*) {
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s.severity(),
ROCKSDB_NAMESPACE::Status::Severity::kUnrecoverableError);
SyncPoint::GetInstance()->DisableProcessing();
Destroy(options);
Reopen(options);
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0",
[&](void*) { fault_fs->IngestDataCorruptionBeforeWrite(); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s.severity(),
ROCKSDB_NAMESPACE::Status::Severity::kUnrecoverableError);
SyncPoint::GetInstance()->DisableProcessing();
Destroy(options);
}
TEST_F(DBCompactionTest, CompactionWithChecksumHandoff2) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
std::shared_ptr<FaultInjectionTestFS> fault_fs(
new FaultInjectionTestFS(FileSystem::Default()));
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
options.env = fault_fs_env.get();
options.create_if_missing = true;
Status s;
Reopen(options);
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
Destroy(options);
Reopen(options);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0", [&](void*) {
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
SyncPoint::GetInstance()->DisableProcessing();
Destroy(options);
Reopen(options);
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0",
[&](void*) { fault_fs->IngestDataCorruptionBeforeWrite(); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
Destroy(options);
}
TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest1) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
std::shared_ptr<FaultInjectionTestFS> fault_fs(
new FaultInjectionTestFS(FileSystem::Default()));
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
options.env = fault_fs_env.get();
options.create_if_missing = true;
options.checksum_handoff_file_types.Add(FileType::kDescriptorFile);
Status s;
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
Reopen(options);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
Destroy(options);
Reopen(options);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0", [&](void*) {
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kxxHash);
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
SyncPoint::GetInstance()->DisableProcessing();
Destroy(options);
}
TEST_F(DBCompactionTest, CompactionWithChecksumHandoffManifest2) {
if (mem_env_ || encrypted_env_) {
ROCKSDB_GTEST_SKIP("Test requires non-mem or non-encrypted environment");
return;
}
std::shared_ptr<FaultInjectionTestFS> fault_fs(
new FaultInjectionTestFS(FileSystem::Default()));
std::unique_ptr<Env> fault_fs_env(NewCompositeEnv(fault_fs));
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = 2;
options.num_levels = 3;
options.env = fault_fs_env.get();
options.create_if_missing = true;
options.checksum_handoff_file_types.Add(FileType::kDescriptorFile);
Status s;
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kNoChecksum);
Reopen(options);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s, Status::OK());
fault_fs->SetChecksumHandoffFuncType(ChecksumType::kCRC32c);
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
s = Flush();
ASSERT_EQ(s, Status::OK());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::FlushMemTable:FlushMemTableFinished",
"BackgroundCallCompaction:0"}});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0",
[&](void*) { fault_fs->IngestDataCorruptionBeforeWrite(); });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(1), "value3"));
s = Flush();
ASSERT_EQ(s, Status::OK());
s = dbfull()->TEST_WaitForCompact();
ASSERT_EQ(s.severity(), ROCKSDB_NAMESPACE::Status::Severity::kFatalError);
SyncPoint::GetInstance()->DisableProcessing();
Destroy(options);
}
TEST_F(DBCompactionTest, FIFOChangeTemperature) {
for (bool should_allow_trivial_copy : {false, true}) {
for (bool write_time_default : {false, true}) {
int32_t before_compaction_calls = 0;
int32_t after_compaction_calls = 0;
if (should_allow_trivial_copy) {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TriviaCopyBeforeCompaction",
[&](void*) { ++before_compaction_calls; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:TriviaCopyAfterCompaction",
[&](void*) { ++after_compaction_calls; });
} else {
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:BeforeCompaction",
[&](void*) { ++before_compaction_calls; });
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:AfterCompaction",
[&](void*) { ++after_compaction_calls; });
}
SCOPED_TRACE("write time default? " + std::to_string(write_time_default));
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleFIFO;
options.num_levels = 1;
options.max_open_files = -1;
options.level0_file_num_compaction_trigger = 2;
options.create_if_missing = true;
CompactionOptionsFIFO fifo_options;
fifo_options.file_temperature_age_thresholds = {
{Temperature::kCold, 1000}};
fifo_options.max_table_files_size = 100000000;
fifo_options.allow_trivial_copy_when_change_temperature =
should_allow_trivial_copy;
fifo_options.trivial_copy_buffer_size = 4096;
options.compaction_options_fifo = fifo_options;
env_->SetMockSleep();
if (write_time_default) {
options.default_write_temperature = Temperature::kWarm;
}
options.last_level_temperature = Temperature::kHot;
Reopen(options);
int total_cold = 0;
int total_warm = 0;
int total_hot = 0;
int total_ice = 0;
int total_unknown = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
Temperature temperature = *(static_cast<Temperature*>(arg));
if (temperature == Temperature::kCold) {
total_cold++;
} else if (temperature == Temperature::kWarm) {
total_warm++;
} else if (temperature == Temperature::kHot) {
total_hot++;
} else if (temperature == Temperature::kIce) {
total_ice++;
} else {
assert(temperature == Temperature::kUnknown);
total_unknown++;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ASSERT_OK(Put(Key(0), "value1"));
env_->MockSleepForSeconds(800);
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(0), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
ASSERT_OK(Put(Key(0), "value1"));
env_->MockSleepForSeconds(1200);
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
if (write_time_default) {
ASSERT_OK(db_->SetOptions({{"default_write_temperature", "kHot"}}));
}
ASSERT_OK(Put(Key(0), "value1"));
env_->MockSleepForSeconds(800);
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
ColumnFamilyMetaData metadata;
db_->GetColumnFamilyMetaData(&metadata);
ASSERT_EQ(4, metadata.file_count);
if (write_time_default) {
ASSERT_EQ(Temperature::kHot, metadata.levels[0].files[0].temperature);
ASSERT_EQ(Temperature::kWarm, metadata.levels[0].files[1].temperature);
ASSERT_EQ(total_warm, 3);
ASSERT_EQ(total_hot, 1);
ASSERT_GT(total_unknown, 0);
} else {
ASSERT_EQ(Temperature::kUnknown,
metadata.levels[0].files[0].temperature);
ASSERT_EQ(Temperature::kUnknown,
metadata.levels[0].files[1].temperature);
ASSERT_EQ(total_warm, 0);
ASSERT_EQ(total_hot, 0);
ASSERT_GT(total_unknown, 4);
}
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[2].temperature);
ASSERT_EQ(Temperature::kCold, metadata.levels[0].files[3].temperature);
ASSERT_EQ(2, total_cold);
ASSERT_EQ(2, before_compaction_calls);
ASSERT_EQ(2, after_compaction_calls);
Destroy(options);
}
}
}
using TemperatureSet = SmallEnumSet<Temperature, Temperature::kLastTemperature>;
static void VerifyTemperatureFileReadStats(const Statistics& st,
TemperatureSet temps) {
SCOPED_TRACE("Temp set size = " + std::to_string(temps.count()));
constexpr uint64_t min_bytes = 100;
constexpr uint64_t min_count = 1;
IOStatsContext* iostats = get_iostats_context();
if (temps.Contains(Temperature::kHot)) {
EXPECT_GE(st.getTickerCount(HOT_FILE_READ_BYTES), min_bytes);
EXPECT_GE(st.getTickerCount(HOT_FILE_READ_COUNT), min_count);
EXPECT_GE(iostats->file_io_stats_by_temperature.hot_file_bytes_read,
min_bytes);
EXPECT_GE(iostats->file_io_stats_by_temperature.hot_file_read_count,
min_count);
} else {
EXPECT_EQ(st.getTickerCount(HOT_FILE_READ_BYTES), 0);
EXPECT_EQ(st.getTickerCount(HOT_FILE_READ_COUNT), 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.hot_file_bytes_read, 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.hot_file_read_count, 0);
}
if (temps.Contains(Temperature::kWarm)) {
EXPECT_GE(st.getTickerCount(WARM_FILE_READ_BYTES), min_bytes);
EXPECT_GE(st.getTickerCount(WARM_FILE_READ_COUNT), min_count);
EXPECT_GE(iostats->file_io_stats_by_temperature.warm_file_bytes_read,
min_bytes);
EXPECT_GE(iostats->file_io_stats_by_temperature.warm_file_read_count,
min_count);
} else {
EXPECT_EQ(st.getTickerCount(WARM_FILE_READ_BYTES), 0);
EXPECT_EQ(st.getTickerCount(WARM_FILE_READ_COUNT), 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.warm_file_bytes_read, 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.warm_file_read_count, 0);
}
if (temps.Contains(Temperature::kCool)) {
EXPECT_GE(st.getTickerCount(COOL_FILE_READ_BYTES), min_bytes);
EXPECT_GE(st.getTickerCount(COOL_FILE_READ_COUNT), min_count);
EXPECT_GE(iostats->file_io_stats_by_temperature.cool_file_bytes_read,
min_bytes);
EXPECT_GE(iostats->file_io_stats_by_temperature.cool_file_read_count,
min_count);
} else {
EXPECT_EQ(st.getTickerCount(COOL_FILE_READ_BYTES), 0);
EXPECT_EQ(st.getTickerCount(COOL_FILE_READ_COUNT), 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.cool_file_bytes_read, 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.cool_file_read_count, 0);
}
if (temps.Contains(Temperature::kCold)) {
EXPECT_GE(st.getTickerCount(COLD_FILE_READ_BYTES), min_bytes);
EXPECT_GE(st.getTickerCount(COLD_FILE_READ_COUNT), min_count);
EXPECT_GE(iostats->file_io_stats_by_temperature.cold_file_bytes_read,
min_bytes);
EXPECT_GE(iostats->file_io_stats_by_temperature.cold_file_read_count,
min_count);
} else {
EXPECT_EQ(st.getTickerCount(COLD_FILE_READ_BYTES), 0);
EXPECT_EQ(st.getTickerCount(COLD_FILE_READ_COUNT), 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.cold_file_bytes_read, 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.cold_file_read_count, 0);
}
if (temps.Contains(Temperature::kIce)) {
EXPECT_GE(st.getTickerCount(ICE_FILE_READ_BYTES), min_bytes);
EXPECT_GE(st.getTickerCount(ICE_FILE_READ_COUNT), min_count);
EXPECT_GE(iostats->file_io_stats_by_temperature.ice_file_bytes_read,
min_bytes);
EXPECT_GE(iostats->file_io_stats_by_temperature.ice_file_read_count,
min_count);
} else {
EXPECT_EQ(st.getTickerCount(ICE_FILE_READ_BYTES), 0);
EXPECT_EQ(st.getTickerCount(ICE_FILE_READ_COUNT), 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.ice_file_bytes_read, 0);
EXPECT_EQ(iostats->file_io_stats_by_temperature.ice_file_read_count, 0);
}
}
TEST_F(DBCompactionTest, FIFOMultiTierTemperatureAging) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleFIFO;
options.num_levels = 1;
options.max_open_files = -1;
options.level0_file_num_compaction_trigger = 2;
options.create_if_missing = true;
options.statistics = CreateDBStatistics();
BlockBasedTableOptions bbto;
bbto.no_block_cache = true; options.table_factory.reset(NewBlockBasedTableFactory(bbto));
CompactionOptionsFIFO fifo_options;
fifo_options.file_temperature_age_thresholds = {
{Temperature::kWarm, 500}, {Temperature::kCool, 1000}, {Temperature::kCold, 1500}, {Temperature::kIce, 2000} };
fifo_options.max_table_files_size = 100000000;
fifo_options.allow_trivial_copy_when_change_temperature = true;
options.compaction_options_fifo = fifo_options;
options.default_write_temperature = Temperature::kHot;
Reopen(options);
env_->SetMockSleep();
int total_hot = 0, total_warm = 0, total_cool = 0, total_cold = 0,
total_ice = 0, total_unknown = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"NewWritableFile::FileOptions.temperature", [&](void* arg) {
Temperature temperature = *(static_cast<Temperature*>(arg));
switch (temperature) {
case Temperature::kHot:
total_hot++;
break;
case Temperature::kWarm:
total_warm++;
break;
case Temperature::kCool:
total_cool++;
break;
case Temperature::kCold:
total_cold++;
break;
case Temperature::kIce:
total_ice++;
break;
case Temperature::kUnknown:
total_unknown++;
break;
default:
break;
}
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < 3; ++i) {
ASSERT_OK(Put(Key(0), Random::GetTLSInstance()->RandomBinaryString(100)));
ASSERT_OK(Flush());
}
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
ASSERT_EQ(100U, Get(Key(0)).size());
VerifyTemperatureFileReadStats(*options.statistics, Temperature::kHot);
env_->MockSleepForSeconds(100);
env_->MockSleepForSeconds(500);
ASSERT_OK(Put(Key(1), Random::GetTLSInstance()->RandomBinaryString(101)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
ASSERT_EQ(100U, Get(Key(0)).size());
VerifyTemperatureFileReadStats(*options.statistics, Temperature::kWarm);
env_->MockSleepForSeconds(500);
ASSERT_OK(Put(Key(2), Random::GetTLSInstance()->RandomBinaryString(102)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
ASSERT_EQ(100U, Get(Key(0)).size());
VerifyTemperatureFileReadStats(*options.statistics, Temperature::kCool);
env_->MockSleepForSeconds(500);
ASSERT_OK(Put(Key(3), Random::GetTLSInstance()->RandomBinaryString(103)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
ASSERT_EQ(100U, Get(Key(0)).size());
VerifyTemperatureFileReadStats(*options.statistics, Temperature::kCold);
env_->MockSleepForSeconds(500);
ASSERT_OK(Put(Key(4), Random::GetTLSInstance()->RandomBinaryString(104)));
ASSERT_OK(Flush());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
ASSERT_EQ(100U, Get(Key(0)).size());
VerifyTemperatureFileReadStats(*options.statistics, Temperature::kIce);
ColumnFamilyMetaData metadata;
db_->GetColumnFamilyMetaData(&metadata);
std::map<Temperature, int> temp_counts;
for (const auto& file : metadata.levels[0].files) {
temp_counts[file.temperature]++;
}
EXPECT_EQ(temp_counts[Temperature::kHot], 1);
EXPECT_EQ(temp_counts[Temperature::kWarm], 1);
EXPECT_EQ(temp_counts[Temperature::kCool], 1);
EXPECT_EQ(temp_counts[Temperature::kCold], 1);
EXPECT_EQ(temp_counts[Temperature::kIce], 3);
EXPECT_EQ(total_hot, 7);
EXPECT_EQ(total_warm, 6);
EXPECT_EQ(total_cool, 5);
EXPECT_EQ(total_cold, 4);
EXPECT_EQ(total_ice, 3);
Reopen(options);
ASSERT_OK(options.statistics->Reset());
get_iostats_context()->Reset();
for (int i = 0; i < 5; i++) {
ASSERT_EQ(static_cast<unsigned>(100 + i), Get(Key(i)).size());
}
VerifyTemperatureFileReadStats(*options.statistics, TemperatureSet::All());
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
}
TEST_F(DBCompactionTest, DisableMultiManualCompaction) {
const int kNumL0Files = 10;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
for (int i = 0; i < 10; i++) {
ASSERT_OK(Put(Key(i), "value"));
if (i % 2) {
ASSERT_OK(Flush());
}
}
MoveFilesToLevel(2);
for (int i = 0; i < 10; i++) {
ASSERT_OK(Put(Key(i), "value"));
if (i % 2) {
ASSERT_OK(Flush());
}
}
MoveFilesToLevel(1);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
port::Thread compact_thread1([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = false;
std::string begin_str = Key(0);
std::string end_str = Key(3);
Slice b = begin_str;
Slice e = end_str;
auto s = db_->CompactRange(cro, &b, &e);
ASSERT_TRUE(s.IsIncomplete());
});
port::Thread compact_thread2([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = false;
std::string begin_str = Key(4);
std::string end_str = Key(7);
Slice b = begin_str;
Slice e = end_str;
auto s = db_->CompactRange(cro, &b, &e);
ASSERT_TRUE(s.IsIncomplete());
});
db_->DisableManualCompaction();
compact_thread1.join();
compact_thread2.join();
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
}
TEST_F(DBCompactionTest, DisableJustStartedManualCompaction) {
const int kNumL0Files = 4;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
for (int i = 0; i < kNumL0Files / 2; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BGWorkCompaction",
"DBCompactionTest::DisableJustStartedManualCompaction:"
"PreDisableManualCompaction"},
{"DBImpl::RunManualCompaction:Unscheduled",
"BackgroundCallCompaction:0"}});
SyncPoint::GetInstance()->EnableProcessing();
port::Thread compact_thread([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
auto s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_TRUE(s.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::DisableJustStartedManualCompaction:"
"PreDisableManualCompaction");
db_->DisableManualCompaction();
compact_thread.join();
}
TEST_F(DBCompactionTest, DisableInProgressManualCompaction) {
const int kNumL0Files = 4;
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction:InProgress",
"DBCompactionTest::DisableInProgressManualCompaction:"
"PreDisableManualCompaction"},
{"DBImpl::RunManualCompaction:Unscheduled",
"CompactionJob::Run():Start"}});
SyncPoint::GetInstance()->EnableProcessing();
for (int i = 0; i < kNumL0Files / 2; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
port::Thread compact_thread([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
auto s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_TRUE(s.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::DisableInProgressManualCompaction:"
"PreDisableManualCompaction");
db_->DisableManualCompaction();
compact_thread.join();
}
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFull) {
const int kNumL0Files = 4;
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::RunManualCompaction:Scheduled",
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
"PreDisableManualCompaction"}});
SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
for (int i = 0; i < kNumL0Files / 2; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
port::Thread compact_thread([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
auto s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_TRUE(s.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::DisableManualCompactionThreadQueueFull:"
"PreDisableManualCompaction");
for (int i = 0; i < kNumL0Files; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
db_->DisableManualCompaction();
compact_thread.join();
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ("0,1", FilesPerLevel(0));
}
TEST_F(DBCompactionTest, DisableManualCompactionThreadQueueFullDBClose) {
const int kNumL0Files = 4;
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::RunManualCompaction:Scheduled",
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
"PreDisableManualCompaction"}});
SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
for (int i = 0; i < kNumL0Files / 2; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
port::Thread compact_thread([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
auto s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_TRUE(s.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
"PreDisableManualCompaction");
for (int i = 0; i < kNumL0Files; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
db_->DisableManualCompaction();
compact_thread.join();
auto s = db_->Close();
ASSERT_OK(s);
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
}
TEST_F(DBCompactionTest, DBCloseWithManualCompaction) {
const int kNumL0Files = 4;
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::RunManualCompaction:Scheduled",
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
"PreDisableManualCompaction"}});
SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
Env::Priority::LOW);
for (int i = 0; i < kNumL0Files / 2; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
port::Thread compact_thread([&]() {
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
auto s = db_->CompactRange(cro, nullptr, nullptr);
ASSERT_TRUE(s.IsIncomplete());
});
TEST_SYNC_POINT(
"DBCompactionTest::DisableManualCompactionThreadQueueFullDBClose:"
"PreDisableManualCompaction");
for (int i = 0; i < kNumL0Files; i++) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
ASSERT_EQ(std::to_string(kNumL0Files + (kNumL0Files / 2)), FilesPerLevel(0));
auto s = db_->Close();
ASSERT_OK(s);
compact_thread.join();
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
}
TEST_F(DBCompactionTest,
DisableManualCompactionDoesNotWaitForDrainingAutomaticCompaction) {
const int kNumL0Files = 4;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BGWorkCompaction", "DBImpl::RunManualCompaction:NotScheduled"},
{"DBImpl::RunManualCompaction:WaitScheduled",
"BackgroundCallCompaction:0"}});
bool callback_completed = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BackgroundCallCompaction:0", [&](void* ) {
db_->DisableManualCompaction();
callback_completed = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
Reopen(options);
for (int i = 0; i < kNumL0Files; ++i) {
ASSERT_OK(Put(Key(1), "value1"));
ASSERT_OK(Put(Key(2), "value2"));
ASSERT_OK(Flush());
}
CompactRangeOptions cro;
cro.exclusive_manual_compaction = true;
ASSERT_TRUE(db_->CompactRange(cro, nullptr, nullptr).IsIncomplete());
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_TRUE(callback_completed);
}
TEST_F(DBCompactionTest, ChangeLevelConflictsWithManual) {
Options options = CurrentOptions();
options.num_levels = 3;
Reopen(options);
Random rnd(301);
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
{
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 2;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
}
ASSERT_EQ("0,0,1", FilesPerLevel(0));
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->LoadDependency({
{
"DBImpl::CompactRange:BeforeRefit:1",
"DBCompactionTest::ChangeLevelConflictsWithManual:"
"PreForegroundCompactRange",
},
{
"DBImpl::RunManualCompaction:0",
"DBImpl::CompactRange:BeforeRefit:2",
},
{
"DBImpl::CompactRange:PreRefitLevel",
"DBImpl::RunManualCompaction:1",
},
{
"DBImpl::RunManualCompaction:PausedAtStart",
"DBImpl::CompactRange:PostRefitLevel",
},
{
"DBImpl::CompactRange:PostRefitLevel:ManualCompactionEnabled",
"BackgroundCallCompaction:0",
},
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
ROCKSDB_NAMESPACE::port::Thread refit_level_thread([&] {
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
});
TEST_SYNC_POINT(
"DBCompactionTest::ChangeLevelConflictsWithManual:"
"PreForegroundCompactRange");
ASSERT_OK(Put(Key(0), rnd.RandomString(990)));
ASSERT_OK(Put(Key(1), rnd.RandomString(990)));
ASSERT_TRUE(dbfull()
->CompactRange(CompactRangeOptions(), nullptr, nullptr)
.IsIncomplete());
refit_level_thread.join();
}
TEST_F(DBCompactionTest, BottomPriCompactionCountsTowardConcurrencyLimit) {
const int kNumL0Files = 4;
const int kNumLevels = 3;
env_->SetBackgroundThreads(1, Env::Priority::BOTTOM);
for (bool universal_reduce_file_locking : {false, true}) {
Options options = CurrentOptions();
options.level0_file_num_compaction_trigger = kNumL0Files;
options.num_levels = kNumLevels;
options.compaction_style = kCompactionStyleUniversal;
options.compaction_options_universal.reduce_file_locking =
universal_reduce_file_locking;
DestroyAndReopen(options);
ASSERT_OK(Put(Key(0), "val"));
ASSERT_OK(Flush());
MoveFilesToLevel(kNumLevels - 1);
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BGWorkBottomCompaction",
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
"PreTriggerCompaction"},
{"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
"PostTriggerCompaction",
"BackgroundCallCompaction:0"}});
SyncPoint::GetInstance()->EnableProcessing();
port::Thread compact_range_thread([&] {
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
cro.exclusive_manual_compaction = false;
ASSERT_OK(dbfull()->CompactRange(cro, nullptr, nullptr));
});
test::SleepingBackgroundTask sleeping_task_low;
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask,
&sleeping_task_low, Env::Priority::LOW);
sleeping_task_low.WaitUntilSleeping();
TEST_SYNC_POINT(
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
"PreTriggerCompaction");
for (int i = 0; i < kNumL0Files; ++i) {
ASSERT_OK(Put(Key(0), "val"));
ASSERT_OK(Flush());
}
ASSERT_EQ(0u, env_->GetThreadPoolQueueLen(Env::Priority::LOW));
TEST_SYNC_POINT(
"DBCompactionTest::BottomPriCompactionCountsTowardConcurrencyLimit:"
"PostTriggerCompaction");
sleeping_task_low.WakeUp();
sleeping_task_low.WaitUntilDone();
compact_range_thread.join();
}
}
TEST_F(DBCompactionTest, BottommostFileCompactionAllowIngestBehind) {
Options options = CurrentOptions();
options.env = env_;
options.compaction_style = kCompactionStyleLevel;
options.allow_ingest_behind = true;
options.comparator = BytewiseComparator();
DestroyAndReopen(options);
WriteOptions write_opts;
ASSERT_OK(db_->Put(write_opts, "infinite", "compaction loop"));
ASSERT_OK(db_->Put(write_opts, "infinite", "loop"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_OK(db_->Put(write_opts, "bumpseqnum", ""));
ASSERT_OK(Flush());
auto snapshot = db_->GetSnapshot();
db_->ReleaseSnapshot(snapshot);
bool compacted = false;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"LevelCompactionPicker::PickCompaction:Return", [&](void* ) {
compacted = true;
});
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
env_->SleepForMicroseconds(2000000);
ASSERT_FALSE(compacted);
}
TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytes) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.allow_ingest_behind = false;
options.level_compaction_dynamic_level_bytes = false;
options.num_levels = 6;
options.compression = kNoCompression;
options.max_bytes_for_level_base = 1 << 20;
options.max_bytes_for_level_multiplier = 10;
DestroyAndReopen(options);
WriteOptions write_opts;
ASSERT_OK(db_->Put(write_opts, Key(1), "val1"));
Random rnd(33);
for (int i = 2; i <= (1 << 10); ++i) {
ASSERT_OK(db_->Put(write_opts, Key(i), rnd.RandomString(2 << 10)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(db_->Put(write_opts, Key(2), "val2"));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(db_->Put(write_opts, Key(1), "new_val1"));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_OK(db_->Put(write_opts, Key(3), "val3"));
ASSERT_OK(Flush());
ASSERT_EQ("1,1,2", FilesPerLevel());
auto verify_db = [&]() {
ASSERT_EQ(Get(Key(1)), "new_val1");
ASSERT_EQ(Get(Key(2)), "val2");
ASSERT_EQ(Get(Key(3)), "val3");
};
verify_db();
options.level_compaction_dynamic_level_bytes = true;
Reopen(options);
ASSERT_EQ("1,0,0,0,1,2", FilesPerLevel());
verify_db();
options.level_compaction_dynamic_level_bytes = false;
Reopen(options);
MoveFilesToLevel(1);
ASSERT_EQ("0,1,0,0,1,2", FilesPerLevel());
verify_db();
options.level_compaction_dynamic_level_bytes = true;
Reopen(options);
ASSERT_EQ(0, NumTableFilesAtLevel(1));
ASSERT_EQ(0, NumTableFilesAtLevel(2));
verify_db();
}
TEST_F(DBCompactionTest, TurnOnLevelCompactionDynamicLevelBytesUCToLC) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleUniversal;
options.allow_ingest_behind = false;
options.level_compaction_dynamic_level_bytes = false;
options.num_levels = 50;
CreateAndReopenWithCF({"pikachu"}, options);
Random rnd(33);
for (int f = 0; f < 10; ++f) {
ASSERT_OK(Put(1, Key(f), rnd.RandomString(1000)));
ASSERT_OK(Flush(1));
}
CompactRangeOptions compact_options;
compact_options.change_level = true;
compact_options.target_level = 1;
ASSERT_OK(db_->CompactRange(compact_options, handles_[1], nullptr, nullptr));
ASSERT_EQ("0,1", FilesPerLevel(1));
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
options.level_compaction_dynamic_level_bytes = true;
ReopenWithColumnFamilies({"default", "pikachu"}, options);
std::string expected_lsm;
for (int i = 0; i < 49; ++i) {
expected_lsm += "0,";
}
expected_lsm += "1";
ASSERT_EQ(expected_lsm, FilesPerLevel(1));
ReopenWithColumnFamilies({"default", "pikachu"}, options);
ASSERT_EQ(expected_lsm, FilesPerLevel(1));
}
TEST_F(DBCompactionTest, DisallowRefitFilesFromNonL0ToL02) {
Options options = CurrentOptions();
options.compaction_style = CompactionStyle::kCompactionStyleLevel;
options.num_levels = 3;
DestroyAndReopen(options);
const Snapshot* s1 = db_->GetSnapshot();
ASSERT_OK(Put("a", "@1"));
ASSERT_OK(Put("k", "@2"));
const Snapshot* s2 = db_->GetSnapshot();
ASSERT_OK(Put("k", "@3"));
ASSERT_OK(Put("z", "v3"));
ASSERT_OK(Flush());
SyncPoint::GetInstance()->SetCallBack(
"CompactionOutputs::ShouldStopBefore::manual_decision",
[options](void* p) {
auto* pair = (std::pair<bool*, const Slice>*)p;
if ((options.comparator->Compare(ExtractUserKey(pair->second), "k") ==
0) &&
(GetInternalKeySeqno(pair->second) == 2)) {
*(pair->first) = true;
}
});
SyncPoint::GetInstance()->EnableProcessing();
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForceOptimized;
cro.change_level = true;
cro.target_level = 2;
Status s = dbfull()->CompactRange(cro, nullptr, nullptr);
ASSERT_OK(s);
ASSERT_EQ("0,0,2", FilesPerLevel());
std::vector<LiveFileMetaData> files;
dbfull()->GetLiveFilesMetaData(&files);
ASSERT_EQ(files.size(), 2);
ASSERT_EQ(files[0].smallestkey, "a");
ASSERT_EQ(files[0].largestkey, "k");
ASSERT_EQ(files[1].smallestkey, "k");
ASSERT_EQ(files[1].largestkey, "z");
CompactRangeOptions cro2;
cro2.change_level = true;
cro2.target_level = 0;
s = dbfull()->CompactRange(cro2, nullptr, nullptr);
ASSERT_TRUE(s.IsAborted());
db_->ReleaseSnapshot(s1);
db_->ReleaseSnapshot(s2);
}
TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterMultiplierChanged) {
const int kBaseLevelBytes = 256 << 10; const int kFileBytes = 64 << 10; const int kInitMultiplier = 2, kChangedMultiplier = 10;
const int kNumFiles = 32;
const int kNumLevels = 5;
const int kValueBytes = 1 << 10;
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = true;
options.max_bytes_for_level_base = kBaseLevelBytes;
options.max_bytes_for_level_multiplier = kInitMultiplier;
options.num_levels = kNumLevels;
Reopen(options);
Random rnd(301);
for (int file = 0; file < kNumFiles; ++file) {
for (int i = 0; i < kFileBytes / kValueBytes; ++i) {
ASSERT_OK(Put(Key(file * kFileBytes / kValueBytes + i),
rnd.RandomString(kValueBytes)));
}
ASSERT_OK(Flush());
}
int init_num_nonempty = 0;
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (int level = 1; level < kNumLevels; ++level) {
if (NumTableFilesAtLevel(level) > 0) {
++init_num_nonempty;
}
}
ASSERT_OK(db_->SetOptions({{"max_bytes_for_level_multiplier",
std::to_string(kChangedMultiplier)}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
int final_num_nonempty = 0;
for (int level = 1; level < kNumLevels; ++level) {
if (NumTableFilesAtLevel(level) > 0) {
++final_num_nonempty;
}
}
ASSERT_GT(init_num_nonempty, final_num_nonempty);
}
TEST_F(DBCompactionTest, DrainUnnecessaryLevelsAfterDBBecomesSmall) {
const int kBaseLevelBytes = 256 << 10; const int kFileBytes = 64 << 10; const int kMultiplier = 2;
const int kNumFiles = 32;
const int kNumLevels = 5;
const int kValueBytes = 1 << 10; const int kDeleteFileNum = 8;
Options options = CurrentOptions();
options.compression = kNoCompression;
options.level_compaction_dynamic_level_bytes = true;
options.max_bytes_for_level_base = kBaseLevelBytes;
options.max_bytes_for_level_multiplier = kMultiplier;
options.num_levels = kNumLevels;
Reopen(options);
Random rnd(301);
for (int file = 0; file < kNumFiles; ++file) {
for (int i = 0; i < kFileBytes / kValueBytes; ++i) {
ASSERT_OK(Put(Key(file * kFileBytes / kValueBytes + i),
rnd.RandomString(kValueBytes)));
}
ASSERT_OK(Flush());
if (file == kDeleteFileNum) {
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ(NumTableFilesAtLevel(kNumLevels - 1), kDeleteFileNum + 1);
}
}
int init_num_nonempty = 0;
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (int level = 1; level < kNumLevels; ++level) {
if (NumTableFilesAtLevel(level) > 0) {
++init_num_nonempty;
}
}
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "true"}}));
std::string begin = Key(0);
std::string end = Key(kDeleteFileNum * kFileBytes / kValueBytes);
ASSERT_OK(
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), begin, end));
Slice begin_slice = begin;
Slice end_slice = end;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &begin_slice, &end_slice));
int after_delete_range_nonempty = 0;
for (int level = 1; level < kNumLevels; ++level) {
if (NumTableFilesAtLevel(level) > 0) {
++after_delete_range_nonempty;
}
}
ASSERT_OK(dbfull()->SetOptions({{"disable_auto_compactions", "false"}}));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
int final_num_nonempty = 0;
for (int level = 1; level < kNumLevels; ++level) {
if (NumTableFilesAtLevel(level) > 0) {
++final_num_nonempty;
}
}
ASSERT_GE(init_num_nonempty, after_delete_range_nonempty);
ASSERT_GT(after_delete_range_nonempty, final_num_nonempty);
}
TEST_F(DBCompactionTest, ManualCompactionCompactAllKeysInRange) {
const int kBaseLevelBytes = 8 << 20; const int kMultiplier = 2;
Options options = CurrentOptions();
options.num_levels = 7;
options.level_compaction_dynamic_level_bytes = false;
options.compaction_style = kCompactionStyleLevel;
options.max_bytes_for_level_base = kBaseLevelBytes;
options.max_bytes_for_level_multiplier = kMultiplier;
options.compression = kNoCompression;
options.target_file_size_base = 2 * kBaseLevelBytes;
DestroyAndReopen(options);
Random rnd(301);
ASSERT_OK(db_->Put(WriteOptions(), Key(1000), rnd.RandomString(100)));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_EQ(1, NumTableFilesAtLevel(2));
ASSERT_OK(
db_->Put(WriteOptions(), Key(5), rnd.RandomString(kBaseLevelBytes / 3)));
ASSERT_OK(
db_->Put(WriteOptions(), Key(6), rnd.RandomString(kBaseLevelBytes / 3)));
ASSERT_OK(Flush());
MoveFilesToLevel(1);
ASSERT_EQ(1, NumTableFilesAtLevel(1));
ASSERT_OK(
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 2)));
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
"DBImpl::RunManualCompaction()::1"}});
SyncPoint::GetInstance()->EnableProcessing();
std::string begin_str = Key(1);
std::string end_str = Key(6);
Slice begin_slice = begin_str;
Slice end_slice = end_str;
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
ASSERT_EQ(NumTableFilesAtLevel(2), 2);
}
TEST_F(DBCompactionTest,
ManualCompactionCompactAllKeysInRangeDynamicLevelBytes) {
const int kBaseLevelBytes = 8 << 20; const int kMultiplier = 2;
Options options = CurrentOptions();
options.num_levels = 7;
options.level_compaction_dynamic_level_bytes = true;
options.compaction_style = kCompactionStyleLevel;
options.max_bytes_for_level_base = kBaseLevelBytes;
options.max_bytes_for_level_multiplier = kMultiplier;
options.compression = kNoCompression;
options.target_file_size_base = 2 * kBaseLevelBytes;
DestroyAndReopen(options);
Random rnd(301);
ASSERT_OK(db_->Put(WriteOptions(), Key(5),
rnd.RandomString(3 * kBaseLevelBytes / 2)));
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
ASSERT_OK(
db_->Put(WriteOptions(), Key(3), rnd.RandomString(kBaseLevelBytes / 3)));
ASSERT_OK(
db_->Put(WriteOptions(), Key(4), rnd.RandomString(kBaseLevelBytes / 3)));
ASSERT_OK(Flush());
MoveFilesToLevel(5);
ASSERT_EQ(1, NumTableFilesAtLevel(5));
ASSERT_OK(
db_->Put(WriteOptions(), Key(1), rnd.RandomString(kBaseLevelBytes / 3)));
ASSERT_OK(
db_->Put(WriteOptions(), Key(2), rnd.RandomString(kBaseLevelBytes / 3)));
SyncPoint::GetInstance()->LoadDependency(
{{"DBImpl::BackgroundCompaction():AfterPickCompaction",
"DBImpl::RunManualCompaction()::1"}});
SyncPoint::GetInstance()->EnableProcessing();
CompactRangeOptions cro;
cro.bottommost_level_compaction = BottommostLevelCompaction::kForce;
std::string begin_str = Key(1);
std::string end_str = Key(4);
Slice begin_slice = begin_str;
Slice end_slice = end_str;
ASSERT_OK(db_->CompactRange(cro, &begin_slice, &end_slice));
ASSERT_EQ(2, NumTableFilesAtLevel(6));
ASSERT_EQ(0, NumTableFilesAtLevel(5));
}
TEST_F(DBCompactionTest, NumberOfSubcompactions) {
class SubCompactionEventListener : public EventListener {
public:
void OnSubcompactionCompleted(const SubcompactionJobInfo&) override {
sub_compaction_finished_++;
}
void OnCompactionCompleted(DB*, const CompactionJobInfo&) override {
compaction_finished_++;
}
std::atomic<int> sub_compaction_finished_{0};
std::atomic<int> compaction_finished_{0};
};
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.compression = kNoCompression;
const int kFileSize = 100 << 10; options.target_file_size_base = kFileSize;
const int kLevel0CompactTrigger = 2;
options.level0_file_num_compaction_trigger = kLevel0CompactTrigger;
Destroy(options);
Random rnd(301);
const int kValueSize = 500;
const int kNumKeyPerFile = 1000;
for (int i = 1; i <= 8; ++i) {
options.max_subcompactions = i;
SubCompactionEventListener* listener = new SubCompactionEventListener();
options.listeners.clear();
options.listeners.emplace_back(listener);
ASSERT_OK(TryReopen(options));
for (int file = 0; file < kLevel0CompactTrigger; ++file) {
for (int key = file; key < 2 * kNumKeyPerFile; key += 2) {
ASSERT_OK(Put(Key(key), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(listener->compaction_finished_, 1);
EXPECT_EQ(listener->sub_compaction_finished_, i);
Destroy(options);
}
}
TEST_F(DBCompactionTest, VerifyInputRecordCount) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
options.compaction_verify_record_count = true;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 1; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
for (int i = 0; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
int num_iter = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"CompactionJob::ProcessKeyValueCompaction()::stop", [&](void* stop_ptr) {
num_iter++;
if (num_iter == 10) {
*(bool*)stop_ptr = true;
}
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.IsCorruption());
const char* expect =
"Compaction number of input keys does not match number of keys "
"processed.";
ASSERT_TRUE(std::strstr(s.getState(), expect));
}
TEST_F(DBCompactionTest, VerifyOutputRecordCountBlockBasedTable) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
options.compaction_verify_record_count = true;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 1; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
ASSERT_OK(db_->DeleteRange(WriteOptions(), Key(10), Key(15)));
for (int i = 0; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
int num_iter = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"BlockBasedTableBuilder::Add::skip", [&](void* skip) {
num_iter++;
if (num_iter % 7 == 0) {
*(bool*)skip = true;
}
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.IsCorruption());
const char* expect =
"Number of keys in compaction output SST files does not match number of "
"keys added.";
ASSERT_TRUE(std::strstr(s.getState(), expect));
}
TEST_F(DBCompactionTest, VerifyOutputRecordCountPlainTable) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
options.level0_file_num_compaction_trigger = 3;
options.compaction_verify_record_count = true;
PlainTableOptions plain_table_options;
plain_table_options.user_key_len = 0;
plain_table_options.bloom_bits_per_key = 2;
plain_table_options.hash_table_ratio = 0.8;
plain_table_options.index_sparseness = 3;
plain_table_options.huge_page_tlb_size = 0;
plain_table_options.encoding_type = kPrefix;
plain_table_options.full_scan_mode = false;
plain_table_options.store_index_in_file = false;
options.table_factory.reset(NewPlainTableFactory(plain_table_options));
options.memtable_factory.reset(NewHashLinkListRepFactory(4, 0, 3, true));
options.prefix_extractor.reset(NewFixedPrefixTransform(8));
options.allow_mmap_reads = false;
options.allow_concurrent_memtable_write = false;
options.unordered_write = false;
DestroyAndReopen(options);
Random rnd(301);
for (int i = 1; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
for (int i = 0; i < 20; i += 2) {
ASSERT_OK(Put(Key(i), rnd.RandomString(100)));
}
ASSERT_OK(Flush());
int num_iter = 0;
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
"PlainTableBuilder::Add::skip", [&](void* skip) {
num_iter++;
if (num_iter % 7 == 0) {
*(bool*)skip = true;
}
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_TRUE(s.IsCorruption());
const char* expect =
"Number of keys in compaction output SST files does not match number of "
"keys added.";
ASSERT_TRUE(std::strstr(s.getState(), expect));
}
TEST_F(DBCompactionTest, ErrorWhenReadFileHead) {
Options opts = CurrentOptions();
opts.num_levels = 7;
opts.compression = kNoCompression;
DestroyAndReopen(opts);
Random rnd(301);
const int kValLen = 100;
for (int error_file = 1; error_file <= 3; ++error_file) {
for (int i = 50; i < 150; ++i) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValLen)));
}
ASSERT_OK(Flush());
MoveFilesToLevel(6);
std::vector<std::string> values;
for (int i = 0; i < 100; ++i) {
values.emplace_back(rnd.RandomString(kValLen));
ASSERT_OK(Put(Key(i), values.back()));
}
ASSERT_OK(Flush());
MoveFilesToLevel(5);
for (int i = 100; i < 200; ++i) {
values.emplace_back(rnd.RandomString(kValLen));
ASSERT_OK(Put(Key(i), values.back()));
}
ASSERT_OK(Flush());
MoveFilesToLevel(5);
ASSERT_EQ(2, NumTableFilesAtLevel(5));
ASSERT_EQ(1, NumTableFilesAtLevel(6));
std::atomic_int count = 0;
SyncPoint::GetInstance()->SetCallBack(
"RandomAccessFileReader::Read::BeforeReturn",
[&count, &error_file](void* pair_ptr) {
auto p = static_cast<std::pair<std::string*, IOStatus*>*>(pair_ptr);
int cur = ++count;
if (cur == error_file) {
IOStatus* io_s = p->second;
*io_s = IOStatus::IOError();
io_s->SetRetryable(true);
}
});
SyncPoint::GetInstance()->EnableProcessing();
Status s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
PinnableSlice slice;
for (int i = 0; i < 200; ++i) {
ASSERT_OK(Get(Key(i), &slice));
ASSERT_EQ(slice, values[i]);
}
ASSERT_NOK(s);
ASSERT_TRUE(s.IsIOError());
s = db_->CompactRange(CompactRangeOptions(), nullptr, nullptr);
ASSERT_OK(s);
for (int i = 0; i < 200; ++i) {
ASSERT_OK(Get(Key(i), &slice));
ASSERT_EQ(slice, values[i]);
}
SyncPoint::GetInstance()->DisableProcessing();
DestroyAndReopen(opts);
}
}
TEST_F(DBCompactionTest, ReleaseCompactionDuringManifestWrite) {
Options options = CurrentOptions();
options.compaction_style = kCompactionStyleLevel;
env_->SetBackgroundThreads(3, Env::Priority::LOW);
env_->SetBackgroundThreads(3, Env::Priority::BOTTOM);
options.max_background_compactions = 3;
options.num_levels = 4;
DestroyAndReopen(options);
Random rnd(301);
ASSERT_OK(Put(Key(1), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(3);
ASSERT_OK(Put(Key(10), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(3);
ASSERT_OK(Put(Key(100), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(3);
ASSERT_OK(Put(Key(100), rnd.RandomString(20)));
ASSERT_OK(Put(Key(101), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(Put(Key(1), rnd.RandomString(20)));
ASSERT_OK(Put(Key(2), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_OK(Put(Key(10), rnd.RandomString(20)));
ASSERT_OK(Put(Key(11), rnd.RandomString(20)));
ASSERT_OK(Flush());
MoveFilesToLevel(2);
ASSERT_EQ(NumTableFilesAtLevel(1), 0);
ASSERT_EQ(NumTableFilesAtLevel(2), 3);
ASSERT_EQ(NumTableFilesAtLevel(3), 3);
SyncPoint::GetInstance()->ClearAllCallBacks();
std::atomic_int count = 0;
SyncPoint::GetInstance()->SetCallBack(
"VersionSet::LogAndApply:BeforeWriterWaiting", [&](void*) {
int c = count.fetch_add(1);
if (c == 2) {
TEST_SYNC_POINT("all threads to enter LogAndApply");
}
});
SyncPoint::GetInstance()->LoadDependency(
{{"all threads to enter LogAndApply",
"VersionSet::LogAndApply:WriteManifestStart"}});
std::atomic_int after_compact_count = 0;
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::BackgroundCompaction:AfterCompaction", [&](void* ptr) {
int c = after_compact_count.fetch_add(1);
if (c > 0) {
ColumnFamilyData* cfd = (ColumnFamilyData*)(ptr);
ASSERT_TRUE(
cfd->compaction_picker()->compactions_in_progress()->empty());
}
});
SyncPoint::GetInstance()->EnableProcessing();
std::vector<std::thread> threads;
threads.emplace_back([&]() {
std::string k1_str = Key(1);
std::string k2_str = Key(2);
Slice k1 = k1_str;
Slice k2 = k2_str;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k1, &k2));
});
threads.emplace_back([&]() {
std::string k10_str = Key(10);
std::string k11_str = Key(11);
Slice k10 = k10_str;
Slice k11 = k11_str;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k10, &k11));
});
std::string k100_str = Key(100);
std::string k101_str = Key(101);
Slice k100 = k100_str;
Slice k101 = k101_str;
ASSERT_OK(db_->CompactRange(CompactRangeOptions(), &k100, &k101));
for (auto& thread : threads) {
thread.join();
}
SyncPoint::GetInstance()->DisableProcessing();
SyncPoint::GetInstance()->ClearAllCallBacks();
}
TEST_F(DBCompactionTest, RecordNewestKeyTimeForTtlCompaction) {
Options options;
SetTimeElapseOnlySleepOnReopen(&options);
options.env = CurrentOptions().env;
options.compaction_style = kCompactionStyleFIFO;
options.statistics = ROCKSDB_NAMESPACE::CreateDBStatistics();
options.write_buffer_size = 10 << 10; options.arena_block_size = 4096;
options.compression = kNoCompression;
options.create_if_missing = true;
options.compaction_options_fifo.allow_compaction = false;
options.num_levels = 1;
env_->SetMockSleep();
options.env = env_;
options.ttl = 1 * 60 * 60; ASSERT_OK(TryReopen(options));
Random rnd(301);
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 10; j++) {
ASSERT_OK(Put(std::to_string(i * 20 + j), rnd.RandomString(980)));
}
ASSERT_OK(Flush());
env_->MockSleepForSeconds(5);
}
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 4);
std::vector<FileMetaData*> file_metadatas = GetLevelFileMetadatas(0);
ASSERT_EQ(file_metadatas.size(), 4);
uint64_t first_newest_key_time =
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time;
ASSERT_NE(first_newest_key_time, kUnknownNewestKeyTime);
uint64_t prev_newest_key_time = first_newest_key_time;
for (size_t idx = 1; idx < file_metadatas.size(); idx++) {
uint64_t newest_key_time = file_metadatas[idx]
->fd.table_reader->GetTableProperties()
->newest_key_time;
ASSERT_LT(newest_key_time, prev_newest_key_time);
prev_newest_key_time = newest_key_time;
ASSERT_EQ(newest_key_time, file_metadatas[idx]
->fd.table_reader->GetTableProperties()
->creation_time);
}
uint64_t last_newest_key_time = prev_newest_key_time;
ASSERT_EQ(15, first_newest_key_time - last_newest_key_time);
options.compaction_options_fifo.allow_compaction = true;
ASSERT_OK(TryReopen(options));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
file_metadatas = GetLevelFileMetadatas(0);
ASSERT_EQ(file_metadatas.size(), 1);
ASSERT_EQ(
file_metadatas[0]->fd.table_reader->GetTableProperties()->newest_key_time,
first_newest_key_time);
ASSERT_EQ(
file_metadatas[0]->fd.table_reader->GetTableProperties()->creation_time,
last_newest_key_time);
ASSERT_EQ(file_metadatas[0]->oldest_ancester_time, last_newest_key_time);
env_->MockSleepForSeconds(6);
ASSERT_OK(dbfull()->SetOptions({{"ttl", "15"}}));
ASSERT_EQ(dbfull()->GetOptions().ttl, 15);
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 1);
ASSERT_OK(dbfull()->SetOptions({{"ttl", "5"}}));
ASSERT_EQ(dbfull()->GetOptions().ttl, 5);
ASSERT_OK(dbfull()->CompactRange(CompactRangeOptions(), nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
ASSERT_EQ(NumTableFilesAtLevel(0), 0);
}
TEST_F(DBCompactionTest, CompactionRespectsTargetSizeWithTailEstimation) {
const int kInitialKeyCount = 10000; const int kValueSize = 100; const int kSeed = 301;
Options options = CurrentOptions();
options.target_file_size_is_upper_bound = true;
options.target_file_size_base = 256 * 1024;
options.write_buffer_size = 2 * 1024 * 1024;
options.level0_file_num_compaction_trigger = 100; options.compression = kNoCompression;
BlockBasedTableOptions table_options;
table_options.partition_filters = true;
table_options.metadata_block_size = 4 * 1024;
table_options.index_type = BlockBasedTableOptions::kBinarySearch;
table_options.filter_policy.reset(NewBloomFilterPolicy(10));
options.table_factory.reset(NewBlockBasedTableFactory(table_options));
DestroyAndReopen(options);
Random rnd(kSeed);
for (int i = 0; i < kInitialKeyCount; i++) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
for (int i = kInitialKeyCount / 2; i < kInitialKeyCount * 1.5; i++) {
ASSERT_OK(Put(Key(i), rnd.RandomString(kValueSize)));
}
ASSERT_OK(Flush());
std::vector<LiveFileMetaData> file_metadata;
db_->GetLiveFilesMetaData(&file_metadata);
ASSERT_EQ(file_metadata.size(), 2);
for (const auto& file : file_metadata) {
ASSERT_EQ(file.level, 0);
};
CompactRangeOptions cro;
cro.change_level = true;
cro.target_level = 1;
ASSERT_OK(db_->CompactRange(cro, nullptr, nullptr));
ASSERT_OK(dbfull()->TEST_WaitForCompact());
for (const auto& file : file_metadata) {
if (file.level > 0) {
EXPECT_LE(file.size, options.target_file_size_base)
<< "Output file size exceeds target size: " << " File: " << file.name
<< " level: " << file.level << " File size: " << file.size
<< " Target size: " << options.target_file_size_base;
}
}
}
class PeriodicCompactionListener : public EventListener {
public:
explicit PeriodicCompactionListener() {}
void OnCompactionBegin(DB* , const CompactionJobInfo& ci) override {
if (ci.compaction_reason == CompactionReason::kPeriodicCompaction) {
++num_periodic_compactions;
}
}
std::atomic<int> num_periodic_compactions = 0;
};
TEST_F(DBCompactionTest, PeriodicTask) {
auto mock_clock = std::make_shared<MockSystemClock>(env_->GetSystemClock());
mock_clock->SetCurrentTime(100);
mock_clock->InstallTimedWaitFixCallback();
auto mock_env = std::make_unique<CompositeEnvWrapper>(env_, mock_clock);
SyncPoint::GetInstance()->SetCallBack(
"DBImpl::StartPeriodicTaskScheduler:Init", [&](void* arg) {
auto periodic_task_scheduler_ptr =
static_cast<PeriodicTaskScheduler*>(arg);
periodic_task_scheduler_ptr->TEST_OverrideTimer(mock_clock.get());
});
Options options;
options.env = mock_env.get();
options.compaction_style = kCompactionStyleUniversal;
options.statistics = CreateDBStatistics();
int kPeriodicCompactionSeconds = 7 * 24 * 60 * 60; options.periodic_compaction_seconds = kPeriodicCompactionSeconds;
options.num_levels = 50;
auto listener = std::make_shared<PeriodicCompactionListener>();
options.listeners.push_back(listener);
ASSERT_OK(TryReopen(options));
Random* rnd = Random::GetTLSInstance();
for (int k = 0; k < 10; ++k) {
ASSERT_OK(Put(Key(k), rnd->RandomString(100)));
}
ASSERT_OK(Flush());
ASSERT_OK(db_->CompactRange({}, nullptr, nullptr));
ASSERT_EQ(1, NumTableFilesAtLevel(49));
dbfull()->TEST_WaitForPeriodicTaskRun(
[&] { mock_clock->MockSleepForSeconds(kPeriodicCompactionSeconds + 1); });
ASSERT_OK(db_->WaitForCompact({}));
ASSERT_EQ(listener->num_periodic_compactions, 1);
Close();
}
}
int main(int argc, char** argv) {
ROCKSDB_NAMESPACE::port::InstallStackTraceHandler();
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}