From 7896a0bf1b77e860285674b21dc5e9d3754044bd Mon Sep 17 00:00:00 2001 From: HwangRock Date: Sat, 6 Jun 2026 08:54:23 +0900 Subject: [PATCH 01/10] Add Cuckoo Filter (CF.*) support 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 #2659, #2660, #2661, #2662, #2663, #2664, #2665, #2666, #2667, #2668, #2669, #2670 --- .../core/AbstractRedisAsyncCommands.java | 86 +++++- .../core/AbstractRedisReactiveCommands.java | 99 ++++++- .../core/RedisCuckooFilterCommandBuilder.java | 268 ++++++++++++++++++ .../core/api/async/RedisAsyncCommands.java | 3 +- .../async/RedisCuckooFilterAsyncCommands.java | 179 ++++++++++++ .../RedisCuckooFilterReactiveCommands.java | 179 ++++++++++++ .../api/reactive/RedisReactiveCommands.java | 16 +- .../lettuce/core/api/sync/RedisCommands.java | 2 +- .../api/sync/RedisCuckooFilterCommands.java | 178 ++++++++++++ .../java/io/lettuce/core/cf/CfInfoValue.java | 130 +++++++++ .../io/lettuce/core/cf/CfInfoValueParser.java | 44 +++ .../io/lettuce/core/cf/CfScanDumpValue.java | 44 +++ .../core/cf/CfScanDumpValueParser.java | 49 ++++ .../core/cf/arguments/CfInsertArgs.java | 109 +++++++ .../core/cf/arguments/CfReserveArgs.java | 113 ++++++++ .../api/async/NodeSelectionAsyncCommands.java | 2 +- ...odeSelectionCuckooFilterAsyncCommands.java | 178 ++++++++++++ .../api/async/RedisClusterAsyncCommands.java | 14 +- .../RedisClusterReactiveCommands.java | 2 +- .../api/sync/NodeSelectionCommands.java | 13 +- .../NodeSelectionCuckooFilterCommands.java | 178 ++++++++++++ .../api/sync/RedisClusterCommands.java | 2 +- .../lettuce/core/protocol/CommandKeyword.java | 2 +- .../io/lettuce/core/protocol/CommandType.java | 5 + .../core/protocol/ReadOnlyCommands.java | 2 + .../api/coroutines/RedisCoroutinesCommands.kt | 3 +- .../coroutines/RedisCoroutinesCommandsImpl.kt | 3 +- .../RedisCuckooFilterCoroutinesCommands.kt | 169 +++++++++++ ...RedisCuckooFilterCoroutinesCommandsImpl.kt | 77 +++++ .../RedisClusterCoroutinesCommandsImpl.kt | 3 +- .../core/api/RedisCuckooFilterCommands.java | 178 ++++++++++++ .../io/lettuce/apigenerator/Constants.java | 2 +- .../apigenerator/CreateReactiveApi.java | 2 +- ...isCuckooFilterCommandBuilderUnitTests.java | 230 +++++++++++++++ .../core/cf/CfInfoValueParserUnitTests.java | 160 +++++++++++ ...isCuckooFilterClusterIntegrationTests.java | 32 +++ .../cf/RedisCuckooFilterIntegrationTests.java | 184 ++++++++++++ ...sCuckooFilterReactiveIntegrationTests.java | 78 +++++ ...edisCuckooFilterResp2IntegrationTests.java | 39 +++ .../ClusterReadOnlyCommandsUnitTests.java | 2 +- 40 files changed, 3018 insertions(+), 41 deletions(-) create mode 100644 src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java create mode 100644 src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java create mode 100644 src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java create mode 100644 src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java create mode 100644 src/main/java/io/lettuce/core/cf/CfInfoValue.java create mode 100644 src/main/java/io/lettuce/core/cf/CfInfoValueParser.java create mode 100644 src/main/java/io/lettuce/core/cf/CfScanDumpValue.java create mode 100644 src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java create mode 100644 src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java create mode 100644 src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java create mode 100644 src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java create mode 100644 src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java create mode 100644 src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt create mode 100644 src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt create mode 100644 src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java create mode 100644 src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java create mode 100644 src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java create mode 100644 src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java create mode 100644 src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java create mode 100644 src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java create mode 100644 src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 38545ba6d6..ab22430cce 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -27,6 +27,10 @@ import io.lettuce.core.bf.BfScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.cluster.PipelinedRedisFuture; import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; @@ -115,7 +119,7 @@ public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncC RedisHLLAsyncCommands, BaseRedisAsyncCommands, RedisTransactionalAsyncCommands, RedisGeoAsyncCommands, RedisClusterAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, RediSearchAsyncCommands, RedisArrayAsyncCommands, - RedisBloomFilterAsyncCommands { + RedisBloomFilterAsyncCommands, RedisCuckooFilterAsyncCommands { private final StatefulConnection connection; @@ -131,6 +135,8 @@ public abstract class AbstractRedisAsyncCommands implements RedisAclAsyncC private final RedisBloomFilterCommandBuilder bloomFilterCommandBuilder; + private final RedisCuckooFilterCommandBuilder cuckooFilterCommandBuilder; + private final Supplier parser; /** @@ -150,6 +156,7 @@ public AbstractRedisAsyncCommands(StatefulConnection connection, RedisCode this.searchCommandBuilder = new RediSearchCommandBuilder<>(codec); this.arrayCommandBuilder = new RedisArrayCommandBuilder<>(codec); this.bloomFilterCommandBuilder = new RedisBloomFilterCommandBuilder<>(codec); + this.cuckooFilterCommandBuilder = new RedisCuckooFilterCommandBuilder<>(codec); } /** @@ -4291,4 +4298,81 @@ public RedisFuture bfScanDump(K key, long iterator) { return dispatch(bloomFilterCommandBuilder.bfScanDump(key, iterator)); } + // --- Redis Cuckoo Filter Commands --- + + @Override + public RedisFuture cfReserve(K key, long capacity) { + return dispatch(cuckooFilterCommandBuilder.cfReserve(key, capacity)); + } + + @Override + public RedisFuture cfReserve(K key, long capacity, CfReserveArgs args) { + return dispatch(cuckooFilterCommandBuilder.cfReserve(key, capacity, args)); + } + + @Override + public RedisFuture cfAdd(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfAdd(key, value)); + } + + @Override + public RedisFuture cfAddNx(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfAddNx(key, value)); + } + + @Override + public RedisFuture> cfInsert(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, values)); + } + + @Override + public RedisFuture> cfInsert(K key, CfInsertArgs args, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, args, values)); + } + + @Override + public RedisFuture> cfInsertNx(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, values)); + } + + @Override + public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, args, values)); + } + + @Override + public RedisFuture cfExists(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfExists(key, value)); + } + + @Override + public RedisFuture> cfMExists(K key, V... values) { + return dispatch(cuckooFilterCommandBuilder.cfMExists(key, values)); + } + + @Override + public RedisFuture cfDel(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfDel(key, value)); + } + + @Override + public RedisFuture cfCount(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfCount(key, value)); + } + + @Override + public RedisFuture cfScanDump(K key, long cursor) { + return dispatch(cuckooFilterCommandBuilder.cfScanDump(key, cursor)); + } + + @Override + public RedisFuture cfLoadChunk(K key, long cursor, byte[] data) { + return dispatch(cuckooFilterCommandBuilder.cfLoadChunk(key, cursor, data)); + } + + @Override + public RedisFuture cfInfo(K key) { + return dispatch(cuckooFilterCommandBuilder.cfInfo(key)); + } + } diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 540880425c..50c8380a7a 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -27,6 +27,10 @@ import io.lettuce.core.bf.BfScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; import io.lettuce.core.cluster.models.partitions.RedisClusterNode; @@ -114,13 +118,14 @@ * @author Yordan Tsintsov * @since 4.0 */ -public abstract class AbstractRedisReactiveCommands implements RedisAclReactiveCommands, - RedisHashReactiveCommands, RedisKeyReactiveCommands, RedisStringReactiveCommands, - RedisListReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, - RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisHLLReactiveCommands, - BaseRedisReactiveCommands, RedisTransactionalReactiveCommands, RedisGeoReactiveCommands, - RedisClusterReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, - RediSearchReactiveCommands, RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { +public abstract class AbstractRedisReactiveCommands + implements RedisAclReactiveCommands, RedisHashReactiveCommands, RedisKeyReactiveCommands, + RedisStringReactiveCommands, RedisListReactiveCommands, RedisSetReactiveCommands, + RedisSortedSetReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, + RedisHLLReactiveCommands, BaseRedisReactiveCommands, RedisTransactionalReactiveCommands, + RedisGeoReactiveCommands, RedisClusterReactiveCommands, RedisJsonReactiveCommands, + RedisVectorSetReactiveCommands, RediSearchReactiveCommands, RedisArrayReactiveCommands, + RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { private final StatefulConnection connection; @@ -136,6 +141,8 @@ public abstract class AbstractRedisReactiveCommands implements RedisAclRea private final RedisBloomFilterCommandBuilder bloomFilterCommandBuilder; + private final RedisCuckooFilterCommandBuilder cuckooFilterCommandBuilder; + private final Supplier parser; private final ClientResources clientResources; @@ -161,6 +168,7 @@ public AbstractRedisReactiveCommands(StatefulConnection connection, RedisC this.searchCommandBuilder = new RediSearchCommandBuilder<>(codec); this.arrayCommandBuilder = new RedisArrayCommandBuilder<>(codec); this.bloomFilterCommandBuilder = new RedisBloomFilterCommandBuilder<>(codec); + this.cuckooFilterCommandBuilder = new RedisCuckooFilterCommandBuilder<>(codec); this.clientResources = connection.getResources(); this.tracingEnabled = clientResources.tracing().isEnabled(); } @@ -4338,4 +4346,81 @@ public Mono bfScanDump(K key, long iterator) { return createMono(() -> bloomFilterCommandBuilder.bfScanDump(key, iterator)); } + // --- Redis Cuckoo Filter Commands --- + + @Override + public Mono cfReserve(K key, long capacity) { + return createMono(() -> cuckooFilterCommandBuilder.cfReserve(key, capacity)); + } + + @Override + public Mono cfReserve(K key, long capacity, CfReserveArgs args) { + return createMono(() -> cuckooFilterCommandBuilder.cfReserve(key, capacity, args)); + } + + @Override + public Mono cfAdd(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfAdd(key, value)); + } + + @Override + public Mono cfAddNx(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfAddNx(key, value)); + } + + @Override + public Flux> cfInsert(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertValues(key, values)); + } + + @Override + public Flux> cfInsert(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertValues(key, args, values)); + } + + @Override + public Flux> cfInsertNx(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNxValues(key, values)); + } + + @Override + public Flux> cfInsertNx(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNxValues(key, args, values)); + } + + @Override + public Mono cfExists(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfExists(key, value)); + } + + @Override + public Flux cfMExists(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfMExists(key, values)); + } + + @Override + public Mono cfDel(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfDel(key, value)); + } + + @Override + public Mono cfCount(K key, V value) { + return createMono(() -> cuckooFilterCommandBuilder.cfCount(key, value)); + } + + @Override + public Mono cfScanDump(K key, long cursor) { + return createMono(() -> cuckooFilterCommandBuilder.cfScanDump(key, cursor)); + } + + @Override + public Mono cfLoadChunk(K key, long cursor, byte[] data) { + return createMono(() -> cuckooFilterCommandBuilder.cfLoadChunk(key, cursor, data)); + } + + @Override + public Mono cfInfo(K key) { + return createMono(() -> cuckooFilterCommandBuilder.cfInfo(key)); + } + } diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java new file mode 100644 index 0000000000..dfa6015ebc --- /dev/null +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core; + +import java.util.List; + +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfInfoValueParser; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.CfScanDumpValueParser; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.output.*; +import io.lettuce.core.protocol.BaseRedisCommandBuilder; +import io.lettuce.core.protocol.Command; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +import static io.lettuce.core.protocol.CommandType.*; + +/** + * Implementation of the {@link BaseRedisCommandBuilder} handling Cuckoo Filter commands. + * + * @param Key type. + * @param Value type. + * @author Yordan Tsintsov + * @since 7.7 + */ +class RedisCuckooFilterCommandBuilder extends BaseRedisCommandBuilder { + + RedisCuckooFilterCommandBuilder(RedisCodec codec) { + super(codec); + } + + Command cfReserve(K key, long capacity) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(capacity); + + return createCommand(CF_RESERVE, new StatusOutput<>(codec), args); + } + + Command cfReserve(K key, long capacity, CfReserveArgs reserveArgs) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(capacity); + reserveArgs.build(args); + + return createCommand(CF_RESERVE, new StatusOutput<>(codec), args); + } + + Command cfAdd(K key, V value) { + notNullKey(key); + + return createCommand(CF_ADD, new BooleanOutput<>(codec), key, value); + } + + Command cfAddNx(K key, V value) { + notNullKey(key); + + return createCommand(CF_ADDNX, new BooleanOutput<>(codec), key, value); + } + + Command> cfInsert(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsert(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsert(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + Command>> cfInsertValues(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + Command>> cfInsertValues(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + @SafeVarargs + final Command>> cfInsertValues(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + @SafeVarargs + final Command>> cfInsertValues(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + Command> cfInsertNx(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsertNx(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + @SafeVarargs + final Command> cfInsertNx(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + } + + Command>> cfInsertNxValues(K key, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + Command>> cfInsertNxValues(K key, CfInsertArgs insertArgs, V value) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValue(value); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + @SafeVarargs + final Command>> cfInsertNxValues(K key, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + @SafeVarargs + final Command>> cfInsertNxValues(K key, CfInsertArgs insertArgs, V... values) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key); + insertArgs.build(args); + args.add(CommandKeyword.ITEMS).addValues(values); + + return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + } + + Command cfExists(K key, V value) { + notNullKey(key); + + return createCommand(CF_EXISTS, new BooleanOutput<>(codec), key, value); + } + + @SafeVarargs + final Command> cfMExists(K key, V... values) { + notNullKey(key); + + return createCommand(CF_MEXISTS, new BooleanListOutput<>(codec), key, values); + } + + Command cfDel(K key, V value) { + notNullKey(key); + + return createCommand(CF_DEL, new BooleanOutput<>(codec), key, value); + } + + Command cfCount(K key, V value) { + notNullKey(key); + + return createCommand(CF_COUNT, new IntegerOutput<>(codec), key, value); + } + + Command cfScanDump(K key, long iterator) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator); + + return createCommand(CF_SCANDUMP, new EncodedComplexOutput<>(codec, CfScanDumpValueParser.INSTANCE), args); + } + + Command cfLoadChunk(K key, long iterator, byte[] data) { + notNullKey(key); + + CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator).add(data); + + return createCommand(CF_LOADCHUNK, new StatusOutput<>(codec), args); + } + + Command cfInfo(K key) { + notNullKey(key); + + return createCommand(CF_INFO, new EncodedComplexOutput<>(codec, CfInfoValueParser.INSTANCE), key); + } + +} diff --git a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java index 6c17d6e97d..88d2bfe6ca 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisAsyncCommands.java @@ -40,7 +40,8 @@ public interface RedisAsyncCommands extends BaseRedisAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands, RedisTransactionalAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, - RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands { + RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands, + RedisCuckooFilterAsyncCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java new file mode 100644 index 0000000000..a2ab5a0081 --- /dev/null +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.async; + +import java.util.List; +import io.lettuce.core.RedisFuture; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; + +/** + * Asynchronous executed commands for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateAsyncApi + */ +public interface RedisCuckooFilterAsyncCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + RedisFuture cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + RedisFuture cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + RedisFuture cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + RedisFuture cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + RedisFuture> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + RedisFuture> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + RedisFuture> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + RedisFuture cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + RedisFuture> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + RedisFuture cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + RedisFuture cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + RedisFuture cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + RedisFuture cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + RedisFuture cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java new file mode 100644 index 0000000000..006b50d103 --- /dev/null +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.reactive; + +import io.lettuce.core.Value; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; +import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; + +/** + * Reactive executed commands for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateReactiveApi + */ +public interface RedisCuckooFilterReactiveCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Mono cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Mono cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Mono cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Mono cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the + * filter. + */ + Flux> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the + * filter. + */ + Flux> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the + * filter. + */ + Flux> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the + * filter. + */ + Flux> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Mono cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return Boolean of {@code true} or {@code false} where {@code true} means that, with high probability, the item was + * already added to the filter, and {@code false} means that the item was definitely not added to the filter. + */ + Flux cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Mono cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Mono cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + Mono cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + Mono cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + Mono cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java index e727a4f538..74ae30a69c 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisReactiveCommands.java @@ -32,14 +32,14 @@ * @author Yordan Tsintsov * @since 5.0 */ -public interface RedisReactiveCommands - extends BaseRedisReactiveCommands, RedisAclReactiveCommands, RedisClusterReactiveCommands, - RedisFunctionReactiveCommands, RedisGeoReactiveCommands, RedisHashReactiveCommands, - RedisHLLReactiveCommands, RedisKeyReactiveCommands, RedisListReactiveCommands, - RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, - RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, RedisStringReactiveCommands, - RedisTransactionalReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, - RediSearchReactiveCommands, RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { +public interface RedisReactiveCommands extends BaseRedisReactiveCommands, RedisAclReactiveCommands, + RedisClusterReactiveCommands, RedisFunctionReactiveCommands, RedisGeoReactiveCommands, + RedisHashReactiveCommands, RedisHLLReactiveCommands, RedisKeyReactiveCommands, + RedisListReactiveCommands, RedisScriptingReactiveCommands, RedisServerReactiveCommands, + RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, + RedisStringReactiveCommands, RedisTransactionalReactiveCommands, RedisJsonReactiveCommands, + RedisVectorSetReactiveCommands, RediSearchReactiveCommands, RedisArrayReactiveCommands, + RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java index ca302b64aa..2c2488fe25 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCommands.java @@ -39,7 +39,7 @@ public interface RedisCommands extends BaseRedisCommands, RedisAclCo RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands, RedisTransactionalCommands, RedisJsonCommands, RedisVectorSetCommands, RediSearchCommands, - RedisArrayCommands, RedisBloomFilterCommands { + RedisArrayCommands, RedisBloomFilterCommands, RedisCuckooFilterCommands { /** * Authenticate to the server. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java new file mode 100644 index 0000000000..394609a5a8 --- /dev/null +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.sync; + +import java.util.List; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; + +/** + * Synchronous executed commands for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateSyncApi + */ +public interface RedisCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Boolean cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + List cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Boolean cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Long cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + CfScanDumpValue cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + String cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + CfInfoValue cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cf/CfInfoValue.java b/src/main/java/io/lettuce/core/cf/CfInfoValue.java new file mode 100644 index 0000000000..181ce7e30e --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/CfInfoValue.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import java.util.Map; + +/** + * Represents the result of the Redis CF.INFO command. + * + * @author HwangRock + * @since 7.7 + */ +public class CfInfoValue { + + private final Map rawInfo; + + private final Long size; + + private final Long numberOfBuckets; + + private final Long numberOfFilters; + + private final Long numberOfItemsInserted; + + private final Long numberOfItemsDeleted; + + private final Long bucketSize; + + private final Long expansionRate; + + private final Long maxIterations; + + public CfInfoValue(Map rawInfo) { + this.rawInfo = rawInfo; + this.size = (Long) rawInfo.get("Size"); + this.numberOfBuckets = (Long) rawInfo.get("Number of buckets"); + this.numberOfFilters = (Long) rawInfo.get("Number of filters"); + this.numberOfItemsInserted = (Long) rawInfo.get("Number of items inserted"); + this.numberOfItemsDeleted = (Long) rawInfo.get("Number of items deleted"); + this.bucketSize = (Long) rawInfo.get("Bucket size"); + this.expansionRate = (Long) rawInfo.get("Expansion rate"); + this.maxIterations = (Long) rawInfo.get("Max iterations"); + } + + /** + * Returns the raw info map returned by the Redis server. + * + * @return the raw info map returned by the Redis server + */ + public Map getRawInfo() { + return rawInfo; + } + + /** + * Returns the current size of the filter in bytes. + * + * @return the current size of the filter in bytes + */ + public Long getSize() { + return size; + } + + /** + * Returns the number of buckets in the filter. + * + * @return the number of buckets in the filter + */ + public Long getNumberOfBuckets() { + return numberOfBuckets; + } + + /** + * Returns the number of sub-filters in the filter. + * + * @return the number of sub-filters in the filter + */ + public Long getNumberOfFilters() { + return numberOfFilters; + } + + /** + * Returns the number of items inserted into the filter. + * + * @return the number of items inserted into the filter + */ + public Long getNumberOfItemsInserted() { + return numberOfItemsInserted; + } + + /** + * Returns the number of items deleted from the filter. + * + * @return the number of items deleted from the filter + */ + public Long getNumberOfItemsDeleted() { + return numberOfItemsDeleted; + } + + /** + * Returns the bucket size of the filter. + * + * @return the bucket size of the filter + */ + public Long getBucketSize() { + return bucketSize; + } + + /** + * Returns the expansion rate of the filter. + * + * @return the expansion rate of the filter + */ + public Long getExpansionRate() { + return expansionRate; + } + + /** + * Returns the maximum number of iterations when inserting items into the filter. + * + * @return the maximum number of iterations when inserting items into the filter + */ + public Long getMaxIterations() { + return maxIterations; + } + +} diff --git a/src/main/java/io/lettuce/core/cf/CfInfoValueParser.java b/src/main/java/io/lettuce/core/cf/CfInfoValueParser.java new file mode 100644 index 0000000000..96b53aa287 --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/CfInfoValueParser.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import java.nio.ByteBuffer; +import java.util.LinkedHashMap; +import java.util.Map; + +import io.lettuce.core.codec.StringCodec; +import io.lettuce.core.output.ComplexData; +import io.lettuce.core.output.ComplexDataParser; + +/** + * Parser for Redis CF.INFO command output. + * + * @author HwangRock + * @since 7.7 + */ +public final class CfInfoValueParser implements ComplexDataParser { + + public static final CfInfoValueParser INSTANCE = new CfInfoValueParser(); + + private CfInfoValueParser() { + } + + @Override + public CfInfoValue parse(ComplexData data) { + if (data == null) { + throw new IllegalArgumentException("Failed parsing CF.INFO: data must not be null"); + } + Map raw = data.getDynamicMap(); + Map info = new LinkedHashMap<>(raw.size()); + for (Map.Entry e : raw.entrySet()) { + String k = StringCodec.UTF8.decodeKey((ByteBuffer) e.getKey()); + info.put(k, e.getValue()); + } + return new CfInfoValue(info); + } + +} diff --git a/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java b/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java new file mode 100644 index 0000000000..65c9e1bda4 --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +/** + * Value object for the Redis CF.SCANDUMP command. + * + * @author HwangRock + * @since 7.7 + */ +public class CfScanDumpValue { + + private final long iterator; + + private final byte[] data; + + public CfScanDumpValue(long iterator, byte[] data) { + this.iterator = iterator; + this.data = data; + } + + /** + * Returns the iterator value. + * + * @return the iterator value + */ + public long getIterator() { + return iterator; + } + + /** + * Returns the data. + * + * @return the data + */ + public byte[] getData() { + return data; + } + +} diff --git a/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java b/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java new file mode 100644 index 0000000000..49704aaabf --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import java.nio.ByteBuffer; +import java.util.List; + +import io.lettuce.core.output.ComplexData; +import io.lettuce.core.output.ComplexDataParser; + +/** + * Parser for Redis CF.SCANDUMP command output. + * + * @author HwangRock + * @since 7.7 + */ +public final class CfScanDumpValueParser implements ComplexDataParser { + + public static final CfScanDumpValueParser INSTANCE = new CfScanDumpValueParser(); + + private CfScanDumpValueParser() { + } + + @Override + public CfScanDumpValue parse(ComplexData data) { + if (data == null) { + throw new IllegalArgumentException("Failed parsing CF.SCANDUMP: data must not be null"); + } + List raw = data.getDynamicList(); + if (raw == null || raw.size() != 2) { + throw new IllegalArgumentException("Failed parsing CF.SCANDUMP: data must be a list of two elements"); + } + long iterator = ((Number) raw.get(0)).longValue(); + ByteBuffer dataByteBuffer = (ByteBuffer) raw.get(1); + byte[] dataBytes; + if (dataByteBuffer == null) { + dataBytes = new byte[0]; + } else { + dataBytes = new byte[dataByteBuffer.remaining()]; + dataByteBuffer.get(dataBytes); + } + return new CfScanDumpValue(iterator, dataBytes); + } + +} diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java b/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java new file mode 100644 index 0000000000..0163810904 --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf.arguments; + +import io.lettuce.core.CompositeArgument; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis CF.INSERT command. + *

