Skip filters for last L0 file if hit-optimized
Summary: Following up on D53493, we can still enable the filter-skipping optimization for last file in L0. It's correct to assume the key will be present in the last L0 file when we're hit-optimized and L0 is deepest. The FilePicker encapsulates the state for traversing each level's files, so I needed to make it expose whether the returned file is last in its level. Test Plan: verified below test fails before this patch and passes afterwards. The change to how the test memtable is populated is needed so file 1 has keys (0, 30, 60), file 2 has keys (10, 40, 70), etc. $ ./db_universal_compaction_test --gtest_filter=UniversalCompactionNumLevels/DBTestUniversalCompaction.OptimizeFiltersForHits/* Reviewers: sdong Reviewed By: sdong Subscribers: dhruba, leveldb Differential Revision: https://reviews.facebook.net/D53583
This commit is contained in:
parent
aa5e3b7c04
commit
fdd70d1495
@ -187,14 +187,16 @@ TEST_P(DBTestUniversalCompaction, OptimizeFiltersForHits) {
|
|||||||
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
env_->Schedule(&test::SleepingBackgroundTask::DoSleepTask, &sleeping_task_low,
|
||||||
Env::Priority::LOW);
|
Env::Priority::LOW);
|
||||||
|
|
||||||
Put("", "");
|
|
||||||
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
|
for (int num = 0; num < options.level0_file_num_compaction_trigger; num++) {
|
||||||
Put(Key(num * 10), "val");
|
Put(Key(num * 10), "val");
|
||||||
|
if (num) {
|
||||||
|
dbfull()->TEST_WaitForFlushMemTable();
|
||||||
|
}
|
||||||
Put(Key(30 + num * 10), "val");
|
Put(Key(30 + num * 10), "val");
|
||||||
Put(Key(60 + num * 10), "val");
|
Put(Key(60 + num * 10), "val");
|
||||||
|
|
||||||
dbfull()->TEST_WaitForFlushMemTable();
|
|
||||||
}
|
}
|
||||||
|
Put("", "");
|
||||||
|
dbfull()->TEST_WaitForFlushMemTable();
|
||||||
|
|
||||||
// Query set of non existing keys
|
// Query set of non existing keys
|
||||||
for (int i = 5; i < 90; i += 10) {
|
for (int i = 5; i < 90; i += 10) {
|
||||||
@ -205,6 +207,13 @@ TEST_P(DBTestUniversalCompaction, OptimizeFiltersForHits) {
|
|||||||
ASSERT_GT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
ASSERT_GT(TestGetTickerCount(options, BLOOM_FILTER_USEFUL), 0);
|
||||||
auto prev_counter = TestGetTickerCount(options, BLOOM_FILTER_USEFUL);
|
auto prev_counter = TestGetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||||
|
|
||||||
|
// Make sure bloom filter is used for all but the last L0 file when looking
|
||||||
|
// up a non-existent key that's in the range of all L0 files.
|
||||||
|
ASSERT_EQ(Get(Key(35)), "NOT_FOUND");
|
||||||
|
ASSERT_EQ(prev_counter + NumTableFilesAtLevel(0) - 1,
|
||||||
|
TestGetTickerCount(options, BLOOM_FILTER_USEFUL));
|
||||||
|
prev_counter = TestGetTickerCount(options, BLOOM_FILTER_USEFUL);
|
||||||
|
|
||||||
// Unblock compaction and wait it for happening.
|
// Unblock compaction and wait it for happening.
|
||||||
sleeping_task_low.WakeUp();
|
sleeping_task_low.WakeUp();
|
||||||
dbfull()->TEST_WaitForCompact();
|
dbfull()->TEST_WaitForCompact();
|
||||||
|
@ -84,15 +84,11 @@ int FindFileInRange(const InternalKeyComparator& icmp,
|
|||||||
// are MergeInProgress).
|
// are MergeInProgress).
|
||||||
class FilePicker {
|
class FilePicker {
|
||||||
public:
|
public:
|
||||||
FilePicker(
|
FilePicker(std::vector<FileMetaData*>* files, const Slice& user_key,
|
||||||
std::vector<FileMetaData*>* files,
|
const Slice& ikey, autovector<LevelFilesBrief>* file_levels,
|
||||||
const Slice& user_key,
|
unsigned int num_levels, FileIndexer* file_indexer,
|
||||||
const Slice& ikey,
|
const Comparator* user_comparator,
|
||||||
autovector<LevelFilesBrief>* file_levels,
|
const InternalKeyComparator* internal_comparator)
|
||||||
unsigned int num_levels,
|
|
||||||
FileIndexer* file_indexer,
|
|
||||||
const Comparator* user_comparator,
|
|
||||||
const InternalKeyComparator* internal_comparator)
|
|
||||||
: num_levels_(num_levels),
|
: num_levels_(num_levels),
|
||||||
curr_level_(-1),
|
curr_level_(-1),
|
||||||
hit_file_level_(-1),
|
hit_file_level_(-1),
|
||||||
@ -102,6 +98,7 @@ class FilePicker {
|
|||||||
files_(files),
|
files_(files),
|
||||||
#endif
|
#endif
|
||||||
level_files_brief_(file_levels),
|
level_files_brief_(file_levels),
|
||||||
|
is_hit_file_last_in_level_(false),
|
||||||
user_key_(user_key),
|
user_key_(user_key),
|
||||||
ikey_(ikey),
|
ikey_(ikey),
|
||||||
file_indexer_(file_indexer),
|
file_indexer_(file_indexer),
|
||||||
@ -126,6 +123,8 @@ class FilePicker {
|
|||||||
// Loops over all files in current level.
|
// Loops over all files in current level.
|
||||||
FdWithKeyRange* f = &curr_file_level_->files[curr_index_in_curr_level_];
|
FdWithKeyRange* f = &curr_file_level_->files[curr_index_in_curr_level_];
|
||||||
hit_file_level_ = curr_level_;
|
hit_file_level_ = curr_level_;
|
||||||
|
is_hit_file_last_in_level_ =
|
||||||
|
curr_index_in_curr_level_ == curr_file_level_->num_files - 1;
|
||||||
int cmp_largest = -1;
|
int cmp_largest = -1;
|
||||||
|
|
||||||
// Do key range filtering of files or/and fractional cascading if:
|
// Do key range filtering of files or/and fractional cascading if:
|
||||||
@ -209,6 +208,10 @@ class FilePicker {
|
|||||||
// for GET_HIT_L0, GET_HIT_L1 & GET_HIT_L2_AND_UP counts
|
// for GET_HIT_L0, GET_HIT_L1 & GET_HIT_L2_AND_UP counts
|
||||||
unsigned int GetHitFileLevel() { return hit_file_level_; }
|
unsigned int GetHitFileLevel() { return hit_file_level_; }
|
||||||
|
|
||||||
|
// Returns true if the most recent "hit file" (i.e., one returned by
|
||||||
|
// GetNextFile()) is at the last index in its level.
|
||||||
|
bool IsHitFileLastInLevel() { return is_hit_file_last_in_level_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned int num_levels_;
|
unsigned int num_levels_;
|
||||||
unsigned int curr_level_;
|
unsigned int curr_level_;
|
||||||
@ -220,6 +223,7 @@ class FilePicker {
|
|||||||
#endif
|
#endif
|
||||||
autovector<LevelFilesBrief>* level_files_brief_;
|
autovector<LevelFilesBrief>* level_files_brief_;
|
||||||
bool search_ended_;
|
bool search_ended_;
|
||||||
|
bool is_hit_file_last_in_level_;
|
||||||
LevelFilesBrief* curr_file_level_;
|
LevelFilesBrief* curr_file_level_;
|
||||||
unsigned int curr_index_in_curr_level_;
|
unsigned int curr_index_in_curr_level_;
|
||||||
unsigned int start_index_in_curr_level_;
|
unsigned int start_index_in_curr_level_;
|
||||||
@ -903,7 +907,8 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
|||||||
*status = table_cache_->Get(
|
*status = table_cache_->Get(
|
||||||
read_options, *internal_comparator(), f->fd, ikey, &get_context,
|
read_options, *internal_comparator(), f->fd, ikey, &get_context,
|
||||||
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
cfd_->internal_stats()->GetFileReadHist(fp.GetHitFileLevel()),
|
||||||
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel())));
|
IsFilterSkipped(static_cast<int>(fp.GetHitFileLevel()),
|
||||||
|
fp.IsHitFileLastInLevel()));
|
||||||
// TODO: examine the behavior for corrupted key
|
// TODO: examine the behavior for corrupted key
|
||||||
if (!status->ok()) {
|
if (!status->ok()) {
|
||||||
return;
|
return;
|
||||||
@ -960,10 +965,11 @@ void Version::Get(const ReadOptions& read_options, const LookupKey& k,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Version::IsFilterSkipped(int level) {
|
bool Version::IsFilterSkipped(int level, bool is_file_last_in_level) {
|
||||||
// Reaching the bottom level implies misses at all upper levels, so we'll
|
// Reaching the bottom level implies misses at all upper levels, so we'll
|
||||||
// skip checking the filters when we predict a hit.
|
// skip checking the filters when we predict a hit.
|
||||||
return cfd_->ioptions()->optimize_filters_for_hits && level > 0 &&
|
return cfd_->ioptions()->optimize_filters_for_hits &&
|
||||||
|
(level > 0 || is_file_last_in_level) &&
|
||||||
level == storage_info_.num_non_empty_levels() - 1;
|
level == storage_info_.num_non_empty_levels() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -530,7 +530,7 @@ class Version {
|
|||||||
// checked during read operations. In certain cases (trivial move or preload),
|
// checked during read operations. In certain cases (trivial move or preload),
|
||||||
// the filter block may already be cached, but we still do not access it such
|
// the filter block may already be cached, but we still do not access it such
|
||||||
// that it eventually expires from the cache.
|
// that it eventually expires from the cache.
|
||||||
bool IsFilterSkipped(int level);
|
bool IsFilterSkipped(int level, bool is_file_last_in_level = false);
|
||||||
|
|
||||||
// The helper function of UpdateAccumulatedStats, which may fill the missing
|
// The helper function of UpdateAccumulatedStats, which may fill the missing
|
||||||
// fields of file_mata from its associated TableProperties.
|
// fields of file_mata from its associated TableProperties.
|
||||||
|
Loading…
Reference in New Issue
Block a user