Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/sanity_check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ jobs:
args: https://raw.githubusercontent.com/llvm/llvm-project/main/clang/tools/clang-format/clang-format-diff.py

- name: Check format
run: VERBOSE_CHECK=1 make check-format
run: FORMAT_UPSTREAM=origin/master VERBOSE_CHECK=1 make check-format

- name: Compare buckify output
run: make check-buck-targets
7 changes: 7 additions & 0 deletions include/rocksdb/sst_file_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ class SstFileReader {
// If "snapshot" is nullptr, the iterator returns only the latest keys.
Iterator* NewIterator(const ReadOptions& options);

// Bloom-only batch check. For each key, false means the key is definitely
// absent; true means it may be present or the reader cannot answer safely.
void MayMatch(const ReadOptions& read_options, const Slice* keys,
size_t num_keys, bool* results);
// Equivalent to MayMatch(ReadOptions(), keys, num_keys, results).
void MayMatch(const Slice* keys, size_t num_keys, bool* results);

std::shared_ptr<const TableProperties> GetTableProperties() const;

// Verifies whether there is corruption in this table.
Expand Down
25 changes: 25 additions & 0 deletions table/block_based/block_based_table_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2823,6 +2823,31 @@ void BlockBasedTable::MultiGet(const ReadOptions& read_options,
}
}

void BlockBasedTable::MayMatch(const ReadOptions& read_options,
const Slice* user_keys, size_t num_keys,
bool* results) {
if (num_keys == 0) {
return;
}

FilterBlockReader* f = rep_->filter.get();
if (!f || f->IsBlockBased() || !rep_->whole_key_filtering) {
for (size_t i = 0; i < num_keys; ++i) results[i] = true;
return;
}
const size_t ts_sz =
rep_->internal_comparator.user_comparator()->timestamp_size();
const bool no_io = read_options.read_tier == kBlockCacheTier;
for (size_t i = 0; i < num_keys; ++i) {
InternalKey ikey(user_keys[i], kMaxSequenceNumber, kTypeValue);
Slice internal_key = ikey.Encode();
results[i] = f->KeyMayMatch(StripTimestampFromUserKey(user_keys[i], ts_sz),
rep_->table_prefix_extractor.get(), kNotValid,
no_io, &internal_key, /*get_context=*/nullptr,
/*lookup_context=*/nullptr);
}
}

Status BlockBasedTable::Prefetch(const Slice* const begin,
const Slice* const end) {
auto& comparator = rep_->internal_comparator;
Expand Down
3 changes: 3 additions & 0 deletions table/block_based/block_based_table_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,9 @@ class BlockBasedTable : public TableReader {
GetContext* get_context, const SliceTransform* prefix_extractor,
bool skip_filters = false) override;

void MayMatch(const ReadOptions& read_options, const Slice* user_keys,
size_t num_keys, bool* results) override;

void MultiGet(const ReadOptions& readOptions,
const MultiGetContext::Range* mget_range,
const SliceTransform* prefix_extractor,
Expand Down
11 changes: 11 additions & 0 deletions table/sst_file_reader.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,17 @@ Status SstFileReader::Open(const std::string& file_path) {
return s;
}

void SstFileReader::MayMatch(const ReadOptions& read_options, const Slice* keys,
size_t num_keys, bool* results) {
auto r = rep_.get();
r->table_reader->MayMatch(read_options, keys, num_keys, results);
}

void SstFileReader::MayMatch(const Slice* keys, size_t num_keys,
bool* results) {
MayMatch(ReadOptions(), keys, num_keys, results);
}

Iterator* SstFileReader::NewIterator(const ReadOptions& roptions) {
auto r = rep_.get();
auto sequence = roptions.snapshot != nullptr
Expand Down
105 changes: 105 additions & 0 deletions table/sst_file_reader_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
#include "port/stack_trace.h"
#include "rocksdb/convenience.h"
#include "rocksdb/db.h"
#include "rocksdb/filter_policy.h"
#include "rocksdb/sst_file_writer.h"
#include "table/block_based/block_based_table_factory.h"
#include "table/sst_file_writer_collectors.h"
#include "test_util/testharness.h"
#include "test_util/testutil.h"
Expand Down Expand Up @@ -127,6 +129,109 @@ TEST_F(SstFileReaderTest, Uint64Comparator) {
CreateFileAndCheck(keys);
}

TEST_F(SstFileReaderTest, MayMatchUsesBloomFilter) {
BlockBasedTableOptions table_options;
table_options.filter_policy.reset(NewBloomFilterPolicy(100, false));
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));

std::vector<std::string> keys;
for (uint64_t i = 0; i < kNumKeys; i++) {
keys.emplace_back(EncodeAsString(i));
}
CreateFile(sst_name_, keys);

SstFileReader reader(options_);
ASSERT_OK(reader.Open(sst_name_));

std::vector<std::string> lookup_storage = {EncodeAsString(0),
EncodeAsString(1), "not-present"};
std::vector<Slice> lookup_keys;
for (const auto& key : lookup_storage) {
lookup_keys.emplace_back(key);
}

bool may_match[3] = {};
ReadOptions read_options;
read_options.read_tier = kBlockCacheTier;
reader.MayMatch(read_options, lookup_keys.data(), lookup_keys.size(),
may_match);
ASSERT_TRUE(may_match[0]);
ASSERT_TRUE(may_match[1]);
ASSERT_FALSE(may_match[2]);
}

TEST_F(SstFileReaderTest, MayMatchReturnsAllTrueForBlockBasedFilter) {
BlockBasedTableOptions table_options;
table_options.filter_policy.reset(NewBloomFilterPolicy(100, true));
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));

