From 677d2b4a8f8fd19d0c39a9ee8f648742e610688d Mon Sep 17 00:00:00 2001 From: Yanqin Jin Date: Thu, 30 Dec 2021 12:46:38 -0800 Subject: [PATCH] Fix a bug in C-binding causing iterator to return incorrect result (#9343) Summary: Fixes https://github.com/facebook/rocksdb/issues/9339 When writing SST file, the name, computed as `prefix_extractor->GetId()` will be written to the properties block. When the SST is opened again in the future, `CreateFromString()` will take the name as argument and try to create a prefix extractor object. Without this fix, the C API will pass a `Wrapper` pointer to the underlying DB's `prefix_extractor`. `Wrapper::GetId()`, in this case, will be missing the prefix length component, causing a prefix extractor of length 0 to be silently created and used. Pull Request resolved: https://github.com/facebook/rocksdb/pull/9343 Test Plan: ``` make c_test ./c_test ``` Reviewed By: mrambacher Differential Revision: D33355549 Pulled By: riversand963 fbshipit-source-id: c92c3acd8be262c3bff8794b4229e42b9ee31203 --- HISTORY.md | 1 + db/c.cc | 13 +++++++------ db/c_test.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index b672f1160..a065347fc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -16,6 +16,7 @@ * Fixed a bug causing two duplicate entries to be appended to a file opened in non-direct mode and tracked by `FaultInjectionTestFS`. * Fixed a bug in TableOptions.prepopulate_block_cache to support block-based filters also. * Block cache keys no longer use `FSRandomAccessFile::GetUniqueId()` (previously used when available), so a filesystem recycling unique ids can no longer lead to incorrect result or crash (#7405). For files generated by RocksDB >= 6.24, the cache keys are stable across DB::Open and DB directory move / copy / import / export / migration, etc. Although collisions are still theoretically possible, they are (a) impossible in many common cases, (b) not dependent on environmental factors, and (c) much less likely than a CPU miscalculation while executing RocksDB. +* Fixed a bug in C bindings causing iterator to return incorrect result (#9343). ### Behavior Changes * MemTableList::TrimHistory now use allocated bytes when max_write_buffer_size_to_maintain > 0(default in TrasactionDB, introduced in PR#5022) Fix #8371. diff --git a/db/c.cc b/db/c.cc index 7e6ee6840..cdf577f8f 100644 --- a/db/c.cc +++ b/db/c.cc @@ -4595,10 +4595,11 @@ void rocksdb_slicetransform_destroy(rocksdb_slicetransform_t* st) { delete st; } -struct Wrapper : public rocksdb_slicetransform_t { +struct SliceTransformWrapper : public rocksdb_slicetransform_t { const SliceTransform* rep_; - ~Wrapper() override { delete rep_; } + ~SliceTransformWrapper() override { delete rep_; } const char* Name() const override { return rep_->Name(); } + std::string GetId() const override { return rep_->GetId(); } Slice Transform(const Slice& src) const override { return rep_->Transform(src); } @@ -4610,18 +4611,18 @@ struct Wrapper : public rocksdb_slicetransform_t { }; rocksdb_slicetransform_t* rocksdb_slicetransform_create_fixed_prefix(size_t prefixLen) { - Wrapper* wrapper = new Wrapper; + SliceTransformWrapper* wrapper = new SliceTransformWrapper; wrapper->rep_ = ROCKSDB_NAMESPACE::NewFixedPrefixTransform(prefixLen); wrapper->state_ = nullptr; - wrapper->destructor_ = &Wrapper::DoNothing; + wrapper->destructor_ = &SliceTransformWrapper::DoNothing; return wrapper; } rocksdb_slicetransform_t* rocksdb_slicetransform_create_noop() { - Wrapper* wrapper = new Wrapper; + SliceTransformWrapper* wrapper = new SliceTransformWrapper; wrapper->rep_ = ROCKSDB_NAMESPACE::NewNoopTransform(); wrapper->state_ = nullptr; - wrapper->destructor_ = &Wrapper::DoNothing; + wrapper->destructor_ = &SliceTransformWrapper::DoNothing; return wrapper; } diff --git a/db/c_test.c b/db/c_test.c index 8f7e05ca2..4a657ead3 100644 --- a/db/c_test.c +++ b/db/c_test.c @@ -2956,6 +2956,51 @@ int main(int argc, char** argv) { CheckNoError(err); } + StartPhase("filter_with_prefix_seek"); + { + rocksdb_close(db); + rocksdb_destroy_db(options, dbname, &err); + CheckNoError(err); + + rocksdb_options_set_prefix_extractor( + options, rocksdb_slicetransform_create_fixed_prefix(1)); + rocksdb_filterpolicy_t* filter_policy = + rocksdb_filterpolicy_create_bloom_full(8.0); + rocksdb_block_based_options_set_filter_policy(table_options, filter_policy); + rocksdb_options_set_block_based_table_factory(options, table_options); + + db = rocksdb_open(options, dbname, &err); + CheckNoError(err); + + int i; + for (i = 0; i < 10; ++i) { + char key = '0' + (char)i; + rocksdb_put(db, woptions, &key, 1, "", 1, &err); + CheckNoError(err); + } + + // Flush to generate an L0 so that filter will be used later. + rocksdb_flushoptions_t* flush_options = rocksdb_flushoptions_create(); + rocksdb_flushoptions_set_wait(flush_options, 1); + rocksdb_flush(db, flush_options, &err); + rocksdb_flushoptions_destroy(flush_options); + CheckNoError(err); + + rocksdb_readoptions_t* ropts = rocksdb_readoptions_create(); + rocksdb_iterator_t* iter = rocksdb_create_iterator(db, ropts); + + rocksdb_iter_seek(iter, "0", 1); + int cnt = 0; + while (rocksdb_iter_valid(iter)) { + ++cnt; + rocksdb_iter_next(iter); + } + CheckCondition(10 == cnt); + + rocksdb_iter_destroy(iter); + rocksdb_readoptions_destroy(ropts); + } + StartPhase("cancel_all_background_work"); rocksdb_cancel_all_background_work(db, 1);