Skip to content

Commit cb91f43

Browse files
committed
optimize code
1 parent adf0ff0 commit cb91f43

2 files changed

Lines changed: 70 additions & 26 deletions

File tree

src/types/redis_cuckoo_chain.cc

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -173,11 +173,11 @@ rocksdb::Status CuckooChain::Add(engine::Context &ctx, const Slice &user_key, co
173173
uint64_t hash = CuckooFilter::Hash(item.data(), item.size());
174174
uint8_t fingerprint = CuckooFilter::GenerateFingerprint(hash);
175175

176-
// Try to insert in each sub-filter (starting from the first/smallest one)
177-
// This follows RedisBloom's behavior and is more efficient
178-
for (uint16_t filter_idx = 0; filter_idx < metadata.n_filters; ++filter_idx) {
176+
// RedisBloom prioritizes the newest sub-filter to avoid repeatedly probing older, fuller filters.
177+
for (int filter_idx = static_cast<int>(metadata.n_filters) - 1; filter_idx >= 0; --filter_idx) {
178+
uint16_t current_filter_idx = static_cast<uint16_t>(filter_idx);
179179
uint64_t filter_capacity = 0;
180-
if (!CalculateFilterCapacity(metadata.base_capacity, metadata.expansion, filter_idx, &filter_capacity) ||
180+
if (!CalculateFilterCapacity(metadata.base_capacity, metadata.expansion, current_filter_idx, &filter_capacity) ||
181181
!CuckooFilter::IsCapacitySupported(filter_capacity, metadata.bucket_size)) {
182182
return rocksdb::Status::Corruption("invalid metadata: filter capacity is too large");
183183
}
@@ -192,7 +192,8 @@ rocksdb::Status CuckooChain::Add(engine::Context &ctx, const Slice &user_key, co
192192

193193
CuckooPageSet pages(storage_, ctx, ns_key, metadata, storage_->IsSlotIdEncoded());
194194
bool inserted = false;
195-
s = pages.TryInsertInCandidateBuckets(filter_idx, num_buckets, bucket1_idx, bucket2_idx, fingerprint, &inserted);
195+
s = pages.TryInsertInCandidateBuckets(current_filter_idx, num_buckets, bucket1_idx, bucket2_idx, fingerprint,
196+
&inserted);
196197
if (!s.ok()) return s;
197198

198199
if (inserted) {

tests/cppunit/types/cuckoo_filter_test.cc

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,17 @@ class RedisCuckooFilterTest : public TestBase {
111111
value);
112112
}
113113

114+
void writeMetadata(const std::string &key, const CuckooChainMetadata &metadata) {
115+
std::string metadata_bytes;
116+
metadata.Encode(&metadata_bytes);
117+
auto batch = storage_->GetWriteBatchBase();
118+
auto s =
119+
batch->Put(storage_->GetCFHandle(ColumnFamilyID::Metadata), db_->AppendNamespacePrefix(key), metadata_bytes);
120+
ASSERT_TRUE(s.ok()) << s.ToString();
121+
s = storage_->Write(*ctx_, storage_->DefaultWriteOptions(), batch->GetWriteBatch());
122+
ASSERT_TRUE(s.ok()) << s.ToString();
123+
}
124+
114125
std::unique_ptr<redis::CuckooChain> cuckoo_;
115126
std::unique_ptr<redis::Database> db_;
116127
std::string key_;
@@ -197,12 +208,10 @@ TEST_F(RedisCuckooFilterTest, ReserveValidParams) {
197208
}
198209

199210
TEST_F(RedisCuckooFilterTest, ReserveDuplicate) {
200-
// First reserve should succeed
201-
auto s = cuckoo_->Reserve(*ctx_, key_, 1000, 4, 500, 2, kCuckooFilterDefaultPageSize);
202-
ASSERT_TRUE(s.ok());
211+
reserveAndVerify(key_, 1000, 4, 500, 2);
203212

204213
// Second reserve with same key should fail
205-
s = cuckoo_->Reserve(*ctx_, key_, 2000, 4, 500, 2, kCuckooFilterDefaultPageSize);
214+
auto s = cuckoo_->Reserve(*ctx_, key_, 2000, 4, 500, 2, kCuckooFilterDefaultPageSize);
206215
ASSERT_FALSE(s.ok());
207216
ASSERT_TRUE(s.IsInvalidArgument());
208217
ASSERT_NE(s.ToString().find("already exists"), std::string::npos);
@@ -315,21 +324,18 @@ TEST_F(RedisCuckooFilterTest, ReserveVerifyMetadata) {
315324
uint16_t expansion = 2;
316325

317326
// Create the filter
318-
auto s =
319-
cuckoo_->Reserve(*ctx_, key_, capacity, bucket_size, max_iterations, expansion, kCuckooFilterDefaultPageSize);
320-
ASSERT_TRUE(s.ok()) << "First reserve should succeed";
327+
reserveAndVerify(key_, capacity, bucket_size, max_iterations, expansion);
321328

322329
// Verify metadata was stored by trying to reserve again with same key
323330
// This should fail with "already exists" error
324-
s = cuckoo_->Reserve(*ctx_, key_, capacity * 2, bucket_size, max_iterations, expansion, kCuckooFilterDefaultPageSize);
331+
auto s =
332+
cuckoo_->Reserve(*ctx_, key_, capacity * 2, bucket_size, max_iterations, expansion, kCuckooFilterDefaultPageSize);
325333
ASSERT_FALSE(s.ok()) << "Second reserve with same key should fail";
326334
ASSERT_TRUE(s.IsInvalidArgument()) << "Should return InvalidArgument error";
327335
ASSERT_NE(s.ToString().find("already exists"), std::string::npos) << "Error message should mention 'already exists'";
328336

329337
// Verify we can still create filters with different keys
330-
s = cuckoo_->Reserve(*ctx_, "different_key", capacity, bucket_size, max_iterations, expansion,
331-
kCuckooFilterDefaultPageSize);
332-
ASSERT_TRUE(s.ok()) << "Should be able to create filter with different key";
338+
reserveAndVerify("different_key", capacity, bucket_size, max_iterations, expansion);
333339

334340
// Verify the original key still exists (can't create it again)
335341
s = cuckoo_->Reserve(*ctx_, key_, capacity, bucket_size, max_iterations, expansion, kCuckooFilterDefaultPageSize);
@@ -395,14 +401,10 @@ TEST_F(RedisCuckooFilterTest, AddWritesPagedLayout) {
395401

396402
TEST_F(RedisCuckooFilterTest, AddToNonExistentFilter) {
397403
std::string key = "nonexistent_key";
398-
bool added = false;
399-
auto s = cuckoo_->Add(*ctx_, key, "item1", &added);
400-
ASSERT_TRUE(s.ok()) << s.ToString();
401-
ASSERT_TRUE(added) << "CF.ADD should create the filter when the key does not exist";
402-
verifyMetadata(key, redis::kCFDefaultCapacity, redis::kCFDefaultBucketSize, redis::kCFDefaultMaxIterations,
403-
redis::kCFDefaultExpansion, 1, 1, 0);
404+
addAndVerify(key, "item1", redis::kCFDefaultCapacity, redis::kCFDefaultBucketSize, redis::kCFDefaultMaxIterations,
405+
redis::kCFDefaultExpansion, 1);
404406

405-
s = cuckoo_->Reserve(*ctx_, key, 1000, 4, 500, 2, kCuckooFilterDefaultPageSize);
407+
auto s = cuckoo_->Reserve(*ctx_, key, 1000, 4, 500, 2, kCuckooFilterDefaultPageSize);
406408
ASSERT_FALSE(s.ok());
407409
ASSERT_TRUE(s.IsInvalidArgument());
408410
ASSERT_NE(s.ToString().find("already exists"), std::string::npos);
@@ -427,6 +429,48 @@ TEST_F(RedisCuckooFilterTest, AddDuplicateItems) {
427429
}
428430
}
429431

432+
TEST_F(RedisCuckooFilterTest, AddPrioritizesNewestFilter) {
433+
CuckooChainMetadata metadata;
434+
metadata.size = 0;
435+
metadata.base_capacity = 1000;
436+
metadata.bucket_size = 4;
437+
metadata.max_iterations = 500;
438+
metadata.expansion = 2;
439+
metadata.n_filters = 2;
440+
metadata.num_deleted_items = 0;
441+
metadata.page_size = kCuckooFilterDefaultPageSize;
442+
writeMetadata(key_, metadata);
443+
444+
const std::string item = "item";
445+
addAndVerify(key_, item, metadata.base_capacity, metadata.bucket_size, metadata.max_iterations, metadata.expansion, 1,
446+
metadata.n_filters);
447+
448+
auto stored_metadata = getMetadata(key_);
449+
ASSERT_EQ(stored_metadata.n_filters, 2);
450+
ASSERT_EQ(stored_metadata.size, 1);
451+
452+
auto hash = redis::CuckooFilter::Hash(item);
453+
uint32_t old_num_buckets = 0;
454+
auto s = redis::CuckooFilter::OptimalNumBuckets(metadata.base_capacity, metadata.bucket_size, &old_num_buckets);
455+
ASSERT_TRUE(s.ok()) << s.ToString();
456+
auto buckets_per_page = stored_metadata.page_size / stored_metadata.bucket_size;
457+
auto old_page_idx = static_cast<uint32_t>(hash % old_num_buckets) / buckets_per_page;
458+
459+
std::string page;
460+
s = readPage(makePageKey(key_, stored_metadata, 0, old_page_idx), &page);
461+
EXPECT_TRUE(s.IsNotFound()) << s.ToString();
462+
463+
uint32_t num_buckets = 0;
464+
s = redis::CuckooFilter::OptimalNumBuckets(metadata.base_capacity * metadata.expansion, metadata.bucket_size,
465+
&num_buckets);
466+
ASSERT_TRUE(s.ok()) << s.ToString();
467+
auto bucket1_idx = static_cast<uint32_t>(hash % num_buckets);
468+
auto expected_page_idx = bucket1_idx / buckets_per_page;
469+
470+
s = readPage(makePageKey(key_, stored_metadata, 1, expected_page_idx), &page);
471+
ASSERT_TRUE(s.ok()) << s.ToString();
472+
}
473+
430474
TEST_F(RedisCuckooFilterTest, AddManyItems) {
431475
reserveAndVerify(key_, 1000, 4, 500, 2);
432476
for (int i = 0; i < 100; ++i) {
@@ -436,15 +480,14 @@ TEST_F(RedisCuckooFilterTest, AddManyItems) {
436480

437481
TEST_F(RedisCuckooFilterTest, AddSmallFilterCapacity) {
438482
uint64_t small_capacity = 10;
439-
auto s = cuckoo_->Reserve(*ctx_, key_, small_capacity, 2, 500, 0, kCuckooFilterDefaultPageSize);
440-
ASSERT_TRUE(s.ok());
483+
reserveAndVerify(key_, small_capacity, 2, 500, 0);
441484

442485
uint64_t added_count = 0;
443486
bool full = false;
444487
for (int i = 0; i < 100; ++i) {
445488
std::string item = "item_" + std::to_string(i);
446489
bool added = false;
447-
s = cuckoo_->Add(*ctx_, key_, item, &added);
490+
auto s = cuckoo_->Add(*ctx_, key_, item, &added);
448491

449492
if (!s.ok()) {
450493
ASSERT_TRUE(s.IsAborted()) << "Should be Aborted status when full";

0 commit comments

Comments
 (0)