std::vector<std::string> keys;
for (uint64_t i = 0; i < kNumKeys; i++) {
keys.emplace_back(EncodeAsString(i));
}
CreateFile(sst_name_, keys);

SstFileReader reader(options_);
ASSERT_OK(reader.Open(sst_name_));

std::vector<std::string> lookup_storage = {EncodeAsString(0),
EncodeAsString(1), "not-present"};
std::vector<Slice> lookup_keys;
for (const auto& key : lookup_storage) {
lookup_keys.emplace_back(key);
}

bool may_match[3] = {};
reader.MayMatch(lookup_keys.data(), lookup_keys.size(), may_match);
ASSERT_TRUE(may_match[0]);
ASSERT_TRUE(may_match[1]);
ASSERT_TRUE(may_match[2]);
}

TEST_F(SstFileReaderTest, MayMatchReturnsAllTrueWithoutFilter) {
std::vector<std::string> keys;
for (uint64_t i = 0; i < kNumKeys; i++) {
keys.emplace_back(EncodeAsString(i));
}
CreateFile(sst_name_, keys);

SstFileReader reader(options_);
ASSERT_OK(reader.Open(sst_name_));

std::vector<std::string> lookup_storage = {EncodeAsString(0),
EncodeAsString(1), "not-present"};
std::vector<Slice> lookup_keys;
for (const auto& key : lookup_storage) {
lookup_keys.emplace_back(key);
}

bool may_match[3] = {};
reader.MayMatch(lookup_keys.data(), lookup_keys.size(), may_match);
ASSERT_TRUE(may_match[0]);
ASSERT_TRUE(may_match[1]);
ASSERT_TRUE(may_match[2]);
}

TEST_F(SstFileReaderTest, MayMatchZeroKeysWithFilterDoesNotWriteResults) {
BlockBasedTableOptions table_options;
table_options.filter_policy.reset(NewBloomFilterPolicy(100, false));
options_.table_factory.reset(NewBlockBasedTableFactory(table_options));

std::vector<std::string> keys;
for (uint64_t i = 0; i < kNumKeys; i++) {
keys.emplace_back(EncodeAsString(i));
}
CreateFile(sst_name_, keys);

SstFileReader reader(options_);
ASSERT_OK(reader.Open(sst_name_));

Slice dummy;
bool may_match = false;
reader.MayMatch(&dummy, 0, &may_match);
ASSERT_FALSE(may_match);
}

TEST_F(SstFileReaderTest, ReadOptionsOutOfScope) {
// Repro a bug where the SstFileReader depended on its configured ReadOptions
// outliving it.
Expand Down
10 changes: 10 additions & 0 deletions table/table_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@ class TableReader {
const SliceTransform* prefix_extractor,
bool skip_filters = false) = 0;

// Bloom-only batch check: for each user key, false means the key is
// definitely absent; true means it may be present or the table cannot answer
// safely, including filter read errors.
// Default: all keys may match (no filter or unsupported table type).
virtual void MayMatch(const ReadOptions& /*read_options*/,
const Slice* /*user_keys*/, size_t num_keys,
bool* results) {
for (size_t i = 0; i < num_keys; ++i) results[i] = true;
}

virtual void MultiGet(const ReadOptions& readOptions,
const MultiGetContext::Range* mget_range,
const SliceTransform* prefix_extractor,
Expand Down
Loading