Skip to content

Add Cuckoo Filter (CF.*) support#3774

Open
HwangRock wants to merge 11 commits into
redis:mainfrom
HwangRock:feature/cuckoo-filter
Open

Add Cuckoo Filter (CF.*) support#3774
HwangRock wants to merge 11 commits into
redis:mainfrom
HwangRock:feature/cuckoo-filter

Conversation

@HwangRock

@HwangRock HwangRock commented Jun 6, 2026

Copy link
Copy Markdown

Adds support for the RedisBloom Cuckoo Filter (CF.) commands, wired the same way as the existing Bloom Filter (BF.) support across the synchronous, asynchronous, reactive, cluster node-selection, and Kotlin coroutine APIs.

Commands: CF.RESERVE, CF.ADD, CF.ADDNX, CF.INSERT, CF.INSERTNX, CF.EXISTS, CF.MEXISTS, CF.DEL, CF.COUNT, CF.SCANDUMP, CF.LOADCHUNK, CF.INFO

CF.INSERT returns List<Boolean> (true = added, false = filter full), and CF.INSERTNX returns List<Long> (1 = added, 0 = already exists, -1 = filter full) since it stays an integer on both RESP2 and RESP3. CF.INSERT itself comes back as integers on RESP2 and booleans on RESP3, with a full filter mapping to false.

CF.EXISTS, CF.MEXISTS, CF.COUNT, CF.INFO and CF.SCANDUMP are registered as read-only for cluster read routing (this also adds the missing BF.SCANDUMP). CF.INFO returns the field names Number of filters and Max iterations (plural), which differs from what the command reference currently shows.

Resolves #2659, #2660, #2661, #2662, #2663, #2664, #2665, #2666, #2667, #2668, #2669, #2670

Testing

Unit tests:

  • RedisCuckooFilterCommandBuilderUnitTests — RESP encoding for the 12 commands, including the single-value overloads
  • CfInfoValueParserUnitTests — CF.INFO field parsing

Integration tests ran against redis-stack-server (Redis 7.4.7, RedisBloom bf module) over the sync and reactive APIs, and again over RESP2:

