Block access tracing: Trace referenced key for Get on non-data blocks. (#5548)
Summary: This PR traces the referenced key for Get for all types of blocks. This is useful when evaluating hybrid row-block caches. Pull Request resolved: https://github.com/facebook/rocksdb/pull/5548 Test Plan: make clean && USE_CLANG=1 make check -j32 Differential Revision: D16157979 Pulled By: HaoyuHuang fbshipit-source-id: f6327411c9deb74e35e22a35f66cdbae09ab9d87
This commit is contained in:
parent
22ce462450
commit
8a008d4170
@ -1983,10 +1983,12 @@ CachableEntry<UncompressionDict> BlockBasedTable::GetUncompressionDict(
|
|||||||
/*block_size=*/usage, rep_->cf_id_for_tracing(),
|
/*block_size=*/usage, rep_->cf_id_for_tracing(),
|
||||||
/*cf_name=*/"", rep_->level_for_tracing(),
|
/*cf_name=*/"", rep_->level_for_tracing(),
|
||||||
rep_->sst_number_for_tracing(), lookup_context->caller, is_cache_hit,
|
rep_->sst_number_for_tracing(), lookup_context->caller, is_cache_hit,
|
||||||
/*no_insert=*/no_io, lookup_context->get_id);
|
/*no_insert=*/no_io, lookup_context->get_id,
|
||||||
|
lookup_context->get_from_user_specified_snapshot,
|
||||||
|
/*referenced_key=*/"");
|
||||||
block_cache_tracer_->WriteBlockAccess(access_record, cache_key,
|
block_cache_tracer_->WriteBlockAccess(access_record, cache_key,
|
||||||
rep_->cf_name_for_tracing(),
|
rep_->cf_name_for_tracing(),
|
||||||
/*referenced_key=*/nullptr);
|
lookup_context->referenced_key);
|
||||||
}
|
}
|
||||||
return {dict, cache_handle ? rep_->table_options.block_cache.get() : nullptr,
|
return {dict, cache_handle ? rep_->table_options.block_cache.get() : nullptr,
|
||||||
cache_handle, false /* own_value */};
|
cache_handle, false /* own_value */};
|
||||||
@ -2237,7 +2239,6 @@ Status BlockBasedTable::MaybeReadBlockAndLoadToCache(
|
|||||||
Slice key /* key to the block cache */;
|
Slice key /* key to the block cache */;
|
||||||
Slice ckey /* key to the compressed block cache */;
|
Slice ckey /* key to the compressed block cache */;
|
||||||
bool is_cache_hit = false;
|
bool is_cache_hit = false;
|
||||||
bool no_insert = true;
|
|
||||||
if (block_cache != nullptr || block_cache_compressed != nullptr) {
|
if (block_cache != nullptr || block_cache_compressed != nullptr) {
|
||||||
// create key for block cache
|
// create key for block cache
|
||||||
if (block_cache != nullptr) {
|
if (block_cache != nullptr) {
|
||||||
@ -2265,7 +2266,6 @@ Status BlockBasedTable::MaybeReadBlockAndLoadToCache(
|
|||||||
// Can't find the block from the cache. If I/O is allowed, read from the
|
// Can't find the block from the cache. If I/O is allowed, read from the
|
||||||
// file.
|
// file.
|
||||||
if (block_entry->GetValue() == nullptr && !no_io && ro.fill_cache) {
|
if (block_entry->GetValue() == nullptr && !no_io && ro.fill_cache) {
|
||||||
no_insert = false;
|
|
||||||
Statistics* statistics = rep_->ioptions.statistics;
|
Statistics* statistics = rep_->ioptions.statistics;
|
||||||
const bool maybe_compressed =
|
const bool maybe_compressed =
|
||||||
block_type != BlockType::kFilter && rep_->blocks_maybe_compressed;
|
block_type != BlockType::kFilter && rep_->blocks_maybe_compressed;
|
||||||
@ -2332,11 +2332,11 @@ Status BlockBasedTable::MaybeReadBlockAndLoadToCache(
|
|||||||
assert(false);
|
assert(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (BlockCacheTraceHelper::ShouldTraceReferencedKey(
|
bool no_insert = no_io || !ro.fill_cache;
|
||||||
|
if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(
|
||||||
trace_block_type, lookup_context->caller)) {
|
trace_block_type, lookup_context->caller)) {
|
||||||
// Defer logging the access to Get() and MultiGet() to trace additional
|
// Defer logging the access to Get() and MultiGet() to trace additional
|
||||||
// information, e.g., the referenced key,
|
// information, e.g., referenced_key_exist_in_block.
|
||||||
// referenced_key_exist_in_block.
|
|
||||||
|
|
||||||
// Make a copy of the block key here since it will be logged later.
|
// Make a copy of the block key here since it will be logged later.
|
||||||
lookup_context->FillLookupContext(
|
lookup_context->FillLookupContext(
|
||||||
@ -2351,10 +2351,12 @@ Status BlockBasedTable::MaybeReadBlockAndLoadToCache(
|
|||||||
/*block_size=*/usage, rep_->cf_id_for_tracing(),
|
/*block_size=*/usage, rep_->cf_id_for_tracing(),
|
||||||
/*cf_name=*/"", rep_->level_for_tracing(),
|
/*cf_name=*/"", rep_->level_for_tracing(),
|
||||||
rep_->sst_number_for_tracing(), lookup_context->caller, is_cache_hit,
|
rep_->sst_number_for_tracing(), lookup_context->caller, is_cache_hit,
|
||||||
no_insert, lookup_context->get_id);
|
no_insert, lookup_context->get_id,
|
||||||
|
lookup_context->get_from_user_specified_snapshot,
|
||||||
|
/*referenced_key=*/"");
|
||||||
block_cache_tracer_->WriteBlockAccess(access_record, key,
|
block_cache_tracer_->WriteBlockAccess(access_record, key,
|
||||||
rep_->cf_name_for_tracing(),
|
rep_->cf_name_for_tracing(),
|
||||||
/*referenced_key=*/nullptr);
|
lookup_context->referenced_key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3288,12 +3290,18 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
|||||||
// First check the full filter
|
// First check the full filter
|
||||||
// If full filter not useful, Then go into each block
|
// If full filter not useful, Then go into each block
|
||||||
uint64_t tracing_get_id = get_context->get_tracing_get_id();
|
uint64_t tracing_get_id = get_context->get_tracing_get_id();
|
||||||
BlockCacheLookupContext lookup_context{TableReaderCaller::kUserGet,
|
BlockCacheLookupContext lookup_context{
|
||||||
tracing_get_id};
|
TableReaderCaller::kUserGet, tracing_get_id,
|
||||||
|
/*get_from_user_specified_snapshot=*/read_options.snapshot != nullptr};
|
||||||
|
if (block_cache_tracer_ && block_cache_tracer_->is_tracing_enabled()) {
|
||||||
|
// Trace the key since it contains both user key and sequence number.
|
||||||
|
lookup_context.referenced_key = key.ToString();
|
||||||
|
lookup_context.get_from_user_specified_snapshot =
|
||||||
|
read_options.snapshot != nullptr;
|
||||||
|
}
|
||||||
const bool may_match =
|
const bool may_match =
|
||||||
FullFilterKeyMayMatch(read_options, filter, key, no_io, prefix_extractor,
|
FullFilterKeyMayMatch(read_options, filter, key, no_io, prefix_extractor,
|
||||||
get_context, &lookup_context);
|
get_context, &lookup_context);
|
||||||
|
|
||||||
if (!may_match) {
|
if (!may_match) {
|
||||||
RecordTick(rep_->ioptions.statistics, BLOOM_FILTER_USEFUL);
|
RecordTick(rep_->ioptions.statistics, BLOOM_FILTER_USEFUL);
|
||||||
PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, rep_->level);
|
PERF_COUNTER_BY_LEVEL_ADD(bloom_filter_useful, 1, rep_->level);
|
||||||
@ -3347,7 +3355,9 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
|||||||
}
|
}
|
||||||
|
|
||||||
BlockCacheLookupContext lookup_data_block_context{
|
BlockCacheLookupContext lookup_data_block_context{
|
||||||
TableReaderCaller::kUserGet, tracing_get_id};
|
TableReaderCaller::kUserGet, tracing_get_id,
|
||||||
|
/*get_from_user_specified_snapshot=*/read_options.snapshot !=
|
||||||
|
nullptr};
|
||||||
bool does_referenced_key_exist = false;
|
bool does_referenced_key_exist = false;
|
||||||
DataBlockIter biter;
|
DataBlockIter biter;
|
||||||
uint64_t referenced_data_size = 0;
|
uint64_t referenced_data_size = 0;
|
||||||
@ -3406,7 +3416,7 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
|||||||
if (does_referenced_key_exist) {
|
if (does_referenced_key_exist) {
|
||||||
referenced_key = biter.key();
|
referenced_key = biter.key();
|
||||||
} else {
|
} else {
|
||||||
referenced_key = ExtractUserKey(key);
|
referenced_key = key;
|
||||||
}
|
}
|
||||||
BlockCacheTraceRecord access_record(
|
BlockCacheTraceRecord access_record(
|
||||||
rep_->ioptions.env->NowMicros(),
|
rep_->ioptions.env->NowMicros(),
|
||||||
@ -3417,6 +3427,7 @@ Status BlockBasedTable::Get(const ReadOptions& read_options, const Slice& key,
|
|||||||
lookup_data_block_context.is_cache_hit,
|
lookup_data_block_context.is_cache_hit,
|
||||||
lookup_data_block_context.no_insert,
|
lookup_data_block_context.no_insert,
|
||||||
lookup_data_block_context.get_id,
|
lookup_data_block_context.get_id,
|
||||||
|
lookup_data_block_context.get_from_user_specified_snapshot,
|
||||||
/*referenced_key=*/"", referenced_data_size,
|
/*referenced_key=*/"", referenced_data_size,
|
||||||
lookup_data_block_context.num_keys_in_block,
|
lookup_data_block_context.num_keys_in_block,
|
||||||
does_referenced_key_exist);
|
does_referenced_key_exist);
|
||||||
@ -3460,8 +3471,9 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
|
|||||||
if (!sst_file_range.empty() && sst_file_range.begin()->get_context) {
|
if (!sst_file_range.empty() && sst_file_range.begin()->get_context) {
|
||||||
tracing_mget_id = sst_file_range.begin()->get_context->get_tracing_get_id();
|
tracing_mget_id = sst_file_range.begin()->get_context->get_tracing_get_id();
|
||||||
}
|
}
|
||||||
BlockCacheLookupContext lookup_context{TableReaderCaller::kUserMultiGet,
|
BlockCacheLookupContext lookup_context{
|
||||||
tracing_mget_id};
|
TableReaderCaller::kUserMultiGet, tracing_mget_id,
|
||||||
|
/*get_from_user_specified_snapshot=*/read_options.snapshot != nullptr};
|
||||||
FullFilterKeysMayMatch(read_options, filter, &sst_file_range, no_io,
|
FullFilterKeysMayMatch(read_options, filter, &sst_file_range, no_io,
|
||||||
prefix_extractor, &lookup_context);
|
prefix_extractor, &lookup_context);
|
||||||
|
|
||||||
@ -3492,11 +3504,8 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
|
|||||||
{
|
{
|
||||||
MultiGetRange data_block_range(sst_file_range, sst_file_range.begin(),
|
MultiGetRange data_block_range(sst_file_range, sst_file_range.begin(),
|
||||||
sst_file_range.end());
|
sst_file_range.end());
|
||||||
BlockCacheLookupContext lookup_compression_dict_context(
|
auto uncompression_dict_storage = GetUncompressionDict(
|
||||||
TableReaderCaller::kUserMultiGet);
|
nullptr, no_io, sst_file_range.begin()->get_context, &lookup_context);
|
||||||
auto uncompression_dict_storage = GetUncompressionDict(nullptr, no_io,
|
|
||||||
sst_file_range.begin()->get_context,
|
|
||||||
&lookup_compression_dict_context);
|
|
||||||
const UncompressionDict& uncompression_dict =
|
const UncompressionDict& uncompression_dict =
|
||||||
uncompression_dict_storage.GetValue() == nullptr
|
uncompression_dict_storage.GetValue() == nullptr
|
||||||
? UncompressionDict::GetEmptyDict()
|
? UncompressionDict::GetEmptyDict()
|
||||||
@ -3591,7 +3600,9 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
|
|||||||
uint64_t referenced_data_size = 0;
|
uint64_t referenced_data_size = 0;
|
||||||
bool does_referenced_key_exist = false;
|
bool does_referenced_key_exist = false;
|
||||||
BlockCacheLookupContext lookup_data_block_context(
|
BlockCacheLookupContext lookup_data_block_context(
|
||||||
TableReaderCaller::kUserMultiGet, tracing_mget_id);
|
TableReaderCaller::kUserMultiGet, tracing_mget_id,
|
||||||
|
/*get_from_user_specified_snapshot=*/read_options.snapshot !=
|
||||||
|
nullptr);
|
||||||
if (first_block) {
|
if (first_block) {
|
||||||
if (!block_handles[idx_in_batch].IsNull() ||
|
if (!block_handles[idx_in_batch].IsNull() ||
|
||||||
!results[idx_in_batch].IsEmpty()) {
|
!results[idx_in_batch].IsEmpty()) {
|
||||||
@ -3685,7 +3696,7 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
|
|||||||
if (does_referenced_key_exist) {
|
if (does_referenced_key_exist) {
|
||||||
referenced_key = biter->key();
|
referenced_key = biter->key();
|
||||||
} else {
|
} else {
|
||||||
referenced_key = ExtractUserKey(key);
|
referenced_key = key;
|
||||||
}
|
}
|
||||||
BlockCacheTraceRecord access_record(
|
BlockCacheTraceRecord access_record(
|
||||||
rep_->ioptions.env->NowMicros(),
|
rep_->ioptions.env->NowMicros(),
|
||||||
@ -3696,6 +3707,7 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
|
|||||||
lookup_data_block_context.is_cache_hit,
|
lookup_data_block_context.is_cache_hit,
|
||||||
lookup_data_block_context.no_insert,
|
lookup_data_block_context.no_insert,
|
||||||
lookup_data_block_context.get_id,
|
lookup_data_block_context.get_id,
|
||||||
|
lookup_data_block_context.get_from_user_specified_snapshot,
|
||||||
/*referenced_key=*/"", referenced_data_size,
|
/*referenced_key=*/"", referenced_data_size,
|
||||||
lookup_data_block_context.num_keys_in_block,
|
lookup_data_block_context.num_keys_in_block,
|
||||||
does_referenced_key_exist);
|
does_referenced_key_exist);
|
||||||
|
@ -63,6 +63,8 @@ extern const uint64_t kPlainTableMagicNumber;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
const std::string kDummyValue(10000, 'o');
|
||||||
|
|
||||||
// DummyPropertiesCollector used to test BlockBasedTableProperties
|
// DummyPropertiesCollector used to test BlockBasedTableProperties
|
||||||
class DummyPropertiesCollector : public TablePropertiesCollector {
|
class DummyPropertiesCollector : public TablePropertiesCollector {
|
||||||
public:
|
public:
|
||||||
@ -312,7 +314,9 @@ class TableConstructor: public Constructor {
|
|||||||
: Constructor(cmp),
|
: Constructor(cmp),
|
||||||
largest_seqno_(largest_seqno),
|
largest_seqno_(largest_seqno),
|
||||||
convert_to_internal_key_(convert_to_internal_key),
|
convert_to_internal_key_(convert_to_internal_key),
|
||||||
level_(level) {}
|
level_(level) {
|
||||||
|
env_ = rocksdb::Env::Default();
|
||||||
|
}
|
||||||
~TableConstructor() override { Reset(); }
|
~TableConstructor() override { Reset(); }
|
||||||
|
|
||||||
Status FinishImpl(const Options& options, const ImmutableCFOptions& ioptions,
|
Status FinishImpl(const Options& options, const ImmutableCFOptions& ioptions,
|
||||||
@ -371,7 +375,7 @@ class TableConstructor: public Constructor {
|
|||||||
return ioptions.table_factory->NewTableReader(
|
return ioptions.table_factory->NewTableReader(
|
||||||
TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
|
TableReaderOptions(ioptions, moptions.prefix_extractor.get(), soptions,
|
||||||
internal_comparator, !kSkipFilters, !kImmortal,
|
internal_comparator, !kSkipFilters, !kImmortal,
|
||||||
level_, largest_seqno_, nullptr),
|
level_, largest_seqno_, &block_cache_tracer_),
|
||||||
std::move(file_reader_), TEST_GetSink()->contents().size(),
|
std::move(file_reader_), TEST_GetSink()->contents().size(),
|
||||||
&table_reader_);
|
&table_reader_);
|
||||||
}
|
}
|
||||||
@ -425,6 +429,8 @@ class TableConstructor: public Constructor {
|
|||||||
return static_cast<test::StringSink*>(file_writer_->writable_file());
|
return static_cast<test::StringSink*>(file_writer_->writable_file());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlockCacheTracer block_cache_tracer_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Reset() {
|
void Reset() {
|
||||||
uniq_id_ = 0;
|
uniq_id_ = 0;
|
||||||
@ -445,6 +451,7 @@ class TableConstructor: public Constructor {
|
|||||||
|
|
||||||
static uint64_t cur_uniq_id_;
|
static uint64_t cur_uniq_id_;
|
||||||
EnvOptions soptions;
|
EnvOptions soptions;
|
||||||
|
Env* env_;
|
||||||
};
|
};
|
||||||
uint64_t TableConstructor::cur_uniq_id_ = 1;
|
uint64_t TableConstructor::cur_uniq_id_ = 1;
|
||||||
|
|
||||||
@ -1063,7 +1070,9 @@ class BlockBasedTableTest
|
|||||||
: public TableTest,
|
: public TableTest,
|
||||||
virtual public ::testing::WithParamInterface<uint32_t> {
|
virtual public ::testing::WithParamInterface<uint32_t> {
|
||||||
public:
|
public:
|
||||||
BlockBasedTableTest() : format_(GetParam()) {}
|
BlockBasedTableTest() : format_(GetParam()) {
|
||||||
|
env_ = rocksdb::Env::Default();
|
||||||
|
}
|
||||||
|
|
||||||
BlockBasedTableOptions GetBlockBasedTableOptions() {
|
BlockBasedTableOptions GetBlockBasedTableOptions() {
|
||||||
BlockBasedTableOptions options;
|
BlockBasedTableOptions options;
|
||||||
@ -1071,11 +1080,91 @@ class BlockBasedTableTest
|
|||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SetupTracingTest(TableConstructor* c) {
|
||||||
|
test_path_ = test::PerThreadDBPath("block_based_table_tracing_test");
|
||||||
|
EXPECT_OK(env_->CreateDir(test_path_));
|
||||||
|
trace_file_path_ = test_path_ + "/block_cache_trace_file";
|
||||||
|
TraceOptions trace_opt;
|
||||||
|
std::unique_ptr<TraceWriter> trace_writer;
|
||||||
|
EXPECT_OK(NewFileTraceWriter(env_, EnvOptions(), trace_file_path_,
|
||||||
|
&trace_writer));
|
||||||
|
c->block_cache_tracer_.StartTrace(env_, trace_opt, std::move(trace_writer));
|
||||||
|
{
|
||||||
|
std::string user_key = "k01";
|
||||||
|
InternalKey internal_key(user_key, 0, kTypeValue);
|
||||||
|
std::string encoded_key = internal_key.Encode().ToString();
|
||||||
|
c->Add(encoded_key, kDummyValue);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::string user_key = "k02";
|
||||||
|
InternalKey internal_key(user_key, 0, kTypeValue);
|
||||||
|
std::string encoded_key = internal_key.Encode().ToString();
|
||||||
|
c->Add(encoded_key, kDummyValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void VerifyBlockAccessTrace(
|
||||||
|
TableConstructor* c,
|
||||||
|
const std::vector<BlockCacheTraceRecord>& expected_records) {
|
||||||
|
c->block_cache_tracer_.EndTrace();
|
||||||
|
|
||||||
|
std::unique_ptr<TraceReader> trace_reader;
|
||||||
|
Status s =
|
||||||
|
NewFileTraceReader(env_, EnvOptions(), trace_file_path_, &trace_reader);
|
||||||
|
EXPECT_OK(s);
|
||||||
|
BlockCacheTraceReader reader(std::move(trace_reader));
|
||||||
|
BlockCacheTraceHeader header;
|
||||||
|
EXPECT_OK(reader.ReadHeader(&header));
|
||||||
|
uint32_t index = 0;
|
||||||
|
while (s.ok()) {
|
||||||
|
BlockCacheTraceRecord access;
|
||||||
|
s = reader.ReadAccess(&access);
|
||||||
|
if (!s.ok()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ASSERT_LT(index, expected_records.size());
|
||||||
|
EXPECT_NE("", access.block_key);
|
||||||
|
EXPECT_EQ(access.block_type, expected_records[index].block_type);
|
||||||
|
EXPECT_GT(access.block_size, 0);
|
||||||
|
EXPECT_EQ(access.caller, expected_records[index].caller);
|
||||||
|
EXPECT_EQ(access.no_insert, expected_records[index].no_insert);
|
||||||
|
EXPECT_EQ(access.is_cache_hit, expected_records[index].is_cache_hit);
|
||||||
|
// Get
|
||||||
|
if (access.caller == TableReaderCaller::kUserGet) {
|
||||||
|
EXPECT_EQ(access.referenced_key,
|
||||||
|
expected_records[index].referenced_key);
|
||||||
|
EXPECT_EQ(access.get_id, expected_records[index].get_id);
|
||||||
|
EXPECT_EQ(access.get_from_user_specified_snapshot,
|
||||||
|
expected_records[index].get_from_user_specified_snapshot);
|
||||||
|
if (access.block_type == TraceType::kBlockTraceDataBlock) {
|
||||||
|
EXPECT_GT(access.referenced_data_size, 0);
|
||||||
|
EXPECT_GT(access.num_keys_in_block, 0);
|
||||||
|
EXPECT_EQ(access.referenced_key_exist_in_block,
|
||||||
|
expected_records[index].referenced_key_exist_in_block);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EXPECT_EQ(access.referenced_key, "");
|
||||||
|
EXPECT_EQ(access.get_id, 0);
|
||||||
|
EXPECT_TRUE(access.get_from_user_specified_snapshot == Boolean::kFalse);
|
||||||
|
EXPECT_EQ(access.referenced_data_size, 0);
|
||||||
|
EXPECT_EQ(access.num_keys_in_block, 0);
|
||||||
|
EXPECT_TRUE(access.referenced_key_exist_in_block == Boolean::kFalse);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(index, expected_records.size());
|
||||||
|
EXPECT_OK(env_->DeleteFile(trace_file_path_));
|
||||||
|
EXPECT_OK(env_->DeleteDir(test_path_));
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
uint64_t IndexUncompressedHelper(bool indexCompress);
|
uint64_t IndexUncompressedHelper(bool indexCompress);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t format_;
|
uint32_t format_;
|
||||||
|
Env* env_;
|
||||||
|
std::string trace_file_path_;
|
||||||
|
std::string test_path_;
|
||||||
};
|
};
|
||||||
class PlainTableTest : public TableTest {};
|
class PlainTableTest : public TableTest {};
|
||||||
class TablePropertyTest : public testing::Test {};
|
class TablePropertyTest : public testing::Test {};
|
||||||
@ -2211,6 +2300,187 @@ TEST_P(BlockBasedTableTest, NumBlockStat) {
|
|||||||
c.ResetTableReader();
|
c.ResetTableReader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(BlockBasedTableTest, TracingGetTest) {
|
||||||
|
TableConstructor c(BytewiseComparator());
|
||||||
|
Options options;
|
||||||
|
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||||
|
options.create_if_missing = true;
|
||||||
|
table_options.block_cache = NewLRUCache(1024 * 1024, 0);
|
||||||
|
table_options.cache_index_and_filter_blocks = true;
|
||||||
|
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||||
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||||
|
SetupTracingTest(&c);
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
stl_wrappers::KVMap kvmap;
|
||||||
|
ImmutableCFOptions ioptions(options);
|
||||||
|
MutableCFOptions moptions(options);
|
||||||
|
c.Finish(options, ioptions, moptions, table_options,
|
||||||
|
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||||
|
std::string user_key = "k01";
|
||||||
|
InternalKey internal_key(user_key, 0, kTypeValue);
|
||||||
|
std::string encoded_key = internal_key.Encode().ToString();
|
||||||
|
for (uint32_t i = 1; i <= 2; i++) {
|
||||||
|
PinnableSlice value;
|
||||||
|
GetContext get_context(options.comparator, nullptr, nullptr, nullptr,
|
||||||
|
GetContext::kNotFound, user_key, &value, nullptr,
|
||||||
|
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||||
|
nullptr, /*get_id=*/i);
|
||||||
|
get_perf_context()->Reset();
|
||||||
|
ASSERT_OK(c.GetTableReader()->Get(ReadOptions(), encoded_key, &get_context,
|
||||||
|
moptions.prefix_extractor.get()));
|
||||||
|
ASSERT_EQ(get_context.State(), GetContext::kFound);
|
||||||
|
ASSERT_EQ(value.ToString(), kDummyValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify traces.
|
||||||
|
std::vector<BlockCacheTraceRecord> expected_records;
|
||||||
|
// The first two records should be prefetching index and filter blocks.
|
||||||
|
BlockCacheTraceRecord record;
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kPrefetch;
|
||||||
|
record.is_cache_hit = Boolean::kFalse;
|
||||||
|
record.no_insert = Boolean::kFalse;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceFilterBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
// Then we should have three records for one index, one filter, and one data
|
||||||
|
// block access.
|
||||||
|
record.get_id = 1;
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kUserGet;
|
||||||
|
record.get_from_user_specified_snapshot = Boolean::kFalse;
|
||||||
|
record.referenced_key = encoded_key;
|
||||||
|
record.referenced_key_exist_in_block = Boolean::kTrue;
|
||||||
|
record.is_cache_hit = Boolean::kTrue;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceFilterBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.is_cache_hit = Boolean::kFalse;
|
||||||
|
record.block_type = TraceType::kBlockTraceDataBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
// The second get should all observe cache hits.
|
||||||
|
record.is_cache_hit = Boolean::kTrue;
|
||||||
|
record.get_id = 2;
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kUserGet;
|
||||||
|
record.get_from_user_specified_snapshot = Boolean::kFalse;
|
||||||
|
record.referenced_key = encoded_key;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceFilterBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceDataBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
VerifyBlockAccessTrace(&c, expected_records);
|
||||||
|
c.ResetTableReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(BlockBasedTableTest, TracingApproximateOffsetOfTest) {
|
||||||
|
TableConstructor c(BytewiseComparator());
|
||||||
|
Options options;
|
||||||
|
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||||
|
options.create_if_missing = true;
|
||||||
|
table_options.block_cache = NewLRUCache(1024 * 1024, 0);
|
||||||
|
table_options.cache_index_and_filter_blocks = true;
|
||||||
|
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||||
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||||
|
SetupTracingTest(&c);
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
stl_wrappers::KVMap kvmap;
|
||||||
|
ImmutableCFOptions ioptions(options);
|
||||||
|
MutableCFOptions moptions(options);
|
||||||
|
c.Finish(options, ioptions, moptions, table_options,
|
||||||
|
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||||
|
for (uint32_t i = 1; i <= 2; i++) {
|
||||||
|
std::string user_key = "k01";
|
||||||
|
InternalKey internal_key(user_key, 0, kTypeValue);
|
||||||
|
std::string encoded_key = internal_key.Encode().ToString();
|
||||||
|
c.GetTableReader()->ApproximateOffsetOf(
|
||||||
|
encoded_key, TableReaderCaller::kUserApproximateSize);
|
||||||
|
}
|
||||||
|
// Verify traces.
|
||||||
|
std::vector<BlockCacheTraceRecord> expected_records;
|
||||||
|
// The first two records should be prefetching index and filter blocks.
|
||||||
|
BlockCacheTraceRecord record;
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kPrefetch;
|
||||||
|
record.is_cache_hit = Boolean::kFalse;
|
||||||
|
record.no_insert = Boolean::kFalse;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceFilterBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
// Then we should have two records for only index blocks.
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kUserApproximateSize;
|
||||||
|
record.is_cache_hit = Boolean::kTrue;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
expected_records.push_back(record);
|
||||||
|
VerifyBlockAccessTrace(&c, expected_records);
|
||||||
|
c.ResetTableReader();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(BlockBasedTableTest, TracingIterator) {
|
||||||
|
TableConstructor c(BytewiseComparator());
|
||||||
|
Options options;
|
||||||
|
BlockBasedTableOptions table_options = GetBlockBasedTableOptions();
|
||||||
|
options.create_if_missing = true;
|
||||||
|
table_options.block_cache = NewLRUCache(1024 * 1024, 0);
|
||||||
|
table_options.cache_index_and_filter_blocks = true;
|
||||||
|
table_options.filter_policy.reset(NewBloomFilterPolicy(10, true));
|
||||||
|
options.table_factory.reset(new BlockBasedTableFactory(table_options));
|
||||||
|
SetupTracingTest(&c);
|
||||||
|
std::vector<std::string> keys;
|
||||||
|
stl_wrappers::KVMap kvmap;
|
||||||
|
ImmutableCFOptions ioptions(options);
|
||||||
|
MutableCFOptions moptions(options);
|
||||||
|
c.Finish(options, ioptions, moptions, table_options,
|
||||||
|
GetPlainInternalComparator(options.comparator), &keys, &kvmap);
|
||||||
|
|
||||||
|
for (uint32_t i = 1; i <= 2; i++) {
|
||||||
|
std::unique_ptr<InternalIterator> iter(c.GetTableReader()->NewIterator(
|
||||||
|
ReadOptions(), moptions.prefix_extractor.get(), /*arena=*/nullptr,
|
||||||
|
/*skip_filters=*/false, TableReaderCaller::kUserIterator));
|
||||||
|
iter->SeekToFirst();
|
||||||
|
while (iter->Valid()) {
|
||||||
|
iter->key();
|
||||||
|
iter->value();
|
||||||
|
iter->Next();
|
||||||
|
}
|
||||||
|
ASSERT_OK(iter->status());
|
||||||
|
iter.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify traces.
|
||||||
|
std::vector<BlockCacheTraceRecord> expected_records;
|
||||||
|
// The first two records should be prefetching index and filter blocks.
|
||||||
|
BlockCacheTraceRecord record;
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kPrefetch;
|
||||||
|
record.is_cache_hit = Boolean::kFalse;
|
||||||
|
record.no_insert = Boolean::kFalse;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceFilterBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
// Then we should have three records for index and two data block access.
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.caller = TableReaderCaller::kUserIterator;
|
||||||
|
record.is_cache_hit = Boolean::kTrue;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceDataBlock;
|
||||||
|
record.is_cache_hit = Boolean::kFalse;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
expected_records.push_back(record);
|
||||||
|
// When we iterate this file for the second time, we should observe all cache
|
||||||
|
// hits.
|
||||||
|
record.block_type = TraceType::kBlockTraceIndexBlock;
|
||||||
|
record.is_cache_hit = Boolean::kTrue;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
record.block_type = TraceType::kBlockTraceDataBlock;
|
||||||
|
expected_records.push_back(record);
|
||||||
|
expected_records.push_back(record);
|
||||||
|
VerifyBlockAccessTrace(&c, expected_records);
|
||||||
|
c.ResetTableReader();
|
||||||
|
}
|
||||||
|
|
||||||
// A simple tool that takes the snapshot of block cache statistics.
|
// A simple tool that takes the snapshot of block cache statistics.
|
||||||
class BlockCachePropertiesSnapshot {
|
class BlockCachePropertiesSnapshot {
|
||||||
public:
|
public:
|
||||||
|
@ -57,8 +57,8 @@ struct BlockAccessInfo {
|
|||||||
const uint64_t timestamp_in_seconds =
|
const uint64_t timestamp_in_seconds =
|
||||||
access.access_timestamp / kMicrosInSecond;
|
access.access_timestamp / kMicrosInSecond;
|
||||||
caller_num_accesses_timeline[access.caller][timestamp_in_seconds] += 1;
|
caller_num_accesses_timeline[access.caller][timestamp_in_seconds] += 1;
|
||||||
if (BlockCacheTraceHelper::ShouldTraceReferencedKey(access.block_type,
|
if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(access.block_type,
|
||||||
access.caller)) {
|
access.caller)) {
|
||||||
num_keys = access.num_keys_in_block;
|
num_keys = access.num_keys_in_block;
|
||||||
if (access.referenced_key_exist_in_block == Boolean::kTrue) {
|
if (access.referenced_key_exist_in_block == Boolean::kTrue) {
|
||||||
if (key_num_access_map.find(access.referenced_key) ==
|
if (key_num_access_map.find(access.referenced_key) ==
|
||||||
|
@ -35,14 +35,13 @@ const std::string BlockCacheTraceHelper::kUnknownColumnFamilyName =
|
|||||||
"UnknownColumnFamily";
|
"UnknownColumnFamily";
|
||||||
const uint64_t BlockCacheTraceHelper::kReservedGetId = 0;
|
const uint64_t BlockCacheTraceHelper::kReservedGetId = 0;
|
||||||
|
|
||||||
bool BlockCacheTraceHelper::ShouldTraceReferencedKey(TraceType block_type,
|
bool BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(
|
||||||
TableReaderCaller caller) {
|
TraceType block_type, TableReaderCaller caller) {
|
||||||
return (block_type == TraceType::kBlockTraceDataBlock) &&
|
return (block_type == TraceType::kBlockTraceDataBlock) &&
|
||||||
(caller == TableReaderCaller::kUserGet ||
|
IsGetOrMultiGet(caller);
|
||||||
caller == TableReaderCaller::kUserMultiGet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BlockCacheTraceHelper::ShouldTraceGetId(TableReaderCaller caller) {
|
bool BlockCacheTraceHelper::IsGetOrMultiGet(TableReaderCaller caller) {
|
||||||
return caller == TableReaderCaller::kUserGet ||
|
return caller == TableReaderCaller::kUserGet ||
|
||||||
caller == TableReaderCaller::kUserMultiGet;
|
caller == TableReaderCaller::kUserMultiGet;
|
||||||
}
|
}
|
||||||
@ -81,12 +80,13 @@ Status BlockCacheTraceWriter::WriteBlockAccess(
|
|||||||
trace.payload.push_back(record.caller);
|
trace.payload.push_back(record.caller);
|
||||||
trace.payload.push_back(record.is_cache_hit);
|
trace.payload.push_back(record.is_cache_hit);
|
||||||
trace.payload.push_back(record.no_insert);
|
trace.payload.push_back(record.no_insert);
|
||||||
if (BlockCacheTraceHelper::ShouldTraceGetId(record.caller)) {
|
if (BlockCacheTraceHelper::IsGetOrMultiGet(record.caller)) {
|
||||||
PutFixed64(&trace.payload, record.get_id);
|
PutFixed64(&trace.payload, record.get_id);
|
||||||
}
|
trace.payload.push_back(record.get_from_user_specified_snapshot);
|
||||||
if (BlockCacheTraceHelper::ShouldTraceReferencedKey(record.block_type,
|
|
||||||
record.caller)) {
|
|
||||||
PutLengthPrefixedSlice(&trace.payload, referenced_key);
|
PutLengthPrefixedSlice(&trace.payload, referenced_key);
|
||||||
|
}
|
||||||
|
if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(record.block_type,
|
||||||
|
record.caller)) {
|
||||||
PutFixed64(&trace.payload, record.referenced_data_size);
|
PutFixed64(&trace.payload, record.referenced_data_size);
|
||||||
PutFixed64(&trace.payload, record.num_keys_in_block);
|
PutFixed64(&trace.payload, record.num_keys_in_block);
|
||||||
trace.payload.push_back(record.referenced_key_exist_in_block);
|
trace.payload.push_back(record.referenced_key_exist_in_block);
|
||||||
@ -216,20 +216,28 @@ Status BlockCacheTraceReader::ReadAccess(BlockCacheTraceRecord* record) {
|
|||||||
}
|
}
|
||||||
record->no_insert = static_cast<Boolean>(enc_slice[0]);
|
record->no_insert = static_cast<Boolean>(enc_slice[0]);
|
||||||
enc_slice.remove_prefix(kCharSize);
|
enc_slice.remove_prefix(kCharSize);
|
||||||
if (BlockCacheTraceHelper::ShouldTraceGetId(record->caller)) {
|
if (BlockCacheTraceHelper::IsGetOrMultiGet(record->caller)) {
|
||||||
if (!GetFixed64(&enc_slice, &record->get_id)) {
|
if (!GetFixed64(&enc_slice, &record->get_id)) {
|
||||||
return Status::Incomplete(
|
return Status::Incomplete(
|
||||||
"Incomplete access record: Failed to read the get id.");
|
"Incomplete access record: Failed to read the get id.");
|
||||||
}
|
}
|
||||||
}
|
if (enc_slice.empty()) {
|
||||||
if (BlockCacheTraceHelper::ShouldTraceReferencedKey(record->block_type,
|
return Status::Incomplete(
|
||||||
record->caller)) {
|
"Incomplete access record: Failed to read "
|
||||||
|
"get_from_user_specified_snapshot.");
|
||||||
|
}
|
||||||
|
record->get_from_user_specified_snapshot =
|
||||||
|
static_cast<Boolean>(enc_slice[0]);
|
||||||
|
enc_slice.remove_prefix(kCharSize);
|
||||||
Slice referenced_key;
|
Slice referenced_key;
|
||||||
if (!GetLengthPrefixedSlice(&enc_slice, &referenced_key)) {
|
if (!GetLengthPrefixedSlice(&enc_slice, &referenced_key)) {
|
||||||
return Status::Incomplete(
|
return Status::Incomplete(
|
||||||
"Incomplete access record: Failed to read the referenced key.");
|
"Incomplete access record: Failed to read the referenced key.");
|
||||||
}
|
}
|
||||||
record->referenced_key = referenced_key.ToString();
|
record->referenced_key = referenced_key.ToString();
|
||||||
|
}
|
||||||
|
if (BlockCacheTraceHelper::IsGetOrMultiGetOnDataBlock(record->block_type,
|
||||||
|
record->caller)) {
|
||||||
if (!GetFixed64(&enc_slice, &record->referenced_data_size)) {
|
if (!GetFixed64(&enc_slice, &record->referenced_data_size)) {
|
||||||
return Status::Incomplete(
|
return Status::Incomplete(
|
||||||
"Incomplete access record: Failed to read the referenced data size.");
|
"Incomplete access record: Failed to read the referenced data size.");
|
||||||
|
@ -23,9 +23,9 @@ extern const uint64_t kSecondInHour;
|
|||||||
|
|
||||||
class BlockCacheTraceHelper {
|
class BlockCacheTraceHelper {
|
||||||
public:
|
public:
|
||||||
static bool ShouldTraceReferencedKey(TraceType block_type,
|
static bool IsGetOrMultiGetOnDataBlock(TraceType block_type,
|
||||||
TableReaderCaller caller);
|
TableReaderCaller caller);
|
||||||
static bool ShouldTraceGetId(TableReaderCaller caller);
|
static bool IsGetOrMultiGet(TableReaderCaller caller);
|
||||||
static bool IsUserAccess(TableReaderCaller caller);
|
static bool IsUserAccess(TableReaderCaller caller);
|
||||||
|
|
||||||
static const std::string kUnknownColumnFamilyName;
|
static const std::string kUnknownColumnFamilyName;
|
||||||
@ -53,8 +53,11 @@ class BlockCacheTraceHelper {
|
|||||||
// kUserApproximateSize).
|
// kUserApproximateSize).
|
||||||
struct BlockCacheLookupContext {
|
struct BlockCacheLookupContext {
|
||||||
BlockCacheLookupContext(const TableReaderCaller& _caller) : caller(_caller) {}
|
BlockCacheLookupContext(const TableReaderCaller& _caller) : caller(_caller) {}
|
||||||
BlockCacheLookupContext(const TableReaderCaller& _caller, uint64_t _get_id)
|
BlockCacheLookupContext(const TableReaderCaller& _caller, uint64_t _get_id,
|
||||||
: caller(_caller), get_id(_get_id) {}
|
bool _get_from_user_specified_snapshot)
|
||||||
|
: caller(_caller),
|
||||||
|
get_id(_get_id),
|
||||||
|
get_from_user_specified_snapshot(_get_from_user_specified_snapshot) {}
|
||||||
const TableReaderCaller caller;
|
const TableReaderCaller caller;
|
||||||
// These are populated when we perform lookup/insert on block cache. The block
|
// These are populated when we perform lookup/insert on block cache. The block
|
||||||
// cache tracer uses these inforation when logging the block access at
|
// cache tracer uses these inforation when logging the block access at
|
||||||
@ -69,6 +72,8 @@ struct BlockCacheLookupContext {
|
|||||||
// how many blocks a Get/MultiGet request accesses. We can also measure the
|
// how many blocks a Get/MultiGet request accesses. We can also measure the
|
||||||
// impact of row cache vs block cache.
|
// impact of row cache vs block cache.
|
||||||
uint64_t get_id = 0;
|
uint64_t get_id = 0;
|
||||||
|
std::string referenced_key;
|
||||||
|
bool get_from_user_specified_snapshot = false;
|
||||||
|
|
||||||
void FillLookupContext(bool _is_cache_hit, bool _no_insert,
|
void FillLookupContext(bool _is_cache_hit, bool _no_insert,
|
||||||
TraceType _block_type, uint64_t _block_size,
|
TraceType _block_type, uint64_t _block_size,
|
||||||
@ -100,23 +105,25 @@ struct BlockCacheTraceRecord {
|
|||||||
Boolean no_insert = Boolean::kFalse;
|
Boolean no_insert = Boolean::kFalse;
|
||||||
// Required field for Get and MultiGet
|
// Required field for Get and MultiGet
|
||||||
uint64_t get_id = BlockCacheTraceHelper::kReservedGetId;
|
uint64_t get_id = BlockCacheTraceHelper::kReservedGetId;
|
||||||
// Required fields for data block and user Get/Multi-Get only.
|
Boolean get_from_user_specified_snapshot = Boolean::kFalse;
|
||||||
std::string referenced_key;
|
std::string referenced_key;
|
||||||
|
// Required fields for data block and user Get/Multi-Get only.
|
||||||
uint64_t referenced_data_size = 0;
|
uint64_t referenced_data_size = 0;
|
||||||
uint64_t num_keys_in_block = 0;
|
uint64_t num_keys_in_block = 0;
|
||||||
Boolean referenced_key_exist_in_block = Boolean::kFalse;
|
Boolean referenced_key_exist_in_block = Boolean::kFalse;
|
||||||
|
|
||||||
BlockCacheTraceRecord() {}
|
BlockCacheTraceRecord() {}
|
||||||
|
|
||||||
BlockCacheTraceRecord(uint64_t _access_timestamp, std::string _block_key,
|
BlockCacheTraceRecord(
|
||||||
TraceType _block_type, uint64_t _block_size,
|
uint64_t _access_timestamp, std::string _block_key, TraceType _block_type,
|
||||||
uint64_t _cf_id, std::string _cf_name, uint32_t _level,
|
uint64_t _block_size, uint64_t _cf_id, std::string _cf_name,
|
||||||
uint64_t _sst_fd_number, TableReaderCaller _caller,
|
uint32_t _level, uint64_t _sst_fd_number, TableReaderCaller _caller,
|
||||||
bool _is_cache_hit, bool _no_insert, uint64_t _get_id,
|
bool _is_cache_hit, bool _no_insert,
|
||||||
std::string _referenced_key = "",
|
uint64_t _get_id = BlockCacheTraceHelper::kReservedGetId,
|
||||||
uint64_t _referenced_data_size = 0,
|
bool _get_from_user_specified_snapshot = false,
|
||||||
uint64_t _num_keys_in_block = 0,
|
std::string _referenced_key = "", uint64_t _referenced_data_size = 0,
|
||||||
bool _referenced_key_exist_in_block = false)
|
uint64_t _num_keys_in_block = 0,
|
||||||
|
bool _referenced_key_exist_in_block = false)
|
||||||
: access_timestamp(_access_timestamp),
|
: access_timestamp(_access_timestamp),
|
||||||
block_key(_block_key),
|
block_key(_block_key),
|
||||||
block_type(_block_type),
|
block_type(_block_type),
|
||||||
@ -129,6 +136,9 @@ struct BlockCacheTraceRecord {
|
|||||||
is_cache_hit(_is_cache_hit ? Boolean::kTrue : Boolean::kFalse),
|
is_cache_hit(_is_cache_hit ? Boolean::kTrue : Boolean::kFalse),
|
||||||
no_insert(_no_insert ? Boolean::kTrue : Boolean::kFalse),
|
no_insert(_no_insert ? Boolean::kTrue : Boolean::kFalse),
|
||||||
get_id(_get_id),
|
get_id(_get_id),
|
||||||
|
get_from_user_specified_snapshot(_get_from_user_specified_snapshot
|
||||||
|
? Boolean::kTrue
|
||||||
|
: Boolean::kFalse),
|
||||||
referenced_key(_referenced_key),
|
referenced_key(_referenced_key),
|
||||||
referenced_data_size(_referenced_data_size),
|
referenced_data_size(_referenced_data_size),
|
||||||
num_keys_in_block(_num_keys_in_block),
|
num_keys_in_block(_num_keys_in_block),
|
||||||
|
@ -74,6 +74,7 @@ class BlockCacheTracerTest : public testing::Test {
|
|||||||
// Provide get_id for all callers. The writer should only write get_id
|
// Provide get_id for all callers. The writer should only write get_id
|
||||||
// when the caller is either GET or MGET.
|
// when the caller is either GET or MGET.
|
||||||
record.get_id = key_id + 1;
|
record.get_id = key_id + 1;
|
||||||
|
record.get_from_user_specified_snapshot = Boolean::kTrue;
|
||||||
// Provide these fields for all block types.
|
// Provide these fields for all block types.
|
||||||
// The writer should only write these fields for data blocks and the
|
// The writer should only write these fields for data blocks and the
|
||||||
// caller is either GET or MGET.
|
// caller is either GET or MGET.
|
||||||
@ -126,20 +127,22 @@ class BlockCacheTracerTest : public testing::Test {
|
|||||||
if (record.caller == TableReaderCaller::kUserGet ||
|
if (record.caller == TableReaderCaller::kUserGet ||
|
||||||
record.caller == TableReaderCaller::kUserMultiGet) {
|
record.caller == TableReaderCaller::kUserMultiGet) {
|
||||||
ASSERT_EQ(key_id + 1, record.get_id);
|
ASSERT_EQ(key_id + 1, record.get_id);
|
||||||
|
ASSERT_EQ(Boolean::kTrue, record.get_from_user_specified_snapshot);
|
||||||
|
ASSERT_EQ(kRefKeyPrefix + std::to_string(key_id),
|
||||||
|
record.referenced_key);
|
||||||
} else {
|
} else {
|
||||||
ASSERT_EQ(BlockCacheTraceHelper::kReservedGetId, record.get_id);
|
ASSERT_EQ(BlockCacheTraceHelper::kReservedGetId, record.get_id);
|
||||||
|
ASSERT_EQ(Boolean::kFalse, record.get_from_user_specified_snapshot);
|
||||||
|
ASSERT_EQ("", record.referenced_key);
|
||||||
}
|
}
|
||||||
if (block_type == TraceType::kBlockTraceDataBlock &&
|
if (block_type == TraceType::kBlockTraceDataBlock &&
|
||||||
(record.caller == TableReaderCaller::kUserGet ||
|
(record.caller == TableReaderCaller::kUserGet ||
|
||||||
record.caller == TableReaderCaller::kUserMultiGet)) {
|
record.caller == TableReaderCaller::kUserMultiGet)) {
|
||||||
ASSERT_EQ(kRefKeyPrefix + std::to_string(key_id),
|
|
||||||
record.referenced_key);
|
|
||||||
ASSERT_EQ(Boolean::kTrue, record.referenced_key_exist_in_block);
|
ASSERT_EQ(Boolean::kTrue, record.referenced_key_exist_in_block);
|
||||||
ASSERT_EQ(kNumKeysInBlock, record.num_keys_in_block);
|
ASSERT_EQ(kNumKeysInBlock, record.num_keys_in_block);
|
||||||
ASSERT_EQ(kReferencedDataSize + key_id, record.referenced_data_size);
|
ASSERT_EQ(kReferencedDataSize + key_id, record.referenced_data_size);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ASSERT_EQ("", record.referenced_key);
|
|
||||||
ASSERT_EQ(Boolean::kFalse, record.referenced_key_exist_in_block);
|
ASSERT_EQ(Boolean::kFalse, record.referenced_key_exist_in_block);
|
||||||
ASSERT_EQ(0, record.num_keys_in_block);
|
ASSERT_EQ(0, record.num_keys_in_block);
|
||||||
ASSERT_EQ(0, record.referenced_data_size);
|
ASSERT_EQ(0, record.referenced_data_size);
|
||||||
|
@ -110,19 +110,22 @@ void PrioritizedCacheSimulator::Access(const BlockCacheTraceRecord& access) {
|
|||||||
std::string HybridRowBlockCacheSimulator::ComputeRowKey(
|
std::string HybridRowBlockCacheSimulator::ComputeRowKey(
|
||||||
const BlockCacheTraceRecord& access) {
|
const BlockCacheTraceRecord& access) {
|
||||||
assert(access.get_id != BlockCacheTraceHelper::kReservedGetId);
|
assert(access.get_id != BlockCacheTraceHelper::kReservedGetId);
|
||||||
Slice key;
|
Slice key = ExtractUserKey(access.referenced_key);
|
||||||
if (access.referenced_key_exist_in_block == Boolean::kTrue) {
|
uint64_t seq_no = access.get_from_user_specified_snapshot == Boolean::kFalse
|
||||||
key = ExtractUserKey(access.referenced_key);
|
? 0
|
||||||
} else {
|
: 1 + GetInternalKeySeqno(access.referenced_key);
|
||||||
key = access.referenced_key;
|
return std::to_string(access.sst_fd_number) + "_" + key.ToString() + "_" +
|
||||||
}
|
std::to_string(seq_no);
|
||||||
return std::to_string(access.sst_fd_number) + "_" + key.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HybridRowBlockCacheSimulator::Access(const BlockCacheTraceRecord& access) {
|
void HybridRowBlockCacheSimulator::Access(const BlockCacheTraceRecord& access) {
|
||||||
bool is_cache_miss = true;
|
bool is_cache_miss = true;
|
||||||
bool admitted = true;
|
bool admitted = true;
|
||||||
if (access.get_id != BlockCacheTraceHelper::kReservedGetId) {
|
// TODO (haoyu): We only support Get for now. We need to extend the tracing
|
||||||
|
// for MultiGet, i.e., non-data block accesses must log all keys in a
|
||||||
|
// MultiGet.
|
||||||
|
if (access.caller == TableReaderCaller::kUserGet &&
|
||||||
|
access.get_id != BlockCacheTraceHelper::kReservedGetId) {
|
||||||
// This is a Get/MultiGet request.
|
// This is a Get/MultiGet request.
|
||||||
const std::string& row_key = ComputeRowKey(access);
|
const std::string& row_key = ComputeRowKey(access);
|
||||||
if (getid_getkeys_map_[access.get_id].find(row_key) ==
|
if (getid_getkeys_map_[access.get_id].find(row_key) ==
|
||||||
|
@ -137,7 +137,6 @@ class HybridRowBlockCacheSimulator : public PrioritizedCacheSimulator {
|
|||||||
private:
|
private:
|
||||||
// Row key is a concatenation of the access's fd_number and the referenced
|
// Row key is a concatenation of the access's fd_number and the referenced
|
||||||
// user key.
|
// user key.
|
||||||
// TODO(haoyu): the row key should contain sequence number.
|
|
||||||
std::string ComputeRowKey(const BlockCacheTraceRecord& access);
|
std::string ComputeRowKey(const BlockCacheTraceRecord& access);
|
||||||
|
|
||||||
enum InsertResult : char {
|
enum InsertResult : char {
|
||||||
|
@ -174,10 +174,11 @@ TEST_F(CacheSimulatorTest, GhostPrioritizedCacheSimulator) {
|
|||||||
TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulator) {
|
TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulator) {
|
||||||
uint64_t block_id = 100;
|
uint64_t block_id = 100;
|
||||||
BlockCacheTraceRecord first_get = GenerateGetRecord(kGetId);
|
BlockCacheTraceRecord first_get = GenerateGetRecord(kGetId);
|
||||||
|
first_get.get_from_user_specified_snapshot = Boolean::kTrue;
|
||||||
BlockCacheTraceRecord second_get = GenerateGetRecord(kGetId + 1);
|
BlockCacheTraceRecord second_get = GenerateGetRecord(kGetId + 1);
|
||||||
second_get.referenced_data_size = 0;
|
second_get.referenced_data_size = 0;
|
||||||
second_get.referenced_key_exist_in_block = Boolean::kFalse;
|
second_get.referenced_key_exist_in_block = Boolean::kFalse;
|
||||||
second_get.referenced_key = kRefKeyPrefix + std::to_string(kGetId);
|
second_get.get_from_user_specified_snapshot = Boolean::kTrue;
|
||||||
BlockCacheTraceRecord third_get = GenerateGetRecord(kGetId + 2);
|
BlockCacheTraceRecord third_get = GenerateGetRecord(kGetId + 2);
|
||||||
third_get.referenced_data_size = 0;
|
third_get.referenced_data_size = 0;
|
||||||
third_get.referenced_key_exist_in_block = Boolean::kFalse;
|
third_get.referenced_key_exist_in_block = Boolean::kFalse;
|
||||||
@ -203,9 +204,10 @@ TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulator) {
|
|||||||
ASSERT_EQ(100, cache_simulator->miss_ratio());
|
ASSERT_EQ(100, cache_simulator->miss_ratio());
|
||||||
ASSERT_EQ(10, cache_simulator->user_accesses());
|
ASSERT_EQ(10, cache_simulator->user_accesses());
|
||||||
ASSERT_EQ(100, cache_simulator->user_miss_ratio());
|
ASSERT_EQ(100, cache_simulator->user_miss_ratio());
|
||||||
auto handle =
|
auto handle = sim_cache->Lookup(
|
||||||
sim_cache->Lookup(ExtractUserKey(std::to_string(first_get.sst_fd_number) +
|
std::to_string(first_get.sst_fd_number) + "_" +
|
||||||
"_" + first_get.referenced_key));
|
ExtractUserKey(first_get.referenced_key).ToString() + "_" +
|
||||||
|
std::to_string(1 + GetInternalKeySeqno(first_get.referenced_key)));
|
||||||
ASSERT_NE(nullptr, handle);
|
ASSERT_NE(nullptr, handle);
|
||||||
sim_cache->Release(handle);
|
sim_cache->Release(handle);
|
||||||
for (uint32_t i = 100; i < block_id; i++) {
|
for (uint32_t i = 100; i < block_id; i++) {
|
||||||
@ -227,8 +229,10 @@ TEST_F(CacheSimulatorTest, HybridRowBlockCacheSimulator) {
|
|||||||
ASSERT_EQ(66, static_cast<uint64_t>(cache_simulator->miss_ratio()));
|
ASSERT_EQ(66, static_cast<uint64_t>(cache_simulator->miss_ratio()));
|
||||||
ASSERT_EQ(15, cache_simulator->user_accesses());
|
ASSERT_EQ(15, cache_simulator->user_accesses());
|
||||||
ASSERT_EQ(66, static_cast<uint64_t>(cache_simulator->user_miss_ratio()));
|
ASSERT_EQ(66, static_cast<uint64_t>(cache_simulator->user_miss_ratio()));
|
||||||
handle = sim_cache->Lookup(std::to_string(second_get.sst_fd_number) + "_" +
|
handle = sim_cache->Lookup(
|
||||||
second_get.referenced_key);
|
std::to_string(second_get.sst_fd_number) + "_" +
|
||||||
|
ExtractUserKey(second_get.referenced_key).ToString() + "_" +
|
||||||
|
std::to_string(1 + GetInternalKeySeqno(second_get.referenced_key)));
|
||||||
ASSERT_NE(nullptr, handle);
|
ASSERT_NE(nullptr, handle);
|
||||||
sim_cache->Release(handle);
|
sim_cache->Release(handle);
|
||||||
for (uint32_t i = 100; i < block_id; i++) {
|
for (uint32_t i = 100; i < block_id; i++) {
|
||||||
@ -283,9 +287,9 @@ TEST_F(CacheSimulatorTest, HybridRowBlockNoInsertCacheSimulator) {
|
|||||||
cache_simulator->Access(first_get);
|
cache_simulator->Access(first_get);
|
||||||
block_id++;
|
block_id++;
|
||||||
}
|
}
|
||||||
auto handle =
|
auto handle = sim_cache->Lookup(
|
||||||
sim_cache->Lookup(ExtractUserKey(std::to_string(first_get.sst_fd_number) +
|
std::to_string(first_get.sst_fd_number) + "_" +
|
||||||
"_" + first_get.referenced_key));
|
ExtractUserKey(first_get.referenced_key).ToString() + "_0");
|
||||||
ASSERT_NE(nullptr, handle);
|
ASSERT_NE(nullptr, handle);
|
||||||
sim_cache->Release(handle);
|
sim_cache->Release(handle);
|
||||||
// All blocks are missing from the cache since insert_blocks_row_kvpair_misses
|
// All blocks are missing from the cache since insert_blocks_row_kvpair_misses
|
||||||
|
Loading…
Reference in New Issue
Block a user