|
|
@ -25,9 +25,13 @@ class MemTableListTest : public testing::Test { |
|
|
|
std::string dbname; |
|
|
|
std::string dbname; |
|
|
|
DB* db; |
|
|
|
DB* db; |
|
|
|
Options options; |
|
|
|
Options options; |
|
|
|
|
|
|
|
std::vector<ColumnFamilyHandle*> handles; |
|
|
|
|
|
|
|
std::atomic<uint64_t> file_number; |
|
|
|
|
|
|
|
|
|
|
|
MemTableListTest() : db(nullptr) { |
|
|
|
MemTableListTest() : db(nullptr), file_number(1) { |
|
|
|
dbname = test::PerThreadDBPath("memtable_list_test"); |
|
|
|
dbname = test::PerThreadDBPath("memtable_list_test"); |
|
|
|
|
|
|
|
options.create_if_missing = true; |
|
|
|
|
|
|
|
DestroyDB(dbname, options); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Create a test db if not yet created
|
|
|
|
// Create a test db if not yet created
|
|
|
@ -35,15 +39,45 @@ class MemTableListTest : public testing::Test { |
|
|
|
if (db == nullptr) { |
|
|
|
if (db == nullptr) { |
|
|
|
options.create_if_missing = true; |
|
|
|
options.create_if_missing = true; |
|
|
|
DestroyDB(dbname, options); |
|
|
|
DestroyDB(dbname, options); |
|
|
|
Status s = DB::Open(options, dbname, &db); |
|
|
|
// Open DB only with default column family
|
|
|
|
|
|
|
|
ColumnFamilyOptions cf_options; |
|
|
|
|
|
|
|
std::vector<ColumnFamilyDescriptor> cf_descs; |
|
|
|
|
|
|
|
cf_descs.emplace_back(kDefaultColumnFamilyName, cf_options); |
|
|
|
|
|
|
|
Status s = DB::Open(options, dbname, cf_descs, &handles, &db); |
|
|
|
EXPECT_OK(s); |
|
|
|
EXPECT_OK(s); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ColumnFamilyOptions cf_opt1, cf_opt2; |
|
|
|
|
|
|
|
cf_opt1.cf_paths.emplace_back(dbname + "_one_1", |
|
|
|
|
|
|
|
std::numeric_limits<uint64_t>::max()); |
|
|
|
|
|
|
|
cf_opt2.cf_paths.emplace_back(dbname + "_two_1", |
|
|
|
|
|
|
|
std::numeric_limits<uint64_t>::max()); |
|
|
|
|
|
|
|
int sz = static_cast<int>(handles.size()); |
|
|
|
|
|
|
|
handles.resize(sz + 2); |
|
|
|
|
|
|
|
s = db->CreateColumnFamily(cf_opt1, "one", &handles[1]); |
|
|
|
|
|
|
|
EXPECT_OK(s); |
|
|
|
|
|
|
|
s = db->CreateColumnFamily(cf_opt2, "two", &handles[2]); |
|
|
|
|
|
|
|
EXPECT_OK(s); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cf_descs.emplace_back("one", cf_options); |
|
|
|
|
|
|
|
cf_descs.emplace_back("two", cf_options); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
~MemTableListTest() { |
|
|
|
~MemTableListTest() { |
|
|
|
if (db) { |
|
|
|
if (db) { |
|
|
|
|
|
|
|
std::vector<ColumnFamilyDescriptor> cf_descs(handles.size()); |
|
|
|
|
|
|
|
for (int i = 0; i != static_cast<int>(handles.size()); ++i) { |
|
|
|
|
|
|
|
handles[i]->GetDescriptor(&cf_descs[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (auto h : handles) { |
|
|
|
|
|
|
|
if (h) { |
|
|
|
|
|
|
|
db->DestroyColumnFamilyHandle(h); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
handles.clear(); |
|
|
|
delete db; |
|
|
|
delete db; |
|
|
|
DestroyDB(dbname, options); |
|
|
|
db = nullptr; |
|
|
|
|
|
|
|
DestroyDB(dbname, options, cf_descs); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -52,10 +86,26 @@ class MemTableListTest : public testing::Test { |
|
|
|
Status Mock_InstallMemtableFlushResults( |
|
|
|
Status Mock_InstallMemtableFlushResults( |
|
|
|
MemTableList* list, const MutableCFOptions& mutable_cf_options, |
|
|
|
MemTableList* list, const MutableCFOptions& mutable_cf_options, |
|
|
|
const autovector<MemTable*>& m, autovector<MemTable*>* to_delete) { |
|
|
|
const autovector<MemTable*>& m, autovector<MemTable*>* to_delete) { |
|
|
|
|
|
|
|
autovector<MemTableList*> lists; |
|
|
|
|
|
|
|
lists.emplace_back(list); |
|
|
|
|
|
|
|
autovector<const autovector<MemTable*>*> mems_list; |
|
|
|
|
|
|
|
mems_list.emplace_back(&m); |
|
|
|
|
|
|
|
return Mock_InstallMemtableFlushResults( |
|
|
|
|
|
|
|
lists, {0} /* cf_ids */, {&mutable_cf_options}, mems_list, to_delete); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Calls MemTableList::InstallMemtableFlushResults() and sets up all
|
|
|
|
|
|
|
|
// structures needed to call this function.
|
|
|
|
|
|
|
|
Status Mock_InstallMemtableFlushResults( |
|
|
|
|
|
|
|
autovector<MemTableList*>& lists, const autovector<uint32_t>& cf_ids, |
|
|
|
|
|
|
|
const autovector<const MutableCFOptions*>& mutable_cf_options_list, |
|
|
|
|
|
|
|
const autovector<const autovector<MemTable*>*>& mems_list, |
|
|
|
|
|
|
|
autovector<MemTable*>* to_delete) { |
|
|
|
// Create a mock Logger
|
|
|
|
// Create a mock Logger
|
|
|
|
test::NullLogger logger; |
|
|
|
test::NullLogger logger; |
|
|
|
LogBuffer log_buffer(DEBUG_LEVEL, &logger); |
|
|
|
LogBuffer log_buffer(DEBUG_LEVEL, &logger); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CreateDB(); |
|
|
|
// Create a mock VersionSet
|
|
|
|
// Create a mock VersionSet
|
|
|
|
DBOptions db_options; |
|
|
|
DBOptions db_options; |
|
|
|
ImmutableDBOptions immutable_db_options(db_options); |
|
|
|
ImmutableDBOptions immutable_db_options(db_options); |
|
|
@ -64,28 +114,58 @@ class MemTableListTest : public testing::Test { |
|
|
|
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size); |
|
|
|
WriteBufferManager write_buffer_manager(db_options.db_write_buffer_size); |
|
|
|
WriteController write_controller(10000000u); |
|
|
|
WriteController write_controller(10000000u); |
|
|
|
|
|
|
|
|
|
|
|
CreateDB(); |
|
|
|
|
|
|
|
VersionSet versions(dbname, &immutable_db_options, env_options, |
|
|
|
VersionSet versions(dbname, &immutable_db_options, env_options, |
|
|
|
table_cache.get(), &write_buffer_manager, |
|
|
|
table_cache.get(), &write_buffer_manager, |
|
|
|
&write_controller); |
|
|
|
&write_controller); |
|
|
|
|
|
|
|
std::vector<ColumnFamilyDescriptor> cf_descs; |
|
|
|
|
|
|
|
cf_descs.emplace_back(kDefaultColumnFamilyName, ColumnFamilyOptions()); |
|
|
|
|
|
|
|
cf_descs.emplace_back("one", ColumnFamilyOptions()); |
|
|
|
|
|
|
|
cf_descs.emplace_back("two", ColumnFamilyOptions()); |
|
|
|
|
|
|
|
EXPECT_OK(versions.Recover(cf_descs, false)); |
|
|
|
|
|
|
|
|
|
|
|
// Create mock default ColumnFamilyData
|
|
|
|
// Create mock default ColumnFamilyData
|
|
|
|
ColumnFamilyOptions cf_options; |
|
|
|
|
|
|
|
std::vector<ColumnFamilyDescriptor> column_families; |
|
|
|
|
|
|
|
column_families.emplace_back(kDefaultColumnFamilyName, cf_options); |
|
|
|
|
|
|
|
EXPECT_OK(versions.Recover(column_families, false)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto column_family_set = versions.GetColumnFamilySet(); |
|
|
|
auto column_family_set = versions.GetColumnFamilySet(); |
|
|
|
auto cfd = column_family_set->GetColumnFamily(0); |
|
|
|
|
|
|
|
EXPECT_TRUE(cfd != nullptr); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create dummy mutex.
|
|
|
|
LogsWithPrepTracker dummy_prep_tracker; |
|
|
|
|
|
|
|
if (1 == cf_ids.size()) { |
|
|
|
|
|
|
|
auto cfd = column_family_set->GetColumnFamily(cf_ids[0]); |
|
|
|
|
|
|
|
EXPECT_TRUE(nullptr != cfd); |
|
|
|
|
|
|
|
EXPECT_EQ(1, lists.size()); |
|
|
|
|
|
|
|
MemTableList* list = lists[0]; |
|
|
|
|
|
|
|
EXPECT_EQ(1, mutable_cf_options_list.size()); |
|
|
|
|
|
|
|
const MutableCFOptions& mutable_cf_options = |
|
|
|
|
|
|
|
*(mutable_cf_options_list.at(0)); |
|
|
|
|
|
|
|
const autovector<MemTable*>* mems = mems_list.at(0); |
|
|
|
|
|
|
|
EXPECT_TRUE(nullptr != mems); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
uint64_t file_num = file_number.fetch_add(1); |
|
|
|
|
|
|
|
// Create dummy mutex.
|
|
|
|
|
|
|
|
InstrumentedMutex mutex; |
|
|
|
|
|
|
|
InstrumentedMutexLock l(&mutex); |
|
|
|
|
|
|
|
return list->TryInstallMemtableFlushResults( |
|
|
|
|
|
|
|
cfd, mutable_cf_options, *mems, &dummy_prep_tracker, &versions, |
|
|
|
|
|
|
|
&mutex, file_num, to_delete, nullptr, &log_buffer); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
autovector<ColumnFamilyData*> cfds; |
|
|
|
|
|
|
|
for (int i = 0; i != static_cast<int>(cf_ids.size()); ++i) { |
|
|
|
|
|
|
|
cfds.emplace_back(column_family_set->GetColumnFamily(cf_ids[i])); |
|
|
|
|
|
|
|
EXPECT_NE(nullptr, cfds[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
autovector<FileMetaData> file_metas; |
|
|
|
|
|
|
|
for (size_t i = 0; i != cf_ids.size(); ++i) { |
|
|
|
|
|
|
|
FileMetaData meta; |
|
|
|
|
|
|
|
uint64_t file_num = file_number.fetch_add(1); |
|
|
|
|
|
|
|
meta.fd = FileDescriptor(file_num, 0, 0); |
|
|
|
|
|
|
|
file_metas.emplace_back(meta); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
bool atomic_flush_commit_in_progress = false; |
|
|
|
InstrumentedMutex mutex; |
|
|
|
InstrumentedMutex mutex; |
|
|
|
InstrumentedMutexLock l(&mutex); |
|
|
|
InstrumentedMutexLock l(&mutex); |
|
|
|
LogsWithPrepTracker dummy_prep_tracker; |
|
|
|
return MemTableList::TryInstallMemtableFlushResults( |
|
|
|
return list->TryInstallMemtableFlushResults( |
|
|
|
lists, cfds, mutable_cf_options_list, mems_list, |
|
|
|
cfd, mutable_cf_options, m, &dummy_prep_tracker, &versions, &mutex, 1, |
|
|
|
&atomic_flush_commit_in_progress, &dummy_prep_tracker, &versions, |
|
|
|
to_delete, nullptr, &log_buffer); |
|
|
|
&mutex, file_metas, to_delete, nullptr, &log_buffer); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -98,7 +178,7 @@ TEST_F(MemTableListTest, Empty) { |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
|
|
|
|
|
|
|
|
autovector<MemTable*> mems; |
|
|
|
autovector<MemTable*> mems; |
|
|
|
list.PickMemtablesToFlush(&mems); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &mems); |
|
|
|
ASSERT_EQ(0, mems.size()); |
|
|
|
ASSERT_EQ(0, mems.size()); |
|
|
|
|
|
|
|
|
|
|
|
autovector<MemTable*> to_delete; |
|
|
|
autovector<MemTable*> to_delete; |
|
|
@ -281,11 +361,12 @@ TEST_F(MemTableListTest, GetFromHistoryTest) { |
|
|
|
// Flush this memtable from the list.
|
|
|
|
// Flush this memtable from the list.
|
|
|
|
// (It will then be a part of the memtable history).
|
|
|
|
// (It will then be a part of the memtable history).
|
|
|
|
autovector<MemTable*> to_flush; |
|
|
|
autovector<MemTable*> to_flush; |
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(1, to_flush.size()); |
|
|
|
ASSERT_EQ(1, to_flush.size()); |
|
|
|
|
|
|
|
|
|
|
|
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options), |
|
|
|
MutableCFOptions mutable_cf_options(options); |
|
|
|
to_flush, &to_delete); |
|
|
|
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush, |
|
|
|
|
|
|
|
&to_delete); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_EQ(0, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(0, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(1, list.NumFlushed()); |
|
|
|
ASSERT_EQ(1, list.NumFlushed()); |
|
|
@ -330,12 +411,12 @@ TEST_F(MemTableListTest, GetFromHistoryTest) { |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
|
|
|
|
|
|
|
|
to_flush.clear(); |
|
|
|
to_flush.clear(); |
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(1, to_flush.size()); |
|
|
|
ASSERT_EQ(1, to_flush.size()); |
|
|
|
|
|
|
|
|
|
|
|
// Flush second memtable
|
|
|
|
// Flush second memtable
|
|
|
|
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options), |
|
|
|
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush, |
|
|
|
to_flush, &to_delete); |
|
|
|
&to_delete); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_EQ(0, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(0, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(2, list.NumFlushed()); |
|
|
|
ASSERT_EQ(2, list.NumFlushed()); |
|
|
@ -396,7 +477,7 @@ TEST_F(MemTableListTest, GetFromHistoryTest) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
const int num_tables = 5; |
|
|
|
const int num_tables = 6; |
|
|
|
SequenceNumber seq = 1; |
|
|
|
SequenceNumber seq = 1; |
|
|
|
Status s; |
|
|
|
Status s; |
|
|
|
|
|
|
|
|
|
|
@ -414,11 +495,13 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
max_write_buffer_number_to_maintain); |
|
|
|
max_write_buffer_number_to_maintain); |
|
|
|
|
|
|
|
|
|
|
|
// Create some MemTables
|
|
|
|
// Create some MemTables
|
|
|
|
|
|
|
|
uint64_t memtable_id = 0; |
|
|
|
std::vector<MemTable*> tables; |
|
|
|
std::vector<MemTable*> tables; |
|
|
|
MutableCFOptions mutable_cf_options(options); |
|
|
|
MutableCFOptions mutable_cf_options(options); |
|
|
|
for (int i = 0; i < num_tables; i++) { |
|
|
|
for (int i = 0; i < num_tables; i++) { |
|
|
|
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb, |
|
|
|
MemTable* mem = new MemTable(cmp, ioptions, mutable_cf_options, &wb, |
|
|
|
kMaxSequenceNumber, 0 /* column_family_id */); |
|
|
|
kMaxSequenceNumber, 0 /* column_family_id */); |
|
|
|
|
|
|
|
mem->SetID(memtable_id++); |
|
|
|
mem->Ref(); |
|
|
|
mem->Ref(); |
|
|
|
|
|
|
|
|
|
|
|
std::string value; |
|
|
|
std::string value; |
|
|
@ -437,7 +520,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
autovector<MemTable*> to_flush; |
|
|
|
autovector<MemTable*> to_flush; |
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(0, to_flush.size()); |
|
|
|
ASSERT_EQ(0, to_flush.size()); |
|
|
|
|
|
|
|
|
|
|
|
// Request a flush even though there is nothing to flush
|
|
|
|
// Request a flush even though there is nothing to flush
|
|
|
@ -446,7 +529,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
|
|
|
|
// Attempt to 'flush' to clear request for flush
|
|
|
|
// Attempt to 'flush' to clear request for flush
|
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(0, to_flush.size()); |
|
|
|
ASSERT_EQ(0, to_flush.size()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
@ -470,7 +553,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush
|
|
|
|
// Pick tables to flush
|
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(2, to_flush.size()); |
|
|
|
ASSERT_EQ(2, to_flush.size()); |
|
|
|
ASSERT_EQ(2, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(2, list.NumNotFlushed()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
@ -491,7 +574,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush
|
|
|
|
// Pick tables to flush
|
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
ASSERT_EQ(3, to_flush.size()); |
|
|
|
ASSERT_EQ(3, to_flush.size()); |
|
|
|
ASSERT_EQ(3, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(3, list.NumNotFlushed()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
@ -499,7 +582,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush again
|
|
|
|
// Pick tables to flush again
|
|
|
|
autovector<MemTable*> to_flush2; |
|
|
|
autovector<MemTable*> to_flush2; |
|
|
|
list.PickMemtablesToFlush(&to_flush2); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush2); |
|
|
|
ASSERT_EQ(0, to_flush2.size()); |
|
|
|
ASSERT_EQ(0, to_flush2.size()); |
|
|
|
ASSERT_EQ(3, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(3, list.NumNotFlushed()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
@ -517,7 +600,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush again
|
|
|
|
// Pick tables to flush again
|
|
|
|
list.PickMemtablesToFlush(&to_flush2); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush2); |
|
|
|
ASSERT_EQ(1, to_flush2.size()); |
|
|
|
ASSERT_EQ(1, to_flush2.size()); |
|
|
|
ASSERT_EQ(4, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(4, list.NumNotFlushed()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
@ -538,7 +621,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
ASSERT_EQ(0, to_delete.size()); |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush
|
|
|
|
// Pick tables to flush
|
|
|
|
list.PickMemtablesToFlush(&to_flush); |
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush); |
|
|
|
// Should pick 4 of 5 since 1 table has been picked in to_flush2
|
|
|
|
// Should pick 4 of 5 since 1 table has been picked in to_flush2
|
|
|
|
ASSERT_EQ(4, to_flush.size()); |
|
|
|
ASSERT_EQ(4, to_flush.size()); |
|
|
|
ASSERT_EQ(5, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(5, list.NumNotFlushed()); |
|
|
@ -547,14 +630,15 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush again
|
|
|
|
// Pick tables to flush again
|
|
|
|
autovector<MemTable*> to_flush3; |
|
|
|
autovector<MemTable*> to_flush3; |
|
|
|
|
|
|
|
list.PickMemtablesToFlush(nullptr /* memtable_id */, &to_flush3); |
|
|
|
ASSERT_EQ(0, to_flush3.size()); // nothing not in progress of being flushed
|
|
|
|
ASSERT_EQ(0, to_flush3.size()); // nothing not in progress of being flushed
|
|
|
|
ASSERT_EQ(5, list.NumNotFlushed()); |
|
|
|
ASSERT_EQ(5, list.NumNotFlushed()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
|
|
|
|
// Flush the 4 memtables that were picked in to_flush
|
|
|
|
// Flush the 4 memtables that were picked in to_flush
|
|
|
|
s = Mock_InstallMemtableFlushResults(&list, MutableCFOptions(options), |
|
|
|
s = Mock_InstallMemtableFlushResults(&list, mutable_cf_options, to_flush, |
|
|
|
to_flush, &to_delete); |
|
|
|
&to_delete); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_OK(s); |
|
|
|
|
|
|
|
|
|
|
|
// Note: now to_flush contains tables[0,1,2,4]. to_flush2 contains
|
|
|
|
// Note: now to_flush contains tables[0,1,2,4]. to_flush2 contains
|
|
|
@ -574,7 +658,7 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
|
|
|
|
|
|
|
|
// Flush the 1 memtable that was picked in to_flush2
|
|
|
|
// Flush the 1 memtable that was picked in to_flush2
|
|
|
|
s = MemTableListTest::Mock_InstallMemtableFlushResults( |
|
|
|
s = MemTableListTest::Mock_InstallMemtableFlushResults( |
|
|
|
&list, MutableCFOptions(options), to_flush2, &to_delete); |
|
|
|
&list, mutable_cf_options, to_flush2, &to_delete); |
|
|
|
ASSERT_OK(s); |
|
|
|
ASSERT_OK(s); |
|
|
|
|
|
|
|
|
|
|
|
// This will actually install 2 tables. The 1 we told it to flush, and also
|
|
|
|
// This will actually install 2 tables. The 1 we told it to flush, and also
|
|
|
@ -593,8 +677,37 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
} |
|
|
|
} |
|
|
|
to_delete.clear(); |
|
|
|
to_delete.clear(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add another table
|
|
|
|
|
|
|
|
list.Add(tables[5], &to_delete); |
|
|
|
|
|
|
|
ASSERT_EQ(1, list.NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_EQ(5, list.GetLatestMemTableID()); |
|
|
|
|
|
|
|
memtable_id = 4; |
|
|
|
|
|
|
|
// Pick tables to flush. The tables to pick must have ID smaller than or
|
|
|
|
|
|
|
|
// equal to 4. Therefore, no table will be selected in this case.
|
|
|
|
|
|
|
|
autovector<MemTable*> to_flush4; |
|
|
|
|
|
|
|
list.FlushRequested(); |
|
|
|
|
|
|
|
ASSERT_TRUE(list.HasFlushRequested()); |
|
|
|
|
|
|
|
list.PickMemtablesToFlush(&memtable_id, &to_flush4); |
|
|
|
|
|
|
|
ASSERT_TRUE(to_flush4.empty()); |
|
|
|
|
|
|
|
ASSERT_EQ(1, list.NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_TRUE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_FALSE(list.HasFlushRequested()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pick tables to flush. The tables to pick must have ID smaller than or
|
|
|
|
|
|
|
|
// equal to 5. Therefore, only tables[5] will be selected.
|
|
|
|
|
|
|
|
memtable_id = 5; |
|
|
|
|
|
|
|
list.FlushRequested(); |
|
|
|
|
|
|
|
list.PickMemtablesToFlush(&memtable_id, &to_flush4); |
|
|
|
|
|
|
|
ASSERT_EQ(1, static_cast<int>(to_flush4.size())); |
|
|
|
|
|
|
|
ASSERT_EQ(1, list.NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_FALSE(list.imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
ASSERT_FALSE(list.IsFlushPending()); |
|
|
|
|
|
|
|
to_delete.clear(); |
|
|
|
|
|
|
|
|
|
|
|
list.current()->Unref(&to_delete); |
|
|
|
list.current()->Unref(&to_delete); |
|
|
|
int to_delete_size = std::min(5, max_write_buffer_number_to_maintain); |
|
|
|
int to_delete_size = |
|
|
|
|
|
|
|
std::min(num_tables, max_write_buffer_number_to_maintain); |
|
|
|
ASSERT_EQ(to_delete_size, to_delete.size()); |
|
|
|
ASSERT_EQ(to_delete_size, to_delete.size()); |
|
|
|
|
|
|
|
|
|
|
|
for (const auto& m : to_delete) { |
|
|
|
for (const auto& m : to_delete) { |
|
|
@ -607,6 +720,330 @@ TEST_F(MemTableListTest, FlushPendingTest) { |
|
|
|
to_delete.clear(); |
|
|
|
to_delete.clear(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(MemTableListTest, FlushMultipleCFsTest) { |
|
|
|
|
|
|
|
const int num_cfs = 3; |
|
|
|
|
|
|
|
const int num_tables_per_cf = 5; |
|
|
|
|
|
|
|
SequenceNumber seq = 1; |
|
|
|
|
|
|
|
Status s; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto factory = std::make_shared<SkipListFactory>(); |
|
|
|
|
|
|
|
options.memtable_factory = factory; |
|
|
|
|
|
|
|
ImmutableCFOptions ioptions(options); |
|
|
|
|
|
|
|
InternalKeyComparator cmp(BytewiseComparator()); |
|
|
|
|
|
|
|
WriteBufferManager wb(options.db_write_buffer_size); |
|
|
|
|
|
|
|
autovector<MemTable*> to_delete; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create MemTableLists
|
|
|
|
|
|
|
|
int min_write_buffer_number_to_merge = 3; |
|
|
|
|
|
|
|
int max_write_buffer_number_to_maintain = 7; |
|
|
|
|
|
|
|
autovector<MemTableList*> lists; |
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge, |
|
|
|
|
|
|
|
max_write_buffer_number_to_maintain)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
autovector<uint32_t> cf_ids; |
|
|
|
|
|
|
|
std::vector<std::vector<MemTable*>> tables(num_cfs); |
|
|
|
|
|
|
|
autovector<const MutableCFOptions*> mutable_cf_options_list; |
|
|
|
|
|
|
|
uint32_t cf_id = 0; |
|
|
|
|
|
|
|
for (auto& elem : tables) { |
|
|
|
|
|
|
|
mutable_cf_options_list.emplace_back(new MutableCFOptions(options)); |
|
|
|
|
|
|
|
uint64_t memtable_id = 0; |
|
|
|
|
|
|
|
for (int i = 0; i != num_tables_per_cf; ++i) { |
|
|
|
|
|
|
|
MemTable* mem = |
|
|
|
|
|
|
|
new MemTable(cmp, ioptions, *(mutable_cf_options_list.back()), &wb, |
|
|
|
|
|
|
|
kMaxSequenceNumber, cf_id); |
|
|
|
|
|
|
|
mem->SetID(memtable_id++); |
|
|
|
|
|
|
|
mem->Ref(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "key1", ToString(i)); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), ""); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elem.push_back(mem); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
cf_ids.push_back(cf_id++); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::vector<autovector<MemTable*>> flush_candidates(num_cfs); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Nothing to flush
|
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
auto list = lists[i]; |
|
|
|
|
|
|
|
ASSERT_FALSE(list->IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
list->PickMemtablesToFlush(nullptr /* memtable_id */, &flush_candidates[i]); |
|
|
|
|
|
|
|
ASSERT_EQ(0, static_cast<int>(flush_candidates[i].size())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Request flush even though there is nothing to flush
|
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
auto list = lists[i]; |
|
|
|
|
|
|
|
list->FlushRequested(); |
|
|
|
|
|
|
|
ASSERT_FALSE(list->IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_FALSE(list->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add tables to column families
|
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
for (int j = 0; j != num_tables_per_cf; ++j) { |
|
|
|
|
|
|
|
lists[i]->Add(tables[i][j], &to_delete); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ASSERT_EQ(num_tables_per_cf, lists[i]->NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_TRUE(lists[i]->IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
autovector<const autovector<MemTable*>*> to_flush; |
|
|
|
|
|
|
|
std::vector<uint64_t> prev_memtable_ids; |
|
|
|
|
|
|
|
// For each column family, determine the memtables to flush
|
|
|
|
|
|
|
|
for (int k = 0; k != 4; ++k) { |
|
|
|
|
|
|
|
std::vector<uint64_t> flush_memtable_ids; |
|
|
|
|
|
|
|
if (0 == k) { |
|
|
|
|
|
|
|
// +----+
|
|
|
|
|
|
|
|
// list[0]: |0 1| 2 3 4
|
|
|
|
|
|
|
|
// list[1]: |0 1| 2 3 4
|
|
|
|
|
|
|
|
// | +--+
|
|
|
|
|
|
|
|
// list[2]: |0| 1 2 3 4
|
|
|
|
|
|
|
|
// +-+
|
|
|
|
|
|
|
|
flush_memtable_ids = {1, 1, 0}; |
|
|
|
|
|
|
|
} else if (1 == k) { |
|
|
|
|
|
|
|
// +----+ +---+
|
|
|
|
|
|
|
|
// list[0]: |0 1| |2 3| 4
|
|
|
|
|
|
|
|
// list[1]: |0 1| |2 3| 4
|
|
|
|
|
|
|
|
// | +--+ +---+
|
|
|
|
|
|
|
|
// list[2]: |0| 1 2 3 4
|
|
|
|
|
|
|
|
// +-+
|
|
|
|
|
|
|
|
flush_memtable_ids = {3, 3, 0}; |
|
|
|
|
|
|
|
} else if (2 == k) { |
|
|
|
|
|
|
|
// +-----+ +---+
|
|
|
|
|
|
|
|
// list[0]: |0 1| |2 3| 4
|
|
|
|
|
|
|
|
// list[1]: |0 1| |2 3| 4
|
|
|
|
|
|
|
|
// | +---+ +---+
|
|
|
|
|
|
|
|
// | | +-------+
|
|
|
|
|
|
|
|
// list[2]: |0| |1 2 3| 4
|
|
|
|
|
|
|
|
// +-+ +-------+
|
|
|
|
|
|
|
|
flush_memtable_ids = {3, 3, 3}; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// +-----+ +---+ +-+
|
|
|
|
|
|
|
|
// list[0]: |0 1| |2 3| |4|
|
|
|
|
|
|
|
|
// list[1]: |0 1| |2 3| |4|
|
|
|
|
|
|
|
|
// | +---+ +---+ | |
|
|
|
|
|
|
|
|
// | | +-------+ | |
|
|
|
|
|
|
|
|
// list[2]: |0| |1 2 3| |4|
|
|
|
|
|
|
|
|
// +-+ +-------+ +-+
|
|
|
|
|
|
|
|
flush_memtable_ids = {4, 4, 4}; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
assert(num_cfs == static_cast<int>(flush_memtable_ids.size())); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Pick memtables to flush
|
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
flush_candidates[i].clear(); |
|
|
|
|
|
|
|
lists[i]->PickMemtablesToFlush(&flush_memtable_ids[i], |
|
|
|
|
|
|
|
&flush_candidates[i]); |
|
|
|
|
|
|
|
for (auto mem : flush_candidates[i]) { |
|
|
|
|
|
|
|
mem->TEST_AtomicFlushSequenceNumber() = SequenceNumber(k); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (prev_memtable_ids.empty()) { |
|
|
|
|
|
|
|
ASSERT_EQ(flush_memtable_ids[i] - 0 + 1, flush_candidates[i].size()); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
ASSERT_EQ(flush_memtable_ids[i] - prev_memtable_ids[i], |
|
|
|
|
|
|
|
flush_candidates[i].size()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ASSERT_EQ(num_tables_per_cf, lists[i]->NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_FALSE(lists[i]->HasFlushRequested()); |
|
|
|
|
|
|
|
if (flush_memtable_ids[i] == num_tables_per_cf - 1) { |
|
|
|
|
|
|
|
ASSERT_FALSE( |
|
|
|
|
|
|
|
lists[i]->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
prev_memtable_ids = flush_memtable_ids; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (k < 3) { |
|
|
|
|
|
|
|
for (const auto& mems : flush_candidates) { |
|
|
|
|
|
|
|
uint64_t file_num = file_number.fetch_add(1); |
|
|
|
|
|
|
|
for (auto m : mems) { |
|
|
|
|
|
|
|
m->TEST_SetFlushCompleted(true); |
|
|
|
|
|
|
|
m->TEST_SetFileNumber(file_num); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (k == 0) { |
|
|
|
|
|
|
|
// Rollback first pick of tables
|
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
auto list = lists[i]; |
|
|
|
|
|
|
|
const auto& mems = flush_candidates[i]; |
|
|
|
|
|
|
|
for (auto m : mems) { |
|
|
|
|
|
|
|
m->TEST_SetFileNumber(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
list->RollbackMemtableFlush(flush_candidates[i], 0); |
|
|
|
|
|
|
|
ASSERT_TRUE(list->IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_TRUE(list->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
prev_memtable_ids.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (k == 3) { |
|
|
|
|
|
|
|
for (int i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
to_flush.emplace_back(&flush_candidates[i]); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s = Mock_InstallMemtableFlushResults(lists, cf_ids, mutable_cf_options_list, |
|
|
|
|
|
|
|
to_flush, &to_delete); |
|
|
|
|
|
|
|
ASSERT_OK(s); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
to_delete.clear(); |
|
|
|
|
|
|
|
for (auto list : lists) { |
|
|
|
|
|
|
|
list->current()->Unref(&to_delete); |
|
|
|
|
|
|
|
delete list; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
for (auto& mutable_cf_options : mutable_cf_options_list) { |
|
|
|
|
|
|
|
if (mutable_cf_options != nullptr) { |
|
|
|
|
|
|
|
delete mutable_cf_options; |
|
|
|
|
|
|
|
mutable_cf_options = nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// All memtables in tables array must have been flushed, thus ready to be
|
|
|
|
|
|
|
|
// deleted.
|
|
|
|
|
|
|
|
ASSERT_EQ(to_delete.size(), tables.size() * tables.front().size()); |
|
|
|
|
|
|
|
for (const auto& m : to_delete) { |
|
|
|
|
|
|
|
// Refcount should be 0 after calling InstallMemtableFlushResults.
|
|
|
|
|
|
|
|
// Verify this by Ref'ing and then Unref'ing.
|
|
|
|
|
|
|
|
m->Ref(); |
|
|
|
|
|
|
|
ASSERT_EQ(m, m->Unref()); |
|
|
|
|
|
|
|
delete m; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
to_delete.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TEST_F(MemTableListTest, HasOlderAtomicFlush) { |
|
|
|
|
|
|
|
const size_t num_cfs = 3; |
|
|
|
|
|
|
|
const size_t num_memtables_per_cf = 2; |
|
|
|
|
|
|
|
SequenceNumber seq = 1; |
|
|
|
|
|
|
|
Status s; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
auto factory = std::make_shared<SkipListFactory>(); |
|
|
|
|
|
|
|
options.memtable_factory = factory; |
|
|
|
|
|
|
|
ImmutableCFOptions ioptions(options); |
|
|
|
|
|
|
|
InternalKeyComparator cmp(BytewiseComparator()); |
|
|
|
|
|
|
|
WriteBufferManager wb(options.db_write_buffer_size); |
|
|
|
|
|
|
|
autovector<MemTable*> to_delete; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create MemTableLists
|
|
|
|
|
|
|
|
int min_write_buffer_number_to_merge = 3; |
|
|
|
|
|
|
|
int max_write_buffer_number_to_maintain = 7; |
|
|
|
|
|
|
|
autovector<MemTableList*> lists; |
|
|
|
|
|
|
|
for (size_t i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
lists.emplace_back(new MemTableList(min_write_buffer_number_to_merge, |
|
|
|
|
|
|
|
max_write_buffer_number_to_maintain)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
autovector<uint32_t> cf_ids; |
|
|
|
|
|
|
|
std::vector<std::vector<MemTable*>> tables; |
|
|
|
|
|
|
|
autovector<const MutableCFOptions*> mutable_cf_options_list; |
|
|
|
|
|
|
|
uint32_t cf_id = 0; |
|
|
|
|
|
|
|
for (size_t k = 0; k != num_cfs; ++k) { |
|
|
|
|
|
|
|
std::vector<MemTable*> elem; |
|
|
|
|
|
|
|
mutable_cf_options_list.emplace_back(new MutableCFOptions(options)); |
|
|
|
|
|
|
|
uint64_t memtable_id = 0; |
|
|
|
|
|
|
|
for (int i = 0; i != num_memtables_per_cf; ++i) { |
|
|
|
|
|
|
|
MemTable* mem = |
|
|
|
|
|
|
|
new MemTable(cmp, ioptions, *(mutable_cf_options_list.back()), &wb, |
|
|
|
|
|
|
|
kMaxSequenceNumber, cf_id); |
|
|
|
|
|
|
|
mem->SetID(memtable_id++); |
|
|
|
|
|
|
|
mem->Ref(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::string value; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "key1", ToString(i)); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyN" + ToString(i), "valueN"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyX" + ToString(i), "value"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeValue, "keyM" + ToString(i), "valueM"); |
|
|
|
|
|
|
|
mem->Add(++seq, kTypeDeletion, "keyX" + ToString(i), ""); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
elem.push_back(mem); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
tables.emplace_back(elem); |
|
|
|
|
|
|
|
cf_ids.push_back(cf_id++); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Add tables to column families' immutable memtable lists
|
|
|
|
|
|
|
|
for (size_t i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
for (size_t j = 0; j != num_memtables_per_cf; ++j) { |
|
|
|
|
|
|
|
lists[i]->Add(tables[i][j], &to_delete); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lists[i]->FlushRequested(); |
|
|
|
|
|
|
|
ASSERT_EQ(num_memtables_per_cf, lists[i]->NumNotFlushed()); |
|
|
|
|
|
|
|
ASSERT_TRUE(lists[i]->IsFlushPending()); |
|
|
|
|
|
|
|
ASSERT_TRUE(lists[i]->imm_flush_needed.load(std::memory_order_acquire)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
std::vector<autovector<MemTable*>> flush_candidates(num_cfs); |
|
|
|
|
|
|
|
for (size_t i = 0; i != num_cfs; ++i) { |
|
|
|
|
|
|
|
lists[i]->PickMemtablesToFlush(nullptr, &flush_candidates[i]); |
|
|
|
|
|
|
|
for (auto m : flush_candidates[i]) { |
|
|
|
|
|
|
|
m->TEST_AtomicFlushSequenceNumber() = 123; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lists[i]->RollbackMemtableFlush(flush_candidates[i], 0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
uint64_t memtable_id = num_memtables_per_cf - 1; |
|
|
|
|
|
|
|
autovector<MemTable*> other_flush_candidates; |
|
|
|
|
|
|
|
lists[0]->PickMemtablesToFlush(&memtable_id, &other_flush_candidates); |
|
|
|
|
|
|
|
for (auto m : other_flush_candidates) { |
|
|
|
|
|
|
|
m->TEST_AtomicFlushSequenceNumber() = 124; |
|
|
|
|
|
|
|
m->TEST_SetFlushCompleted(true); |
|
|
|
|
|
|
|
m->TEST_SetFileNumber(1); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
autovector<const autovector<MemTable*>*> to_flush; |
|
|
|
|
|
|
|
to_flush.emplace_back(&other_flush_candidates); |
|
|
|
|
|
|
|
bool has_older_unfinished_atomic_flush = false; |
|
|
|
|
|
|
|
bool found_batch_to_commit = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->SetCallBack( |
|
|
|
|
|
|
|
"MemTableList::TryInstallMemtableFlushResults:" |
|
|
|
|
|
|
|
"HasOlderUnfinishedAtomicFlush:0", |
|
|
|
|
|
|
|
[&](void* /*arg*/) { has_older_unfinished_atomic_flush = true; }); |
|
|
|
|
|
|
|
SyncPoint::GetInstance()->SetCallBack( |
|
|
|
|
|
|
|
"MemTableList::TryInstallMemtableFlushResults:FoundBatchToCommit:0", |
|
|
|
|
|
|
|
[&](void* /*arg*/) { found_batch_to_commit = true; }); |
|
|
|
|
|
|
|
SyncPoint::GetInstance()->EnableProcessing(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s = Mock_InstallMemtableFlushResults(lists, cf_ids, mutable_cf_options_list, |
|
|
|
|
|
|
|
to_flush, &to_delete); |
|
|
|
|
|
|
|
ASSERT_OK(s); |
|
|
|
|
|
|
|
ASSERT_TRUE(has_older_unfinished_atomic_flush); |
|
|
|
|
|
|
|
ASSERT_FALSE(found_batch_to_commit); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SyncPoint::GetInstance()->ClearAllCallBacks(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ASSERT_TRUE(to_delete.empty()); |
|
|
|
|
|
|
|
for (auto list : lists) { |
|
|
|
|
|
|
|
list->current()->Unref(&to_delete); |
|
|
|
|
|
|
|
delete list; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
lists.clear(); |
|
|
|
|
|
|
|
ASSERT_EQ(num_cfs * num_memtables_per_cf, to_delete.size()); |
|
|
|
|
|
|
|
for (auto m : to_delete) { |
|
|
|
|
|
|
|
m->Ref(); |
|
|
|
|
|
|
|
ASSERT_EQ(m, m->Unref()); |
|
|
|
|
|
|
|
delete m; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
to_delete.clear(); |
|
|
|
|
|
|
|
for (auto& opts : mutable_cf_options_list) { |
|
|
|
|
|
|
|
delete opts; |
|
|
|
|
|
|
|
opts = nullptr; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
mutable_cf_options_list.clear(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace rocksdb
|
|
|
|
} // namespace rocksdb
|
|
|
|
|
|
|
|
|
|
|
|
int main(int argc, char** argv) { |
|
|
|
int main(int argc, char** argv) { |
|
|
|