[INFO] Running io.lettuce.core.cf.RedisCuckooFilterResp2IntegrationTests
[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0 -- in io.lettuce.core.cf.RedisCuckooFilterResp2IntegrationTests
[INFO] Running io.lettuce.core.cf.RedisCuckooFilterReactiveIntegrationTests
[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0 -- in io.lettuce.core.cf.RedisCuckooFilterReactiveIntegrationTests
[INFO] Running io.lettuce.core.cf.RedisCuckooFilterIntegrationTests
[INFO] Tests run: 18, Failures: 0, Errors: 0, Skipped: 0 -- in io.lettuce.core.cf.RedisCuckooFilterIntegrationTests
[INFO] Tests run: 54, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS

The filter-full case is asserted in the integration tests — cfInsertReturnsFalseWhenFilterIsFull checks the overflow items come back as false, and CF.INSERTNX results are asserted as List<Long>. Skipped: 0 shows the @EnabledOnCommand("CF.ADD") guard ran against a CF-capable server. The cluster integration test mirrors the BF one but needs a running cluster, so it was not part of this run.

Make sure that:

  • You have read the contribution guidelines.
  • You have created a feature request first to discuss your contribution intent. Please reference the feature request ticket number in the pull request.
  • You applied code formatting rules using the mvn formatter:format target. Don’t submit any formatting related changes.
  • You submit test cases (unit or integration tests) that back your changes.

Note

Medium Risk
Large API surface addition plus package/type renames (io.lettuce.core.bfprobabilistic, BfScanDumpValueScanDumpValue) are breaking for downstream imports; new command encoding and cluster read-only lists need correct routing behavior.

Overview
Adds Redis Cuckoo Filter (CF.*) support end-to-end (sync/async/reactive, cluster node-selection, Kotlin coroutines), mirroring the existing Bloom Filter wiring via RedisCuckooFilterCommandBuilder and new cf* command surfaces.

Bloom-related types move from io.lettuce.core.bf to io.lettuce.core.probabilistic; BfScanDumpValue is renamed to shared ScanDumpValue (used by both BF.SCANDUMP and CF.SCANDUMP). New CfInfoValue, CfInsertArgs, CfReserveArgs, and protocol entries (CF_* commands, BUCKETSIZE / MAXITERATIONS keywords).

Cluster read-only routing gains BF.SCANDUMP and the CF read commands (CF.EXISTS, CF.MEXISTS, CF.COUNT, CF.INFO, CF.SCANDUMP).

Reviewed by Cursor Bugbot for commit 64735ac. Bugbot is set up for automated code reviews on this repo. Configure here.

Adds client support for the RedisBloom Cuckoo Filter commands across the
synchronous, asynchronous, reactive, cluster and Kotlin coroutine APIs,
following the same module pattern as Bloom Filter (BF.*).

Commands: CF.RESERVE, CF.ADD, CF.ADDNX, CF.INSERT, CF.INSERTNX, CF.EXISTS,
CF.MEXISTS, CF.DEL, CF.COUNT, CF.SCANDUMP, CF.LOADCHUNK, CF.INFO

Read-only commands (CF.EXISTS, CF.MEXISTS, CF.COUNT, CF.INFO, CF.SCANDUMP)
are registered for cluster read routing. CF.INSERT/CF.INSERTNX map the
per-item -1 (filter full) result to null via ErrorTolerantBooleanListOutput,
consistent with BF.INSERT.

Resolves redis#2659, redis#2660, redis#2661, redis#2662, redis#2663, redis#2664, redis#2665, redis#2666, redis#2667,
redis#2668, redis#2669, redis#2670
@jit-ci

jit-ci Bot commented Jun 6, 2026

Copy link
Copy Markdown

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit 7896a0b. Configure here.

Comment thread src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java Outdated
HwangRock added 3 commits June 7, 2026 17:39
ErrorTolerantBooleanListOutput mapped any non-1 integer to false, conflating
the per-item -1 (filter full) returned by CF.INSERT/CF.INSERTNX with 0
("already exists"). Add CF-specific outputs mapping 1->true, 0->false,
-1->null (reactive: Value.empty()), keeping the three states distinct.

Verified end-to-end against redis-stack (filter-full case asserts null /
Value.empty()).
The RedisBloom server returns the per-item -1 (filter full) as an integer
only over RESP2; over RESP3 it returns boolean false instead, so the -1 vs 0
distinction is not observable on the client. Move the -1 -> null integration
assertion into RedisCuckooFilterResp2IntegrationTests and drop the filter-full
case from the RESP3 sync/reactive tests. The output mapping is independently
covered by CuckooInsertBooleanListOutput/ValueListOutput unit tests.
- cfInsert/cfInsertNx Javadoc now document the per-item null (RESP2) /
  Value.empty() (reactive) result for a full filter, and drop the incorrect
  "false = already exists" note from cfInsert (CF.INSERT never returns 0).
  Fix the broken reactive @return text, add cfAdd @throws and a RESP3 note.
- Restore bfInsert/bfMAdd in CreateReactiveApi VALUE_WRAP; they were
  accidentally dropped while adding the CF entries. Without them an
  api-generator rerun would regenerate the Bloom reactive insert as
  Flux<Boolean> and break BF reactive signatures, wiring and tests.
@a-TODO-rov

Copy link
Copy Markdown
Contributor

Thank you for the contribution @HwangRock
@Dgramada FYI

@Dgramada

Dgramada commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

@a-TODO-rov BfScanDumpValue and CfScanDumpValue are identical. Would it be beneficial to use one class with a more generic name for both methods? Also, CuckooFilter, BloomFilter, Top-K and so on are all parts of one Redis module. Would it be an improvement to move bf, cf packages and other features from the probabilistic module in a package named something like probabilistic so that these features are grouped together in the repository instead of spread out as separate packages?

Comment thread src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java Outdated
Comment thread src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java Outdated
Comment thread src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java Outdated
Comment thread src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java
Comment thread src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java Outdated
Comment thread src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java Outdated
HwangRock added 2 commits June 8, 2026 21:10
…terfaces

- Set @author to the contributor on the new Cuckoo Filter files.
- Drop CF.SCANDUMP from ReadOnlyCommands to match BF.SCANDUMP, which is not
  registered there, and adjust the read-only command count test accordingly.
- Declare RedisBloomFilterCoroutinesCommands and RedisCuckooFilterCoroutinesCommands
  on the RedisClusterCoroutinesCommands interface; they were previously only
  implemented in the impl class.
…NSERT

CF.INSERT returns integers on RESP2 and booleans on RESP3, never null, so the
Value wrapper wasn't needed. cfInsert now returns List<Boolean> (Flux<Boolean>
reactive) with a full filter mapped to false.

CF.INSERTNX stays an integer on both RESP2 and RESP3, so it now returns
List<Long> (1 = added, 0 = already exists, -1 = filter full) instead of being
squeezed into booleans, which keeps the three states distinct without a wrapper.

Also adds the single-value overloads requested in review and removes the
now-unused CuckooInsertBoolean* outputs.
@HwangRock HwangRock requested a review from Dgramada June 8, 2026 13:31
Both are registered with the readonly flag in RedisBloom and don't mutate the
filter, so they're safe to route to replicas. Adds both to ReadOnlyCommands
(BF.SCANDUMP was missing) and updates the read-only count test to 123.
@HwangRock

Copy link
Copy Markdown
Author

@a-TODO-rov BfScanDumpValue and CfScanDumpValue are identical. Would it be beneficial to use one class with a more generic name for both methods? Also, CuckooFilter, BloomFilter, Top-K and so on are all parts of one Redis module. Would it be an improvement to move bf, cf packages and other features from the probabilistic module in a package named something like probabilistic so that these features are grouped together in the repository instead of spread out as separate packages?

I think that's a great approach. Merging the two dump value classes into one will definitely clean up the code.

Moving them into a probabilistic package also makes sense for future features like Top-K and T-Digest.
Since this PR already covers both BF and CF, I'm happy to handle both here — or keep the package move as a follow-up if you'd prefer.

@Dgramada

Dgramada commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@a-TODO-rov BfScanDumpValue and CfScanDumpValue are identical. Would it be beneficial to use one class with a more generic name for both methods? Also, CuckooFilter, BloomFilter, Top-K and so on are all parts of one Redis module. Would it be an improvement to move bf, cf packages and other features from the probabilistic module in a package named something like probabilistic so that these features are grouped together in the repository instead of spread out as separate packages?

I think that's a great approach. Merging the two dump value classes into one will definitely clean up the code.

Moving them into a probabilistic package also makes sense for future features like Top-K and T-Digest. Since this PR already covers both BF and CF, I'm happy to handle both here — or keep the package move as a follow-up if you'd prefer.

I think it is fine to do both in this pull request (merging into one class and moving arguments and values to a single probabilistic package). Would you mind adding these changes too?

BfScanDumpValue and CfScanDumpValue were identical, so consolidate them into
a single io.lettuce.core.probabilistic.ScanDumpValue (with ScanDumpValueParser)
shared by both BF.SCANDUMP and CF.SCANDUMP.
Group the RedisBloom value, parser, and argument types that were spread
across io.lettuce.core.bf and io.lettuce.core.cf into a shared
io.lettuce.core.probabilistic package (arguments under
io.lettuce.core.probabilistic.arguments), so the Bloom Filter and Cuckoo
Filter features sit together for future probabilistic commands.
Relocate the Bloom Filter and Cuckoo Filter test classes from
io.lettuce.core.bf and io.lettuce.core.cf into io.lettuce.core.probabilistic
to match the production layout and keep the probabilistic features together.
@HwangRock

Copy link
Copy Markdown
Author

@a-TODO-rov BfScanDumpValue and CfScanDumpValue are identical. Would it be beneficial to use one class with a more generic name for both methods? Also, CuckooFilter, BloomFilter, Top-K and so on are all parts of one Redis module. Would it be an improvement to move bf, cf packages and other features from the probabilistic module in a package named something like probabilistic so that these features are grouped together in the repository instead of spread out as separate packages?

I think that's a great approach. Merging the two dump value classes into one will definitely clean up the code.
Moving them into a probabilistic package also makes sense for future features like Top-K and T-Digest. Since this PR already covers both BF and CF, I'm happy to handle both here — or keep the package move as a follow-up if you'd prefer.

I think it is fine to do both in this pull request (merging into one class and moving arguments and values to a single probabilistic package). Would you mind adding these changes too?

Done. I merged the two dump value classes into a single ScanDumpValue, and moved the values, arguments, and tests from both bf and cf into a shared probabilistic package (arguments under probabilistic.arguments).
The BF and CF unit and integration tests all pass.

[INFO] Tests run: 42, Failures: 0, Errors: 0, Skipped: 0

[INFO] Tests run: 111, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

@Dgramada Dgramada linked an issue Jun 11, 2026 that may be closed by this pull request
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

4 participants