1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 #include "table/full_filter_block.h"
8 #ifdef ROCKSDB_MALLOC_USABLE_SIZE
10 #include <malloc_np.h>
16 #include "monitoring/perf_context_imp.h"
17 #include "port/port.h"
18 #include "rocksdb/filter_policy.h"
19 #include "util/coding.h"
23 FullFilterBlockBuilder::FullFilterBlockBuilder(
24 const SliceTransform
* prefix_extractor
, bool whole_key_filtering
,
25 FilterBitsBuilder
* filter_bits_builder
)
26 : prefix_extractor_(prefix_extractor
),
27 whole_key_filtering_(whole_key_filtering
),
28 last_whole_key_recorded_(false),
29 last_prefix_recorded_(false),
31 assert(filter_bits_builder
!= nullptr);
32 filter_bits_builder_
.reset(filter_bits_builder
);
35 void FullFilterBlockBuilder::Add(const Slice
& key
) {
36 const bool add_prefix
= prefix_extractor_
&& prefix_extractor_
->InDomain(key
);
37 if (whole_key_filtering_
) {
41 // if both whole_key and prefix are added to bloom then we will have whole
42 // key and prefix addition being interleaved and thus cannot rely on the
43 // bits builder to properly detect the duplicates by comparing with the
45 Slice last_whole_key
= Slice(last_whole_key_str_
);
46 if (!last_whole_key_recorded_
|| last_whole_key
.compare(key
) != 0) {
48 last_whole_key_recorded_
= true;
49 last_whole_key_str_
.assign(key
.data(), key
.size());
58 // Add key to filter if needed
59 inline void FullFilterBlockBuilder::AddKey(const Slice
& key
) {
60 filter_bits_builder_
->AddKey(key
);
64 // Add prefix to filter if needed
65 inline void FullFilterBlockBuilder::AddPrefix(const Slice
& key
) {
66 Slice prefix
= prefix_extractor_
->Transform(key
);
67 if (whole_key_filtering_
) {
68 // if both whole_key and prefix are added to bloom then we will have whole
69 // key and prefix addition being interleaved and thus cannot rely on the
70 // bits builder to properly detect the duplicates by comparing with the last
72 Slice last_prefix
= Slice(last_prefix_str_
);
73 if (!last_prefix_recorded_
|| last_prefix
.compare(prefix
) != 0) {
75 last_prefix_recorded_
= true;
76 last_prefix_str_
.assign(prefix
.data(), prefix
.size());
83 void FullFilterBlockBuilder::Reset() {
84 last_whole_key_recorded_
= false;
85 last_prefix_recorded_
= false;
88 Slice
FullFilterBlockBuilder::Finish(const BlockHandle
& /*tmp*/,
91 // In this impl we ignore BlockHandle
92 *status
= Status::OK();
93 if (num_added_
!= 0) {
95 return filter_bits_builder_
->Finish(&filter_data_
);
100 FullFilterBlockReader::FullFilterBlockReader(
101 const SliceTransform
* prefix_extractor
, bool _whole_key_filtering
,
102 const Slice
& contents
, FilterBitsReader
* filter_bits_reader
,
104 : FilterBlockReader(contents
.size(), stats
, _whole_key_filtering
),
105 prefix_extractor_(prefix_extractor
),
106 contents_(contents
) {
107 assert(filter_bits_reader
!= nullptr);
108 filter_bits_reader_
.reset(filter_bits_reader
);
109 if (prefix_extractor_
!= nullptr) {
110 full_length_enabled_
=
111 prefix_extractor_
->FullLengthEnabled(&prefix_extractor_full_length_
);
115 FullFilterBlockReader::FullFilterBlockReader(
116 const SliceTransform
* prefix_extractor
, bool _whole_key_filtering
,
117 BlockContents
&& contents
, FilterBitsReader
* filter_bits_reader
,
119 : FullFilterBlockReader(prefix_extractor
, _whole_key_filtering
,
120 contents
.data
, filter_bits_reader
, stats
) {
121 block_contents_
= std::move(contents
);
124 bool FullFilterBlockReader::KeyMayMatch(
125 const Slice
& key
, const SliceTransform
* /*prefix_extractor*/,
126 uint64_t block_offset
, const bool /*no_io*/,
127 const Slice
* const /*const_ikey_ptr*/) {
131 assert(block_offset
== kNotValid
);
132 if (!whole_key_filtering_
) {
135 return MayMatch(key
);
138 bool FullFilterBlockReader::PrefixMayMatch(
139 const Slice
& prefix
, const SliceTransform
* /* prefix_extractor */,
140 uint64_t block_offset
, const bool /*no_io*/,
141 const Slice
* const /*const_ikey_ptr*/) {
145 assert(block_offset
== kNotValid
);
146 return MayMatch(prefix
);
149 bool FullFilterBlockReader::MayMatch(const Slice
& entry
) {
150 if (contents_
.size() != 0) {
151 if (filter_bits_reader_
->MayMatch(entry
)) {
152 PERF_COUNTER_ADD(bloom_sst_hit_count
, 1);
155 PERF_COUNTER_ADD(bloom_sst_miss_count
, 1);
159 return true; // remain the same with block_based filter
162 size_t FullFilterBlockReader::ApproximateMemoryUsage() const {
163 size_t usage
= block_contents_
.usable_size();
164 #ifdef ROCKSDB_MALLOC_USABLE_SIZE
165 usage
+= malloc_usable_size((void*)this);
166 usage
+= malloc_usable_size(filter_bits_reader_
.get());
168 usage
+= sizeof(*this);
169 usage
+= sizeof(*filter_bits_reader_
.get());
170 #endif // ROCKSDB_MALLOC_USABLE_SIZE
174 bool FullFilterBlockReader::RangeMayExist(const Slice
* iterate_upper_bound
,
175 const Slice
& user_key
, const SliceTransform
* prefix_extractor
,
176 const Comparator
* comparator
, const Slice
* const const_ikey_ptr
,
177 bool* filter_checked
, bool need_upper_bound_check
) {
178 if (!prefix_extractor
|| !prefix_extractor
->InDomain(user_key
)) {
179 *filter_checked
= false;
182 Slice prefix
= prefix_extractor
->Transform(user_key
);
183 if (need_upper_bound_check
&&
184 !IsFilterCompatible(iterate_upper_bound
, prefix
, comparator
)) {
185 *filter_checked
= false;
188 *filter_checked
= true;
189 return PrefixMayMatch(prefix
, prefix_extractor
, kNotValid
, false,
194 bool FullFilterBlockReader::IsFilterCompatible(
195 const Slice
* iterate_upper_bound
, const Slice
& prefix
,
196 const Comparator
* comparator
) {
197 // Try to reuse the bloom filter in the SST table if prefix_extractor in
198 // mutable_cf_options has changed. If range [user_key, upper_bound) all
199 // share the same prefix then we may still be able to use the bloom filter.
200 if (iterate_upper_bound
!= nullptr && prefix_extractor_
) {
201 if (!prefix_extractor_
->InDomain(*iterate_upper_bound
)) {
204 Slice upper_bound_xform
=
205 prefix_extractor_
->Transform(*iterate_upper_bound
);
206 // first check if user_key and upper_bound all share the same prefix
207 if (!comparator
->Equal(prefix
, upper_bound_xform
)) {
208 // second check if user_key's prefix is the immediate predecessor of
209 // upper_bound and have the same length. If so, we know for sure all
210 // keys in the range [user_key, upper_bound) share the same prefix.
211 // Also need to make sure upper_bound are full length to ensure
213 if (!full_length_enabled_
||
214 iterate_upper_bound
->size() != prefix_extractor_full_length_
||
215 !comparator
->IsSameLengthImmediateSuccessor(prefix
,
216 *iterate_upper_bound
)) {
226 } // namespace rocksdb