Store the filter bits reader alongside the filter block contents (#5936)

Summary:
Amongst other things, PR https://github.com/facebook/rocksdb/issues/5504 refactored the filter block readers so that
only the filter block contents are stored in the block cache (as opposed to the
earlier design where the cache stored the filter block reader itself, leading to
potentially dangling pointers and concurrency bugs). However, this change
introduced a performance hit since with the new code, the metadata fields are
re-parsed upon every access. This patch reunites the block contents with the
filter bits reader to eliminate this overhead; since this is still a self-contained
pure data object, it is safe to store it in the cache. (Note: this is similar to how
the zstd digest is handled.)
Pull Request resolved: https://github.com/facebook/rocksdb/pull/5936

Test Plan:
make asan_check

filter_bench results for the old code:

```
$ ./filter_bench -quick
WARNING: Assertions are enabled; benchmarks unnecessarily slow
Building...
Build avg ns/key: 26.7153
Number of filters: 16669
Total memory (MB): 200.009
Bits/key actual: 10.0647
----------------------------
Inside queries...
  Dry run (46b) ns/op: 33.4258
  Single filter ns/op: 42.5974
  Random filter ns/op: 217.861
----------------------------
Outside queries...
  Dry run (25d) ns/op: 32.4217
  Single filter ns/op: 50.9855
  Random filter ns/op: 219.167
    Average FP rate %: 1.13993
----------------------------
Done. (For more info, run with -legend or -help.)

$ ./filter_bench -quick -use_full_block_reader
WARNING: Assertions are enabled; benchmarks unnecessarily slow
Building...
Build avg ns/key: 26.5172
Number of filters: 16669
Total memory (MB): 200.009
Bits/key actual: 10.0647
----------------------------
Inside queries...
  Dry run (46b) ns/op: 32.3556
  Single filter ns/op: 83.2239
  Random filter ns/op: 370.676
----------------------------
Outside queries...
  Dry run (25d) ns/op: 32.2265
  Single filter ns/op: 93.5651
  Random filter ns/op: 408.393
    Average FP rate %: 1.13993
----------------------------
Done. (For more info, run with -legend or -help.)
```

With the new code:

```
$ ./filter_bench -quick
WARNING: Assertions are enabled; benchmarks unnecessarily slow
Building...
Build avg ns/key: 25.4285
Number of filters: 16669
Total memory (MB): 200.009
Bits/key actual: 10.0647
----------------------------
Inside queries...
  Dry run (46b) ns/op: 31.0594
  Single filter ns/op: 43.8974
  Random filter ns/op: 226.075
----------------------------
Outside queries...
  Dry run (25d) ns/op: 31.0295
  Single filter ns/op: 50.3824
  Random filter ns/op: 226.805
    Average FP rate %: 1.13993
----------------------------
Done. (For more info, run with -legend or -help.)

$ ./filter_bench -quick -use_full_block_reader
WARNING: Assertions are enabled; benchmarks unnecessarily slow
Building...
Build avg ns/key: 26.5308
Number of filters: 16669
Total memory (MB): 200.009
Bits/key actual: 10.0647
----------------------------
Inside queries...
  Dry run (46b) ns/op: 33.2968
  Single filter ns/op: 58.6163
  Random filter ns/op: 291.434
----------------------------
Outside queries...
  Dry run (25d) ns/op: 32.1839
  Single filter ns/op: 66.9039
  Random filter ns/op: 292.828
    Average FP rate %: 1.13993
----------------------------
Done. (For more info, run with -legend or -help.)
```

Differential Revision: D17991712

Pulled By: ltamasi

fbshipit-source-id: 7ea205550217bfaaa1d5158ebd658e5832e60f29
main
Levi Tamasi 5 years ago committed by Facebook Github Bot
parent c53db172a1
commit 29ccf2075c
  1. 1
      CMakeLists.txt
  2. 1
      TARGETS
  3. 1
      src.mk
  4. 51
      table/block_based/block_based_table_reader.cc
  5. 2
      table/block_based/filter_block_reader_common.cc
  6. 34
      table/block_based/full_filter_block.cc
  7. 7
      table/block_based/full_filter_block.h
  8. 28
      table/block_based/full_filter_block_test.cc
  9. 22
      table/block_based/parsed_full_filter_block.cc
  10. 40
      table/block_based/parsed_full_filter_block.h
  11. 6
      table/block_based/partitioned_filter_block.cc
  12. 5
      table/block_based/partitioned_filter_block.h
  13. 21
      table/block_based/partitioned_filter_block_test.cc
  14. 8
      util/filter_bench.cc

@ -602,6 +602,7 @@ set(SOURCES
table/block_based/flush_block_policy.cc table/block_based/flush_block_policy.cc
table/block_based/full_filter_block.cc table/block_based/full_filter_block.cc
table/block_based/index_builder.cc table/block_based/index_builder.cc
table/block_based/parsed_full_filter_block.cc
table/block_based/partitioned_filter_block.cc table/block_based/partitioned_filter_block.cc
table/block_based/uncompression_dict_reader.cc table/block_based/uncompression_dict_reader.cc
table/block_fetcher.cc table/block_fetcher.cc

@ -233,6 +233,7 @@ cpp_library(
"table/block_based/flush_block_policy.cc", "table/block_based/flush_block_policy.cc",
"table/block_based/full_filter_block.cc", "table/block_based/full_filter_block.cc",
"table/block_based/index_builder.cc", "table/block_based/index_builder.cc",
"table/block_based/parsed_full_filter_block.cc",
"table/block_based/partitioned_filter_block.cc", "table/block_based/partitioned_filter_block.cc",
"table/block_based/uncompression_dict_reader.cc", "table/block_based/uncompression_dict_reader.cc",
"table/block_fetcher.cc", "table/block_fetcher.cc",

@ -128,6 +128,7 @@ LIB_SOURCES = \
table/block_based/flush_block_policy.cc \ table/block_based/flush_block_policy.cc \
table/block_based/full_filter_block.cc \ table/block_based/full_filter_block.cc \
table/block_based/index_builder.cc \ table/block_based/index_builder.cc \
table/block_based/parsed_full_filter_block.cc \
table/block_based/partitioned_filter_block.cc \ table/block_based/partitioned_filter_block.cc \
table/block_based/uncompression_dict_reader.cc \ table/block_based/uncompression_dict_reader.cc \
table/block_fetcher.cc \ table/block_fetcher.cc \

@ -84,7 +84,8 @@ class BlocklikeTraits<BlockContents> {
SequenceNumber /* global_seqno */, SequenceNumber /* global_seqno */,
size_t /* read_amp_bytes_per_bit */, size_t /* read_amp_bytes_per_bit */,
Statistics* /* statistics */, Statistics* /* statistics */,
bool /* using_zstd */) { bool /* using_zstd */,
const FilterPolicy* /* filter_policy */) {
return new BlockContents(std::move(contents)); return new BlockContents(std::move(contents));
} }
@ -93,12 +94,30 @@ class BlocklikeTraits<BlockContents> {
} }
}; };
template <>
class BlocklikeTraits<ParsedFullFilterBlock> {
public:
static ParsedFullFilterBlock* Create(BlockContents&& contents,
SequenceNumber /* global_seqno */,
size_t /* read_amp_bytes_per_bit */,
Statistics* /* statistics */,
bool /* using_zstd */,
const FilterPolicy* filter_policy) {
return new ParsedFullFilterBlock(filter_policy, std::move(contents));
}
static uint32_t GetNumRestarts(const ParsedFullFilterBlock& /* block */) {
return 0;
}
};
template <> template <>
class BlocklikeTraits<Block> { class BlocklikeTraits<Block> {
public: public:
static Block* Create(BlockContents&& contents, SequenceNumber global_seqno, static Block* Create(BlockContents&& contents, SequenceNumber global_seqno,
size_t read_amp_bytes_per_bit, Statistics* statistics, size_t read_amp_bytes_per_bit, Statistics* statistics,
bool /* using_zstd */) { bool /* using_zstd */,
const FilterPolicy* /* filter_policy */) {
return new Block(std::move(contents), global_seqno, read_amp_bytes_per_bit, return new Block(std::move(contents), global_seqno, read_amp_bytes_per_bit,
statistics); statistics);
} }
@ -115,7 +134,8 @@ class BlocklikeTraits<UncompressionDict> {
SequenceNumber /* global_seqno */, SequenceNumber /* global_seqno */,
size_t /* read_amp_bytes_per_bit */, size_t /* read_amp_bytes_per_bit */,
Statistics* /* statistics */, Statistics* /* statistics */,
bool using_zstd) { bool using_zstd,
const FilterPolicy* /* filter_policy */) {
return new UncompressionDict(contents.data, std::move(contents.allocation), return new UncompressionDict(contents.data, std::move(contents.allocation),
using_zstd); using_zstd);
} }
@ -141,7 +161,7 @@ Status ReadBlockFromFile(
const UncompressionDict& uncompression_dict, const UncompressionDict& uncompression_dict,
const PersistentCacheOptions& cache_options, SequenceNumber global_seqno, const PersistentCacheOptions& cache_options, SequenceNumber global_seqno,
size_t read_amp_bytes_per_bit, MemoryAllocator* memory_allocator, size_t read_amp_bytes_per_bit, MemoryAllocator* memory_allocator,
bool for_compaction, bool using_zstd) { bool for_compaction, bool using_zstd, const FilterPolicy* filter_policy) {
assert(result); assert(result);
BlockContents contents; BlockContents contents;
@ -153,7 +173,7 @@ Status ReadBlockFromFile(
if (s.ok()) { if (s.ok()) {
result->reset(BlocklikeTraits<TBlocklike>::Create( result->reset(BlocklikeTraits<TBlocklike>::Create(
std::move(contents), global_seqno, read_amp_bytes_per_bit, std::move(contents), global_seqno, read_amp_bytes_per_bit,
ioptions.statistics, using_zstd)); ioptions.statistics, using_zstd, filter_policy));
} }
return s; return s;
@ -1629,7 +1649,7 @@ Status BlockBasedTable::ReadMetaBlock(FilePrefetchBuffer* prefetch_buffer,
UncompressionDict::GetEmptyDict(), rep_->persistent_cache_options, UncompressionDict::GetEmptyDict(), rep_->persistent_cache_options,
kDisableGlobalSequenceNumber, 0 /* read_amp_bytes_per_bit */, kDisableGlobalSequenceNumber, 0 /* read_amp_bytes_per_bit */,
GetMemoryAllocator(rep_->table_options), false /* for_compaction */, GetMemoryAllocator(rep_->table_options), false /* for_compaction */,
rep_->blocks_definitely_zstd_compressed); rep_->blocks_definitely_zstd_compressed, nullptr /* filter_policy */);
if (!s.ok()) { if (!s.ok()) {
ROCKS_LOG_ERROR(rep_->ioptions.info_log, ROCKS_LOG_ERROR(rep_->ioptions.info_log,
@ -1718,7 +1738,8 @@ Status BlockBasedTable::GetDataBlockFromCache(
BlocklikeTraits<TBlocklike>::Create( BlocklikeTraits<TBlocklike>::Create(
std::move(contents), rep_->get_global_seqno(block_type), std::move(contents), rep_->get_global_seqno(block_type),
read_amp_bytes_per_bit, statistics, read_amp_bytes_per_bit, statistics,
rep_->blocks_definitely_zstd_compressed)); // uncompressed block rep_->blocks_definitely_zstd_compressed,
rep_->table_options.filter_policy.get())); // uncompressed block
if (block_cache != nullptr && block_holder->own_bytes() && if (block_cache != nullptr && block_holder->own_bytes() &&
read_options.fill_cache) { read_options.fill_cache) {
@ -1789,11 +1810,13 @@ Status BlockBasedTable::PutDataBlockToCache(
block_holder.reset(BlocklikeTraits<TBlocklike>::Create( block_holder.reset(BlocklikeTraits<TBlocklike>::Create(
std::move(uncompressed_block_contents), seq_no, read_amp_bytes_per_bit, std::move(uncompressed_block_contents), seq_no, read_amp_bytes_per_bit,
statistics, rep_->blocks_definitely_zstd_compressed)); statistics, rep_->blocks_definitely_zstd_compressed,
rep_->table_options.filter_policy.get()));
} else { } else {
block_holder.reset(BlocklikeTraits<TBlocklike>::Create( block_holder.reset(BlocklikeTraits<TBlocklike>::Create(
std::move(*raw_block_contents), seq_no, read_amp_bytes_per_bit, std::move(*raw_block_contents), seq_no, read_amp_bytes_per_bit,
statistics, rep_->blocks_definitely_zstd_compressed)); statistics, rep_->blocks_definitely_zstd_compressed,
rep_->table_options.filter_policy.get()));
} }
// Insert compressed block into compressed block cache. // Insert compressed block into compressed block cache.
@ -2458,7 +2481,8 @@ Status BlockBasedTable::RetrieveBlock(
? rep_->table_options.read_amp_bytes_per_bit ? rep_->table_options.read_amp_bytes_per_bit
: 0, : 0,
GetMemoryAllocator(rep_->table_options), for_compaction, GetMemoryAllocator(rep_->table_options), for_compaction,
rep_->blocks_definitely_zstd_compressed); rep_->blocks_definitely_zstd_compressed,
rep_->table_options.filter_policy.get());
} }
if (!s.ok()) { if (!s.ok()) {
@ -2480,6 +2504,13 @@ template Status BlockBasedTable::RetrieveBlock<BlockContents>(
GetContext* get_context, BlockCacheLookupContext* lookup_context, GetContext* get_context, BlockCacheLookupContext* lookup_context,
bool for_compaction, bool use_cache) const; bool for_compaction, bool use_cache) const;
template Status BlockBasedTable::RetrieveBlock<ParsedFullFilterBlock>(
FilePrefetchBuffer* prefetch_buffer, const ReadOptions& ro,
const BlockHandle& handle, const UncompressionDict& uncompression_dict,
CachableEntry<ParsedFullFilterBlock>* block_entry, BlockType block_type,
GetContext* get_context, BlockCacheLookupContext* lookup_context,
bool for_compaction, bool use_cache) const;
template Status BlockBasedTable::RetrieveBlock<Block>( template Status BlockBasedTable::RetrieveBlock<Block>(
FilePrefetchBuffer* prefetch_buffer, const ReadOptions& ro, FilePrefetchBuffer* prefetch_buffer, const ReadOptions& ro,
const BlockHandle& handle, const UncompressionDict& uncompression_dict, const BlockHandle& handle, const UncompressionDict& uncompression_dict,

@ -7,6 +7,7 @@
#include "table/block_based/filter_block_reader_common.h" #include "table/block_based/filter_block_reader_common.h"
#include "monitoring/perf_context_imp.h" #include "monitoring/perf_context_imp.h"
#include "table/block_based/block_based_table_reader.h" #include "table/block_based/block_based_table_reader.h"
#include "table/block_based/parsed_full_filter_block.h"
namespace rocksdb { namespace rocksdb {
@ -96,5 +97,6 @@ size_t FilterBlockReaderCommon<TBlocklike>::ApproximateFilterBlockMemoryUsage()
// This makes it possible to keep the template definitions in the .cc file. // This makes it possible to keep the template definitions in the .cc file.
template class FilterBlockReaderCommon<BlockContents>; template class FilterBlockReaderCommon<BlockContents>;
template class FilterBlockReaderCommon<Block>; template class FilterBlockReaderCommon<Block>;
template class FilterBlockReaderCommon<ParsedFullFilterBlock>;
} // namespace rocksdb } // namespace rocksdb

@ -93,7 +93,8 @@ Slice FullFilterBlockBuilder::Finish(const BlockHandle& /*tmp*/,
} }
FullFilterBlockReader::FullFilterBlockReader( FullFilterBlockReader::FullFilterBlockReader(
const BlockBasedTable* t, CachableEntry<BlockContents>&& filter_block) const BlockBasedTable* t,
CachableEntry<ParsedFullFilterBlock>&& filter_block)
: FilterBlockReaderCommon(t, std::move(filter_block)) { : FilterBlockReaderCommon(t, std::move(filter_block)) {
const SliceTransform* const prefix_extractor = table_prefix_extractor(); const SliceTransform* const prefix_extractor = table_prefix_extractor();
if (prefix_extractor) { if (prefix_extractor) {
@ -125,7 +126,7 @@ std::unique_ptr<FilterBlockReader> FullFilterBlockReader::Create(
assert(table->get_rep()); assert(table->get_rep());
assert(!pin || prefetch); assert(!pin || prefetch);
CachableEntry<BlockContents> filter_block; CachableEntry<ParsedFullFilterBlock> filter_block;
if (prefetch || !use_cache) { if (prefetch || !use_cache) {
const Status s = ReadFilterBlock(table, prefetch_buffer, ReadOptions(), const Status s = ReadFilterBlock(table, prefetch_buffer, ReadOptions(),
use_cache, nullptr /* get_context */, use_cache, nullptr /* get_context */,
@ -158,7 +159,7 @@ bool FullFilterBlockReader::PrefixMayMatch(
bool FullFilterBlockReader::MayMatch( bool FullFilterBlockReader::MayMatch(
const Slice& entry, bool no_io, GetContext* get_context, const Slice& entry, bool no_io, GetContext* get_context,
BlockCacheLookupContext* lookup_context) const { BlockCacheLookupContext* lookup_context) const {
CachableEntry<BlockContents> filter_block; CachableEntry<ParsedFullFilterBlock> filter_block;
const Status s = const Status s =
GetOrReadFilterBlock(no_io, get_context, lookup_context, &filter_block); GetOrReadFilterBlock(no_io, get_context, lookup_context, &filter_block);
@ -168,15 +169,10 @@ bool FullFilterBlockReader::MayMatch(
assert(filter_block.GetValue()); assert(filter_block.GetValue());
if (filter_block.GetValue()->data.size() != 0) { FilterBitsReader* const filter_bits_reader =
assert(table()); filter_block.GetValue()->filter_bits_reader();
assert(table()->get_rep());
std::unique_ptr<FilterBitsReader> filter_bits_reader(
table()->get_rep()->filter_policy->GetFilterBitsReader(
filter_block.GetValue()->data));
assert(filter_bits_reader != nullptr);
if (filter_bits_reader) {
if (filter_bits_reader->MayMatch(entry)) { if (filter_bits_reader->MayMatch(entry)) {
PERF_COUNTER_ADD(bloom_sst_hit_count, 1); PERF_COUNTER_ADD(bloom_sst_hit_count, 1);
return true; return true;
@ -220,7 +216,7 @@ void FullFilterBlockReader::PrefixesMayMatch(
void FullFilterBlockReader::MayMatch( void FullFilterBlockReader::MayMatch(
MultiGetRange* range, bool no_io, const SliceTransform* prefix_extractor, MultiGetRange* range, bool no_io, const SliceTransform* prefix_extractor,
BlockCacheLookupContext* lookup_context) const { BlockCacheLookupContext* lookup_context) const {
CachableEntry<BlockContents> filter_block; CachableEntry<ParsedFullFilterBlock> filter_block;
const Status s = GetOrReadFilterBlock(no_io, range->begin()->get_context, const Status s = GetOrReadFilterBlock(no_io, range->begin()->get_context,
lookup_context, &filter_block); lookup_context, &filter_block);
@ -230,18 +226,13 @@ void FullFilterBlockReader::MayMatch(
assert(filter_block.GetValue()); assert(filter_block.GetValue());
if (filter_block.GetValue()->data.size() == 0) { FilterBitsReader* const filter_bits_reader =
filter_block.GetValue()->filter_bits_reader();
if (!filter_bits_reader) {
return; return;
} }
assert(table());
assert(table()->get_rep());
std::unique_ptr<FilterBitsReader> filter_bits_reader(
table()->get_rep()->filter_policy->GetFilterBitsReader(
filter_block.GetValue()->data));
assert(filter_bits_reader != nullptr);
// We need to use an array instead of autovector for may_match since // We need to use an array instead of autovector for may_match since
// &may_match[0] doesn't work for autovector<bool> (compiler error). So // &may_match[0] doesn't work for autovector<bool> (compiler error). So
// declare both keys and may_match as arrays, which is also slightly less // declare both keys and may_match as arrays, which is also slightly less
@ -261,6 +252,7 @@ void FullFilterBlockReader::MayMatch(
filter_range.SkipKey(iter); filter_range.SkipKey(iter);
} }
} }
filter_bits_reader->MayMatch(num_keys, &keys[0], &may_match[0]); filter_bits_reader->MayMatch(num_keys, &keys[0], &may_match[0]);
int i = 0; int i = 0;

@ -16,7 +16,7 @@
#include "rocksdb/slice.h" #include "rocksdb/slice.h"
#include "rocksdb/slice_transform.h" #include "rocksdb/slice_transform.h"
#include "table/block_based/filter_block_reader_common.h" #include "table/block_based/filter_block_reader_common.h"
#include "table/format.h" #include "table/block_based/parsed_full_filter_block.h"
#include "util/hash.h" #include "util/hash.h"
namespace rocksdb { namespace rocksdb {
@ -80,10 +80,11 @@ class FullFilterBlockBuilder : public FilterBlockBuilder {
// A FilterBlockReader is used to parse filter from SST table. // A FilterBlockReader is used to parse filter from SST table.
// KeyMayMatch and PrefixMayMatch would trigger filter checking // KeyMayMatch and PrefixMayMatch would trigger filter checking
class FullFilterBlockReader : public FilterBlockReaderCommon<BlockContents> { class FullFilterBlockReader
: public FilterBlockReaderCommon<ParsedFullFilterBlock> {
public: public:
FullFilterBlockReader(const BlockBasedTable* t, FullFilterBlockReader(const BlockBasedTable* t,
CachableEntry<BlockContents>&& filter_block); CachableEntry<ParsedFullFilterBlock>&& filter_block);
static std::unique_ptr<FilterBlockReader> Create( static std::unique_ptr<FilterBlockReader> Create(
const BlockBasedTable* table, FilePrefetchBuffer* prefetch_buffer, const BlockBasedTable* table, FilePrefetchBuffer* prefetch_buffer,

@ -113,9 +113,10 @@ TEST_F(PluginFullFilterBlockTest, PluginEmptyBuilder) {
Slice slice = builder.Finish(); Slice slice = builder.Finish();
ASSERT_EQ("", EscapeString(slice)); ASSERT_EQ("", EscapeString(slice));
CachableEntry<BlockContents> block( CachableEntry<ParsedFullFilterBlock> block(
new BlockContents(slice), nullptr /* cache */, nullptr /* cache_handle */, new ParsedFullFilterBlock(table_options_.filter_policy.get(),
true /* own_value */); BlockContents(slice)),
nullptr /* cache */, nullptr /* cache_handle */, true /* own_value */);
FullFilterBlockReader reader(table_.get(), std::move(block)); FullFilterBlockReader reader(table_.get(), std::move(block));
// Remain same symantic with blockbased filter // Remain same symantic with blockbased filter
@ -136,9 +137,10 @@ TEST_F(PluginFullFilterBlockTest, PluginSingleChunk) {
builder.Add("hello"); builder.Add("hello");
Slice slice = builder.Finish(); Slice slice = builder.Finish();
CachableEntry<BlockContents> block( CachableEntry<ParsedFullFilterBlock> block(
new BlockContents(slice), nullptr /* cache */, nullptr /* cache_handle */, new ParsedFullFilterBlock(table_options_.filter_policy.get(),
true /* own_value */); BlockContents(slice)),
nullptr /* cache */, nullptr /* cache_handle */, true /* own_value */);
FullFilterBlockReader reader(table_.get(), std::move(block)); FullFilterBlockReader reader(table_.get(), std::move(block));
ASSERT_TRUE(reader.KeyMayMatch("foo", /*prefix_extractor=*/nullptr, ASSERT_TRUE(reader.KeyMayMatch("foo", /*prefix_extractor=*/nullptr,
@ -189,9 +191,10 @@ TEST_F(FullFilterBlockTest, EmptyBuilder) {
Slice slice = builder.Finish(); Slice slice = builder.Finish();
ASSERT_EQ("", EscapeString(slice)); ASSERT_EQ("", EscapeString(slice));
CachableEntry<BlockContents> block( CachableEntry<ParsedFullFilterBlock> block(
new BlockContents(slice), nullptr /* cache */, nullptr /* cache_handle */, new ParsedFullFilterBlock(table_options_.filter_policy.get(),
true /* own_value */); BlockContents(slice)),
nullptr /* cache */, nullptr /* cache_handle */, true /* own_value */);
FullFilterBlockReader reader(table_.get(), std::move(block)); FullFilterBlockReader reader(table_.get(), std::move(block));
// Remain same symantic with blockbased filter // Remain same symantic with blockbased filter
@ -247,9 +250,10 @@ TEST_F(FullFilterBlockTest, SingleChunk) {
ASSERT_EQ(5, builder.NumAdded()); ASSERT_EQ(5, builder.NumAdded());
Slice slice = builder.Finish(); Slice slice = builder.Finish();
CachableEntry<BlockContents> block( CachableEntry<ParsedFullFilterBlock> block(
new BlockContents(slice), nullptr /* cache */, nullptr /* cache_handle */, new ParsedFullFilterBlock(table_options_.filter_policy.get(),
true /* own_value */); BlockContents(slice)),
nullptr /* cache */, nullptr /* cache_handle */, true /* own_value */);
FullFilterBlockReader reader(table_.get(), std::move(block)); FullFilterBlockReader reader(table_.get(), std::move(block));
ASSERT_TRUE(reader.KeyMayMatch("foo", /*prefix_extractor=*/nullptr, ASSERT_TRUE(reader.KeyMayMatch("foo", /*prefix_extractor=*/nullptr,

@ -0,0 +1,22 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
//
#include "table/block_based/parsed_full_filter_block.h"
#include "rocksdb/filter_policy.h"
namespace rocksdb {
ParsedFullFilterBlock::ParsedFullFilterBlock(const FilterPolicy* filter_policy,
BlockContents&& contents)
: block_contents_(std::move(contents)),
filter_bits_reader_(
!block_contents_.data.empty()
? filter_policy->GetFilterBitsReader(block_contents_.data)
: nullptr) {}
ParsedFullFilterBlock::~ParsedFullFilterBlock() = default;
} // namespace rocksdb

@ -0,0 +1,40 @@
// Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
// This source code is licensed under both the GPLv2 (found in the
// COPYING file in the root directory) and Apache 2.0 License
// (found in the LICENSE.Apache file in the root directory).
#pragma once
#include <memory>
#include "table/format.h"
namespace rocksdb {
class FilterBitsReader;
class FilterPolicy;
// The sharable/cachable part of the full filter.
class ParsedFullFilterBlock {
public:
ParsedFullFilterBlock(const FilterPolicy* filter_policy,
BlockContents&& contents);
~ParsedFullFilterBlock();
FilterBitsReader* filter_bits_reader() const {
return filter_bits_reader_.get();
}
// TODO: consider memory usage of FullFilterBitsReader
size_t ApproximateMemoryUsage() const {
return block_contents_.ApproximateMemoryUsage();
}
bool own_bytes() const { return block_contents_.own_bytes(); }
private:
BlockContents block_contents_;
std::unique_ptr<FilterBitsReader> filter_bits_reader_;
};
} // namespace rocksdb

@ -217,7 +217,7 @@ Status PartitionedFilterBlockReader::GetFilterPartitionBlock(
FilePrefetchBuffer* prefetch_buffer, const BlockHandle& fltr_blk_handle, FilePrefetchBuffer* prefetch_buffer, const BlockHandle& fltr_blk_handle,
bool no_io, GetContext* get_context, bool no_io, GetContext* get_context,
BlockCacheLookupContext* lookup_context, BlockCacheLookupContext* lookup_context,
CachableEntry<BlockContents>* filter_block) const { CachableEntry<ParsedFullFilterBlock>* filter_block) const {
assert(table()); assert(table());
assert(filter_block); assert(filter_block);
assert(filter_block->IsEmpty()); assert(filter_block->IsEmpty());
@ -267,7 +267,7 @@ bool PartitionedFilterBlockReader::MayMatch(
return false; return false;
} }
CachableEntry<BlockContents> filter_partition_block; CachableEntry<ParsedFullFilterBlock> filter_partition_block;
s = GetFilterPartitionBlock(nullptr /* prefetch_buffer */, filter_handle, s = GetFilterPartitionBlock(nullptr /* prefetch_buffer */, filter_handle,
no_io, get_context, lookup_context, no_io, get_context, lookup_context,
&filter_partition_block); &filter_partition_block);
@ -346,7 +346,7 @@ void PartitionedFilterBlockReader::CacheDependencies(bool pin) {
for (biter.SeekToFirst(); biter.Valid(); biter.Next()) { for (biter.SeekToFirst(); biter.Valid(); biter.Next()) {
handle = biter.value().handle; handle = biter.value().handle;
CachableEntry<BlockContents> block; CachableEntry<ParsedFullFilterBlock> block;
// TODO: Support counter batch update for partitioned index and // TODO: Support counter batch update for partitioned index and
// filter blocks // filter blocks
s = table()->MaybeReadBlockAndLoadToCache( s = table()->MaybeReadBlockAndLoadToCache(

@ -100,7 +100,7 @@ class PartitionedFilterBlockReader : public FilterBlockReaderCommon<Block> {
FilePrefetchBuffer* prefetch_buffer, const BlockHandle& handle, FilePrefetchBuffer* prefetch_buffer, const BlockHandle& handle,
bool no_io, GetContext* get_context, bool no_io, GetContext* get_context,
BlockCacheLookupContext* lookup_context, BlockCacheLookupContext* lookup_context,
CachableEntry<BlockContents>* filter_block) const; CachableEntry<ParsedFullFilterBlock>* filter_block) const;
using FilterFunction = bool (FullFilterBlockReader::*)( using FilterFunction = bool (FullFilterBlockReader::*)(
const Slice& slice, const SliceTransform* prefix_extractor, const Slice& slice, const SliceTransform* prefix_extractor,
@ -119,7 +119,8 @@ class PartitionedFilterBlockReader : public FilterBlockReaderCommon<Block> {
bool index_value_is_full() const; bool index_value_is_full() const;
protected: protected:
std::unordered_map<uint64_t, CachableEntry<BlockContents>> filter_map_; std::unordered_map<uint64_t, CachableEntry<ParsedFullFilterBlock>>
filter_map_;
}; };
} // namespace rocksdb } // namespace rocksdb

@ -20,7 +20,7 @@
namespace rocksdb { namespace rocksdb {
std::map<uint64_t, Slice> slices; std::map<uint64_t, std::string> blooms;
class MockedBlockBasedTable : public BlockBasedTable { class MockedBlockBasedTable : public BlockBasedTable {
public: public:
@ -37,13 +37,18 @@ class MyPartitionedFilterBlockReader : public PartitionedFilterBlockReader {
MyPartitionedFilterBlockReader(BlockBasedTable* t, MyPartitionedFilterBlockReader(BlockBasedTable* t,
CachableEntry<Block>&& filter_block) CachableEntry<Block>&& filter_block)
: PartitionedFilterBlockReader(t, std::move(filter_block)) { : PartitionedFilterBlockReader(t, std::move(filter_block)) {
for (const auto& pair : slices) { for (const auto& pair : blooms) {
const uint64_t offset = pair.first; const uint64_t offset = pair.first;
const Slice& slice = pair.second; const std::string& bloom = pair.second;
CachableEntry<BlockContents> block( assert(t);
new BlockContents(slice), nullptr /* cache */, assert(t->get_rep());
nullptr /* cache_handle */, true /* own_value */); CachableEntry<ParsedFullFilterBlock> block(
new ParsedFullFilterBlock(
t->get_rep()->table_options.filter_policy.get(),
BlockContents(Slice(bloom))),
nullptr /* cache */, nullptr /* cache_handle */,
true /* own_value */);
filter_map_[offset] = std::move(block); filter_map_[offset] = std::move(block);
} }
} }
@ -101,7 +106,7 @@ class PartitionedFilterBlockTest
uint64_t last_offset = 10; uint64_t last_offset = 10;
BlockHandle Write(const Slice& slice) { BlockHandle Write(const Slice& slice) {
BlockHandle bh(last_offset + 1, slice.size()); BlockHandle bh(last_offset + 1, slice.size());
slices[bh.offset()] = slice; blooms[bh.offset()] = slice.ToString();
last_offset += bh.size(); last_offset += bh.size();
return bh; return bh;
} }

@ -70,6 +70,7 @@ using rocksdb::fastrange32;
using rocksdb::FilterBitsBuilder; using rocksdb::FilterBitsBuilder;
using rocksdb::FilterBitsReader; using rocksdb::FilterBitsReader;
using rocksdb::FullFilterBlockReader; using rocksdb::FullFilterBlockReader;
using rocksdb::ParsedFullFilterBlock;
using rocksdb::Random32; using rocksdb::Random32;
using rocksdb::Slice; using rocksdb::Slice;
using rocksdb::mock::MockBlockBasedTableTester; using rocksdb::mock::MockBlockBasedTableTester;
@ -208,9 +209,10 @@ void FilterBench::Go() {
info.keys_added_ = keys_to_add; info.keys_added_ = keys_to_add;
info.reader_.reset( info.reader_.reset(
table_options_.filter_policy->GetFilterBitsReader(info.filter_)); table_options_.filter_policy->GetFilterBitsReader(info.filter_));
CachableEntry<BlockContents> block( CachableEntry<ParsedFullFilterBlock> block(
new BlockContents(info.filter_), nullptr /* cache */, new ParsedFullFilterBlock(table_options_.filter_policy.get(),
nullptr /* cache_handle */, true /* own_value */); BlockContents(info.filter_)),
nullptr /* cache */, nullptr /* cache_handle */, true /* own_value */);
info.full_block_reader_.reset( info.full_block_reader_.reset(
new FullFilterBlockReader(table_.get(), std::move(block))); new FullFilterBlockReader(table_.get(), std::move(block)));
total_memory_used += info.filter_.size(); total_memory_used += info.filter_.size();

Loading…
Cancel
Save