+ * {@link CfInsertArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Yordan Tsintsov + * @since 7.7 + */ +public class CfInsertArgs implements CompositeArgument { + + private Long capacity; + + private boolean noCreate; + + /** + * Builder entry points for {@link CfInsertArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates a new {@link CfInsertArgs} and sets the desired capacity of the filter. + * + * @return a new {@link CfInsertArgs} with capacity configured. + */ + public static CfInsertArgs capacity(long capacity) { + return new CfInsertArgs().capacity(capacity); + } + + /** + * Creates a new {@link CfInsertArgs} and sets the no create flag. + * + * @return a new {@link CfInsertArgs} with no create flag configured. + */ + public static CfInsertArgs noCreate() { + return new CfInsertArgs().noCreate(); + } + + /** + * Creates a new {@link CfInsertArgs} with default settings. + * + * @return a new {@link CfInsertArgs} with default settings. + */ + public static CfInsertArgs defaults() { + return new CfInsertArgs(); + } + + } + + /** + * Set the desired capacity of the filter. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs capacity(long capacity) { + this.capacity = capacity; + return this; + } + + /** + * Set the no create flag. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs noCreate() { + this.noCreate = true; + return this; + } + + /** + * Set the default settings. + * + * @return {@code this} {@link CfInsertArgs}. + */ + public CfInsertArgs defaults() { + this.capacity = null; + this.noCreate = false; + return this; + } + + @Override + public void build(CommandArgs args) { + + if (capacity != null) { + args.add(CommandKeyword.CAPACITY).add(capacity); + } + if (noCreate) { + args.add(CommandKeyword.NOCREATE); + } + } + +} diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java b/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java new file mode 100644 index 0000000000..a8fa9ba427 --- /dev/null +++ b/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf.arguments; + +import io.lettuce.core.CompositeArgument; +import io.lettuce.core.protocol.CommandArgs; +import io.lettuce.core.protocol.CommandKeyword; + +/** + * Argument list builder for the Redis CF.RESERVE command. + *

+ * {@link CfReserveArgs} is a mutable object and instances should be used only once to avoid shared mutable state. + * + * @author Yordan Tsintsov + * @since 7.7 + */ +public class CfReserveArgs implements CompositeArgument { + + private Long bucketSize; + + private Long maxIterations; + + private Long expansion; + + /** + * Builder entry points for {@link CfReserveArgs}. + */ + public static class Builder { + + /** + * Utility constructor. + */ + private Builder() { + } + + /** + * Creates a new {@link CfReserveArgs} and sets the bucket size. + * + * @return a new {@link CfReserveArgs} with bucket size configured. + */ + public static CfReserveArgs bucketSize(long bucketSize) { + return new CfReserveArgs().bucketSize(bucketSize); + } + + /** + * Creates a new {@link CfReserveArgs} and sets the max iterations. + * + * @return a new {@link CfReserveArgs} with max iterations configured. + */ + public static CfReserveArgs maxIterations(long maxIterations) { + return new CfReserveArgs().maxIterations(maxIterations); + } + + /** + * Creates a new {@link CfReserveArgs} and sets the expansion rate of the filter. + * + * @return a new {@link CfReserveArgs} with expansion rate configured. + */ + public static CfReserveArgs expansion(long expansion) { + return new CfReserveArgs().expansion(expansion); + } + + } + + /** + * Sets the bucket size. + * + * @return this + */ + public CfReserveArgs bucketSize(long bucketSize) { + this.bucketSize = bucketSize; + return this; + } + + /** + * Sets the max iterations. + * + * @return this + */ + public CfReserveArgs maxIterations(long maxIterations) { + this.maxIterations = maxIterations; + return this; + } + + /** + * Sets the expansion rate of the filter. + * + * @return this + */ + public CfReserveArgs expansion(long expansion) { + this.expansion = expansion; + return this; + } + + @Override + public void build(CommandArgs args) { + + if (bucketSize != null) { + args.add(CommandKeyword.BUCKETSIZE).add(bucketSize); + } + if (maxIterations != null) { + args.add(CommandKeyword.MAXITERATIONS).add(maxIterations); + } + if (expansion != null) { + args.add(CommandKeyword.EXPANSION).add(expansion); + } + } + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java index e49a62f864..195c6e23f9 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionAsyncCommands.java @@ -16,5 +16,5 @@ public interface NodeSelectionAsyncCommands extends BaseNodeSelectionAsync NodeSelectionScriptingAsyncCommands, NodeSelectionServerAsyncCommands, NodeSelectionSetAsyncCommands, NodeSelectionSortedSetAsyncCommands, NodeSelectionStreamCommands, NodeSelectionStringAsyncCommands, NodeSelectionJsonAsyncCommands, NodeSelectionVectorSetAsyncCommands, NodeSelectionSearchAsyncCommands, - NodeSelectionBloomFilterAsyncCommands { + NodeSelectionBloomFilterAsyncCommands, NodeSelectionCuckooFilterAsyncCommands { } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java new file mode 100644 index 0000000000..24dd2ae9fc --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cluster.api.async; + +import java.util.List; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; + +/** + * Asynchronous executed commands on a node selection for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateAsyncNodeSelectionClusterApi + */ +public interface NodeSelectionCuckooFilterAsyncCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + AsyncExecutions cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + AsyncExecutions cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + AsyncExecutions cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + AsyncExecutions cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + AsyncExecutions> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + AsyncExecutions> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + AsyncExecutions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + AsyncExecutions cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + AsyncExecutions> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + AsyncExecutions cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + AsyncExecutions cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + AsyncExecutions cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + AsyncExecutions cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + AsyncExecutions cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java index a6c05f8b18..2c1c396ec3 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/RedisClusterAsyncCommands.java @@ -41,13 +41,13 @@ * @author Yordan Tsintsov * @since 4.0 */ -public interface RedisClusterAsyncCommands - extends BaseRedisAsyncCommands, RedisAclAsyncCommands, RedisFunctionAsyncCommands, - RedisGeoAsyncCommands, RedisHashAsyncCommands, RedisHLLAsyncCommands, RedisKeyAsyncCommands, - RedisListAsyncCommands, RedisScriptingAsyncCommands, RedisServerAsyncCommands, - RedisSetAsyncCommands, RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, - RedisStringAsyncCommands, RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, - RediSearchAsyncCommands, RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands { +public interface RedisClusterAsyncCommands extends BaseRedisAsyncCommands, RedisAclAsyncCommands, + RedisFunctionAsyncCommands, RedisGeoAsyncCommands, RedisHashAsyncCommands, + RedisHLLAsyncCommands, RedisKeyAsyncCommands, RedisListAsyncCommands, + RedisScriptingAsyncCommands, RedisServerAsyncCommands, RedisSetAsyncCommands, + RedisSortedSetAsyncCommands, RedisStreamAsyncCommands, RedisStringAsyncCommands, + RedisJsonAsyncCommands, RedisVectorSetAsyncCommands, RediSearchAsyncCommands, + RedisArrayAsyncCommands, RedisBloomFilterAsyncCommands, RedisCuckooFilterAsyncCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java index b6756db506..0ec6330320 100644 --- a/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/reactive/RedisClusterReactiveCommands.java @@ -48,7 +48,7 @@ public interface RedisClusterReactiveCommands extends BaseRedisReactiveCom RedisScriptingReactiveCommands, RedisServerReactiveCommands, RedisSetReactiveCommands, RedisSortedSetReactiveCommands, RedisStreamReactiveCommands, RedisStringReactiveCommands, RedisJsonReactiveCommands, RedisVectorSetReactiveCommands, RediSearchReactiveCommands, - RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands { + RedisArrayReactiveCommands, RedisBloomFilterReactiveCommands, RedisCuckooFilterReactiveCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java index d18afd2360..52dbb6a94a 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCommands.java @@ -8,10 +8,11 @@ * @author Mark Paluch * @author Tihomir Mateev */ -public interface NodeSelectionCommands extends BaseNodeSelectionCommands, NodeSelectionFunctionCommands, - NodeSelectionGeoCommands, NodeSelectionHashCommands, NodeSelectionHLLCommands, - NodeSelectionKeyCommands, NodeSelectionListCommands, NodeSelectionScriptingCommands, - NodeSelectionServerCommands, NodeSelectionSetCommands, NodeSelectionSortedSetCommands, - NodeSelectionStreamCommands, NodeSelectionStringCommands, NodeSelectionJsonCommands, - NodeSelectionVectorSetCommands, NodeSelectionSearchCommands, NodeSelectionBloomFilterCommands { +public interface NodeSelectionCommands + extends BaseNodeSelectionCommands, NodeSelectionFunctionCommands, NodeSelectionGeoCommands, + NodeSelectionHashCommands, NodeSelectionHLLCommands, NodeSelectionKeyCommands, + NodeSelectionListCommands, NodeSelectionScriptingCommands, NodeSelectionServerCommands, + NodeSelectionSetCommands, NodeSelectionSortedSetCommands, NodeSelectionStreamCommands, + NodeSelectionStringCommands, NodeSelectionJsonCommands, NodeSelectionVectorSetCommands, + NodeSelectionSearchCommands, NodeSelectionBloomFilterCommands, NodeSelectionCuckooFilterCommands { } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java new file mode 100644 index 0000000000..b6529a6e31 --- /dev/null +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cluster.api.sync; + +import java.util.List; +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; + +/** + * Synchronous executed commands on a node selection for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateSyncNodeSelectionClusterApi + */ +public interface NodeSelectionCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Executions cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + Executions cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Executions cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Executions cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + Executions> cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + Executions> cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + Executions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + Executions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Executions cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + Executions> cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Executions cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Executions cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + Executions cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + Executions cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + Executions cfInfo(K key); + +} diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java index 15e1426b1c..63880b297e 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/RedisClusterCommands.java @@ -46,7 +46,7 @@ public interface RedisClusterCommands RedisHashCommands, RedisHLLCommands, RedisKeyCommands, RedisListCommands, RedisScriptingCommands, RedisServerCommands, RedisSetCommands, RedisSortedSetCommands, RedisStreamCommands, RedisStringCommands, RedisJsonCommands, RedisVectorSetCommands, - RediSearchCommands, RedisArrayCommands, RedisBloomFilterCommands { + RediSearchCommands, RedisArrayCommands, RedisBloomFilterCommands, RedisCuckooFilterCommands { /** * Set the default timeout for operations. A zero timeout value indicates to not time out. diff --git a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java index 7d9f604018..2465b77b80 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandKeyword.java +++ b/src/main/java/io/lettuce/core/protocol/CommandKeyword.java @@ -71,7 +71,7 @@ CAS, EF, ELE, SETATTR, M, NOQUANT, BIN, Q8, FILTER, FILTER_EF( // INCREX keywords BYFLOAT, BYINT, ENX, LBOUND, SATURATE, UBOUND, - CAPACITY, SIZE, FILTERS, ITEMS, EXPANSION, NONSCALING, ERROR, NOCREATE; + CAPACITY, SIZE, FILTERS, ITEMS, EXPANSION, NONSCALING, ERROR, NOCREATE, BUCKETSIZE, MAXITERATIONS; public final byte[] bytes; diff --git a/src/main/java/io/lettuce/core/protocol/CommandType.java b/src/main/java/io/lettuce/core/protocol/CommandType.java index 56d0313c16..658f76b37d 100644 --- a/src/main/java/io/lettuce/core/protocol/CommandType.java +++ b/src/main/java/io/lettuce/core/protocol/CommandType.java @@ -133,6 +133,11 @@ public enum CommandType implements ProtocolKeyword { BF_ADD("BF.ADD"), BF_CARD("BF.CARD"), BF_EXISTS("BF.EXISTS"), BF_INFO("BF.INFO"), BF_INSERT("BF.INSERT"), BF_LOADCHUNK( "BF.LOADCHUNK"), BF_MADD("BF.MADD"), BF_MEXISTS("BF.MEXISTS"), BF_RESERVE("BF.RESERVE"), BF_SCANDUMP("BF.SCANDUMP"), + // Cuckoo Filter + CF_ADD("CF.ADD"), CF_ADDNX("CF.ADDNX"), CF_COUNT("CF.COUNT"), CF_DEL("CF.DEL"), CF_EXISTS("CF.EXISTS"), CF_INFO( + "CF.INFO"), CF_INSERT("CF.INSERT"), CF_INSERTNX("CF.INSERTNX"), CF_LOADCHUNK( + "CF.LOADCHUNK"), CF_MEXISTS("CF.MEXISTS"), CF_RESERVE("CF.RESERVE"), CF_SCANDUMP("CF.SCANDUMP"), + // Others TIME, WAIT, diff --git a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java index 795689d4ce..393e2d60f7 100644 --- a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java +++ b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java @@ -85,6 +85,8 @@ enum CommandName { ZRANDMEMBER, ZRANGEBYLEX, ZRANGEBYSCORE, ZRANK, ZREVRANGE, ZREVRANGEBYLEX, ZREVRANGEBYSCORE, ZREVRANK, ZSCAN, ZSCORE, // RediSearch (keyless read-only) FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, + // Cuckoo Filter read-only commands + CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, CF_SCANDUMP, } /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt index 78146215d1..f9f0a10035 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommands.kt @@ -52,7 +52,8 @@ interface RedisCoroutinesCommands : RedisJsonCoroutinesCommands, RedisVectorSetCoroutinesCommands, RedisArrayCoroutinesCommands, - RedisBloomFilterCoroutinesCommands { + RedisBloomFilterCoroutinesCommands, + RedisCuckooFilterCoroutinesCommands { /** * Authenticate to the server. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt index b29053c621..6b1b4e81ac 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCoroutinesCommandsImpl.kt @@ -58,7 +58,8 @@ open class RedisCoroutinesCommandsImpl( RedisVectorSetCoroutinesCommands by RedisVectorSetCoroutinesCommandsImpl(ops), RediSearchCoroutinesCommands by RediSearchCoroutinesCommandsImpl(ops), RedisArrayCoroutinesCommands by RedisArrayCoroutinesCommandsImpl(ops), - RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops) { + RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops), + RedisCuckooFilterCoroutinesCommands by RedisCuckooFilterCoroutinesCommandsImpl(ops) { /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt new file mode 100644 index 0000000000..7863bbd1c8 --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.coroutines + +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.cf.CfInfoValue +import io.lettuce.core.cf.CfScanDumpValue +import io.lettuce.core.cf.arguments.CfInsertArgs +import io.lettuce.core.cf.arguments.CfReserveArgs + +/** + * Coroutine executed commands for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + * @generated by io.lettuce.apigenerator.CreateKotlinCoroutinesApi + */ +@ExperimentalLettuceCoroutinesApi +interface RedisCuckooFilterCoroutinesCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply `OK` if `CF.RESERVE` was executed correctly. + */ + suspend fun cfReserve(key: K, capacity: Long): String? + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply `OK` if `CF.RESERVE` was executed correctly. + */ + suspend fun cfReserve(key: K, capacity: Long, args: CfReserveArgs): String? + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was added, `false` if it was already in the filter. + */ + suspend fun cfAdd(key: K, value: V): Boolean? + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was added, `false` if it was already in the filter. + */ + suspend fun cfAddNx(key: K, value: V): Boolean? + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + */ + suspend fun cfInsert(key: K, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + */ + suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + */ + suspend fun cfInsertNx(key: K, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + */ + suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item may exist in the filter, `false` if it definitely does not. + */ + suspend fun cfExists(key: K, value: V): Boolean? + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List of `true` or `false` where `true` means that, with high probability, the item was already added to + * the filter, and `false` means that the item was definitely not added to the filter. + */ + suspend fun cfMExists(key: K, vararg values: V): List + + /** + * Delete an item from the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply `true` if the item was deleted, `false` if the item was not found. + */ + suspend fun cfDel(key: K, value: V): Boolean? + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + suspend fun cfCount(key: K, value: V): Long? + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + suspend fun cfScanDump(key: K, cursor: Long): CfScanDumpValue? + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply `OK` if `CF.LOADCHUNK` was executed correctly. + */ + suspend fun cfLoadChunk(key: K, cursor: Long, data: ByteArray): String? + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + suspend fun cfInfo(key: K): CfInfoValue? + +} diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt new file mode 100644 index 0000000000..e17ccd8e05 --- /dev/null +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api.coroutines + +import io.lettuce.core.ExperimentalLettuceCoroutinesApi +import io.lettuce.core.api.reactive.RedisCuckooFilterReactiveCommands +import io.lettuce.core.cf.CfInfoValue +import io.lettuce.core.cf.CfScanDumpValue +import io.lettuce.core.cf.arguments.CfInsertArgs +import io.lettuce.core.cf.arguments.CfReserveArgs +import kotlinx.coroutines.flow.toList +import kotlinx.coroutines.reactive.asFlow +import kotlinx.coroutines.reactive.awaitFirstOrNull + +/** + * Coroutine executed commands (based on reactive commands) for Cuckoo Filter commands. + * + * @param Key type. + * @param Value type. + * @author Yordan Tsintsov + * @since 7.7 + */ +@ExperimentalLettuceCoroutinesApi +internal class RedisCuckooFilterCoroutinesCommandsImpl( + internal val ops: RedisCuckooFilterReactiveCommands +) : RedisCuckooFilterCoroutinesCommands { + + override suspend fun cfReserve(key: K, capacity: Long): String? = + ops.cfReserve(key, capacity).awaitFirstOrNull() + + override suspend fun cfReserve(key: K, capacity: Long, args: CfReserveArgs): String? = + ops.cfReserve(key, capacity, args).awaitFirstOrNull() + + override suspend fun cfAdd(key: K, value: V): Boolean? = + ops.cfAdd(key, value).awaitFirstOrNull() + + override suspend fun cfAddNx(key: K, value: V): Boolean? = + ops.cfAddNx(key, value).awaitFirstOrNull() + + override suspend fun cfInsert(key: K, vararg values: V): List = + ops.cfInsert(key, *values).asFlow().toList().map { it.getValueOrElse(null) } + + override suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsert(key, args, *values).asFlow().toList().map { it.getValueOrElse(null) } + + override suspend fun cfInsertNx(key: K, vararg values: V): List = + ops.cfInsertNx(key, *values).asFlow().toList().map { it.getValueOrElse(null) } + + override suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsertNx(key, args, *values).asFlow().toList().map { it.getValueOrElse(null) } + + override suspend fun cfExists(key: K, value: V): Boolean? = + ops.cfExists(key, value).awaitFirstOrNull() + + override suspend fun cfMExists(key: K, vararg values: V): List = + ops.cfMExists(key, *values).asFlow().toList() + + override suspend fun cfDel(key: K, value: V): Boolean? = + ops.cfDel(key, value).awaitFirstOrNull() + + override suspend fun cfCount(key: K, value: V): Long? = + ops.cfCount(key, value).awaitFirstOrNull() + + override suspend fun cfScanDump(key: K, cursor: Long): CfScanDumpValue? = + ops.cfScanDump(key, cursor).awaitFirstOrNull() + + override suspend fun cfLoadChunk(key: K, cursor: Long, data: ByteArray): String? = + ops.cfLoadChunk(key, cursor, data).awaitFirstOrNull() + + override suspend fun cfInfo(key: K): CfInfoValue? = + ops.cfInfo(key).awaitFirstOrNull() + +} diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt index 45cb8e5b96..925be57333 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommandsImpl.kt @@ -53,7 +53,8 @@ internal class RedisClusterCoroutinesCommandsImpl( RedisSortedSetCoroutinesCommands by RedisSortedSetCoroutinesCommandsImpl(ops), RedisStreamCoroutinesCommands by RedisStreamCoroutinesCommandsImpl(ops), RedisStringCoroutinesCommands by RedisStringCoroutinesCommandsImpl(ops), - RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops) { + RedisBloomFilterCoroutinesCommands by RedisBloomFilterCoroutinesCommandsImpl(ops), + RedisCuckooFilterCoroutinesCommands by RedisCuckooFilterCoroutinesCommandsImpl(ops) { /** * Authenticate to the server. diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java new file mode 100644 index 0000000000..d1b0b2a418 --- /dev/null +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.api; + +import java.util.List; + +import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; + +/** + * ${intent} for Cuckoo Filter. + * + * @author Yordan Tsintsov + * @param Key type. + * @param Value type. + * @see Redis Cuckoo Filter + * @since 7.7 + */ +public interface RedisCuckooFilterCommands { + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity); + + /** + * Creates an empty Cuckoo filter with a single sub-filter for the initial specified capacity. + * + * @param key the key. + * @param capacity the capacity. + * @param args the reserve arguments. + * @return String simple-string-reply {@code OK} if {@code CF.RESERVE} was executed correctly. + */ + String cfReserve(K key, long capacity, CfReserveArgs args); + + /** + * Add an item to the Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAdd(K key, V value); + + /** + * Add an item to the Cuckoo Filter only if the item does not already exist. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + */ + Boolean cfAddNx(K key, V value); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param values the values. + * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already + * exists in the filter. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Check if an item exists in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item may exist in the filter, {@code false} if it definitely does not. + */ + Boolean cfExists(K key, V value); + + /** + * Check if one or more items exist in the Cuckoo Filter. + * + * @param key the key. + * @param values the values. + * @return List<Boolean> of {@code true} or {@code false} where {@code true} means that, with high probability, the + * item was already added to the filter, and {@code false} means that the item was definitely not added to the + * filter. + */ + List cfMExists(K key, V... values); + + /** + * Delete an item from the Cuckoo Filter. + * + *

+ * Warning: Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + *

+ * + * @param key the key. + * @param value the value. + * @return Boolean integer-reply {@code true} if the item was deleted, {@code false} if the item was not found. + */ + Boolean cfDel(K key, V value); + + /** + * Return the number of times an item may be in the Cuckoo Filter. + * + * @param key the key. + * @param value the value. + * @return Long integer-reply the number of times the item may be in the filter. + */ + Long cfCount(K key, V value); + + /** + * Begins an incremental save of the Cuckoo filter. + * + * @param key the key. + * @param cursor the cursor. + * @return CfScanDumpValue the scan dump value. + */ + CfScanDumpValue cfScanDump(K key, long cursor); + + /** + * Restores a Cuckoo filter previously saved using CF.SCANDUMP. + * + * @param key the key. + * @param cursor the cursor. + * @param data the data. + * @return String simple-string-reply {@code OK} if {@code CF.LOADCHUNK} was executed correctly. + */ + String cfLoadChunk(K key, long cursor, byte[] data); + + /** + * Get information about the Cuckoo Filter. + * + * @param key the key. + * @return CfInfoValue the information about the filter. + */ + CfInfoValue cfInfo(K key); + +} diff --git a/src/test/java/io/lettuce/apigenerator/Constants.java b/src/test/java/io/lettuce/apigenerator/Constants.java index 5f00063b0d..32d7300cfe 100644 --- a/src/test/java/io/lettuce/apigenerator/Constants.java +++ b/src/test/java/io/lettuce/apigenerator/Constants.java @@ -32,7 +32,7 @@ class Constants { "RedisScriptingCommands", "RedisSentinelCommands", "RedisServerCommands", "RedisSetCommands", "RedisSortedSetCommands", "RedisStreamCommands", "RedisStringCommands", "RedisTransactionalCommands", "RedisJsonCommands", "RedisVectorSetCommands", "RediSearchCommands", "RedisArrayCommands", - "RedisBloomFilterCommands" }; + "RedisBloomFilterCommands", "RedisCuckooFilterCommands" }; public static final File TEMPLATES = new File("src/main/templates"); diff --git a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java index 21097102be..8a3ba547fb 100644 --- a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java +++ b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java @@ -60,7 +60,7 @@ public class CreateReactiveApi { public static Set FORCE_FLUX_RESULT = LettuceSets.unmodifiableSet("eval", "evalsha", "evalReadOnly", "evalshaReadOnly", "fcall", "fcallReadOnly", "dispatch"); - public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield"); + public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "cfInsert", "cfInsertNx"); private static final Map RESULT_SPEC; diff --git a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java new file mode 100644 index 0000000000..0dabcfee5c --- /dev/null +++ b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core; + +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.codec.StringCodec; +import io.lettuce.core.protocol.Command; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import static io.lettuce.TestTags.UNIT_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link RedisCuckooFilterCommandBuilder}. + * + * @author Yordan Tsintsov + */ +@Tag(UNIT_TEST) +class RedisCuckooFilterCommandBuilderUnitTests { + + private static final String MY_KEY = "books:name"; + + private static final String MY_VALUE = "Dune"; + + private static final String MY_VALUE_2 = "Dune Messiah"; + + private final RedisCuckooFilterCommandBuilder builder = new RedisCuckooFilterCommandBuilder<>( + StringCodec.UTF8); + + @Test + void shouldCorrectlyConstructCfReserveCommand() { + Command command = builder.cfReserve(MY_KEY, 100); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$10\r\nCF.RESERVE\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$3\r\n100\r\n"); + } + + @Test + void shouldCorrectlyConstructCfReserveCommandWithArgs() { + CfReserveArgs reserveArgs = CfReserveArgs.Builder.bucketSize(4).maxIterations(10).expansion(2); + Command command = builder.cfReserve(MY_KEY, 100, reserveArgs); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo( + "*9\r\n" + "$10\r\nCF.RESERVE\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$3\r\n100\r\n" + "$10\r\nBUCKETSIZE\r\n" + + "$1\r\n4\r\n" + "$13\r\nMAXITERATIONS\r\n" + "$2\r\n10\r\n" + "$9\r\nEXPANSION\r\n" + "$1\r\n2\r\n"); + } + + @Test + void shouldCorrectlyConstructCfAddCommand() { + Command command = builder.cfAdd(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$6\r\nCF.ADD\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfAddNxCommand() { + Command command = builder.cfAddNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$8\r\nCF.ADDNX\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommand() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100).noCreate(); + Command> command = builder.cfInsert(MY_KEY, insertArgs, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n100\r\n" + "$8\r\nNOCREATE\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandWithVarargs() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*5\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertCommandVarargWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + Command> command = builder.cfInsert(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n100\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommand() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommandWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(200).noCreate(); + Command> command = builder.cfInsertNx(MY_KEY, insertArgs, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*7\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$8\r\nCAPACITY\r\n" + + "$3\r\n200\r\n" + "$8\r\nNOCREATE\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInsertNxCommandWithVarargs() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*5\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfExistsCommand() { + Command command = builder.cfExists(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$9\r\nCF.EXISTS\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfMExistsCommand() { + Command> command = builder.cfMExists(MY_KEY, MY_VALUE, MY_VALUE_2); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$10\r\nCF.MEXISTS\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfDelCommand() { + Command command = builder.cfDel(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$6\r\nCF.DEL\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfCountCommand() { + Command command = builder.cfCount(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$8\r\nCF.COUNT\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfScanDumpCommand() { + Command command = builder.cfScanDump(MY_KEY, 0); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*3\r\n" + "$11\r\nCF.SCANDUMP\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$1\r\n0\r\n"); + } + + @Test + void shouldCorrectlyConstructCfLoadChunkCommand() { + byte[] valueBytes = MY_VALUE.getBytes(); + Command command = builder.cfLoadChunk(MY_KEY, 0, valueBytes); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$12\r\nCF.LOADCHUNK\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$1\r\n0\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + + @Test + void shouldCorrectlyConstructCfInfoCommand() { + Command command = builder.cfInfo(MY_KEY); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)) + .isEqualTo("*2\r\n" + "$7\r\nCF.INFO\r\n" + "$10\r\n" + MY_KEY + "\r\n"); + } + +} diff --git a/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java b/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java new file mode 100644 index 0000000000..76e228190e --- /dev/null +++ b/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +import io.lettuce.core.output.ComplexData; + +/** + * Unit tests for {@link CfInfoValueParser}. + * + * @author HwangRock + * @since 7.7 + */ +class CfInfoValueParserUnitTests { + + private final CfInfoValueParser parser = CfInfoValueParser.INSTANCE; + + // --------------------------------------------------------------------------- + // Helper: build a ComplexData backed by a flat key-value list (RESP2 array) + // --------------------------------------------------------------------------- + private static ComplexData buildMapData(Object... pairs) { + // Use ArrayComplexData via package-accessible test stub instead. + // We need to produce a ComplexData whose getDynamicMap() returns the pairs. + // We'll use a simple anonymous subclass to avoid package-private visibility issues. + return new ComplexData() { + + @Override + public void storeObject(Object value) { + // not needed + } + + @Override + public java.util.Map getDynamicMap() { + java.util.LinkedHashMap map = new java.util.LinkedHashMap<>(); + for (int i = 0; i < pairs.length - 1; i += 2) { + map.put(pairs[i], pairs[i + 1]); + } + return map; + } + + }; + } + + private static ByteBuffer buf(String s) { + return ByteBuffer.wrap(s.getBytes(StandardCharsets.UTF_8)); + } + + // --------------------------------------------------------------------------- + // INSTANCE singleton + // --------------------------------------------------------------------------- + + @Test + void instanceIsSingleton() { + assertThat(CfInfoValueParser.INSTANCE).isSameAs(CfInfoValueParser.INSTANCE); + } + + // --------------------------------------------------------------------------- + // null guard + // --------------------------------------------------------------------------- + + @Test + void parseNullThrows() { + assertThatThrownBy(() -> parser.parse(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("CF.INFO"); + } + + // --------------------------------------------------------------------------- + // All 8 fields parsed correctly + // --------------------------------------------------------------------------- + + @Test + void parseAllEightFields() { + ComplexData data = buildMapData(buf("Size"), 100L, buf("Number of buckets"), 200L, buf("Number of filters"), 1L, + buf("Number of items inserted"), 50L, buf("Number of items deleted"), 5L, buf("Bucket size"), 2L, + buf("Expansion rate"), 1L, buf("Max iterations"), 20L); + + CfInfoValue value = parser.parse(data); + + assertThat(value.getSize()).isEqualTo(100L); + assertThat(value.getNumberOfBuckets()).isEqualTo(200L); + assertThat(value.getNumberOfFilters()).isEqualTo(1L); + assertThat(value.getNumberOfItemsInserted()).isEqualTo(50L); + assertThat(value.getNumberOfItemsDeleted()).isEqualTo(5L); + assertThat(value.getBucketSize()).isEqualTo(2L); + assertThat(value.getExpansionRate()).isEqualTo(1L); + assertThat(value.getMaxIterations()).isEqualTo(20L); + } + + // --------------------------------------------------------------------------- + // Plural field names — "Number of filters" and "Max iterations" + // --------------------------------------------------------------------------- + + @Test + void numberOfFiltersIsPlural() { + ComplexData data = buildMapData(buf("Number of filters"), 3L); + CfInfoValue value = parser.parse(data); + assertThat(value.getNumberOfFilters()).isEqualTo(3L); + } + + @Test + void maxIterationsIsPlural() { + ComplexData data = buildMapData(buf("Max iterations"), 15L); + CfInfoValue value = parser.parse(data); + assertThat(value.getMaxIterations()).isEqualTo(15L); + } + + // --------------------------------------------------------------------------- + // rawInfo preserved + // --------------------------------------------------------------------------- + + @Test + void rawInfoIsPreserved() { + ComplexData data = buildMapData(buf("Size"), 42L); + CfInfoValue value = parser.parse(data); + assertThat(value.getRawInfo()).containsKey("Size"); + assertThat(value.getRawInfo().get("Size")).isEqualTo(42L); + } + + // --------------------------------------------------------------------------- + // Missing fields yield null (not NPE) + // --------------------------------------------------------------------------- + + @Test + void missingFieldsYieldNull() { + ComplexData data = buildMapData(buf("Size"), 10L); + CfInfoValue value = parser.parse(data); + assertThat(value.getNumberOfBuckets()).isNull(); + assertThat(value.getNumberOfFilters()).isNull(); + assertThat(value.getNumberOfItemsInserted()).isNull(); + assertThat(value.getNumberOfItemsDeleted()).isNull(); + assertThat(value.getBucketSize()).isNull(); + assertThat(value.getExpansionRate()).isNull(); + assertThat(value.getMaxIterations()).isNull(); + } + + // --------------------------------------------------------------------------- + // Empty map — all fields null, no exception + // --------------------------------------------------------------------------- + + @Test + void emptyMapDoesNotThrow() { + ComplexData data = buildMapData(); + CfInfoValue value = parser.parse(data); + assertThat(value).isNotNull(); + assertThat(value.getSize()).isNull(); + assertThat(value.getRawInfo()).isEmpty(); + } + +} diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java new file mode 100644 index 0000000000..d2abbc7859 --- /dev/null +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import javax.inject.Inject; + +import org.junit.jupiter.api.Tag; + +import io.lettuce.core.cluster.ClusterTestUtil; +import io.lettuce.core.cluster.api.StatefulRedisClusterConnection; + +import static io.lettuce.TestTags.INTEGRATION_TEST; + +/** + * Integration tests for Redis Cuckoo Filter commands using Redis Cluster. + * + * @author Yordan Tsintsov + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterClusterIntegrationTests extends RedisCuckooFilterIntegrationTests { + + @Inject + RedisCuckooFilterClusterIntegrationTests(StatefulRedisClusterConnection connection) { + super(ClusterTestUtil.redisCommandsOverCluster(connection)); + } + +} diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java new file mode 100644 index 0000000000..01aac00a68 --- /dev/null +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import javax.inject.Inject; +import java.util.List; + +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.test.LettuceExtension; +import io.lettuce.test.condition.EnabledOnCommand; +import org.junit.jupiter.api.*; +import org.junit.jupiter.api.extension.ExtendWith; + +import static io.lettuce.TestTags.INTEGRATION_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration tests for {@link io.lettuce.core.api.sync.RedisCuckooFilterCommands}. + * + * @author Yordan Tsintsov + */ +@Tag(INTEGRATION_TEST) +@ExtendWith(LettuceExtension.class) +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +@EnabledOnCommand("CF.ADD") +public class RedisCuckooFilterIntegrationTests { + + private static final String MY_KEY = "books:name"; + + private static final String MY_VALUE = "Dune"; + + private static final String MY_VALUE_2 = "Dune Messiah"; + + private final RedisCommands redis; + + @Inject + protected RedisCuckooFilterIntegrationTests(RedisCommands redis) { + this.redis = redis; + } + + @BeforeEach + void prepare() { + redis.flushall(); + } + + @AfterAll + void teardown() { + redis.flushall(); + } + + @Test + void cfReserve() { + String result = redis.cfReserve(MY_KEY, 100); + + assertThat(result).isEqualTo("OK"); + } + + @Test + void cfReserveWithArgs() { + String result = redis.cfReserve(MY_KEY, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)); + + assertThat(result).isEqualTo("OK"); + } + + @Test + void cfAdd() { + Boolean result = redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(result).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + } + + @Test + void cfAddNx() { + assertThat(redis.cfAddNx(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfAddNx(MY_KEY, MY_VALUE)).isFalse(); + } + + @Test + void cfInsert() { + List result = redis.cfInsert(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, true); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE_2)).isTrue(); + } + + @Test + void cfInsertWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + List result = redis.cfInsert(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, true); + } + + @Test + void cfInsertNx() { + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(false, true); + } + + @Test + void cfInsertNxWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfInsertNx(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(false, true); + } + + @Test + void cfExists() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE_2)).isFalse(); + } + + @Test + void cfMExists() { + redis.cfAdd(MY_KEY, MY_VALUE); + + List result = redis.cfMExists(MY_KEY, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(true, false); + } + + @Test + void cfDel() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfDel(MY_KEY, MY_VALUE)).isTrue(); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isFalse(); + } + + @Test + void cfCount() { + redis.cfAdd(MY_KEY, MY_VALUE); + + assertThat(redis.cfCount(MY_KEY, MY_VALUE)).isEqualTo(1L); + assertThat(redis.cfCount(MY_KEY, MY_VALUE_2)).isEqualTo(0L); + } + + @Test + void cfInfo() { + redis.cfAdd(MY_KEY, MY_VALUE); + + CfInfoValue result = redis.cfInfo(MY_KEY); + + assertThat(result).isNotNull(); + assertThat(result.getNumberOfItemsInserted()).isEqualTo(1L); + assertThat(result.getRawInfo()).containsKey("Number of filters"); + assertThat(result.getRawInfo()).containsKey("Max iterations"); + } + + @Test + @Timeout(2) + void cfScanDumpAndLoadChunk() { + redis.cfAdd("cuckoo-dump", MY_VALUE); + + long cursor = 0; + while (true) { + CfScanDumpValue chunkData = redis.cfScanDump("cuckoo-dump", cursor); + cursor = chunkData.getIterator(); + if (cursor == 0L) { + break; + } + assertThat(redis.cfLoadChunk("cuckoo-load", cursor, chunkData.getData())).isEqualTo("OK"); + } + + assertThat(redis.cfExists("cuckoo-load", MY_VALUE)).isTrue(); + } + +} diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java new file mode 100644 index 0000000000..3619398271 --- /dev/null +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.cf; + +import javax.inject.Inject; + +import io.lettuce.core.Value; +import io.lettuce.core.api.StatefulRedisConnection; +import io.lettuce.core.api.reactive.RedisReactiveCommands; +import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.test.ReactiveSyncInvocationHandler; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; +import reactor.test.StepVerifier; + +import static io.lettuce.TestTags.INTEGRATION_TEST; + +/** + * Reactive integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} + * routing every call through the reactive API via {@link ReactiveSyncInvocationHandler}. + *

+ * Tests the reactive API returns {@code Flux>} are overridden. (wrapping nulls in {@link Value#empty()}) while the + * sync API returns {@code List} with raw booleans. + * + * @author Yordan Tsintsov + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterReactiveIntegrationTests extends RedisCuckooFilterIntegrationTests { + + private final RedisReactiveCommands reactive; + + @Inject + public RedisCuckooFilterReactiveIntegrationTests(StatefulRedisConnection connection) { + super(ReactiveSyncInvocationHandler.sync(connection)); + this.reactive = connection.reactive(); + } + + @Test + @Override + void cfInsert() { + StepVerifier.create(reactive.cfInsert("books:name", "Dune", "Dune Messiah")).expectNext(Value.just(Boolean.TRUE)) + .expectNext(Value.just(Boolean.TRUE)).verifyComplete(); + } + + @Test + @Override + void cfInsertNx() { + reactive.cfAdd("books:name", "Dune").block(); + + StepVerifier.create(reactive.cfInsertNx("books:name", "Dune", "Dune Messiah")).expectNext(Value.just(Boolean.FALSE)) + .expectNext(Value.just(Boolean.TRUE)).verifyComplete(); + } + + @Test + @Override + void cfInsertWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + + StepVerifier.create(reactive.cfInsert("books:name", insertArgs, "Dune", "Dune Messiah")) + .expectNext(Value.just(Boolean.TRUE)).expectNext(Value.just(Boolean.TRUE)).verifyComplete(); + } + + @Test + @Override + void cfInsertNxWithArgs() { + CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); + reactive.cfAdd("books:name", "Dune").block(); + + StepVerifier.create(reactive.cfInsertNx("books:name", insertArgs, "Dune", "Dune Messiah")) + .expectNext(Value.just(Boolean.FALSE)).expectNext(Value.just(Boolean.TRUE)).verifyComplete(); + } + +} diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java new file mode 100644 index 0000000000..742264f4d3 --- /dev/null +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java @@ -0,0 +1,39 @@ +/* + * Copyright 2026, Redis Ltd. and Contributors + * All rights reserved. + * + * Licensed under the MIT License. + */ +package io.lettuce.core.cf; + +import io.lettuce.core.ClientOptions; +import io.lettuce.core.RedisClient; +import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.protocol.ProtocolVersion; +import org.junit.jupiter.api.Tag; + +import javax.inject.Inject; + +import static io.lettuce.TestTags.INTEGRATION_TEST; + +/** + * RESP2 integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} + * using RESP2. + * + * @author Yordan Tsintsov + * @since 7.7 + */ +@Tag(INTEGRATION_TEST) +public class RedisCuckooFilterResp2IntegrationTests extends RedisCuckooFilterIntegrationTests { + + @Inject + RedisCuckooFilterResp2IntegrationTests(RedisClient client) { + super(connectWithResp2(client)); + } + + private static RedisCommands connectWithResp2(RedisClient client) { + client.setOptions(ClientOptions.builder().protocolVersion(ProtocolVersion.RESP2).build()); + return client.connect().sync(); + } + +} diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java index c08044ef7b..b2f45df751 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java @@ -20,7 +20,7 @@ class ClusterReadOnlyCommandsUnitTests { @Test void testCount() { - assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(117); + assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(122); } @Test From ca7ed128d75386662bf92d7f672ad0707558a023 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Sun, 7 Jun 2026 17:39:52 +0900 Subject: [PATCH 02/10] Map CF.INSERT/INSERTNX filter-full (-1) to null instead of false 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()). --- .../core/RedisCuckooFilterCommandBuilder.java | 32 ++--- .../output/CuckooInsertBooleanListOutput.java | 99 +++++++++++++++ .../CuckooInsertBooleanValueListOutput.java | 102 ++++++++++++++++ .../cf/RedisCuckooFilterIntegrationTests.java | 61 ++++++++++ ...sCuckooFilterReactiveIntegrationTests.java | 51 ++++++++ ...uckooInsertBooleanListOutputUnitTests.java | 114 +++++++++++++++++ ...InsertBooleanValueListOutputUnitTests.java | 115 ++++++++++++++++++ 7 files changed, 558 insertions(+), 16 deletions(-) create mode 100644 src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java create mode 100644 src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java create mode 100644 src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java create mode 100644 src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java index dfa6015ebc..a6d295893c 100644 --- a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -71,7 +71,7 @@ Command> cfInsert(K key, V value) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); } Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { @@ -81,7 +81,7 @@ Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); } @SafeVarargs @@ -90,7 +90,7 @@ final Command> cfInsert(K key, V... values) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); } @SafeVarargs @@ -101,7 +101,7 @@ final Command> cfInsert(K key, CfInsertArgs insertArgs, V... insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); } Command>> cfInsertValues(K key, V value) { @@ -109,7 +109,7 @@ Command>> cfInsertValues(K key, V value) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); } Command>> cfInsertValues(K key, CfInsertArgs insertArgs, V value) { @@ -119,7 +119,7 @@ Command>> cfInsertValues(K key, CfInsertArgs insertArg insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); } @SafeVarargs @@ -128,7 +128,7 @@ final Command>> cfInsertValues(K key, V... values) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); } @SafeVarargs @@ -139,7 +139,7 @@ final Command>> cfInsertValues(K key, CfInsertArgs ins insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); } Command> cfInsertNx(K key, V value) { @@ -147,7 +147,7 @@ Command> cfInsertNx(K key, V value) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); } Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) { @@ -157,7 +157,7 @@ Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); } @SafeVarargs @@ -166,7 +166,7 @@ final Command> cfInsertNx(K key, V... values) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); } @SafeVarargs @@ -177,7 +177,7 @@ final Command> cfInsertNx(K key, CfInsertArgs insertArgs, V. insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); } Command>> cfInsertNxValues(K key, V value) { @@ -185,7 +185,7 @@ Command>> cfInsertNxValues(K key, V value) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); } Command>> cfInsertNxValues(K key, CfInsertArgs insertArgs, V value) { @@ -195,7 +195,7 @@ Command>> cfInsertNxValues(K key, CfInsertArgs insertA insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); } @SafeVarargs @@ -204,7 +204,7 @@ final Command>> cfInsertNxValues(K key, V... values) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); } @SafeVarargs @@ -215,7 +215,7 @@ final Command>> cfInsertNxValues(K key, CfInsertArgs i insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERTNX, new ErrorTolerantBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); } Command cfExists(K key, V value) { diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java new file mode 100644 index 0000000000..12fd108eaf --- /dev/null +++ b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.output; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.internal.LettuceAssert; + +/** + * {@link List} of boolean output for CF.INSERT / CF.INSERTNX commands. + * + *

+ * Maps the per-item integer response to a 3-state {@link Boolean}: + *

    + *
  • {@code 1} → {@link Boolean#TRUE} – item was added
  • + *
  • {@code 0} → {@link Boolean#FALSE} – item already exists (INSERTNX only)
  • + *
  • negative (e.g. {@code -1}) → {@code null} – filter is full
  • + *
+ * + *

+ * Unlike {@link ErrorTolerantBooleanListOutput} (used by BF commands), this output distinguishes "already exists" + * ({@code false}) from "filter full" ({@code null}), which is required by the CF.INSERT / CF.INSERTNX semantics. + * + * @param Key type. + * @param Value type. + * @author Yordan Tsintsov + * @since 7.7 + */ +public class CuckooInsertBooleanListOutput extends CommandOutput> + implements StreamingOutput { + + private boolean initialized; + + private Subscriber subscriber; + + public CuckooInsertBooleanListOutput(RedisCodec codec) { + super(codec, Collections.emptyList()); + setSubscriber(ListSubscriber.instance()); + } + + @Override + public void set(long integer) { + Boolean value = integer == 1 ? Boolean.TRUE : (integer == 0 ? Boolean.FALSE : null); + subscriber.onNext(output, value); + } + + @Override + public void set(boolean value) { + subscriber.onNext(output, value); + } + + @Override + public void setError(ByteBuffer error) { + + if (initialized) { + subscriber.onNext(output, null); + return; + } + super.setError(error); + } + + @Override + public void set(ByteBuffer bytes) { + + if (initialized && bytes == null) { + subscriber.onNext(output, null); + return; + } + super.set(bytes); + } + + @Override + public void multi(int count) { + + if (!initialized) { + output = OutputFactory.newList(count); + initialized = true; + } + } + + @Override + public void setSubscriber(Subscriber subscriber) { + LettuceAssert.notNull(subscriber, "Subscriber must not be null"); + this.subscriber = subscriber; + } + + @Override + public Subscriber getSubscriber() { + return subscriber; + } + +} diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java new file mode 100644 index 0000000000..43dfd14610 --- /dev/null +++ b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.output; + +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; + +import io.lettuce.core.Value; +import io.lettuce.core.codec.RedisCodec; +import io.lettuce.core.internal.LettuceAssert; + +/** + * {@link List} of {@link Value}-wrapped boolean output for reactive CF.INSERT / CF.INSERTNX commands. + * + *

+ * Maps the per-item integer response to a 3-state {@link Value}: + *

    + *
  • {@code 1} → {@code Value.just(Boolean.TRUE)} – item was added
  • + *
  • {@code 0} → {@code Value.just(Boolean.FALSE)} – item already exists (INSERTNX only)
  • + *
  • negative (e.g. {@code -1}) → {@code Value.empty()} – filter is full
  • + *
+ * + *

+ * Unlike {@link ErrorTolerantBooleanValueListOutput} (used by BF commands), this output distinguishes "already exists" + * ({@code Value.just(false)}) from "filter full" ({@code Value.empty()}), which is required by the CF.INSERT / CF.INSERTNX + * semantics. + * + * @param Key type. + * @param Value type. + * @author Yordan Tsintsov + * @since 7.7 + */ +public class CuckooInsertBooleanValueListOutput extends CommandOutput>> + implements StreamingOutput> { + + private boolean initialized; + + private Subscriber> subscriber; + + public CuckooInsertBooleanValueListOutput(RedisCodec codec) { + super(codec, Collections.emptyList()); + setSubscriber(ListSubscriber.instance()); + } + + @Override + public void set(long integer) { + Value value = integer == 1 ? Value.just(Boolean.TRUE) + : (integer == 0 ? Value.just(Boolean.FALSE) : Value.empty()); + subscriber.onNext(output, value); + } + + @Override + public void set(boolean value) { + subscriber.onNext(output, Value.just(value)); + } + + @Override + public void setError(ByteBuffer error) { + + if (initialized) { + subscriber.onNext(output, Value.empty()); + return; + } + super.setError(error); + } + + @Override + public void set(ByteBuffer bytes) { + + if (initialized) { + subscriber.onNext(output, (bytes == null ? Value.empty() : Value.just(Boolean.parseBoolean(decodeString(bytes))))); + return; + } + super.set(bytes); + } + + @Override + public void multi(int count) { + + if (!initialized) { + output = OutputFactory.newList(count); + initialized = true; + } + } + + @Override + public void setSubscriber(Subscriber> subscriber) { + LettuceAssert.notNull(subscriber, "Subscriber must not be null"); + this.subscriber = subscriber; + } + + @Override + public Subscriber> getSubscriber() { + return subscriber; + } + +} diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index 01aac00a68..3f08479bbf 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -163,6 +163,67 @@ void cfInfo() { assertThat(result.getRawInfo()).containsKey("Max iterations"); } + /** + * Verifies that CF.INSERT correctly handles items that cannot be inserted because the filter is full. + * + *

+ * Redis returns integer {@code -1} per-item when the filter is full. The output class + * {@link io.lettuce.core.output.CuckooInsertBooleanListOutput} maps: + *

    + *
  • 1 → {@code true} (inserted)
  • + *
  • 0 → {@code false} (already exists)
  • + *
  • -1 → {@code null} (filter full)
  • + *
+ * This test reserves a tiny filter (BUCKETSIZE 1, EXPANSION 0) and inserts the same value repeatedly: the first two + * insertions succeed ({@code true}) while the remaining ones are full and return {@code -1}, which must map to {@code null} + * (distinct from {@code false}). + */ + @Test + void cfInsertReturnsNullWhenFilterIsFull() { + String key = "cf:full:insert"; + // BUCKETSIZE 1 EXPANSION 0: same value can appear in at most 2*BUCKETSIZE=2 slots; EXPANSION 0 disables growth + redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); + + // Same value repeated: first 2 succeed; subsequent inserts cannot be placed in the filter + List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); + + assertThat(result).hasSize(6); + assertThat(result.get(0)).isEqualTo(Boolean.TRUE); + assertThat(result.get(1)).isEqualTo(Boolean.TRUE); + // Elements 2-5: filter is full, server returns -1, which must map to null (distinct from false) + for (int i = 2; i < result.size(); i++) { + assertThat(result.get(i)).as("result[%d] must be null (filter full = -1)", i).isNull(); + } + } + + /** + * Verifies that CF.INSERTNX distinguishes between "already exists" ({@code false}) and "filter full" ({@code null}). + * + *

+ * With the old {@link io.lettuce.core.output.ErrorTolerantBooleanListOutput} both 0 and -1 were mapped to {@code false}, + * making them indistinguishable. The new {@link io.lettuce.core.output.CuckooInsertBooleanListOutput} maps 0 → + * {@code false} and -1 → {@code null}. + * + *

+ * Note: reproducing -1 via INSERTNX is difficult because inserting the same value always returns 0 ("already exists"). The + * -1 → null mapping is verified end-to-end by {@link #cfInsertReturnsNullWhenFilterIsFull()}, which uses CF.INSERT and the + * same underlying output class ({@link io.lettuce.core.output.CuckooInsertBooleanListOutput}). This test focuses on + * confirming that 0 ("already exists") maps to {@code false}, NOT to {@code null}. + */ + @Test + void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { + String key = "cf:full:insertnx"; + redis.cfReserve(key, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)); + + // Pre-populate the filter so INSERTNX sees "already exists" (0) for known items + redis.cfAdd(key, "known"); + + // existing item must return false (0), NOT null — 0 and -1 must remain distinguishable + List existing = redis.cfInsertNx(key, "known"); + assertThat(existing).hasSize(1); + assertThat(existing.get(0)).isEqualTo(Boolean.FALSE); // "already exists" = false, NOT null + } + @Test @Timeout(2) void cfScanDumpAndLoadChunk() { diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java index 3619398271..ee8bb9dbeb 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -7,17 +7,20 @@ package io.lettuce.core.cf; import javax.inject.Inject; +import java.util.List; import io.lettuce.core.Value; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.reactive.RedisReactiveCommands; import io.lettuce.core.cf.arguments.CfInsertArgs; +import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.test.ReactiveSyncInvocationHandler; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import reactor.test.StepVerifier; import static io.lettuce.TestTags.INTEGRATION_TEST; +import static org.assertj.core.api.Assertions.assertThat; /** * Reactive integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} @@ -75,4 +78,52 @@ void cfInsertNxWithArgs() { .expectNext(Value.just(Boolean.FALSE)).expectNext(Value.just(Boolean.TRUE)).verifyComplete(); } + /** + * Reactive counterpart of {@link RedisCuckooFilterIntegrationTests#cfInsertReturnsNullWhenFilterIsFull()}. + * + *

+ * Verifies that CF.INSERT emits the correct {@link Value} wrappers for a full filter. Redis returns {@code -1} per-item + * when the filter is full, which maps to {@code Value.empty()}. The first two insertions succeed ({@code Value.just(true)}) + * while the remaining ones return {@code -1} and must map to {@code Value.empty()}. + */ + @Test + @Override + void cfInsertReturnsNullWhenFilterIsFull() { + String key = "cf:full:reactive:insert"; + // BUCKETSIZE 1 EXPANSION 0: same value can appear at most 2*BUCKETSIZE=2 times; expansion disabled + reactive.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)).block(); + + List> result = reactive.cfInsert(key, "W", "W", "W", "W", "W", "W").collectList().block(); + + assertThat(result).isNotNull().hasSize(6); + assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); + assertThat(result.get(1)).isEqualTo(Value.just(Boolean.TRUE)); + // Elements 2-5: filter full, server returns -1, which must map to Value.empty() (distinct from Value.just(false)) + for (int i = 2; i < result.size(); i++) { + assertThat(result.get(i)).as("result[%d] must be Value.empty() (filter full = -1)", i).isEqualTo(Value.empty()); + } + } + + /** + * Reactive counterpart of {@link RedisCuckooFilterIntegrationTests#cfInsertNxDistinguishesAlreadyExistsFromFilterFull()}. + * + *

+ * Verifies that CF.INSERTNX emits {@code Value.just(false)} for "already exists" (0), NOT {@code Value.empty()} which + * represents "filter full" (-1). The -1 → Value.empty() mapping is verified by + * {@link #cfInsertReturnsNullWhenFilterIsFull()}. + */ + @Test + @Override + void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { + String key = "cf:full:reactive:insertnx"; + reactive.cfReserve(key, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)).block(); + + reactive.cfAdd(key, "known").block(); + + // existing item must return Value.just(false), NOT Value.empty() — 0 and -1 must remain distinguishable + List> existing = reactive.cfInsertNx(key, "known").collectList().block(); + assertThat(existing).isNotNull().hasSize(1); + assertThat(existing.get(0)).isEqualTo(Value.just(Boolean.FALSE)); + } + } diff --git a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java new file mode 100644 index 0000000000..90e65f5543 --- /dev/null +++ b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.output; + +import static io.lettuce.TestTags.UNIT_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +import java.nio.ByteBuffer; +import java.util.List; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import io.lettuce.core.codec.StringCodec; + +/** + * Unit tests for {@link CuckooInsertBooleanListOutput}. + * + * Verifies the 3-state mapping required by CF.INSERT / CF.INSERTNX: + *

    + *
  • 1 → {@code Boolean.TRUE} (item added)
  • + *
  • 0 → {@code Boolean.FALSE} (item already exists, INSERTNX only)
  • + *
  • -1 → {@code null} (filter is full)
  • + *
+ */ +@Tag(UNIT_TEST) +class CuckooInsertBooleanListOutputUnitTests { + + private final CuckooInsertBooleanListOutput sut = new CuckooInsertBooleanListOutput<>(StringCodec.UTF8); + + @Test + void defaultSubscriberIsSet() { + assertThat(sut.getSubscriber()).isNotNull().isInstanceOf(ListSubscriber.class); + } + + @Test + void set1MappedToTrue() { + sut.multi(1); + sut.set(1L); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Boolean.TRUE); + } + + @Test + void set0MappedToFalse() { + sut.multi(1); + sut.set(0L); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Boolean.FALSE); + } + + @Test + void setNegativeOneMappedToNull() { + sut.multi(1); + sut.set(-1L); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isNull(); + } + + @Test + void otherNegativeValueMappedToNull() { + sut.multi(1); + sut.set(-2L); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isNull(); + } + + @Test + void mixedResponsesAllDistinct() { + sut.multi(3); + sut.set(1L); + sut.set(0L); + sut.set(-1L); + + List result = sut.get(); + assertThat(result).hasSize(3); + assertThat(result.get(0)).isEqualTo(Boolean.TRUE); + assertThat(result.get(1)).isEqualTo(Boolean.FALSE); + assertThat(result.get(2)).isNull(); + } + + @Test + void setErrorPushesNullWhenInitialized() { + sut.multi(1); + sut.setError(ByteBuffer.wrap("ERR filter full".getBytes())); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isNull(); + } + + @Test + void setNullByteBufferPushesNullWhenInitialized() { + sut.multi(1); + sut.set((ByteBuffer) null); + + List result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isNull(); + } + +} diff --git a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java new file mode 100644 index 0000000000..3dd53db026 --- /dev/null +++ b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2026-Present, Redis Ltd. + * All rights reserved. + * + * SPDX-License-Identifier: MIT + */ +package io.lettuce.core.output; + +import static io.lettuce.TestTags.UNIT_TEST; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.List; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +import io.lettuce.core.Value; +import io.lettuce.core.codec.StringCodec; + +/** + * Unit tests for {@link CuckooInsertBooleanValueListOutput}. + * + * Verifies the 3-state mapping required by reactive CF.INSERT / CF.INSERTNX: + *
    + *
  • 1 → {@code Value.just(Boolean.TRUE)} (item added)
  • + *
  • 0 → {@code Value.just(Boolean.FALSE)} (item already exists, INSERTNX only)
  • + *
  • -1 → {@code Value.empty()} (filter is full)
  • + *
+ */ +@Tag(UNIT_TEST) +class CuckooInsertBooleanValueListOutputUnitTests { + + private final CuckooInsertBooleanValueListOutput sut = new CuckooInsertBooleanValueListOutput<>( + StringCodec.UTF8); + + @Test + void defaultSubscriberIsSet() { + assertThat(sut.getSubscriber()).isNotNull().isInstanceOf(ListSubscriber.class); + } + + @Test + void set1MappedToValueTrue() { + sut.multi(1); + sut.set(1L); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); + } + + @Test + void set0MappedToValueFalse() { + sut.multi(1); + sut.set(0L); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.just(Boolean.FALSE)); + } + + @Test + void setNegativeOneMappedToValueEmpty() { + sut.multi(1); + sut.set(-1L); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.empty()); + } + + @Test + void otherNegativeValueMappedToValueEmpty() { + sut.multi(1); + sut.set(-2L); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.empty()); + } + + @Test + void mixedResponsesAllDistinct() { + sut.multi(3); + sut.set(1L); + sut.set(0L); + sut.set(-1L); + + List> result = sut.get(); + assertThat(result).hasSize(3); + assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); + assertThat(result.get(1)).isEqualTo(Value.just(Boolean.FALSE)); + assertThat(result.get(2)).isEqualTo(Value.empty()); + } + + @Test + void setErrorPushesValueEmptyWhenInitialized() { + sut.multi(1); + sut.setError(java.nio.ByteBuffer.wrap("ERR filter full".getBytes())); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.empty()); + } + + @Test + void setNullByteBufferPushesValueEmptyWhenInitialized() { + sut.multi(1); + sut.set((java.nio.ByteBuffer) null); + + List> result = sut.get(); + assertThat(result).hasSize(1); + assertThat(result.get(0)).isEqualTo(Value.empty()); + } + +} From ea0294892915682f64df7f0fbae10e2200a03cc4 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Sun, 7 Jun 2026 17:57:59 +0900 Subject: [PATCH 03/10] Scope CF.INSERT filter-full (-1) assertion to RESP2 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. --- .../cf/RedisCuckooFilterIntegrationTests.java | 43 +++---------------- ...sCuckooFilterReactiveIntegrationTests.java | 29 +------------ ...edisCuckooFilterResp2IntegrationTests.java | 23 ++++++++++ 3 files changed, 31 insertions(+), 64 deletions(-) diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index 3f08479bbf..322d50be9b 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -37,7 +37,7 @@ public class RedisCuckooFilterIntegrationTests { private static final String MY_VALUE_2 = "Dune Messiah"; - private final RedisCommands redis; + protected final RedisCommands redis; @Inject protected RedisCuckooFilterIntegrationTests(RedisCommands redis) { @@ -163,39 +163,6 @@ void cfInfo() { assertThat(result.getRawInfo()).containsKey("Max iterations"); } - /** - * Verifies that CF.INSERT correctly handles items that cannot be inserted because the filter is full. - * - *

- * Redis returns integer {@code -1} per-item when the filter is full. The output class - * {@link io.lettuce.core.output.CuckooInsertBooleanListOutput} maps: - *

    - *
  • 1 → {@code true} (inserted)
  • - *
  • 0 → {@code false} (already exists)
  • - *
  • -1 → {@code null} (filter full)
  • - *
- * This test reserves a tiny filter (BUCKETSIZE 1, EXPANSION 0) and inserts the same value repeatedly: the first two - * insertions succeed ({@code true}) while the remaining ones are full and return {@code -1}, which must map to {@code null} - * (distinct from {@code false}). - */ - @Test - void cfInsertReturnsNullWhenFilterIsFull() { - String key = "cf:full:insert"; - // BUCKETSIZE 1 EXPANSION 0: same value can appear in at most 2*BUCKETSIZE=2 slots; EXPANSION 0 disables growth - redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); - - // Same value repeated: first 2 succeed; subsequent inserts cannot be placed in the filter - List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); - - assertThat(result).hasSize(6); - assertThat(result.get(0)).isEqualTo(Boolean.TRUE); - assertThat(result.get(1)).isEqualTo(Boolean.TRUE); - // Elements 2-5: filter is full, server returns -1, which must map to null (distinct from false) - for (int i = 2; i < result.size(); i++) { - assertThat(result.get(i)).as("result[%d] must be null (filter full = -1)", i).isNull(); - } - } - /** * Verifies that CF.INSERTNX distinguishes between "already exists" ({@code false}) and "filter full" ({@code null}). * @@ -206,9 +173,11 @@ void cfInsertReturnsNullWhenFilterIsFull() { * *

* Note: reproducing -1 via INSERTNX is difficult because inserting the same value always returns 0 ("already exists"). The - * -1 → null mapping is verified end-to-end by {@link #cfInsertReturnsNullWhenFilterIsFull()}, which uses CF.INSERT and the - * same underlying output class ({@link io.lettuce.core.output.CuckooInsertBooleanListOutput}). This test focuses on - * confirming that 0 ("already exists") maps to {@code false}, NOT to {@code null}. + * -1 → null mapping is verified in the RESP2 integration tests + * ({@link RedisCuckooFilterResp2IntegrationTests#cfInsertReturnsNullWhenFilterIsFull()}) and in the unit tests + * (CuckooInsertBooleanListOutputUnitTests), which use CF.INSERT and the same underlying output class + * ({@link io.lettuce.core.output.CuckooInsertBooleanListOutput}). This test focuses on confirming that 0 ("already exists") + * maps to {@code false}, NOT to {@code null}. */ @Test void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java index ee8bb9dbeb..2002254818 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -78,39 +78,14 @@ void cfInsertNxWithArgs() { .expectNext(Value.just(Boolean.FALSE)).expectNext(Value.just(Boolean.TRUE)).verifyComplete(); } - /** - * Reactive counterpart of {@link RedisCuckooFilterIntegrationTests#cfInsertReturnsNullWhenFilterIsFull()}. - * - *

- * Verifies that CF.INSERT emits the correct {@link Value} wrappers for a full filter. Redis returns {@code -1} per-item - * when the filter is full, which maps to {@code Value.empty()}. The first two insertions succeed ({@code Value.just(true)}) - * while the remaining ones return {@code -1} and must map to {@code Value.empty()}. - */ - @Test - @Override - void cfInsertReturnsNullWhenFilterIsFull() { - String key = "cf:full:reactive:insert"; - // BUCKETSIZE 1 EXPANSION 0: same value can appear at most 2*BUCKETSIZE=2 times; expansion disabled - reactive.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)).block(); - - List> result = reactive.cfInsert(key, "W", "W", "W", "W", "W", "W").collectList().block(); - - assertThat(result).isNotNull().hasSize(6); - assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); - assertThat(result.get(1)).isEqualTo(Value.just(Boolean.TRUE)); - // Elements 2-5: filter full, server returns -1, which must map to Value.empty() (distinct from Value.just(false)) - for (int i = 2; i < result.size(); i++) { - assertThat(result.get(i)).as("result[%d] must be Value.empty() (filter full = -1)", i).isEqualTo(Value.empty()); - } - } - /** * Reactive counterpart of {@link RedisCuckooFilterIntegrationTests#cfInsertNxDistinguishesAlreadyExistsFromFilterFull()}. * *

* Verifies that CF.INSERTNX emits {@code Value.just(false)} for "already exists" (0), NOT {@code Value.empty()} which * represents "filter full" (-1). The -1 → Value.empty() mapping is verified by - * {@link #cfInsertReturnsNullWhenFilterIsFull()}. + * {@link RedisCuckooFilterResp2IntegrationTests#cfInsertReturnsNullWhenFilterIsFull()} (RESP2) and + * CuckooInsertBooleanValueListOutputUnitTests (unit tests). */ @Test @Override diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java index 742264f4d3..d49a934d42 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java @@ -9,17 +9,25 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; import io.lettuce.core.api.sync.RedisCommands; +import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.protocol.ProtocolVersion; import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; import javax.inject.Inject; +import java.util.List; import static io.lettuce.TestTags.INTEGRATION_TEST; +import static org.assertj.core.api.Assertions.assertThat; /** * RESP2 integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} * using RESP2. * + *

+ * RESP3 differs: the server returns boolean {@code false} instead of integer {@code -1} for a full filter, so the filter-full + * (-1 → {@code null}) distinction is only observable over RESP2. This class verifies it explicitly. + * * @author Yordan Tsintsov * @since 7.7 */ @@ -36,4 +44,19 @@ private static RedisCommands connectWithResp2(RedisClient client return client.connect().sync(); } + @Test + void cfInsertReturnsNullWhenFilterIsFull() { + String key = "cf:full:resp2:insert"; + // RESP2: server returns integer -1 (filter full). BUCKETSIZE 1 EXPANSION 0: same value fits at most 2*1=2 slots. + redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); + List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); + assertThat(result).hasSize(6); + assertThat(result.get(0)).isEqualTo(Boolean.TRUE); + assertThat(result.get(1)).isEqualTo(Boolean.TRUE); + // -1 (filter full) must map to null, distinct from false (0 = already exists) + for (int i = 2; i < result.size(); i++) { + assertThat(result.get(i)).as("result[%d] must be null (filter full = -1, RESP2)", i).isNull(); + } + } + } From 8ef4454d9bf57ab7be6f2365d887c67cf7d79195 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Sun, 7 Jun 2026 18:19:13 +0900 Subject: [PATCH 04/10] Document filter-full (null) reply and restore BF VALUE_WRAP entries - 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 and break BF reactive signatures, wiring and tests. --- .../async/RedisCuckooFilterAsyncCommands.java | 38 +++++++++++++++---- .../RedisCuckooFilterReactiveCommands.java | 36 +++++++++++++----- .../api/sync/RedisCuckooFilterCommands.java | 33 ++++++++++++---- ...odeSelectionCuckooFilterAsyncCommands.java | 34 +++++++++++++---- .../NodeSelectionCuckooFilterCommands.java | 33 ++++++++++++---- .../RedisCuckooFilterCoroutinesCommands.kt | 24 ++++++++++-- .../core/api/RedisCuckooFilterCommands.java | 33 ++++++++++++---- .../apigenerator/CreateReactiveApi.java | 3 +- 8 files changed, 180 insertions(+), 54 deletions(-) diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java index a2ab5a0081..82124bb56c 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -50,6 +50,8 @@ public interface RedisCuckooFilterAsyncCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand (future fails with this + * exception). */ RedisFuture cfAdd(K key, V value); @@ -67,8 +69,13 @@ public interface RedisCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code null} if + * the item could not be added because the filter is full (server reply {@code -1}). CF.INSERT does not report + * already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ RedisFuture> cfInsert(K key, V... values); @@ -78,8 +85,13 @@ public interface RedisCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code null} if + * the item could not be added because the filter is full (server reply {@code -1}). CF.INSERT does not report + * already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ RedisFuture> cfInsert(K key, CfInsertArgs args, V... values); @@ -89,8 +101,13 @@ public interface RedisCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code false} if + * the item already exists, {@code null} if the item could not be added because the filter is full (server reply + * {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ RedisFuture> cfInsertNx(K key, V... values); @@ -101,8 +118,13 @@ public interface RedisCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code false} if + * the item already exists, {@code null} if the item could not be added because the filter is full (server reply + * {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java index 006b50d103..b8bcae1079 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -50,7 +50,9 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param value the value. - * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. The + * {@code Mono} signals an error with {@code io.lettuce.core.RedisCommandExecutionException} if the filter is full + * and cannot expand. */ Mono cfAdd(K key, V value); @@ -68,8 +70,12 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param values the values. - * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the - * filter. + * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, {@code Value.empty()} + * if the filter is full. CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Flux> cfInsert(K key, V... values); @@ -79,8 +85,12 @@ public interface RedisCuckooFilterReactiveCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the - * filter. + * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, {@code Value.empty()} + * if the filter is full. CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Flux> cfInsert(K key, CfInsertArgs args, V... values); @@ -90,8 +100,12 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param values the values. - * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the - * filter. + * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, + * {@code Value.just(false)} if it already exists, {@code Value.empty()} if the filter is full. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Flux> cfInsertNx(K key, V... values); @@ -102,8 +116,12 @@ public interface RedisCuckooFilterReactiveCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return Boolean where {@code true} means that the item was added; {@code false} means that the item already exists in the - * filter. + * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, + * {@code Value.just(false)} if it already exists, {@code Value.empty()} if the filter is full. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Flux> cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java index 394609a5a8..6e2dd0692d 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -49,6 +49,7 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. */ Boolean cfAdd(K key, V value); @@ -66,8 +67,12 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsert(K key, V... values); @@ -77,8 +82,12 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsert(K key, CfInsertArgs args, V... values); @@ -88,8 +97,12 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsertNx(K key, V... values); @@ -100,8 +113,12 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java index 24dd2ae9fc..98f84945b2 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -49,6 +49,8 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand (future fails with this + * exception). */ AsyncExecutions cfAdd(K key, V value); @@ -66,8 +68,12 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ AsyncExecutions> cfInsert(K key, V... values); @@ -77,8 +83,12 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ AsyncExecutions> cfInsert(K key, CfInsertArgs args, V... values); @@ -88,8 +98,12 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ AsyncExecutions> cfInsertNx(K key, V... values); @@ -100,8 +114,12 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java index b6529a6e31..0598384ae2 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -49,6 +49,7 @@ public interface NodeSelectionCuckooFilterCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. */ Executions cfAdd(K key, V value); @@ -66,8 +67,12 @@ public interface NodeSelectionCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Executions> cfInsert(K key, V... values); @@ -77,8 +82,12 @@ public interface NodeSelectionCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Executions> cfInsert(K key, CfInsertArgs args, V... values); @@ -88,8 +97,12 @@ public interface NodeSelectionCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Executions> cfInsertNx(K key, V... values); @@ -100,8 +113,12 @@ public interface NodeSelectionCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ Executions> cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt index 7863bbd1c8..e0df0d0d17 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -50,6 +50,7 @@ interface RedisCuckooFilterCoroutinesCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply `true` if the item was added, `false` if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. */ suspend fun cfAdd(key: K, value: V): Boolean? @@ -67,7 +68,10 @@ interface RedisCuckooFilterCoroutinesCommands { * * @param key the key. * @param values the values. - * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + * @return List one entry per item: `true` if the item was added, `null` if the item could not be added because the + * filter is full (server reply `-1`). CF.INSERT does not report already-existing items. + * + * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. */ suspend fun cfInsert(key: K, vararg values: V): List @@ -77,7 +81,10 @@ interface RedisCuckooFilterCoroutinesCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + * @return List one entry per item: `true` if the item was added, `null` if the item could not be added because the + * filter is full (server reply `-1`). CF.INSERT does not report already-existing items. + * + * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. */ suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List @@ -87,7 +94,10 @@ interface RedisCuckooFilterCoroutinesCommands { * * @param key the key. * @param values the values. - * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + * @return List one entry per item: `true` if the item was added, `false` if the item already exists, `null` if the + * item could not be added because the filter is full (server reply `-1`). + * + * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. */ suspend fun cfInsertNx(key: K, vararg values: V): List @@ -98,7 +108,10 @@ interface RedisCuckooFilterCoroutinesCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List where `true` means that the item was added; `false` means that the item already exists in the filter. + * @return List one entry per item: `true` if the item was added, `false` if the item already exists, `null` if the + * item could not be added because the filter is full (server reply `-1`). + * + * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. */ suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List @@ -124,6 +137,9 @@ interface RedisCuckooFilterCoroutinesCommands { /** * Delete an item from the Cuckoo Filter. * + * **Warning:** Deleting an item that was not previously added to the filter may cause false negatives for items that are + * actually present. Only delete items that have been previously added. + * * @param key the key. * @param value the value. * @return Boolean integer-reply `true` if the item was deleted, `false` if the item was not found. diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java index d1b0b2a418..3640f069d4 100644 --- a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -49,6 +49,7 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param value the value. * @return Boolean integer-reply {@code true} if the item was added, {@code false} if it was already in the filter. + * @throws io.lettuce.core.RedisCommandExecutionException if the filter is full and cannot expand. */ Boolean cfAdd(K key, V value); @@ -66,8 +67,12 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsert(K key, V... values); @@ -77,8 +82,12 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be + * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsert(K key, CfInsertArgs args, V... values); @@ -88,8 +97,12 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsertNx(K key, V... values); @@ -100,8 +113,12 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> where {@code true} means that the item was added; {@code false} means that the item already - * exists in the filter. + * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already + * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). + *

+ * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of + * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. + *

*/ List cfInsertNx(K key, CfInsertArgs args, V... values); diff --git a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java index 8a3ba547fb..989a253d60 100644 --- a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java +++ b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java @@ -60,7 +60,8 @@ public class CreateReactiveApi { public static Set FORCE_FLUX_RESULT = LettuceSets.unmodifiableSet("eval", "evalsha", "evalReadOnly", "evalshaReadOnly", "fcall", "fcallReadOnly", "dispatch"); - public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "cfInsert", "cfInsertNx"); + public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "bfInsert", "bfMAdd", "cfInsert", + "cfInsertNx"); private static final Map RESULT_SPEC; From 677912a18371cee7e7e6820f3f588d6e5129b12a Mon Sep 17 00:00:00 2001 From: HwangRock Date: Mon, 8 Jun 2026 21:10:55 +0900 Subject: [PATCH 05/10] Address review: author tag, CF.SCANDUMP routing, cluster coroutine interfaces - 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. --- .../java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java | 2 +- .../core/api/async/RedisCuckooFilterAsyncCommands.java | 2 +- .../core/api/reactive/RedisCuckooFilterReactiveCommands.java | 2 +- .../io/lettuce/core/api/sync/RedisCuckooFilterCommands.java | 2 +- src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java | 2 +- src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java | 2 +- .../api/async/NodeSelectionCuckooFilterAsyncCommands.java | 2 +- .../cluster/api/sync/NodeSelectionCuckooFilterCommands.java | 2 +- .../io/lettuce/core/output/CuckooInsertBooleanListOutput.java | 2 +- .../core/output/CuckooInsertBooleanValueListOutput.java | 2 +- src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java | 2 +- .../api/coroutines/RedisCuckooFilterCoroutinesCommands.kt | 2 +- .../api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt | 2 +- .../cluster/api/coroutines/RedisClusterCoroutinesCommands.kt | 4 +++- .../io/lettuce/core/api/RedisCuckooFilterCommands.java | 2 +- .../core/RedisCuckooFilterCommandBuilderUnitTests.java | 2 +- .../core/cf/RedisCuckooFilterClusterIntegrationTests.java | 2 +- .../io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java | 2 +- .../core/cf/RedisCuckooFilterReactiveIntegrationTests.java | 2 +- .../core/cf/RedisCuckooFilterResp2IntegrationTests.java | 2 +- .../core/cluster/ClusterReadOnlyCommandsUnitTests.java | 2 +- 21 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java index a6d295893c..622460f205 100644 --- a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -28,7 +28,7 @@ * * @param Key type. * @param Value type. - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ class RedisCuckooFilterCommandBuilder extends BaseRedisCommandBuilder { diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java index 82124bb56c..8865ec566c 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -16,7 +16,7 @@ /** * Asynchronous executed commands for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java index b8bcae1079..61bdc42aa7 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -17,7 +17,7 @@ /** * Reactive executed commands for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java index 6e2dd0692d..c44b32c483 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -15,7 +15,7 @@ /** * Synchronous executed commands for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java b/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java index 0163810904..c263157b4e 100644 --- a/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java +++ b/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java @@ -15,7 +15,7 @@ *

* {@link CfInsertArgs} is a mutable object and instances should be used only once to avoid shared mutable state. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ public class CfInsertArgs implements CompositeArgument { diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java b/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java index a8fa9ba427..7dc79fa184 100644 --- a/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java +++ b/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java @@ -15,7 +15,7 @@ *

* {@link CfReserveArgs} is a mutable object and instances should be used only once to avoid shared mutable state. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ public class CfReserveArgs implements CompositeArgument { diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java index 98f84945b2..838a45f02a 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -15,7 +15,7 @@ /** * Asynchronous executed commands on a node selection for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java index 0598384ae2..afc45a6bb9 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -15,7 +15,7 @@ /** * Synchronous executed commands on a node selection for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java index 12fd108eaf..53a0733d36 100644 --- a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java +++ b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java @@ -30,7 +30,7 @@ * * @param Key type. * @param Value type. - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ public class CuckooInsertBooleanListOutput extends CommandOutput> diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java index 43dfd14610..760961d57e 100644 --- a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java +++ b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java @@ -32,7 +32,7 @@ * * @param Key type. * @param Value type. - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ public class CuckooInsertBooleanValueListOutput extends CommandOutput>> diff --git a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java index 393e2d60f7..aa25f8a5d9 100644 --- a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java +++ b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java @@ -86,7 +86,7 @@ enum CommandName { // RediSearch (keyless read-only) FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, // Cuckoo Filter read-only commands - CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, CF_SCANDUMP, + CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, } /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt index e0df0d0d17..eaebb65010 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -15,7 +15,7 @@ import io.lettuce.core.cf.arguments.CfReserveArgs /** * Coroutine executed commands for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt index e17ccd8e05..21c13ddd3d 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.reactive.awaitFirstOrNull * * @param Key type. * @param Value type. - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ @ExperimentalLettuceCoroutinesApi diff --git a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt index c6fc638a0f..39c1e35b4d 100644 --- a/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/cluster/api/coroutines/RedisClusterCoroutinesCommands.kt @@ -44,7 +44,9 @@ interface RedisClusterCoroutinesCommands : RedisSetCoroutinesCommands, RedisSortedSetCoroutinesCommands, RedisStreamCoroutinesCommands, - RedisStringCoroutinesCommands { + RedisStringCoroutinesCommands, + RedisBloomFilterCoroutinesCommands, + RedisCuckooFilterCoroutinesCommands { /** * Authenticate to the server. diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java index 3640f069d4..31ad962ab2 100644 --- a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -16,7 +16,7 @@ /** * ${intent} for Cuckoo Filter. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @param Key type. * @param Value type. * @see Redis Cuckoo Filter diff --git a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java index 0dabcfee5c..6dd56a8123 100644 --- a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java @@ -24,7 +24,7 @@ /** * Unit tests for {@link RedisCuckooFilterCommandBuilder}. * - * @author Yordan Tsintsov + * @author Gyumin Hwang */ @Tag(UNIT_TEST) class RedisCuckooFilterCommandBuilderUnitTests { diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java index d2abbc7859..0216fdffc6 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java @@ -18,7 +18,7 @@ /** * Integration tests for Redis Cuckoo Filter commands using Redis Cluster. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ @Tag(INTEGRATION_TEST) diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index 322d50be9b..68a3b23a20 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -23,7 +23,7 @@ /** * Integration tests for {@link io.lettuce.core.api.sync.RedisCuckooFilterCommands}. * - * @author Yordan Tsintsov + * @author Gyumin Hwang */ @Tag(INTEGRATION_TEST) @ExtendWith(LettuceExtension.class) diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java index 2002254818..c1eb223562 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -29,7 +29,7 @@ * Tests the reactive API returns {@code Flux>} are overridden. (wrapping nulls in {@link Value#empty()}) while the * sync API returns {@code List} with raw booleans. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ @Tag(INTEGRATION_TEST) diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java index d49a934d42..0289e8cbb1 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java @@ -28,7 +28,7 @@ * RESP3 differs: the server returns boolean {@code false} instead of integer {@code -1} for a full filter, so the filter-full * (-1 → {@code null}) distinction is only observable over RESP2. This class verifies it explicitly. * - * @author Yordan Tsintsov + * @author Gyumin Hwang * @since 7.7 */ @Tag(INTEGRATION_TEST) diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java index b2f45df751..0dd280395b 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java @@ -20,7 +20,7 @@ class ClusterReadOnlyCommandsUnitTests { @Test void testCount() { - assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(122); + assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(121); } @Test From 60249415b607e514cc0e0ace857f1fb900428c7e Mon Sep 17 00:00:00 2001 From: HwangRock Date: Mon, 8 Jun 2026 22:11:35 +0900 Subject: [PATCH 06/10] Return List for CF.INSERTNX and drop the Value wrapper for CF.INSERT CF.INSERT returns integers on RESP2 and booleans on RESP3, never null, so the Value wrapper wasn't needed. cfInsert now returns List (Flux reactive) with a full filter mapped to false. CF.INSERTNX stays an integer on both RESP2 and RESP3, so it now returns List (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. --- .../core/AbstractRedisAsyncCommands.java | 24 +++- .../core/AbstractRedisReactiveCommands.java | 36 ++++-- .../core/RedisCuckooFilterCommandBuilder.java | 100 ++------------- .../async/RedisCuckooFilterAsyncCommands.java | 86 ++++++++----- .../RedisCuckooFilterReactiveCommands.java | 83 ++++++++----- .../api/sync/RedisCuckooFilterCommands.java | 82 +++++++++---- ...odeSelectionCuckooFilterAsyncCommands.java | 82 +++++++++---- .../NodeSelectionCuckooFilterCommands.java | 82 +++++++++---- .../output/CuckooInsertBooleanListOutput.java | 99 --------------- .../CuckooInsertBooleanValueListOutput.java | 102 ---------------- .../RedisCuckooFilterCoroutinesCommands.kt | 70 ++++++++--- ...RedisCuckooFilterCoroutinesCommandsImpl.kt | 28 +++-- .../core/api/RedisCuckooFilterCommands.java | 82 +++++++++---- .../apigenerator/CreateReactiveApi.java | 3 +- ...isCuckooFilterCommandBuilderUnitTests.java | 26 +++- .../cf/RedisCuckooFilterIntegrationTests.java | 66 ++++++---- ...sCuckooFilterReactiveIntegrationTests.java | 52 ++++---- ...edisCuckooFilterResp2IntegrationTests.java | 23 +--- ...uckooInsertBooleanListOutputUnitTests.java | 114 ----------------- ...InsertBooleanValueListOutputUnitTests.java | 115 ------------------ 20 files changed, 561 insertions(+), 794 deletions(-) delete mode 100644 src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java delete mode 100644 src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java delete mode 100644 src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java delete mode 100644 src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index ab22430cce..41ce772071 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -4320,6 +4320,16 @@ public RedisFuture cfAddNx(K key, V value) { return dispatch(cuckooFilterCommandBuilder.cfAddNx(key, value)); } + @Override + public RedisFuture> cfInsert(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, value)); + } + + @Override + public RedisFuture> cfInsert(K key, CfInsertArgs args, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsert(key, args, value)); + } + @Override public RedisFuture> cfInsert(K key, V... values) { return dispatch(cuckooFilterCommandBuilder.cfInsert(key, values)); @@ -4331,12 +4341,22 @@ public RedisFuture> cfInsert(K key, CfInsertArgs args, V... values } @Override - public RedisFuture> cfInsertNx(K key, V... values) { + public RedisFuture> cfInsertNx(K key, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, value)); + } + + @Override + public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V value) { + return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, args, value)); + } + + @Override + public RedisFuture> cfInsertNx(K key, V... values) { return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, values)); } @Override - public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values) { + public RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values) { return dispatch(cuckooFilterCommandBuilder.cfInsertNx(key, args, values)); } diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index 50c8380a7a..de91543b43 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -4369,23 +4369,43 @@ public Mono cfAddNx(K key, V value) { } @Override - public Flux> cfInsert(K key, V... values) { - return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertValues(key, values)); + public Flux cfInsert(K key, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, value)); } @Override - public Flux> cfInsert(K key, CfInsertArgs args, V... values) { - return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertValues(key, args, values)); + public Flux cfInsert(K key, CfInsertArgs args, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, args, value)); } @Override - public Flux> cfInsertNx(K key, V... values) { - return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNxValues(key, values)); + public Flux cfInsert(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, values)); } @Override - public Flux> cfInsertNx(K key, CfInsertArgs args, V... values) { - return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNxValues(key, args, values)); + public Flux cfInsert(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsert(key, args, values)); + } + + @Override + public Flux cfInsertNx(K key, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, value)); + } + + @Override + public Flux cfInsertNx(K key, CfInsertArgs args, V value) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, args, value)); + } + + @Override + public Flux cfInsertNx(K key, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, values)); + } + + @Override + public Flux cfInsertNx(K key, CfInsertArgs args, V... values) { + return createDissolvingFlux(() -> cuckooFilterCommandBuilder.cfInsertNx(key, args, values)); } @Override diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java index 622460f205..8ac5742d2d 100644 --- a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -71,7 +71,7 @@ Command> cfInsert(K key, V value) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); } Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { @@ -81,7 +81,7 @@ Command> cfInsert(K key, CfInsertArgs insertArgs, V value) { insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); } @SafeVarargs @@ -90,7 +90,7 @@ final Command> cfInsert(K key, V... values) { CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); } @SafeVarargs @@ -101,121 +101,45 @@ final Command> cfInsert(K key, CfInsertArgs insertArgs, V... insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new CuckooInsertBooleanListOutput<>(codec), args); + return createCommand(CF_INSERT, new BooleanListOutput<>(codec), args); } - Command>> cfInsertValues(K key, V value) { + Command> cfInsertNx(K key, V value) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); } - Command>> cfInsertValues(K key, CfInsertArgs insertArgs, V value) { + Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key); insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValue(value); - return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); } @SafeVarargs - final Command>> cfInsertValues(K key, V... values) { + final Command> cfInsertNx(K key, V... values) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); } @SafeVarargs - final Command>> cfInsertValues(K key, CfInsertArgs insertArgs, V... values) { + final Command> cfInsertNx(K key, CfInsertArgs insertArgs, V... values) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key); insertArgs.build(args); args.add(CommandKeyword.ITEMS).addValues(values); - return createCommand(CF_INSERT, new CuckooInsertBooleanValueListOutput<>(codec), args); - } - - Command> cfInsertNx(K key, V value) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); - } - - Command> cfInsertNx(K key, CfInsertArgs insertArgs, V value) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key); - insertArgs.build(args); - args.add(CommandKeyword.ITEMS).addValue(value); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); - } - - @SafeVarargs - final Command> cfInsertNx(K key, V... values) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); - } - - @SafeVarargs - final Command> cfInsertNx(K key, CfInsertArgs insertArgs, V... values) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key); - insertArgs.build(args); - args.add(CommandKeyword.ITEMS).addValues(values); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanListOutput<>(codec), args); - } - - Command>> cfInsertNxValues(K key, V value) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValue(value); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); - } - - Command>> cfInsertNxValues(K key, CfInsertArgs insertArgs, V value) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key); - insertArgs.build(args); - args.add(CommandKeyword.ITEMS).addValue(value); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); - } - - @SafeVarargs - final Command>> cfInsertNxValues(K key, V... values) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key).add(CommandKeyword.ITEMS).addValues(values); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); - } - - @SafeVarargs - final Command>> cfInsertNxValues(K key, CfInsertArgs insertArgs, V... values) { - notNullKey(key); - - CommandArgs args = new CommandArgs<>(codec).addKey(key); - insertArgs.build(args); - args.add(CommandKeyword.ITEMS).addValues(values); - - return createCommand(CF_INSERTNX, new CuckooInsertBooleanValueListOutput<>(codec), args); + return createCommand(CF_INSERTNX, new IntegerListOutput<>(codec), args); } Command cfExists(K key, V value) { diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java index 8865ec566c..ad7c5cc6bc 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -69,47 +69,64 @@ public interface RedisCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code null} if - * the item could not be added because the filter is full (server reply {@code -1}). CF.INSERT does not report - * already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). */ RedisFuture> cfInsert(K key, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, V value); + /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. * @param args the insert arguments. * @param values the values. - * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code null} if - * the item could not be added because the filter is full (server reply {@code -1}). CF.INSERT does not report - * already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). */ RedisFuture> cfInsert(K key, CfInsertArgs args, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if added, {@code false} if the filter is + * full (one entry per item). + */ + RedisFuture> cfInsert(K key, CfInsertArgs args, V value); + /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. * @param values the values. - * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code false} if - * the item already exists, {@code null} if the item could not be added because the filter is full (server reply - * {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. */ - RedisFuture> cfInsertNx(K key, V... values); + RedisFuture> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -118,15 +135,22 @@ public interface RedisCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return RedisFuture<List<Boolean>> one entry per item: {@code true} if the item was added, {@code false} if - * the item already exists, {@code null} if the item could not be added because the filter is full (server reply - * {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values); + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return RedisFuture<List<Long>> one entry per item: {@code 1} if added, {@code 0} if the item already exists, + * {@code -1} if the filter is full. + */ + RedisFuture> cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java index 61bdc42aa7..55bdad69b3 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -6,7 +6,6 @@ */ package io.lettuce.core.api.reactive; -import io.lettuce.core.Value; import io.lettuce.core.cf.CfInfoValue; import io.lettuce.core.cf.CfScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; @@ -70,14 +69,18 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param values the values. - * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, {@code Value.empty()} - * if the filter is full. CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. */ - Flux> cfInsert(K key, V... values); + Flux cfInsert(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, V value); /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. @@ -85,14 +88,19 @@ public interface RedisCuckooFilterReactiveCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, {@code Value.empty()} - * if the filter is full. CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. */ - Flux> cfInsert(K key, CfInsertArgs args, V... values); + Flux cfInsert(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return Flux<Boolean> one element per item: {@code true} if added, {@code false} if the filter is full. + */ + Flux cfInsert(K key, CfInsertArgs args, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -100,14 +108,21 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param values the values. - * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, - * {@code Value.just(false)} if it already exists, {@code Value.empty()} if the filter is full. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. */ - Flux> cfInsertNx(K key, V... values); + Flux cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -116,14 +131,22 @@ public interface RedisCuckooFilterReactiveCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return Flux<Value<Boolean>> one element per item: {@code Value.just(true)} if added, - * {@code Value.just(false)} if it already exists, {@code Value.empty()} if the filter is full. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - Flux> cfInsertNx(K key, CfInsertArgs args, V... values); + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return Flux<Long> one element per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if + * the filter is full. + */ + Flux cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java index c44b32c483..fab5ccc04e 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -67,44 +67,64 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ List cfInsert(K key, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V value); + /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ List cfInsert(K key, CfInsertArgs args, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V value); + /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. */ - List cfInsertNx(K key, V... values); + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -113,14 +133,22 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - List cfInsertNx(K key, CfInsertArgs args, V... values); + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java index 838a45f02a..cd548a5dd9 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -68,44 +68,64 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ AsyncExecutions> cfInsert(K key, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, V value); + /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ AsyncExecutions> cfInsert(K key, CfInsertArgs args, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + AsyncExecutions> cfInsert(K key, CfInsertArgs args, V value); + /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. */ - AsyncExecutions> cfInsertNx(K key, V... values); + AsyncExecutions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -114,14 +134,22 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V... values); + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + AsyncExecutions> cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java index afc45a6bb9..2b86239dc2 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -67,44 +67,64 @@ public interface NodeSelectionCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ Executions> cfInsert(K key, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, V value); + /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ Executions> cfInsert(K key, CfInsertArgs args, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + Executions> cfInsert(K key, CfInsertArgs args, V value); + /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. */ - Executions> cfInsertNx(K key, V... values); + Executions> cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -113,14 +133,22 @@ public interface NodeSelectionCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - Executions> cfInsertNx(K key, CfInsertArgs args, V... values); + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + Executions> cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java deleted file mode 100644 index 53a0733d36..0000000000 --- a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanListOutput.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.output; - -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.List; - -import io.lettuce.core.codec.RedisCodec; -import io.lettuce.core.internal.LettuceAssert; - -/** - * {@link List} of boolean output for CF.INSERT / CF.INSERTNX commands. - * - *

- * Maps the per-item integer response to a 3-state {@link Boolean}: - *

    - *
  • {@code 1} → {@link Boolean#TRUE} – item was added
  • - *
  • {@code 0} → {@link Boolean#FALSE} – item already exists (INSERTNX only)
  • - *
  • negative (e.g. {@code -1}) → {@code null} – filter is full
  • - *
- * - *

- * Unlike {@link ErrorTolerantBooleanListOutput} (used by BF commands), this output distinguishes "already exists" - * ({@code false}) from "filter full" ({@code null}), which is required by the CF.INSERT / CF.INSERTNX semantics. - * - * @param Key type. - * @param Value type. - * @author Gyumin Hwang - * @since 7.7 - */ -public class CuckooInsertBooleanListOutput extends CommandOutput> - implements StreamingOutput { - - private boolean initialized; - - private Subscriber subscriber; - - public CuckooInsertBooleanListOutput(RedisCodec codec) { - super(codec, Collections.emptyList()); - setSubscriber(ListSubscriber.instance()); - } - - @Override - public void set(long integer) { - Boolean value = integer == 1 ? Boolean.TRUE : (integer == 0 ? Boolean.FALSE : null); - subscriber.onNext(output, value); - } - - @Override - public void set(boolean value) { - subscriber.onNext(output, value); - } - - @Override - public void setError(ByteBuffer error) { - - if (initialized) { - subscriber.onNext(output, null); - return; - } - super.setError(error); - } - - @Override - public void set(ByteBuffer bytes) { - - if (initialized && bytes == null) { - subscriber.onNext(output, null); - return; - } - super.set(bytes); - } - - @Override - public void multi(int count) { - - if (!initialized) { - output = OutputFactory.newList(count); - initialized = true; - } - } - - @Override - public void setSubscriber(Subscriber subscriber) { - LettuceAssert.notNull(subscriber, "Subscriber must not be null"); - this.subscriber = subscriber; - } - - @Override - public Subscriber getSubscriber() { - return subscriber; - } - -} diff --git a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java b/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java deleted file mode 100644 index 760961d57e..0000000000 --- a/src/main/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutput.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.output; - -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.List; - -import io.lettuce.core.Value; -import io.lettuce.core.codec.RedisCodec; -import io.lettuce.core.internal.LettuceAssert; - -/** - * {@link List} of {@link Value}-wrapped boolean output for reactive CF.INSERT / CF.INSERTNX commands. - * - *

- * Maps the per-item integer response to a 3-state {@link Value}: - *

    - *
  • {@code 1} → {@code Value.just(Boolean.TRUE)} – item was added
  • - *
  • {@code 0} → {@code Value.just(Boolean.FALSE)} – item already exists (INSERTNX only)
  • - *
  • negative (e.g. {@code -1}) → {@code Value.empty()} – filter is full
  • - *
- * - *

- * Unlike {@link ErrorTolerantBooleanValueListOutput} (used by BF commands), this output distinguishes "already exists" - * ({@code Value.just(false)}) from "filter full" ({@code Value.empty()}), which is required by the CF.INSERT / CF.INSERTNX - * semantics. - * - * @param Key type. - * @param Value type. - * @author Gyumin Hwang - * @since 7.7 - */ -public class CuckooInsertBooleanValueListOutput extends CommandOutput>> - implements StreamingOutput> { - - private boolean initialized; - - private Subscriber> subscriber; - - public CuckooInsertBooleanValueListOutput(RedisCodec codec) { - super(codec, Collections.emptyList()); - setSubscriber(ListSubscriber.instance()); - } - - @Override - public void set(long integer) { - Value value = integer == 1 ? Value.just(Boolean.TRUE) - : (integer == 0 ? Value.just(Boolean.FALSE) : Value.empty()); - subscriber.onNext(output, value); - } - - @Override - public void set(boolean value) { - subscriber.onNext(output, Value.just(value)); - } - - @Override - public void setError(ByteBuffer error) { - - if (initialized) { - subscriber.onNext(output, Value.empty()); - return; - } - super.setError(error); - } - - @Override - public void set(ByteBuffer bytes) { - - if (initialized) { - subscriber.onNext(output, (bytes == null ? Value.empty() : Value.just(Boolean.parseBoolean(decodeString(bytes))))); - return; - } - super.set(bytes); - } - - @Override - public void multi(int count) { - - if (!initialized) { - output = OutputFactory.newList(count); - initialized = true; - } - } - - @Override - public void setSubscriber(Subscriber> subscriber) { - LettuceAssert.notNull(subscriber, "Subscriber must not be null"); - this.subscriber = subscriber; - } - - @Override - public Subscriber> getSubscriber() { - return subscriber; - } - -} diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt index eaebb65010..7695b1a99f 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -67,13 +67,32 @@ interface RedisCuckooFilterCoroutinesCommands { * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. - * @param values the values. - * @return List one entry per item: `true` if the item was added, `null` if the item could not be added because the - * filter is full (server reply `-1`). CF.INSERT does not report already-existing items. + * @param value the value. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, value: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * - * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. */ - suspend fun cfInsert(key: K, vararg values: V): List + suspend fun cfInsert(key: K, args: CfInsertArgs, value: V): List + + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param values the values. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, vararg values: V): List /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. @@ -81,25 +100,41 @@ interface RedisCuckooFilterCoroutinesCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List one entry per item: `true` if the item was added, `null` if the item could not be added because the - * filter is full (server reply `-1`). CF.INSERT does not report already-existing items. + * @return List one entry per item: `true` if the item was added, `false` if the filter is full. + * CF.INSERT does not report already-existing items. + */ + suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. * - * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. + * @param key the key. + * @param value the value. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. */ - suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List + suspend fun cfInsertNx(key: K, value: V): List /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. - * @param values the values. - * @return List one entry per item: `true` if the item was added, `false` if the item already exists, `null` if the - * item could not be added because the filter is full (server reply `-1`). + * @param args the insert arguments. + * @param value the value. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. + */ + suspend fun cfInsertNx(key: K, args: CfInsertArgs, value: V): List + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. * - * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. + * @param key the key. + * @param values the values. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. */ - suspend fun cfInsertNx(key: K, vararg values: V): List + suspend fun cfInsertNx(key: K, vararg values: V): List /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -108,12 +143,9 @@ interface RedisCuckooFilterCoroutinesCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List one entry per item: `true` if the item was added, `false` if the item already exists, `null` if the - * item could not be added because the filter is full (server reply `-1`). - * - * Note: over RESP3 a full-filter result may surface as `false` instead of `null`, as the server encodes -1 as a boolean. + * @return List one entry per item: `1` if the item was added, `0` if it already exists, `-1` if the filter is full. */ - suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List + suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt index 21c13ddd3d..b7653119cf 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -41,17 +41,29 @@ internal class RedisCuckooFilterCoroutinesCommandsImpl( override suspend fun cfAddNx(key: K, value: V): Boolean? = ops.cfAddNx(key, value).awaitFirstOrNull() - override suspend fun cfInsert(key: K, vararg values: V): List = - ops.cfInsert(key, *values).asFlow().toList().map { it.getValueOrElse(null) } + override suspend fun cfInsert(key: K, value: V): List = + ops.cfInsert(key, value).asFlow().toList() - override suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List = - ops.cfInsert(key, args, *values).asFlow().toList().map { it.getValueOrElse(null) } + override suspend fun cfInsert(key: K, args: CfInsertArgs, value: V): List = + ops.cfInsert(key, args, value).asFlow().toList() - override suspend fun cfInsertNx(key: K, vararg values: V): List = - ops.cfInsertNx(key, *values).asFlow().toList().map { it.getValueOrElse(null) } + override suspend fun cfInsert(key: K, vararg values: V): List = + ops.cfInsert(key, *values).asFlow().toList() - override suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List = - ops.cfInsertNx(key, args, *values).asFlow().toList().map { it.getValueOrElse(null) } + override suspend fun cfInsert(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsert(key, args, *values).asFlow().toList() + + override suspend fun cfInsertNx(key: K, value: V): List = + ops.cfInsertNx(key, value).asFlow().toList() + + override suspend fun cfInsertNx(key: K, args: CfInsertArgs, value: V): List = + ops.cfInsertNx(key, args, value).asFlow().toList() + + override suspend fun cfInsertNx(key: K, vararg values: V): List = + ops.cfInsertNx(key, *values).asFlow().toList() + + override suspend fun cfInsertNx(key: K, args: CfInsertArgs, vararg values: V): List = + ops.cfInsertNx(key, args, *values).asFlow().toList() override suspend fun cfExists(key: K, value: V): Boolean? = ops.cfExists(key, value).awaitFirstOrNull() diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java index 31ad962ab2..26fc82e11b 100644 --- a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -67,44 +67,64 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ List cfInsert(K key, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, V value); + /** * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. * * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code null} if the item could not be - * added because the filter is full (server reply {@code -1}). CF.INSERT does not report already-existing items. - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). */ List cfInsert(K key, CfInsertArgs args, V... values); + /** + * Add one or more items to a Cuckoo Filter. A filter will be created if one does not exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Boolean> one entry per item: {@code true} if added, {@code false} if the filter is full (one entry per + * item). + */ + List cfInsert(K key, CfInsertArgs args, V value); + /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not * exist. * * @param key the key. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

+ * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. */ - List cfInsertNx(K key, V... values); + List cfInsertNx(K key, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, V value); /** * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not @@ -113,14 +133,22 @@ public interface RedisCuckooFilterCommands { * @param key the key. * @param args the insert arguments. * @param values the values. - * @return List<Boolean> one entry per item: {@code true} if the item was added, {@code false} if the item already - * exists, {@code null} if the item could not be added because the filter is full (server reply {@code -1}). - *

- * Note: over RESP3 a full-filter result may surface as {@code false}/{@code Value.just(false)} instead of - * {@code null}/{@code Value.empty()}, as the server encodes -1 as a boolean. - *

- */ - List cfInsertNx(K key, CfInsertArgs args, V... values); + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V... values); + + /** + * Add one or more items to a Cuckoo Filter only if the items do not already exist. A filter will be created if one does not + * exist. + * + * @param key the key. + * @param args the insert arguments. + * @param value the value. + * @return List<Long> one entry per item: {@code 1} if added, {@code 0} if the item already exists, {@code -1} if the + * filter is full. + */ + List cfInsertNx(K key, CfInsertArgs args, V value); /** * Check if an item exists in the Cuckoo Filter. diff --git a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java index 989a253d60..415ab58d9e 100644 --- a/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java +++ b/src/test/java/io/lettuce/apigenerator/CreateReactiveApi.java @@ -60,8 +60,7 @@ public class CreateReactiveApi { public static Set FORCE_FLUX_RESULT = LettuceSets.unmodifiableSet("eval", "evalsha", "evalReadOnly", "evalshaReadOnly", "fcall", "fcallReadOnly", "dispatch"); - public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "bfInsert", "bfMAdd", "cfInsert", - "cfInsertNx"); + public static Set VALUE_WRAP = LettuceSets.unmodifiableSet("geopos", "bitfield", "bfInsert", "bfMAdd"); private static final Map RESULT_SPEC; diff --git a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java index 6dd56a8123..2e53918d82 100644 --- a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java @@ -80,6 +80,16 @@ void shouldCorrectlyConstructCfAddNxCommand() { .isEqualTo("*3\r\n" + "$8\r\nCF.ADDNX\r\n" + "$10\r\n" + MY_KEY + "\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); } + @Test + void shouldCorrectlyConstructCfInsertSingleValueCommand() { + Command> command = builder.cfInsert(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$9\r\nCF.INSERT\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + @Test void shouldCorrectlyConstructCfInsertCommand() { Command> command = builder.cfInsert(MY_KEY, MY_VALUE); @@ -124,9 +134,19 @@ void shouldCorrectlyConstructCfInsertCommandVarargWithArgs() { + "$3\r\n100\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n" + "$12\r\n" + MY_VALUE_2 + "\r\n"); } + @Test + void shouldCorrectlyConstructCfInsertNxSingleValueCommand() { + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); + ByteBuf buff = Unpooled.buffer(); + command.encode(buff); + + assertThat(buff.toString(StandardCharsets.UTF_8)).isEqualTo("*4\r\n" + "$11\r\nCF.INSERTNX\r\n" + "$10\r\n" + MY_KEY + + "\r\n" + "$5\r\nITEMS\r\n" + "$4\r\n" + MY_VALUE + "\r\n"); + } + @Test void shouldCorrectlyConstructCfInsertNxCommand() { - Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE); ByteBuf buff = Unpooled.buffer(); command.encode(buff); @@ -137,7 +157,7 @@ void shouldCorrectlyConstructCfInsertNxCommand() { @Test void shouldCorrectlyConstructCfInsertNxCommandWithArgs() { CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(200).noCreate(); - Command> command = builder.cfInsertNx(MY_KEY, insertArgs, MY_VALUE); + Command> command = builder.cfInsertNx(MY_KEY, insertArgs, MY_VALUE); ByteBuf buff = Unpooled.buffer(); command.encode(buff); @@ -148,7 +168,7 @@ void shouldCorrectlyConstructCfInsertNxCommandWithArgs() { @Test void shouldCorrectlyConstructCfInsertNxCommandWithVarargs() { - Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + Command> command = builder.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); ByteBuf buff = Unpooled.buffer(); command.encode(buff); diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index 68a3b23a20..e67705a0c6 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -99,13 +99,21 @@ void cfInsertWithArgs() { assertThat(result).containsExactly(true, true); } + @Test + void cfInsertSingleValue() { + List result = redis.cfInsert(MY_KEY, MY_VALUE); + + assertThat(result).containsExactly(true); + assertThat(redis.cfExists(MY_KEY, MY_VALUE)).isTrue(); + } + @Test void cfInsertNx() { redis.cfAdd(MY_KEY, MY_VALUE); - List result = redis.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); + List result = redis.cfInsertNx(MY_KEY, MY_VALUE, MY_VALUE_2); - assertThat(result).containsExactly(false, true); + assertThat(result).containsExactly(0L, 1L); } @Test @@ -113,9 +121,16 @@ void cfInsertNxWithArgs() { CfInsertArgs insertArgs = CfInsertArgs.Builder.capacity(100); redis.cfAdd(MY_KEY, MY_VALUE); - List result = redis.cfInsertNx(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + List result = redis.cfInsertNx(MY_KEY, insertArgs, MY_VALUE, MY_VALUE_2); + + assertThat(result).containsExactly(0L, 1L); + } + + @Test + void cfInsertNxSingleValue() { + List result = redis.cfInsertNx(MY_KEY, MY_VALUE); - assertThat(result).containsExactly(false, true); + assertThat(result).containsExactly(1L); } @Test @@ -164,33 +179,40 @@ void cfInfo() { } /** - * Verifies that CF.INSERTNX distinguishes between "already exists" ({@code false}) and "filter full" ({@code null}). - * - *

- * With the old {@link io.lettuce.core.output.ErrorTolerantBooleanListOutput} both 0 and -1 were mapped to {@code false}, - * making them indistinguishable. The new {@link io.lettuce.core.output.CuckooInsertBooleanListOutput} maps 0 → - * {@code false} and -1 → {@code null}. - * - *

- * Note: reproducing -1 via INSERTNX is difficult because inserting the same value always returns 0 ("already exists"). The - * -1 → null mapping is verified in the RESP2 integration tests - * ({@link RedisCuckooFilterResp2IntegrationTests#cfInsertReturnsNullWhenFilterIsFull()}) and in the unit tests - * (CuckooInsertBooleanListOutputUnitTests), which use CF.INSERT and the same underlying output class - * ({@link io.lettuce.core.output.CuckooInsertBooleanListOutput}). This test focuses on confirming that 0 ("already exists") - * maps to {@code false}, NOT to {@code null}. + * Verifies that CF.INSERTNX returns {@code 0L} ("already exists") which is distinct from {@code -1L} ("filter full"). */ @Test void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { String key = "cf:full:insertnx"; redis.cfReserve(key, 100, CfReserveArgs.Builder.bucketSize(2).expansion(1)); - // Pre-populate the filter so INSERTNX sees "already exists" (0) for known items redis.cfAdd(key, "known"); - // existing item must return false (0), NOT null — 0 and -1 must remain distinguishable - List existing = redis.cfInsertNx(key, "known"); + List existing = redis.cfInsertNx(key, "known"); assertThat(existing).hasSize(1); - assertThat(existing.get(0)).isEqualTo(Boolean.FALSE); // "already exists" = false, NOT null + assertThat(existing.get(0)).isEqualTo(0L); + } + + /** + * Verifies that CF.INSERT returns {@code false} when the filter is full (server returns {@code -1}). + * + *

+ * BUCKETSIZE 1 EXPANSION 0: same value fills at most 2 slots, so inserting the same value 6 times causes filter-full + * conditions. RESP2 and RESP3 both produce {@code false} for filter-full via + * {@link io.lettuce.core.output.BooleanListOutput}. + */ + @Test + void cfInsertReturnsFalseWhenFilterIsFull() { + String key = "cf:full:insert"; + redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); + + List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); + + assertThat(result).hasSize(6); + assertThat(result).startsWith(true, true); + for (int i = 2; i < result.size(); i++) { + assertThat(result.get(i)).as("result[%d] must be false (filter full)", i).isEqualTo(Boolean.FALSE); + } } @Test diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java index c1eb223562..3efbe818e5 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -9,7 +9,6 @@ import javax.inject.Inject; import java.util.List; -import io.lettuce.core.Value; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.reactive.RedisReactiveCommands; import io.lettuce.core.cf.arguments.CfInsertArgs; @@ -25,9 +24,10 @@ /** * Reactive integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} * routing every call through the reactive API via {@link ReactiveSyncInvocationHandler}. + * *

- * Tests the reactive API returns {@code Flux>} are overridden. (wrapping nulls in {@link Value#empty()}) while the - * sync API returns {@code List} with raw booleans. + * Overrides that verify reactive-specific streaming behaviour (Flux) for cfInsert ({@code Flux}) and cfInsertNx + * ({@code Flux}). * * @author Gyumin Hwang * @since 7.7 @@ -46,26 +46,32 @@ public RedisCuckooFilterReactiveIntegrationTests(StatefulRedisConnection - * Verifies that CF.INSERTNX emits {@code Value.just(false)} for "already exists" (0), NOT {@code Value.empty()} which - * represents "filter full" (-1). The -1 → Value.empty() mapping is verified by - * {@link RedisCuckooFilterResp2IntegrationTests#cfInsertReturnsNullWhenFilterIsFull()} (RESP2) and - * CuckooInsertBooleanValueListOutputUnitTests (unit tests). + * Verifies that CF.INSERTNX emits {@code 0L} for "already exists", distinct from {@code -1L} for "filter full". */ @Test @Override @@ -95,10 +104,9 @@ void cfInsertNxDistinguishesAlreadyExistsFromFilterFull() { reactive.cfAdd(key, "known").block(); - // existing item must return Value.just(false), NOT Value.empty() — 0 and -1 must remain distinguishable - List> existing = reactive.cfInsertNx(key, "known").collectList().block(); + List existing = reactive.cfInsertNx(key, "known").collectList().block(); assertThat(existing).isNotNull().hasSize(1); - assertThat(existing.get(0)).isEqualTo(Value.just(Boolean.FALSE)); + assertThat(existing.get(0)).isEqualTo(0L); } } diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java index 0289e8cbb1..dd865fc7a0 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java @@ -9,24 +9,20 @@ import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.protocol.ProtocolVersion; import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; import javax.inject.Inject; -import java.util.List; import static io.lettuce.TestTags.INTEGRATION_TEST; -import static org.assertj.core.api.Assertions.assertThat; /** * RESP2 integration tests for Redis Cuckoo Filter commands. Re-runs all tests from {@link RedisCuckooFilterIntegrationTests} * using RESP2. * *

- * RESP3 differs: the server returns boolean {@code false} instead of integer {@code -1} for a full filter, so the filter-full - * (-1 → {@code null}) distinction is only observable over RESP2. This class verifies it explicitly. + * RESP2 and RESP3 behave identically for CF.INSERT (filter-full = {@code false}) and CF.INSERTNX (filter-full = {@code -1L}), + * so no protocol-specific overrides are needed. The parent-class tests cover both protocols. * * @author Gyumin Hwang * @since 7.7 @@ -44,19 +40,4 @@ private static RedisCommands connectWithResp2(RedisClient client return client.connect().sync(); } - @Test - void cfInsertReturnsNullWhenFilterIsFull() { - String key = "cf:full:resp2:insert"; - // RESP2: server returns integer -1 (filter full). BUCKETSIZE 1 EXPANSION 0: same value fits at most 2*1=2 slots. - redis.cfReserve(key, 1000, CfReserveArgs.Builder.bucketSize(1).expansion(0)); - List result = redis.cfInsert(key, "W", "W", "W", "W", "W", "W"); - assertThat(result).hasSize(6); - assertThat(result.get(0)).isEqualTo(Boolean.TRUE); - assertThat(result.get(1)).isEqualTo(Boolean.TRUE); - // -1 (filter full) must map to null, distinct from false (0 = already exists) - for (int i = 2; i < result.size(); i++) { - assertThat(result.get(i)).as("result[%d] must be null (filter full = -1, RESP2)", i).isNull(); - } - } - } diff --git a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java deleted file mode 100644 index 90e65f5543..0000000000 --- a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanListOutputUnitTests.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.output; - -import static io.lettuce.TestTags.UNIT_TEST; -import static org.assertj.core.api.Assertions.assertThat; - -import java.nio.ByteBuffer; -import java.util.List; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.lettuce.core.codec.StringCodec; - -/** - * Unit tests for {@link CuckooInsertBooleanListOutput}. - * - * Verifies the 3-state mapping required by CF.INSERT / CF.INSERTNX: - *

    - *
  • 1 → {@code Boolean.TRUE} (item added)
  • - *
  • 0 → {@code Boolean.FALSE} (item already exists, INSERTNX only)
  • - *
  • -1 → {@code null} (filter is full)
  • - *
- */ -@Tag(UNIT_TEST) -class CuckooInsertBooleanListOutputUnitTests { - - private final CuckooInsertBooleanListOutput sut = new CuckooInsertBooleanListOutput<>(StringCodec.UTF8); - - @Test - void defaultSubscriberIsSet() { - assertThat(sut.getSubscriber()).isNotNull().isInstanceOf(ListSubscriber.class); - } - - @Test - void set1MappedToTrue() { - sut.multi(1); - sut.set(1L); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Boolean.TRUE); - } - - @Test - void set0MappedToFalse() { - sut.multi(1); - sut.set(0L); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Boolean.FALSE); - } - - @Test - void setNegativeOneMappedToNull() { - sut.multi(1); - sut.set(-1L); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isNull(); - } - - @Test - void otherNegativeValueMappedToNull() { - sut.multi(1); - sut.set(-2L); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isNull(); - } - - @Test - void mixedResponsesAllDistinct() { - sut.multi(3); - sut.set(1L); - sut.set(0L); - sut.set(-1L); - - List result = sut.get(); - assertThat(result).hasSize(3); - assertThat(result.get(0)).isEqualTo(Boolean.TRUE); - assertThat(result.get(1)).isEqualTo(Boolean.FALSE); - assertThat(result.get(2)).isNull(); - } - - @Test - void setErrorPushesNullWhenInitialized() { - sut.multi(1); - sut.setError(ByteBuffer.wrap("ERR filter full".getBytes())); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isNull(); - } - - @Test - void setNullByteBufferPushesNullWhenInitialized() { - sut.multi(1); - sut.set((ByteBuffer) null); - - List result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isNull(); - } - -} diff --git a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java b/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java deleted file mode 100644 index 3dd53db026..0000000000 --- a/src/test/java/io/lettuce/core/output/CuckooInsertBooleanValueListOutputUnitTests.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.output; - -import static io.lettuce.TestTags.UNIT_TEST; -import static org.assertj.core.api.Assertions.assertThat; - -import java.util.List; - -import org.junit.jupiter.api.Tag; -import org.junit.jupiter.api.Test; - -import io.lettuce.core.Value; -import io.lettuce.core.codec.StringCodec; - -/** - * Unit tests for {@link CuckooInsertBooleanValueListOutput}. - * - * Verifies the 3-state mapping required by reactive CF.INSERT / CF.INSERTNX: - *
    - *
  • 1 → {@code Value.just(Boolean.TRUE)} (item added)
  • - *
  • 0 → {@code Value.just(Boolean.FALSE)} (item already exists, INSERTNX only)
  • - *
  • -1 → {@code Value.empty()} (filter is full)
  • - *
- */ -@Tag(UNIT_TEST) -class CuckooInsertBooleanValueListOutputUnitTests { - - private final CuckooInsertBooleanValueListOutput sut = new CuckooInsertBooleanValueListOutput<>( - StringCodec.UTF8); - - @Test - void defaultSubscriberIsSet() { - assertThat(sut.getSubscriber()).isNotNull().isInstanceOf(ListSubscriber.class); - } - - @Test - void set1MappedToValueTrue() { - sut.multi(1); - sut.set(1L); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); - } - - @Test - void set0MappedToValueFalse() { - sut.multi(1); - sut.set(0L); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.just(Boolean.FALSE)); - } - - @Test - void setNegativeOneMappedToValueEmpty() { - sut.multi(1); - sut.set(-1L); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.empty()); - } - - @Test - void otherNegativeValueMappedToValueEmpty() { - sut.multi(1); - sut.set(-2L); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.empty()); - } - - @Test - void mixedResponsesAllDistinct() { - sut.multi(3); - sut.set(1L); - sut.set(0L); - sut.set(-1L); - - List> result = sut.get(); - assertThat(result).hasSize(3); - assertThat(result.get(0)).isEqualTo(Value.just(Boolean.TRUE)); - assertThat(result.get(1)).isEqualTo(Value.just(Boolean.FALSE)); - assertThat(result.get(2)).isEqualTo(Value.empty()); - } - - @Test - void setErrorPushesValueEmptyWhenInitialized() { - sut.multi(1); - sut.setError(java.nio.ByteBuffer.wrap("ERR filter full".getBytes())); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.empty()); - } - - @Test - void setNullByteBufferPushesValueEmptyWhenInitialized() { - sut.multi(1); - sut.set((java.nio.ByteBuffer) null); - - List> result = sut.get(); - assertThat(result).hasSize(1); - assertThat(result.get(0)).isEqualTo(Value.empty()); - } - -} From 351ec9347471728ad9d849a44eb3be7ebd3f97e1 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Mon, 8 Jun 2026 23:20:31 +0900 Subject: [PATCH 07/10] Register BF.SCANDUMP and CF.SCANDUMP as read-only 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. --- src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java | 4 ++-- .../core/cluster/ClusterReadOnlyCommandsUnitTests.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java index aa25f8a5d9..23d486cc8d 100644 --- a/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java +++ b/src/main/java/io/lettuce/core/protocol/ReadOnlyCommands.java @@ -84,9 +84,9 @@ enum CommandName { ZCARD, ZCOUNT, ZLEXCOUNT, ZRANGE, // ZRANDMEMBER, ZRANGEBYLEX, ZRANGEBYSCORE, ZRANK, ZREVRANGE, ZREVRANGEBYLEX, ZREVRANGEBYSCORE, ZREVRANK, ZSCAN, ZSCORE, // RediSearch (keyless read-only) - FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, + FT_AGGREGATE, FT_SEARCH, FT_HYBRID, FT_EXPLAIN, FT_SPELLCHECK, FT_TAGVALS, FT_SYNDUMP, FT_LIST, FT_DICTDUMP, BF_CARD, BF_EXISTS, BF_INFO, BF_MEXISTS, BF_SCANDUMP, // Cuckoo Filter read-only commands - CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, + CF_EXISTS, CF_MEXISTS, CF_COUNT, CF_INFO, CF_SCANDUMP, } /** diff --git a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java index 0dd280395b..0c099147f5 100644 --- a/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java +++ b/src/test/java/io/lettuce/core/cluster/ClusterReadOnlyCommandsUnitTests.java @@ -20,7 +20,7 @@ class ClusterReadOnlyCommandsUnitTests { @Test void testCount() { - assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(121); + assertThat(ClusterReadOnlyCommands.getReadOnlyCommands()).hasSize(123); } @Test From 29ae7188c91c7d6f621aa54d277bcce5c643ef65 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Wed, 10 Jun 2026 22:10:47 +0900 Subject: [PATCH 08/10] Merge the BF and CF scan-dump value objects into one shared class 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. --- .../core/AbstractRedisAsyncCommands.java | 7 ++- .../core/AbstractRedisReactiveCommands.java | 7 ++- .../core/RedisBloomFilterCommandBuilder.java | 8 +-- .../core/RedisCuckooFilterCommandBuilder.java | 8 +-- .../async/RedisBloomFilterAsyncCommands.java | 6 +-- .../async/RedisCuckooFilterAsyncCommands.java | 6 +-- .../RedisBloomFilterReactiveCommands.java | 6 +-- .../RedisCuckooFilterReactiveCommands.java | 6 +-- .../api/sync/RedisBloomFilterCommands.java | 6 +-- .../api/sync/RedisCuckooFilterCommands.java | 6 +-- .../io/lettuce/core/cf/CfScanDumpValue.java | 44 ----------------- .../core/cf/CfScanDumpValueParser.java | 49 ------------------- ...NodeSelectionBloomFilterAsyncCommands.java | 6 +-- ...odeSelectionCuckooFilterAsyncCommands.java | 6 +-- .../NodeSelectionBloomFilterCommands.java | 6 +-- .../NodeSelectionCuckooFilterCommands.java | 6 +-- .../ScanDumpValue.java} | 10 ++-- .../ScanDumpValueParser.java} | 20 ++++---- .../RedisBloomFilterCoroutinesCommands.kt | 6 +-- .../RedisBloomFilterCoroutinesCommandsImpl.kt | 4 +- .../RedisCuckooFilterCoroutinesCommands.kt | 6 +-- ...RedisCuckooFilterCoroutinesCommandsImpl.kt | 4 +- .../core/api/RedisBloomFilterCommands.java | 6 +-- .../core/api/RedisCuckooFilterCommands.java | 6 +-- ...disBloomFilterCommandBuilderUnitTests.java | 4 +- .../bf/RedisBloomFilterIntegrationTests.java | 3 +- .../cf/RedisCuckooFilterIntegrationTests.java | 3 +- 27 files changed, 83 insertions(+), 172 deletions(-) delete mode 100644 src/main/java/io/lettuce/core/cf/CfScanDumpValue.java delete mode 100644 src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java rename src/main/java/io/lettuce/core/{bf/BfScanDumpValue.java => probabilistic/ScanDumpValue.java} (61%) rename src/main/java/io/lettuce/core/{bf/BfScanDumpValueParser.java => probabilistic/ScanDumpValueParser.java} (51%) diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 41ce772071..732de95cc8 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -24,11 +24,10 @@ import io.lettuce.core.api.async.*; import io.lettuce.core.array.*; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.cluster.PipelinedRedisFuture; @@ -4294,7 +4293,7 @@ public RedisFuture bfReserve(K key, double errorRate, long capacity, BfR } @Override - public RedisFuture bfScanDump(K key, long iterator) { + public RedisFuture bfScanDump(K key, long iterator) { return dispatch(bloomFilterCommandBuilder.bfScanDump(key, iterator)); } @@ -4381,7 +4380,7 @@ public RedisFuture cfCount(K key, V value) { } @Override - public RedisFuture cfScanDump(K key, long cursor) { + public RedisFuture cfScanDump(K key, long cursor) { return dispatch(cuckooFilterCommandBuilder.cfScanDump(key, cursor)); } diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index de91543b43..e508dbf766 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -24,11 +24,10 @@ import io.lettuce.core.api.reactive.*; import io.lettuce.core.array.*; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands; @@ -4342,7 +4341,7 @@ public Mono bfReserve(K key, double errorRate, long capacity, BfReserveA } @Override - public Mono bfScanDump(K key, long iterator) { + public Mono bfScanDump(K key, long iterator) { return createMono(() -> bloomFilterCommandBuilder.bfScanDump(key, iterator)); } @@ -4429,7 +4428,7 @@ public Mono cfCount(K key, V value) { } @Override - public Mono cfScanDump(K key, long cursor) { + public Mono cfScanDump(K key, long cursor) { return createMono(() -> cuckooFilterCommandBuilder.cfScanDump(key, cursor)); } diff --git a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java index e75b9b91c6..31495e77c5 100644 --- a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java @@ -10,8 +10,8 @@ import io.lettuce.core.bf.BfInfoValue; import io.lettuce.core.bf.BfInfoValueParser; -import io.lettuce.core.bf.BfScanDumpValue; -import io.lettuce.core.bf.BfScanDumpValueParser; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValueParser; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; import io.lettuce.core.codec.RedisCodec; @@ -183,12 +183,12 @@ Command bfReserve(K key, double errorRate, long capacity, BfReserv return createCommand(BF_RESERVE, new StatusOutput<>(codec), args); } - Command bfScanDump(K key, long iterator) { + Command bfScanDump(K key, long iterator) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator); - return createCommand(BF_SCANDUMP, new EncodedComplexOutput<>(codec, BfScanDumpValueParser.INSTANCE), args); + return createCommand(BF_SCANDUMP, new EncodedComplexOutput<>(codec, ScanDumpValueParser.INSTANCE), args); } } diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java index 8ac5742d2d..0b4db58896 100644 --- a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -10,8 +10,8 @@ import io.lettuce.core.cf.CfInfoValue; import io.lettuce.core.cf.CfInfoValueParser; -import io.lettuce.core.cf.CfScanDumpValue; -import io.lettuce.core.cf.CfScanDumpValueParser; +import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValueParser; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; import io.lettuce.core.codec.RedisCodec; @@ -167,12 +167,12 @@ Command cfCount(K key, V value) { return createCommand(CF_COUNT, new IntegerOutput<>(codec), key, value); } - Command cfScanDump(K key, long iterator) { + Command cfScanDump(K key, long iterator) { notNullKey(key); CommandArgs args = new CommandArgs<>(codec).addKey(key).add(iterator); - return createCommand(CF_SCANDUMP, new EncodedComplexOutput<>(codec, CfScanDumpValueParser.INSTANCE), args); + return createCommand(CF_SCANDUMP, new EncodedComplexOutput<>(codec, ScanDumpValueParser.INSTANCE), args); } Command cfLoadChunk(K key, long iterator, byte[] data) { diff --git a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java index adbdfbb8c5..ae4c09a1de 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java @@ -9,7 +9,7 @@ import java.util.List; import io.lettuce.core.RedisFuture; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; @@ -165,8 +165,8 @@ public interface RedisBloomFilterAsyncCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - RedisFuture bfScanDump(K key, long iterator); + RedisFuture bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java index ad7c5cc6bc..9459130850 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -9,7 +9,7 @@ import java.util.List; import io.lettuce.core.RedisFuture; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; @@ -200,9 +200,9 @@ public interface RedisCuckooFilterAsyncCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - RedisFuture cfScanDump(K key, long cursor); + RedisFuture cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java index dc640e95ba..5d4cb749de 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java @@ -8,7 +8,7 @@ import io.lettuce.core.Value; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; import reactor.core.publisher.Flux; @@ -165,8 +165,8 @@ public interface RedisBloomFilterReactiveCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Mono bfScanDump(K key, long iterator); + Mono bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java index 55bdad69b3..6892224c22 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -7,7 +7,7 @@ package io.lettuce.core.api.reactive; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; import reactor.core.publisher.Flux; @@ -195,9 +195,9 @@ public interface RedisCuckooFilterReactiveCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Mono cfScanDump(K key, long cursor); + Mono cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java index b92493a572..69379f08d1 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; @@ -164,8 +164,8 @@ public interface RedisBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - BfScanDumpValue bfScanDump(K key, long iterator); + ScanDumpValue bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java index fab5ccc04e..7aa3e635e7 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; @@ -198,9 +198,9 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - CfScanDumpValue cfScanDump(K key, long cursor); + ScanDumpValue cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java b/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java deleted file mode 100644 index 65c9e1bda4..0000000000 --- a/src/main/java/io/lettuce/core/cf/CfScanDumpValue.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.cf; - -/** - * Value object for the Redis CF.SCANDUMP command. - * - * @author HwangRock - * @since 7.7 - */ -public class CfScanDumpValue { - - private final long iterator; - - private final byte[] data; - - public CfScanDumpValue(long iterator, byte[] data) { - this.iterator = iterator; - this.data = data; - } - - /** - * Returns the iterator value. - * - * @return the iterator value - */ - public long getIterator() { - return iterator; - } - - /** - * Returns the data. - * - * @return the data - */ - public byte[] getData() { - return data; - } - -} diff --git a/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java b/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java deleted file mode 100644 index 49704aaabf..0000000000 --- a/src/main/java/io/lettuce/core/cf/CfScanDumpValueParser.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2026-Present, Redis Ltd. - * All rights reserved. - * - * SPDX-License-Identifier: MIT - */ -package io.lettuce.core.cf; - -import java.nio.ByteBuffer; -import java.util.List; - -import io.lettuce.core.output.ComplexData; -import io.lettuce.core.output.ComplexDataParser; - -/** - * Parser for Redis CF.SCANDUMP command output. - * - * @author HwangRock - * @since 7.7 - */ -public final class CfScanDumpValueParser implements ComplexDataParser { - - public static final CfScanDumpValueParser INSTANCE = new CfScanDumpValueParser(); - - private CfScanDumpValueParser() { - } - - @Override - public CfScanDumpValue parse(ComplexData data) { - if (data == null) { - throw new IllegalArgumentException("Failed parsing CF.SCANDUMP: data must not be null"); - } - List raw = data.getDynamicList(); - if (raw == null || raw.size() != 2) { - throw new IllegalArgumentException("Failed parsing CF.SCANDUMP: data must be a list of two elements"); - } - long iterator = ((Number) raw.get(0)).longValue(); - ByteBuffer dataByteBuffer = (ByteBuffer) raw.get(1); - byte[] dataBytes; - if (dataByteBuffer == null) { - dataBytes = new byte[0]; - } else { - dataBytes = new byte[dataByteBuffer.remaining()]; - dataByteBuffer.get(dataBytes); - } - return new CfScanDumpValue(iterator, dataBytes); - } - -} diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java index 64380e9d5b..3da50ca909 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; @@ -164,8 +164,8 @@ public interface NodeSelectionBloomFilterAsyncCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - AsyncExecutions bfScanDump(K key, long iterator); + AsyncExecutions bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java index cd548a5dd9..38b4fca2a2 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; @@ -199,9 +199,9 @@ public interface NodeSelectionCuckooFilterAsyncCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - AsyncExecutions cfScanDump(K key, long cursor); + AsyncExecutions cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java index 9aa2101890..f00b63a342 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; @@ -164,8 +164,8 @@ public interface NodeSelectionBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Executions bfScanDump(K key, long iterator); + Executions bfScanDump(K key, long iterator); } diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java index 2b86239dc2..06b4bebd2c 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -8,7 +8,7 @@ import java.util.List; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; @@ -198,9 +198,9 @@ public interface NodeSelectionCuckooFilterCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - Executions cfScanDump(K key, long cursor); + Executions cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/java/io/lettuce/core/bf/BfScanDumpValue.java b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java similarity index 61% rename from src/main/java/io/lettuce/core/bf/BfScanDumpValue.java rename to src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java index a7205b822e..65140ca094 100644 --- a/src/main/java/io/lettuce/core/bf/BfScanDumpValue.java +++ b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValue.java @@ -4,21 +4,23 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; /** - * Value object for the Redis BF.SCANDUMP command. + * Value object for the scan-dump commands of the RedisBloom module, such as + * BF.SCANDUMP and + * CF.SCANDUMP. * * @author Yordan Tsintsov * @since 7.7 */ -public class BfScanDumpValue { +public class ScanDumpValue { private final long iterator; private final byte[] data; - public BfScanDumpValue(long iterator, byte[] data) { + public ScanDumpValue(long iterator, byte[] data) { this.iterator = iterator; this.data = data; } diff --git a/src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java similarity index 51% rename from src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java rename to src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java index d03f46faf7..e5277876b3 100644 --- a/src/main/java/io/lettuce/core/bf/BfScanDumpValueParser.java +++ b/src/main/java/io/lettuce/core/probabilistic/ScanDumpValueParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.nio.ByteBuffer; import java.util.List; @@ -13,26 +13,28 @@ import io.lettuce.core.output.ComplexDataParser; /** - * Parser for Redis BF.SCANDUMP command output. + * Parser for the scan-dump command output of the RedisBloom module, such as + * BF.SCANDUMP and + * CF.SCANDUMP. * * @author Yordan Tsintsov * @since 7.7 */ -public final class BfScanDumpValueParser implements ComplexDataParser { +public final class ScanDumpValueParser implements ComplexDataParser { - public static final BfScanDumpValueParser INSTANCE = new BfScanDumpValueParser(); + public static final ScanDumpValueParser INSTANCE = new ScanDumpValueParser(); - private BfScanDumpValueParser() { + private ScanDumpValueParser() { } @Override - public BfScanDumpValue parse(ComplexData data) { + public ScanDumpValue parse(ComplexData data) { if (data == null) { - throw new IllegalArgumentException("Failed parsing BF.SCANDUMP: data must not be null"); + throw new IllegalArgumentException("Failed parsing SCANDUMP: data must not be null"); } List raw = data.getDynamicList(); if (raw == null || raw.size() != 2) { - throw new IllegalArgumentException("Failed parsing BF.SCANDUMP: data must be a list of two elements"); + throw new IllegalArgumentException("Failed parsing SCANDUMP: data must be a list of two elements"); } long iterator = ((Number) raw.get(0)).longValue(); ByteBuffer dataByteBuffer = (ByteBuffer) raw.get(1); @@ -43,7 +45,7 @@ public BfScanDumpValue parse(ComplexData data) { dataBytes = new byte[dataByteBuffer.remaining()]; dataByteBuffer.get(dataBytes); } - return new BfScanDumpValue(iterator, dataBytes); + return new ScanDumpValue(iterator, dataBytes); } } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt index 6616121639..d20839b282 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt @@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow import io.lettuce.core.bf.BfInfoValue import io.lettuce.core.bf.arguments.BfInsertArgs import io.lettuce.core.bf.arguments.BfReserveArgs -import io.lettuce.core.bf.BfScanDumpValue +import io.lettuce.core.probabilistic.ScanDumpValue /** * Coroutine executed commands for Bloom Filter. @@ -166,9 +166,9 @@ interface RedisBloomFilterCoroutinesCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - suspend fun bfScanDump(key: K, iterator: Long): BfScanDumpValue? + suspend fun bfScanDump(key: K, iterator: Long): ScanDumpValue? } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt index 26de7a8fa4..5fac1ca3dd 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt @@ -9,7 +9,7 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.api.reactive.RedisBloomFilterReactiveCommands import io.lettuce.core.bf.BfInfoValue -import io.lettuce.core.bf.BfScanDumpValue +import io.lettuce.core.probabilistic.ScanDumpValue import io.lettuce.core.bf.arguments.BfInsertArgs import io.lettuce.core.bf.arguments.BfReserveArgs import kotlinx.coroutines.flow.toList @@ -77,7 +77,7 @@ internal class RedisBloomFilterCoroutinesCommandsImpl( ): String? = ops.bfReserve(key, errorRate, capacity, reserveArgs).awaitFirstOrNull() - override suspend fun bfScanDump(key: K, iterator: Long): BfScanDumpValue? = + override suspend fun bfScanDump(key: K, iterator: Long): ScanDumpValue? = ops.bfScanDump(key, iterator).awaitFirstOrNull() } diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt index 7695b1a99f..1f61168741 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -8,7 +8,7 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.cf.CfInfoValue -import io.lettuce.core.cf.CfScanDumpValue +import io.lettuce.core.probabilistic.ScanDumpValue import io.lettuce.core.cf.arguments.CfInsertArgs import io.lettuce.core.cf.arguments.CfReserveArgs @@ -192,9 +192,9 @@ interface RedisCuckooFilterCoroutinesCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - suspend fun cfScanDump(key: K, cursor: Long): CfScanDumpValue? + suspend fun cfScanDump(key: K, cursor: Long): ScanDumpValue? /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt index b7653119cf..24fad19ecb 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -9,7 +9,7 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.api.reactive.RedisCuckooFilterReactiveCommands import io.lettuce.core.cf.CfInfoValue -import io.lettuce.core.cf.CfScanDumpValue +import io.lettuce.core.probabilistic.ScanDumpValue import io.lettuce.core.cf.arguments.CfInsertArgs import io.lettuce.core.cf.arguments.CfReserveArgs import kotlinx.coroutines.flow.toList @@ -77,7 +77,7 @@ internal class RedisCuckooFilterCoroutinesCommandsImpl( override suspend fun cfCount(key: K, value: V): Long? = ops.cfCount(key, value).awaitFirstOrNull() - override suspend fun cfScanDump(key: K, cursor: Long): CfScanDumpValue? = + override suspend fun cfScanDump(key: K, cursor: Long): ScanDumpValue? = ops.cfScanDump(key, cursor).awaitFirstOrNull() override suspend fun cfLoadChunk(key: K, cursor: Long, data: ByteArray): String? = diff --git a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java index fc7a94b890..dc89a251a2 100644 --- a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java @@ -11,7 +11,7 @@ import io.lettuce.core.bf.BfInfoValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; /** * ${intent} for Bloom Filter. @@ -164,8 +164,8 @@ public interface RedisBloomFilterCommands { * * @param key the key. * @param iterator the iterator. - * @return BfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - BfScanDumpValue bfScanDump(K key, long iterator); + ScanDumpValue bfScanDump(K key, long iterator); } diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java index 26fc82e11b..9df9c95bc8 100644 --- a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -9,7 +9,7 @@ import java.util.List; import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; @@ -198,9 +198,9 @@ public interface RedisCuckooFilterCommands { * * @param key the key. * @param cursor the cursor. - * @return CfScanDumpValue the scan dump value. + * @return ScanDumpValue the scan dump value. */ - CfScanDumpValue cfScanDump(K key, long cursor); + ScanDumpValue cfScanDump(K key, long cursor); /** * Restores a Cuckoo filter previously saved using CF.SCANDUMP. diff --git a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java index 1ca580c29d..6eebef462b 100644 --- a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java @@ -7,7 +7,7 @@ package io.lettuce.core; import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfScanDumpValue; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; import io.lettuce.core.codec.StringCodec; @@ -179,7 +179,7 @@ void shouldCorrectlyConstructBfReserveCommandWithArgs() { @Test void shouldCorrectlyConstructBfScanDumpCommand() { - Command command = builder.bfScanDump(MY_KEY, 0); + Command command = builder.bfScanDump(MY_KEY, 0); ByteBuf buff = Unpooled.buffer(); command.encode(buff); diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java b/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java index 2a3a662081..29751e9941 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java @@ -13,6 +13,7 @@ import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.bf.arguments.BfInsertArgs; import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; import org.junit.jupiter.api.*; @@ -212,7 +213,7 @@ void bfScanDumpAndLoadChunk() { long iterator = 0; while (true) { - BfScanDumpValue chunkData = redis.bfScanDump("bloom-dump", iterator); + ScanDumpValue chunkData = redis.bfScanDump("bloom-dump", iterator); iterator = chunkData.getIterator(); if (iterator == 0L) { break; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index e67705a0c6..a9ba72cde3 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -12,6 +12,7 @@ import io.lettuce.core.api.sync.RedisCommands; import io.lettuce.core.cf.arguments.CfInsertArgs; import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; import org.junit.jupiter.api.*; @@ -222,7 +223,7 @@ void cfScanDumpAndLoadChunk() { long cursor = 0; while (true) { - CfScanDumpValue chunkData = redis.cfScanDump("cuckoo-dump", cursor); + ScanDumpValue chunkData = redis.cfScanDump("cuckoo-dump", cursor); cursor = chunkData.getIterator(); if (cursor == 0L) { break; From 0177e434d9c4b230ed1685399b84602ced10c45e Mon Sep 17 00:00:00 2001 From: HwangRock Date: Wed, 10 Jun 2026 22:20:36 +0900 Subject: [PATCH 09/10] Move the probabilistic value and argument types into one package 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. --- .../io/lettuce/core/AbstractRedisAsyncCommands.java | 12 ++++++------ .../lettuce/core/AbstractRedisReactiveCommands.java | 12 ++++++------ .../lettuce/core/RedisBloomFilterCommandBuilder.java | 8 ++++---- .../core/RedisCuckooFilterCommandBuilder.java | 8 ++++---- .../api/async/RedisBloomFilterAsyncCommands.java | 6 +++--- .../api/async/RedisCuckooFilterAsyncCommands.java | 6 +++--- .../reactive/RedisBloomFilterReactiveCommands.java | 6 +++--- .../reactive/RedisCuckooFilterReactiveCommands.java | 6 +++--- .../core/api/sync/RedisBloomFilterCommands.java | 6 +++--- .../core/api/sync/RedisCuckooFilterCommands.java | 6 +++--- .../async/NodeSelectionBloomFilterAsyncCommands.java | 6 +++--- .../NodeSelectionCuckooFilterAsyncCommands.java | 6 +++--- .../api/sync/NodeSelectionBloomFilterCommands.java | 6 +++--- .../api/sync/NodeSelectionCuckooFilterCommands.java | 6 +++--- .../core/{bf => probabilistic}/BfInfoValue.java | 2 +- .../{bf => probabilistic}/BfInfoValueParser.java | 2 +- .../core/{cf => probabilistic}/CfInfoValue.java | 2 +- .../{cf => probabilistic}/CfInfoValueParser.java | 2 +- .../arguments/BfInsertArgs.java | 2 +- .../arguments/BfReserveArgs.java | 2 +- .../arguments/CfInsertArgs.java | 2 +- .../arguments/CfReserveArgs.java | 2 +- .../coroutines/RedisBloomFilterCoroutinesCommands.kt | 6 +++--- .../RedisBloomFilterCoroutinesCommandsImpl.kt | 6 +++--- .../RedisCuckooFilterCoroutinesCommands.kt | 6 +++--- .../RedisCuckooFilterCoroutinesCommandsImpl.kt | 6 +++--- .../lettuce/core/api/RedisBloomFilterCommands.java | 6 +++--- .../lettuce/core/api/RedisCuckooFilterCommands.java | 6 +++--- .../RedisBloomFilterCommandBuilderUnitTests.java | 6 +++--- .../RedisCuckooFilterCommandBuilderUnitTests.java | 4 ++-- .../core/bf/RedisBloomFilterIntegrationTests.java | 5 +++-- .../bf/RedisBloomFilterReactiveIntegrationTests.java | 4 ++-- .../lettuce/core/cf/CfInfoValueParserUnitTests.java | 2 ++ .../core/cf/RedisCuckooFilterIntegrationTests.java | 5 +++-- .../RedisCuckooFilterReactiveIntegrationTests.java | 4 ++-- 35 files changed, 93 insertions(+), 89 deletions(-) rename src/main/java/io/lettuce/core/{bf => probabilistic}/BfInfoValue.java (98%) rename src/main/java/io/lettuce/core/{bf => probabilistic}/BfInfoValueParser.java (96%) rename src/main/java/io/lettuce/core/{cf => probabilistic}/CfInfoValue.java (98%) rename src/main/java/io/lettuce/core/{cf => probabilistic}/CfInfoValueParser.java (96%) rename src/main/java/io/lettuce/core/{bf => probabilistic}/arguments/BfInsertArgs.java (98%) rename src/main/java/io/lettuce/core/{bf => probabilistic}/arguments/BfReserveArgs.java (97%) rename src/main/java/io/lettuce/core/{cf => probabilistic}/arguments/CfInsertArgs.java (98%) rename src/main/java/io/lettuce/core/{cf => probabilistic}/arguments/CfReserveArgs.java (98%) diff --git a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java index 732de95cc8..4a3a9a4fc2 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisAsyncCommands.java @@ -23,13 +23,13 @@ import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.async.*; import io.lettuce.core.array.*; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.cluster.PipelinedRedisFuture; import io.lettuce.core.cluster.api.async.RedisClusterAsyncCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; diff --git a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java index e508dbf766..c80468471e 100644 --- a/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java +++ b/src/main/java/io/lettuce/core/AbstractRedisReactiveCommands.java @@ -23,13 +23,13 @@ import io.lettuce.core.api.StatefulConnection; import io.lettuce.core.api.reactive.*; import io.lettuce.core.array.*; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.cluster.api.reactive.RedisClusterReactiveCommands; import io.lettuce.core.cluster.models.partitions.ClusterPartitionParser; import io.lettuce.core.cluster.models.partitions.RedisClusterNode; diff --git a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java index 31495e77c5..79e6d47240 100644 --- a/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisBloomFilterCommandBuilder.java @@ -8,12 +8,12 @@ import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.BfInfoValueParser; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValueParser; import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.probabilistic.ScanDumpValueParser; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.output.*; import io.lettuce.core.protocol.BaseRedisCommandBuilder; diff --git a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java index 0b4db58896..1d90ec80f9 100644 --- a/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java +++ b/src/main/java/io/lettuce/core/RedisCuckooFilterCommandBuilder.java @@ -8,12 +8,12 @@ import java.util.List; -import io.lettuce.core.cf.CfInfoValue; -import io.lettuce.core.cf.CfInfoValueParser; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValueParser; import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.probabilistic.ScanDumpValueParser; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.codec.RedisCodec; import io.lettuce.core.output.*; import io.lettuce.core.protocol.BaseRedisCommandBuilder; diff --git a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java index ae4c09a1de..7397aa113b 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisBloomFilterAsyncCommands.java @@ -8,10 +8,10 @@ import java.util.List; import io.lettuce.core.RedisFuture; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Asynchronous executed commands for Bloom Filter. diff --git a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java index 9459130850..9dd383888c 100644 --- a/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/api/async/RedisCuckooFilterAsyncCommands.java @@ -8,10 +8,10 @@ import java.util.List; import io.lettuce.core.RedisFuture; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; /** * Asynchronous executed commands for Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java index 5d4cb749de..01c5919583 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisBloomFilterReactiveCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.api.reactive; import io.lettuce.core.Value; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; diff --git a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java index 6892224c22..40b4911dde 100644 --- a/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java +++ b/src/main/java/io/lettuce/core/api/reactive/RedisCuckooFilterReactiveCommands.java @@ -6,10 +6,10 @@ */ package io.lettuce.core.api.reactive; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; diff --git a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java index 69379f08d1..0c23419b9b 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisBloomFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.api.sync; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Synchronous executed commands for Bloom Filter. diff --git a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java index 7aa3e635e7..dbbf3a3ee5 100644 --- a/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/api/sync/RedisCuckooFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.api.sync; import java.util.List; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; /** * Synchronous executed commands for Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java index 3da50ca909..69e453fd9a 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionBloomFilterAsyncCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.async; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Asynchronous executed commands on a node selection for Bloom Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java index 38b4fca2a2..7fde717895 100644 --- a/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/async/NodeSelectionCuckooFilterAsyncCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.async; import java.util.List; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; /** * Asynchronous executed commands on a node selection for Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java index f00b63a342..22d026db3f 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionBloomFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.sync; import java.util.List; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; /** * Synchronous executed commands on a node selection for Bloom Filter. diff --git a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java index 06b4bebd2c..7080f147aa 100644 --- a/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java +++ b/src/main/java/io/lettuce/core/cluster/api/sync/NodeSelectionCuckooFilterCommands.java @@ -7,10 +7,10 @@ package io.lettuce.core.cluster.api.sync; import java.util.List; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; /** * Synchronous executed commands on a node selection for Cuckoo Filter. diff --git a/src/main/java/io/lettuce/core/bf/BfInfoValue.java b/src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java similarity index 98% rename from src/main/java/io/lettuce/core/bf/BfInfoValue.java rename to src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java index 8b121961fa..742ad9a095 100644 --- a/src/main/java/io/lettuce/core/bf/BfInfoValue.java +++ b/src/main/java/io/lettuce/core/probabilistic/BfInfoValue.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.util.Map; diff --git a/src/main/java/io/lettuce/core/bf/BfInfoValueParser.java b/src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java similarity index 96% rename from src/main/java/io/lettuce/core/bf/BfInfoValueParser.java rename to src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java index 57affe8027..e4fd2b5614 100644 --- a/src/main/java/io/lettuce/core/bf/BfInfoValueParser.java +++ b/src/main/java/io/lettuce/core/probabilistic/BfInfoValueParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import java.nio.ByteBuffer; import java.util.LinkedHashMap; diff --git a/src/main/java/io/lettuce/core/cf/CfInfoValue.java b/src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java similarity index 98% rename from src/main/java/io/lettuce/core/cf/CfInfoValue.java rename to src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java index 181ce7e30e..410217e698 100644 --- a/src/main/java/io/lettuce/core/cf/CfInfoValue.java +++ b/src/main/java/io/lettuce/core/probabilistic/CfInfoValue.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import java.util.Map; diff --git a/src/main/java/io/lettuce/core/cf/CfInfoValueParser.java b/src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java similarity index 96% rename from src/main/java/io/lettuce/core/cf/CfInfoValueParser.java rename to src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java index 96b53aa287..7098a69daa 100644 --- a/src/main/java/io/lettuce/core/cf/CfInfoValueParser.java +++ b/src/main/java/io/lettuce/core/probabilistic/CfInfoValueParser.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import java.nio.ByteBuffer; import java.util.LinkedHashMap; diff --git a/src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java similarity index 98% rename from src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java index 527afab229..85fb1f6da5 100644 --- a/src/main/java/io/lettuce/core/bf/arguments/BfInsertArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/BfInsertArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java similarity index 97% rename from src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java index df7e3105ea..b62a86997b 100644 --- a/src/main/java/io/lettuce/core/bf/arguments/BfReserveArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/BfReserveArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java similarity index 98% rename from src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java index c263157b4e..5adaa1120f 100644 --- a/src/main/java/io/lettuce/core/cf/arguments/CfInsertArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/CfInsertArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java b/src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java similarity index 98% rename from src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java rename to src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java index 7dc79fa184..556a2d72d3 100644 --- a/src/main/java/io/lettuce/core/cf/arguments/CfReserveArgs.java +++ b/src/main/java/io/lettuce/core/probabilistic/arguments/CfReserveArgs.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf.arguments; +package io.lettuce.core.probabilistic.arguments; import io.lettuce.core.CompositeArgument; import io.lettuce.core.protocol.CommandArgs; diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt index d20839b282..3c68bbb5d1 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommands.kt @@ -8,9 +8,9 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import kotlinx.coroutines.flow.Flow -import io.lettuce.core.bf.BfInfoValue -import io.lettuce.core.bf.arguments.BfInsertArgs -import io.lettuce.core.bf.arguments.BfReserveArgs +import io.lettuce.core.probabilistic.BfInfoValue +import io.lettuce.core.probabilistic.arguments.BfInsertArgs +import io.lettuce.core.probabilistic.arguments.BfReserveArgs import io.lettuce.core.probabilistic.ScanDumpValue /** diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt index 5fac1ca3dd..35302b2d84 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisBloomFilterCoroutinesCommandsImpl.kt @@ -8,10 +8,10 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.api.reactive.RedisBloomFilterReactiveCommands -import io.lettuce.core.bf.BfInfoValue +import io.lettuce.core.probabilistic.BfInfoValue import io.lettuce.core.probabilistic.ScanDumpValue -import io.lettuce.core.bf.arguments.BfInsertArgs -import io.lettuce.core.bf.arguments.BfReserveArgs +import io.lettuce.core.probabilistic.arguments.BfInsertArgs +import io.lettuce.core.probabilistic.arguments.BfReserveArgs import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.reactive.awaitFirstOrNull diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt index 1f61168741..9d31145dc7 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommands.kt @@ -7,10 +7,10 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi -import io.lettuce.core.cf.CfInfoValue +import io.lettuce.core.probabilistic.CfInfoValue import io.lettuce.core.probabilistic.ScanDumpValue -import io.lettuce.core.cf.arguments.CfInsertArgs -import io.lettuce.core.cf.arguments.CfReserveArgs +import io.lettuce.core.probabilistic.arguments.CfInsertArgs +import io.lettuce.core.probabilistic.arguments.CfReserveArgs /** * Coroutine executed commands for Cuckoo Filter. diff --git a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt index 24fad19ecb..0832504c1a 100644 --- a/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt +++ b/src/main/kotlin/io/lettuce/core/api/coroutines/RedisCuckooFilterCoroutinesCommandsImpl.kt @@ -8,10 +8,10 @@ package io.lettuce.core.api.coroutines import io.lettuce.core.ExperimentalLettuceCoroutinesApi import io.lettuce.core.api.reactive.RedisCuckooFilterReactiveCommands -import io.lettuce.core.cf.CfInfoValue +import io.lettuce.core.probabilistic.CfInfoValue import io.lettuce.core.probabilistic.ScanDumpValue -import io.lettuce.core.cf.arguments.CfInsertArgs -import io.lettuce.core.cf.arguments.CfReserveArgs +import io.lettuce.core.probabilistic.arguments.CfInsertArgs +import io.lettuce.core.probabilistic.arguments.CfReserveArgs import kotlinx.coroutines.flow.toList import kotlinx.coroutines.reactive.asFlow import kotlinx.coroutines.reactive.awaitFirstOrNull diff --git a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java index dc89a251a2..21c21f754f 100644 --- a/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisBloomFilterCommands.java @@ -8,9 +8,9 @@ import java.util.List; -import io.lettuce.core.bf.BfInfoValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.core.probabilistic.ScanDumpValue; /** diff --git a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java index 9df9c95bc8..fbeafb49b6 100644 --- a/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java +++ b/src/main/templates/io/lettuce/core/api/RedisCuckooFilterCommands.java @@ -8,10 +8,10 @@ import java.util.List; -import io.lettuce.core.cf.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; /** * ${intent} for Cuckoo Filter. diff --git a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java index 6eebef462b..6c6791a272 100644 --- a/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisBloomFilterCommandBuilderUnitTests.java @@ -6,10 +6,10 @@ */ package io.lettuce.core; -import io.lettuce.core.bf.BfInfoValue; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.core.codec.StringCodec; import io.lettuce.core.protocol.Command; import io.netty.buffer.ByteBuf; diff --git a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java index 2e53918d82..56932a5b53 100644 --- a/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java +++ b/src/test/java/io/lettuce/core/RedisCuckooFilterCommandBuilderUnitTests.java @@ -6,8 +6,8 @@ */ package io.lettuce.core; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.core.codec.StringCodec; import io.lettuce.core.protocol.Command; import io.netty.buffer.ByteBuf; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java b/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java index 29751e9941..75724088bf 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java @@ -11,9 +11,10 @@ import io.lettuce.core.RedisCommandExecutionException; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.BfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; import org.junit.jupiter.api.*; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java index a8661f8595..0e03f705ac 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java @@ -11,8 +11,8 @@ import io.lettuce.core.Value; import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.reactive.RedisReactiveCommands; -import io.lettuce.core.bf.arguments.BfInsertArgs; -import io.lettuce.core.bf.arguments.BfReserveArgs; +import io.lettuce.core.probabilistic.arguments.BfInsertArgs; +import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.test.ReactiveSyncInvocationHandler; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; diff --git a/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java b/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java index 76e228190e..e079075a3f 100644 --- a/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java +++ b/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java @@ -15,6 +15,8 @@ import org.junit.jupiter.api.Test; import io.lettuce.core.output.ComplexData; +import io.lettuce.core.probabilistic.CfInfoValue; +import io.lettuce.core.probabilistic.CfInfoValueParser; /** * Unit tests for {@link CfInfoValueParser}. diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java index a9ba72cde3..a861f87d15 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java @@ -10,9 +10,10 @@ import java.util.List; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.CfInfoValue; import io.lettuce.core.probabilistic.ScanDumpValue; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.test.LettuceExtension; import io.lettuce.test.condition.EnabledOnCommand; import org.junit.jupiter.api.*; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java index 3efbe818e5..770d2ce707 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java @@ -11,8 +11,8 @@ import io.lettuce.core.api.StatefulRedisConnection; import io.lettuce.core.api.reactive.RedisReactiveCommands; -import io.lettuce.core.cf.arguments.CfInsertArgs; -import io.lettuce.core.cf.arguments.CfReserveArgs; +import io.lettuce.core.probabilistic.arguments.CfInsertArgs; +import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.test.ReactiveSyncInvocationHandler; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; From 666ae3c4a88d6844544e58c3da96d8e83deaa469 Mon Sep 17 00:00:00 2001 From: HwangRock Date: Wed, 10 Jun 2026 22:45:11 +0900 Subject: [PATCH 10/10] Move the BF and CF tests into the probabilistic package 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. --- .../{cf => probabilistic}/CfInfoValueParserUnitTests.java | 4 +--- .../RedisBloomFilterClusterIntegrationTests.java | 2 +- .../RedisBloomFilterIntegrationTests.java | 4 +--- .../RedisBloomFilterReactiveIntegrationTests.java | 2 +- .../RedisBloomFilterResp2IntegrationTests.java | 2 +- .../RedisCuckooFilterClusterIntegrationTests.java | 2 +- .../RedisCuckooFilterIntegrationTests.java | 4 +--- .../RedisCuckooFilterReactiveIntegrationTests.java | 2 +- .../RedisCuckooFilterResp2IntegrationTests.java | 2 +- 9 files changed, 9 insertions(+), 15 deletions(-) rename src/test/java/io/lettuce/core/{cf => probabilistic}/CfInfoValueParserUnitTests.java (97%) rename src/test/java/io/lettuce/core/{bf => probabilistic}/RedisBloomFilterClusterIntegrationTests.java (95%) rename src/test/java/io/lettuce/core/{bf => probabilistic}/RedisBloomFilterIntegrationTests.java (98%) rename src/test/java/io/lettuce/core/{bf => probabilistic}/RedisBloomFilterReactiveIntegrationTests.java (98%) rename src/test/java/io/lettuce/core/{bf => probabilistic}/RedisBloomFilterResp2IntegrationTests.java (96%) rename src/test/java/io/lettuce/core/{cf => probabilistic}/RedisCuckooFilterClusterIntegrationTests.java (95%) rename src/test/java/io/lettuce/core/{cf => probabilistic}/RedisCuckooFilterIntegrationTests.java (98%) rename src/test/java/io/lettuce/core/{cf => probabilistic}/RedisCuckooFilterReactiveIntegrationTests.java (98%) rename src/test/java/io/lettuce/core/{cf => probabilistic}/RedisCuckooFilterResp2IntegrationTests.java (97%) diff --git a/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java b/src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java similarity index 97% rename from src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java rename to src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java index e079075a3f..86b949254e 100644 --- a/src/test/java/io/lettuce/core/cf/CfInfoValueParserUnitTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/CfInfoValueParserUnitTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -15,8 +15,6 @@ import org.junit.jupiter.api.Test; import io.lettuce.core.output.ComplexData; -import io.lettuce.core.probabilistic.CfInfoValue; -import io.lettuce.core.probabilistic.CfInfoValueParser; /** * Unit tests for {@link CfInfoValueParser}. diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java similarity index 95% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java index efc95a7e7a..c3ffd28d74 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterClusterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterClusterIntegrationTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java similarity index 98% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java index 75724088bf..6868898245 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterIntegrationTests.java @@ -4,15 +4,13 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; import java.util.List; import io.lettuce.core.RedisCommandExecutionException; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.probabilistic.BfInfoValue; -import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.probabilistic.arguments.BfInsertArgs; import io.lettuce.core.probabilistic.arguments.BfReserveArgs; import io.lettuce.test.LettuceExtension; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java similarity index 98% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java index 0e03f705ac..67169faf49 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterReactiveIntegrationTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; diff --git a/src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java similarity index 96% rename from src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java index 6e140eefd2..c1522de45d 100644 --- a/src/test/java/io/lettuce/core/bf/RedisBloomFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisBloomFilterResp2IntegrationTests.java @@ -4,7 +4,7 @@ * * Licensed under the MIT License. */ -package io.lettuce.core.bf; +package io.lettuce.core.probabilistic; import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java similarity index 95% rename from src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java index 0216fdffc6..5c0078e9c8 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterClusterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterClusterIntegrationTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java similarity index 98% rename from src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java index a861f87d15..30ba741957 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterIntegrationTests.java @@ -4,14 +4,12 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; import java.util.List; import io.lettuce.core.api.sync.RedisCommands; -import io.lettuce.core.probabilistic.CfInfoValue; -import io.lettuce.core.probabilistic.ScanDumpValue; import io.lettuce.core.probabilistic.arguments.CfInsertArgs; import io.lettuce.core.probabilistic.arguments.CfReserveArgs; import io.lettuce.test.LettuceExtension; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java similarity index 98% rename from src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java index 770d2ce707..a423d4347f 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterReactiveIntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterReactiveIntegrationTests.java @@ -4,7 +4,7 @@ * * SPDX-License-Identifier: MIT */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import javax.inject.Inject; import java.util.List; diff --git a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java similarity index 97% rename from src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java rename to src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java index dd865fc7a0..dbaff1b58f 100644 --- a/src/test/java/io/lettuce/core/cf/RedisCuckooFilterResp2IntegrationTests.java +++ b/src/test/java/io/lettuce/core/probabilistic/RedisCuckooFilterResp2IntegrationTests.java @@ -4,7 +4,7 @@ * * Licensed under the MIT License. */ -package io.lettuce.core.cf; +package io.lettuce.core.probabilistic; import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisClient;