From fa940b92b1a6f4f96f891a202396c10162effb2d Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 12:03:45 +0200 Subject: [PATCH 1/6] Add initial impl of CAS/CAD --- .../redis/clients/jedis/CommandObjects.java | 55 ++++++++++++++++ src/main/java/redis/clients/jedis/Jedis.java | 62 +++++++++++++++++++ .../java/redis/clients/jedis/Protocol.java | 4 +- .../redis/clients/jedis/UnifiedJedis.java | 52 +++++++++++++++- .../jedis/commands/KeyBinaryCommands.java | 12 ++++ .../clients/jedis/commands/KeyCommands.java | 16 +++++ .../jedis/commands/StringBinaryCommands.java | 19 ++++++ .../jedis/commands/StringCommands.java | 22 +++++++ .../jedis/conditions/ValueCondition.java | 53 ++++++++++++++++ 9 files changed, 292 insertions(+), 3 deletions(-) create mode 100644 src/main/java/redis/clients/jedis/conditions/ValueCondition.java diff --git a/src/main/java/redis/clients/jedis/CommandObjects.java b/src/main/java/redis/clients/jedis/CommandObjects.java index 1d0173ffff..6b57d8b4b0 100644 --- a/src/main/java/redis/clients/jedis/CommandObjects.java +++ b/src/main/java/redis/clients/jedis/CommandObjects.java @@ -32,6 +32,7 @@ import redis.clients.jedis.timeseries.*; import redis.clients.jedis.timeseries.TimeSeriesProtocol.*; import redis.clients.jedis.util.KeyValue; +import redis.clients.jedis.conditions.ValueCondition; public class CommandObjects { @@ -346,6 +347,26 @@ public final CommandObject del(byte[]... keys) { return new CommandObject<>(commandArguments(DEL).keys((Object[]) keys), BuilderFactory.LONG); } + public final CommandObject delex(String key) { + return new CommandObject<>(commandArguments(Command.DELEX).key(key), BuilderFactory.LONG); + } + + public final CommandObject delex(String key, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.DELEX).key(key); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.LONG); + } + + public final CommandObject delex(byte[] key) { + return new CommandObject<>(commandArguments(Command.DELEX).key(key), BuilderFactory.LONG); + } + + public final CommandObject delex(byte[] key, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.DELEX).key(key); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.LONG); + } + public final CommandObject unlink(String key) { return new CommandObject<>(commandArguments(UNLINK).key(key), BuilderFactory.LONG); } @@ -454,6 +475,32 @@ public final CommandObject set(byte[] key, byte[] value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value), BuilderFactory.STRING); } + public final CommandObject set(String key, String value, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject setGet(String key, String value, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); + cond.addTo(ca); + ca.add(Keyword.GET); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject set(byte[] key, byte[] value, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject setGet(byte[] key, byte[] value, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); + cond.addTo(ca); + ca.add(Keyword.GET); + return new CommandObject<>(ca, BuilderFactory.BINARY); + } + public final CommandObject set(byte[] key, byte[] value, SetParams params) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params), BuilderFactory.STRING); } @@ -462,6 +509,10 @@ public final CommandObject get(String key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.STRING); } + public final CommandObject digest(String key) { + return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.STRING); + } + public final CommandObject setGet(String key, String value) { return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).add(Keyword.GET), BuilderFactory.STRING); } @@ -479,6 +530,10 @@ public final CommandObject getEx(String key, GetExParams params) { return new CommandObject<>(commandArguments(Command.GETEX).key(key).addParams(params), BuilderFactory.STRING); } + public final CommandObject digest(byte[] key) { + return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.BINARY); + } + public final CommandObject get(byte[] key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.BINARY); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 1dc47145e3..4dfc81f78c 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -21,8 +21,10 @@ import javax.net.ssl.SSLSocketFactory; import redis.clients.jedis.Protocol.*; +import redis.clients.jedis.annots.Experimental; import redis.clients.jedis.args.*; import redis.clients.jedis.commands.*; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.exceptions.InvalidURIException; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.exceptions.JedisException; @@ -490,6 +492,18 @@ public String set(final byte[] key, final byte[] value, final SetParams params) return connection.executeCommand(commandObjects.set(key, value, params)); } + @Override + public String set(final byte[] key, final byte[] value, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.set(key, value, condition)); + } + + @Override + public byte[] setGet(final byte[] key, final byte[] value, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.setGet(key, value, condition)); + } + /** * Get the value of the specified key. If the key does not exist the special value 'nil' is * returned. If the value stored at key is not a string an error is returned because GET can only @@ -505,6 +519,12 @@ public byte[] get(final byte[] key) { return connection.executeCommand(commandObjects.get(key)); } + @Override + public byte[] digest(final byte[] key) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.digest(key)); + } + @Override public byte[] setGet(final byte[] key, final byte[] value) { checkIsInMultiOrPipeline(); @@ -573,6 +593,12 @@ public long del(final byte[]... keys) { checkIsInMultiOrPipeline(); return connection.executeCommand(commandObjects.del(keys)); } + @Override + public long delex(final byte[] key, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.delex(key, condition)); + } + @Override public long del(final byte[] key) { @@ -580,6 +606,12 @@ public long del(final byte[] key) { return connection.executeCommand(commandObjects.del(key)); } + @Override + public long delex(final byte[] key) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.delex(key)); + } + /** * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is * ignored if it does not exist. However, the command performs the actual memory reclaiming in a @@ -5100,6 +5132,18 @@ public String set(final String key, final String value, final SetParams params) return connection.executeCommand(commandObjects.set(key, value, params)); } + @Override + public String set(final String key, final String value, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.set(key, value, condition)); + } + + @Override + public String setGet(final String key, final String value, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.setGet(key, value, condition)); + } + /** * Get the value of the specified key. If the key does not exist the special value 'nil' is * returned. If the value stored at key is not a string an error is returned because GET can only @@ -5147,6 +5191,18 @@ public String getEx(String key, GetExParams params) { return connection.executeCommand(commandObjects.getEx(key, params)); } + @Override + public String digest(final String key) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.digest(key)); + } + + @Override + public long delex(final String key, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.delex(key, condition)); + } + /** * Test if the specified keys exist. The command returns the number of keys exist. * Time complexity: O(N) @@ -5172,6 +5228,12 @@ public boolean exists(final String key) { return connection.executeCommand(commandObjects.exists(key)); } + @Override + public long delex(final String key) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.delex(key)); + } + /** * Remove the specified keys. If a given key does not exist no operation is performed for this * key. The command returns the number of keys removed. Time complexity: O(1) diff --git a/src/main/java/redis/clients/jedis/Protocol.java b/src/main/java/redis/clients/jedis/Protocol.java index ccc185188a..3c87af866c 100644 --- a/src/main/java/redis/clients/jedis/Protocol.java +++ b/src/main/java/redis/clients/jedis/Protocol.java @@ -283,7 +283,7 @@ public static final byte[] toByteArray(final double value) { public static enum Command implements ProtocolCommand { - PING, AUTH, HELLO, SET, GET, GETDEL, GETEX, EXISTS, DEL, UNLINK, TYPE, FLUSHDB, FLUSHALL, MOVE, + PING, AUTH, HELLO, SET, GET, GETDEL, GETEX, DIGEST, EXISTS, DEL, DELEX, UNLINK, TYPE, FLUSHDB, FLUSHALL, MOVE, KEYS, RANDOMKEY, RENAME, RENAMENX, DUMP, RESTORE, DBSIZE, SELECT, SWAPDB, MIGRATE, ECHO, // EXPIRE, EXPIREAT, EXPIRETIME, PEXPIRE, PEXPIREAT, PEXPIRETIME, TTL, PTTL, // <-- key expiration MULTI, DISCARD, EXEC, WATCH, UNWATCH, SORT, SORT_RO, INFO, SHUTDOWN, MONITOR, CONFIG, LCS, // @@ -334,7 +334,7 @@ public static enum Keyword implements Rawable { STREAMS, CREATE, MKSTREAM, SETID, DESTROY, DELCONSUMER, MAXLEN, GROUP, IDLE, TIME, BLOCK, NOACK, RETRYCOUNT, STREAM, GROUPS, CONSUMERS, JUSTID, WITHVALUES, NOMKSTREAM, MINID, CREATECONSUMER, SETUSER, GETUSER, DELUSER, WHOAMI, USERS, CAT, GENPASS, LOG, SAVE, DRYRUN, COPY, AUTH, AUTH2, - NX, XX, EX, PX, EXAT, PXAT, ABSTTL, KEEPTTL, INCR, LT, GT, CH, INFO, PAUSE, UNPAUSE, UNBLOCK, + NX, XX, IFEQ, IFNE, IFDEQ, IFDNE, EX, PX, EXAT, PXAT, ABSTTL, KEEPTTL, INCR, LT, GT, CH, INFO, PAUSE, UNPAUSE, UNBLOCK, REV, WITHCOORD, WITHDIST, WITHHASH, ANY, FROMMEMBER, FROMLONLAT, BYRADIUS, BYBOX, BYLEX, BYSCORE, STOREDIST, TO, FORCE, TIMEOUT, DB, UNLOAD, ABORT, IDX, MINMATCHLEN, WITHMATCHLEN, FULL, DELETE, LIBRARYNAME, WITHCODE, DESCRIPTION, GETKEYS, GETKEYSANDFLAGS, DOCS, FILTERBY, DUMP, diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index 3839c38ea2..025dbaedfe 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -17,6 +17,7 @@ import redis.clients.jedis.commands.SampleBinaryKeyedCommands; import redis.clients.jedis.commands.SampleKeyedCommands; import redis.clients.jedis.commands.RedisModuleCommands; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.csc.Cache; import redis.clients.jedis.csc.CacheConfig; import redis.clients.jedis.csc.CacheConnection; @@ -26,7 +27,6 @@ import redis.clients.jedis.json.JsonSetParams; import redis.clients.jedis.json.Path; import redis.clients.jedis.json.Path2; -import redis.clients.jedis.mcf.MultiDbCommandExecutor; import redis.clients.jedis.params.VAddParams; import redis.clients.jedis.params.VSimParams; import redis.clients.jedis.resps.RawVector; @@ -585,6 +585,16 @@ public long del(String key) { return executeCommand(commandObjects.del(key)); } + @Override + public long delex(String key) { + return executeCommand(commandObjects.delex(key)); + } + + @Override + public long delex(String key, ValueCondition condition) { + return executeCommand(commandObjects.delex(key, condition)); + } + @Override public long del(String... keys) { return executeCommand(commandObjects.del(keys)); @@ -595,6 +605,16 @@ public long unlink(String key) { return executeCommand(commandObjects.unlink(key)); } + @Override + public long delex(byte[] key) { + return executeCommand(commandObjects.delex(key)); + } + + @Override + public long delex(byte[] key, ValueCondition condition) { + return executeCommand(commandObjects.delex(key, condition)); + } + @Override public long unlink(String... keys) { return executeCommand(commandObjects.unlink(keys)); @@ -760,6 +780,21 @@ public String get(String key) { return executeCommand(commandObjects.get(key)); } + @Override + public String digest(String key) { + return executeCommand(commandObjects.digest(key)); + } + + @Override + public String set(String key, String value, ValueCondition condition) { + return executeCommand(commandObjects.set(key, value, condition)); + } + + @Override + public String setGet(String key, String value, ValueCondition condition) { + return executeCommand(commandObjects.setGet(key, value, condition)); + } + @Override public String setGet(String key, String value) { return executeCommand(commandObjects.setGet(key, value)); @@ -795,6 +830,21 @@ public byte[] get(byte[] key) { return executeCommand(commandObjects.get(key)); } + @Override + public String set(byte[] key, byte[] value, ValueCondition condition) { + return executeCommand(commandObjects.set(key, value, condition)); + } + + @Override + public byte[] setGet(byte[] key, byte[] value, ValueCondition condition) { + return executeCommand(commandObjects.setGet(key, value, condition)); + } + + @Override + public byte[] digest(byte[] key) { + return executeCommand(commandObjects.digest(key)); + } + @Override public byte[] setGet(byte[] key, byte[] value) { return executeCommand(commandObjects.setGet(key, value)); diff --git a/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java index 7593a42944..55a684b01f 100644 --- a/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java @@ -6,6 +6,8 @@ import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; +import redis.clients.jedis.annots.Experimental; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; @@ -60,6 +62,16 @@ public interface KeyBinaryCommands { long del(byte[] key); + + /** + * Experimental: Compare-and-delete guarded by value/digest condition. + */ + @Experimental + long delex(byte[] key, ValueCondition condition); + + @Experimental + long delex(byte[] key); + long del(byte[]... keys); long unlink(byte[] key); diff --git a/src/main/java/redis/clients/jedis/commands/KeyCommands.java b/src/main/java/redis/clients/jedis/commands/KeyCommands.java index e64f4fc3d7..e426cbb67c 100644 --- a/src/main/java/redis/clients/jedis/commands/KeyCommands.java +++ b/src/main/java/redis/clients/jedis/commands/KeyCommands.java @@ -6,6 +6,8 @@ import redis.clients.jedis.args.ExpiryOption; import redis.clients.jedis.params.MigrateParams; import redis.clients.jedis.params.RestoreParams; +import redis.clients.jedis.annots.Experimental; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.params.SortingParams; import redis.clients.jedis.resps.ScanResult; @@ -418,6 +420,20 @@ public interface KeyCommands { */ long del(String... keys); + /** + * Compare-and-delete: delete key if optional value/digest condition matches. + * @return 1 if the key was deleted, 0 otherwise + */ + + /** + * Experimental: Compare-and-delete guarded by value/digest condition. + */ + @Experimental + long delex(String key, ValueCondition condition); + + @Experimental + long delex(String key); + /** * Unlink Command * This command is very similar to {@link KeyCommands#del(String) DEL}: it removes the specified key. diff --git a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java index 0d087bc1b3..f9c06a3204 100644 --- a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java @@ -7,8 +7,23 @@ import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; +import redis.clients.jedis.annots.Experimental; +import redis.clients.jedis.conditions.ValueCondition; + public interface StringBinaryCommands extends BitBinaryCommands { + /** + * Experimental: SET with compare-and-* condition. + */ + @Experimental + String set(byte[] key, byte[] value, ValueCondition condition); + + /** + * Experimental: SET+GET with compare-and-* condition. + */ + @Experimental + byte[] setGet(byte[] key, byte[] value, ValueCondition condition); + String set(byte[] key, byte[] value); String set(byte[] key, byte[] value, SetParams params); @@ -23,6 +38,10 @@ public interface StringBinaryCommands extends BitBinaryCommands { byte[] getEx(byte[] key, GetExParams params); + /** Returns the 64-bit XXH3 digest hex (ASCII bytes) of the string value stored at key, or null if missing. */ + @Experimental + byte[] digest(byte[] key); + long setrange(byte[] key, long offset, byte[] value); byte[] getrange(byte[] key, long startOffset, long endOffset); diff --git a/src/main/java/redis/clients/jedis/commands/StringCommands.java b/src/main/java/redis/clients/jedis/commands/StringCommands.java index c4ea21fea7..d0b5d2e134 100644 --- a/src/main/java/redis/clients/jedis/commands/StringCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringCommands.java @@ -7,6 +7,9 @@ import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; +import redis.clients.jedis.annots.Experimental; +import redis.clients.jedis.conditions.ValueCondition; + public interface StringCommands extends BitCommands { /** @@ -34,6 +37,12 @@ public interface StringCommands extends BitCommands { */ String set(String key, String value, SetParams params); + /** + * Experimental: SET with compare-and-* condition. + */ + @Experimental + String set(String key, String value, ValueCondition condition); + /** * Get Command * Get the value of the specified key. If the key does not exist the special value 'nil' is @@ -50,6 +59,12 @@ public interface StringCommands extends BitCommands { String setGet(String key, String value, SetParams params); + /** + * Experimental: SET+GET with compare-and-* condition. + */ + @Experimental + String setGet(String key, String value, ValueCondition condition); + /** * GetDel Command * Get the value of key and delete the key. This command is similar to GET, except for the fact @@ -78,6 +93,13 @@ public interface StringCommands extends BitCommands { */ String getEx(String key, GetExParams params); + /** + * Compute and return the 64-bit XXH3 digest hex of the string value stored at key. + * Returns null if key does not exist. + */ + @Experimental + String digest(String key); + /** * SetRange Command * GETRANGE overwrite part of the string stored at key, starting at the specified offset, for the entire diff --git a/src/main/java/redis/clients/jedis/conditions/ValueCondition.java b/src/main/java/redis/clients/jedis/conditions/ValueCondition.java new file mode 100644 index 0000000000..ac0427daa4 --- /dev/null +++ b/src/main/java/redis/clients/jedis/conditions/ValueCondition.java @@ -0,0 +1,53 @@ +package redis.clients.jedis.conditions; + +import redis.clients.jedis.CommandArguments; +import redis.clients.jedis.Protocol.Keyword; +import redis.clients.jedis.annots.Experimental; + +/** + * Experimental condition object for optimistic concurrency with SET/DELEX. + * Encapsulates either a value-based or digest-based condition with equality or inequality. + */ +@Experimental +public final class ValueCondition { + + public enum Kind { VALUE, DIGEST } + public enum Mode { EQ, NE } + + private final Kind kind; + private final Mode mode; + private final Object payload; // String or byte[] + + private ValueCondition(Kind kind, Mode mode, Object payload) { + if (!(payload instanceof String) && !(payload instanceof byte[])) { + throw new IllegalArgumentException("payload must be String or byte[]"); + } + this.kind = kind; + this.mode = mode; + this.payload = payload; + } + + // Factory methods: value-based + public static ValueCondition valueEq(String value) { return new ValueCondition(Kind.VALUE, Mode.EQ, value); } + public static ValueCondition valueNe(String value) { return new ValueCondition(Kind.VALUE, Mode.NE, value); } + public static ValueCondition valueEq(byte[] value) { return new ValueCondition(Kind.VALUE, Mode.EQ, value); } + public static ValueCondition valueNe(byte[] value) { return new ValueCondition(Kind.VALUE, Mode.NE, value); } + + // Factory methods: digest-based + public static ValueCondition digestEq(String hex16) { return new ValueCondition(Kind.DIGEST, Mode.EQ, hex16); } + public static ValueCondition digestNe(String hex16) { return new ValueCondition(Kind.DIGEST, Mode.NE, hex16); } + public static ValueCondition digestEq(byte[] digest) { return new ValueCondition(Kind.DIGEST, Mode.EQ, digest); } + public static ValueCondition digestNe(byte[] digest) { return new ValueCondition(Kind.DIGEST, Mode.NE, digest); } + + /** + * Append this condition to the command arguments by emitting the appropriate keyword and payload. + */ + public void addTo(CommandArguments args) { + if (kind == Kind.VALUE) { + args.add(mode == Mode.EQ ? Keyword.IFEQ : Keyword.IFNE).add(payload); + } else { // DIGEST + args.add(mode == Mode.EQ ? Keyword.IFDEQ : Keyword.IFDNE).add(payload); + } + } +} + From da26935c22fbe5d31edae110ecd03146ffdb0742 Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 12:03:57 +0200 Subject: [PATCH 2/6] Add tests --- .../jedis/AllKindOfValuesCommandsTest.java | 22 +++ .../jedis/StringValuesCommandsTest.java | 72 +++++++++ .../AllKindOfValuesCommandsTestBase.java | 26 +++ .../unified/StringValuesCommandsTestBase.java | 125 +++++++++++++++ .../UnifiedJedisStringCommandsTest.java | 151 ++++++++++++++++++ 5 files changed, 396 insertions(+) diff --git a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java index 9bbf4d323e..c71ce4b982 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java @@ -35,6 +35,7 @@ import redis.clients.jedis.*; import redis.clients.jedis.args.ExpiryOption; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.args.FlushMode; @@ -1178,4 +1179,25 @@ public void reset() { jedis.auth(endpoint.getPassword()); assertEquals("1", jedis.get(counter)); } + + @Test + @SinceRedisVersion("8.3.224") + public void delexBasicAndConditions() { + jedis.set("dk", "v"); + assertEquals(1L, jedis.delex("dk")); + assertFalse(jedis.exists("dk")); + + jedis.set("dk", "v1"); + assertEquals(0L, jedis.delex("dk", ValueCondition.valueEq("nope"))); + assertEquals(1L, jedis.delex("dk", ValueCondition.valueEq("v1"))); + + jedis.set("dk2", "x"); + assertEquals(0L, jedis.delex("dk2", ValueCondition.valueNe("x"))); + jedis.set("dk3", "y"); + assertEquals(1L, jedis.delex("dk3", ValueCondition.valueNe("z"))); + + jedis.del("missing"); + assertEquals(0L, jedis.delex("missing")); + assertEquals(0L, jedis.delex("missing", ValueCondition.valueNe("anything"))); + } } diff --git a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java index ce73ad5a6e..d4631e0350 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.params.provider.MethodSource; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; import redis.clients.jedis.exceptions.JedisDataException; @@ -40,6 +41,44 @@ public void setAndGet() { assertNull(jedis.get("bar")); } + @Test + @SinceRedisVersion("8.3.224") + public void digestSimple() { + assertNull(jedis.digest("missing")); + jedis.set("foo", "bar"); + String hex = jedis.digest("foo"); + org.junit.jupiter.api.Assertions.assertNotNull(hex); + assertTrue(hex.matches("(?i)[0-9a-f]{16}")); + } + + @Test + @SinceRedisVersion("8.3.224") + public void setWithIFConditionsValue() { + jedis.del("k"); + + // IFEQ on existing value: success + jedis.set("k", "v1"); + assertEquals("OK", jedis.set("k", "v2", ValueCondition.valueEq("v1"))); + assertEquals("v2", jedis.get("k")); + + // IFEQ no match: no change + assertNull(jedis.set("k", "v3", ValueCondition.valueEq("nope"))); + assertEquals("v2", jedis.get("k")); + + // IFNE on missing key: treated as true -> creates key + jedis.del("k"); + assertEquals("OK", jedis.set("k", "vx", ValueCondition.valueNe("anything"))); + assertEquals("vx", jedis.get("k")); + + // IFEQ on missing key: false -> no create + jedis.del("k"); + assertNull(jedis.set("k", "vy", ValueCondition.valueEq("anything"))); + assertNull(jedis.get("k")); + } + + + + @Test public void getSet() { String value = jedis.getSet("foo", "bar"); @@ -226,6 +265,39 @@ public void substr() { } @Test + @SinceRedisVersion("8.3.224") + public void digestBasic() { + jedis.del("dg"); + assertNull(jedis.digest("dg")); + jedis.set("dg", "val"); + String hex = jedis.digest("dg"); + assertTrue(hex != null && (hex.length() == 16)); + } + + @Test + @SinceRedisVersion("8.3.224") + public void setWithIfConditions() { + jedis.set("kif", "v1"); + + // IFEQ matches -> set + assertEquals("OK", jedis.set("kif", "v2", ValueCondition.valueEq("v1"))); + assertEquals("v2", jedis.get("kif")); + + // IFEQ fails -> no set + assertNull(jedis.set("kif", "v3", ValueCondition.valueEq("nope"))); + assertEquals("v2", jedis.get("kif")); + + // IFNE matches -> set + assertEquals("OK", jedis.set("kif", "v4", ValueCondition.valueNe("nope"))); + assertEquals("v4", jedis.get("kif")); + + // Missing key semantics + jedis.del("kif_missing"); + assertNull(jedis.set("kif_missing", "x", ValueCondition.valueEq("anything"))); + assertEquals("OK", jedis.set("kif_missing", "x", ValueCondition.valueNe("anything"))); + } + + public void strlen() { String str = "This is a string"; jedis.set("s", str); diff --git a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java index f1891ac893..41d720416b 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java @@ -42,6 +42,7 @@ import redis.clients.jedis.ScanIteration; import redis.clients.jedis.StreamEntryID; import redis.clients.jedis.args.ExpiryOption; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; import redis.clients.jedis.params.RestoreParams; @@ -968,4 +969,29 @@ public void scanIteration() { } assertEquals(allIn, allScan); } + @Test + @SinceRedisVersion("8.3.224") + public void delexBasicAndConditions() { + // basic + jedis.set("dk", "v"); + assertEquals(1L, jedis.delex("dk")); + assertFalse(jedis.exists("dk")); + + // IFEQ match + jedis.set("dk", "v1"); + assertEquals(0L, jedis.delex("dk", ValueCondition.valueEq("nope"))); + assertEquals(1L, jedis.delex("dk", ValueCondition.valueEq("v1"))); + + // IFNE non-match + jedis.set("dk2", "x"); + assertEquals(0L, jedis.delex("dk2", ValueCondition.valueNe("x"))); + jedis.set("dk3", "y"); + assertEquals(1L, jedis.delex("dk3", ValueCondition.valueNe("z"))); + + // Missing key: regardless of condition, deletion count should be 0 + jedis.del("missing"); + assertEquals(0L, jedis.delex("missing")); + assertEquals(0L, jedis.delex("missing", ValueCondition.valueNe("anything"))); + } + } diff --git a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java index 60fbbbd595..166bd45478 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; import static redis.clients.jedis.params.SetParams.setParams; import java.util.ArrayList; @@ -14,6 +15,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Tag; import redis.clients.jedis.RedisProtocol; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.resps.LCSMatchResult; import redis.clients.jedis.exceptions.JedisDataException; @@ -281,4 +283,127 @@ public void lcs() { assertEquals(0, stringMatchResult.getMatches().size()); } + @Test + @SinceRedisVersion("8.3.224") + public void digestBasic() { + jedis.del("dg"); + assertNull(jedis.digest("dg")); + jedis.set("dg", "val"); + String hex = jedis.digest("dg"); + assertTrue(hex != null && (hex.length() == 16)); + } + + @Test + @SinceRedisVersion("8.3.224") + public void setWithIfConditions() { + jedis.set("kif", "v1"); + + // IFEQ matches -> set + assertEquals("OK", jedis.set("kif", "v2", ValueCondition.valueEq("v1"))); + assertEquals("v2", jedis.get("kif")); + + // IFEQ fails -> no set + assertNull(jedis.set("kif", "v3", ValueCondition.valueEq("nope"))); + assertEquals("v2", jedis.get("kif")); + + // IFNE matches -> set + assertEquals("OK", jedis.set("kif", "v4", ValueCondition.valueNe("nope"))); + assertEquals("v4", jedis.get("kif")); + + // Missing key semantics + jedis.del("kif_missing"); + assertNull(jedis.set("kif_missing", "x", ValueCondition.valueEq("anything"))); // missing + IFEQ should fail + assertEquals("OK", jedis.set("kif_missing", "x", ValueCondition.valueNe("anything"))); // missing + IFNE should pass + } + + + @Test + @SinceRedisVersion("8.3.224") + public void setGetWithIFConditions() { + jedis.del("sgk"); + // Missing + IFNE should set and return previous (null) + assertNull(jedis.setGet("sgk", "v1", ValueCondition.valueNe("x"))); + assertEquals("v1", jedis.get("sgk")); + + // IFEQ matches -> returns old value and sets + assertEquals("v1", jedis.setGet("sgk", "v2", ValueCondition.valueEq("v1"))); + assertEquals("v2", jedis.get("sgk")); + + // IFEQ fails -> returns old value and does not set + assertEquals("v2", jedis.setGet("sgk", "v3", ValueCondition.valueEq("nope"))); + assertEquals("v2", jedis.get("sgk")); + } + + @Test + @SinceRedisVersion("8.3.224") + public void setWithIFDigestConditions() { + jedis.set("dk", "abc"); + String dig = jedis.digest("dk"); + + // IFDEQ matches -> set + assertEquals("OK", jedis.set("dk", "def", ValueCondition.digestEq(dig))); + String newDig = jedis.digest("dk"); + assertTrue(newDig != null && newDig.length() == 16); + + // IFDEQ fails -> no set + assertNull(jedis.set("dk", "ghi", ValueCondition.digestEq(dig))); + assertEquals("def", jedis.get("dk")); + + // IFDNE equal digest -> fail (no set) + assertNull(jedis.set("dk", "ghi", ValueCondition.digestNe(newDig))); + assertEquals("def", jedis.get("dk")); + + // Missing key semantics + jedis.del("dm"); + assertNull(jedis.set("dm", "x", ValueCondition.digestEq("0000000000000000"))); + jedis.del("dm"); + assertEquals("OK", jedis.set("dm", "x", ValueCondition.digestNe("0000000000000000"))); + } + + @Test + @SinceRedisVersion("8.3.224") + public void casCadEndToEndExample() { + final String k = "cas:ex"; + jedis.del(k); + + // 1) Create initial value + assertEquals("OK", jedis.set(k, "v1")); + assertEquals("v1", jedis.get(k)); + + // 2) Read digest and use it to CAS to v2 + String d1 = jedis.digest(k); + assertTrue(d1 != null && d1.length() == 16); + + // Wrong digest must not set + assertNull(jedis.set(k, "bad", ValueCondition.digestEq("0000000000000000"))); + assertEquals("v1", jedis.get(k)); + + // Correct digest sets the new value + assertEquals("OK", jedis.set(k, "v2", ValueCondition.digestEq(d1))); + assertEquals("v2", jedis.get(k)); + + // 3) Delete using DELEX guarded by the latest digest + String d2 = jedis.digest(k); + assertEquals(0L, jedis.delex(k, ValueCondition.digestEq("0000000000000000"))); + assertEquals(1L, jedis.delex(k, ValueCondition.digestEq(d2))); + assertFalse(jedis.exists(k)); + } + @Test + @SinceRedisVersion("8.3.224") + public void casCadEndToEndExample_Experimental() { + final String k = "cas:ex2"; + jedis.del(k); + + assertEquals("OK", jedis.set(k, "v1")); + + String d1 = jedis.digest(k); + ValueCondition cond1 = ValueCondition.digestEq(d1); + assertEquals("OK", jedis.set(k, "v2", cond1)); + assertEquals("v2", jedis.get(k)); + + String d2 = jedis.digest(k); + ValueCondition cond2 = ValueCondition.digestEq(d2); + assertEquals(1L, jedis.delex(k, cond2)); + assertFalse(jedis.exists(k)); + } } diff --git a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java index d12e9e6224..10fa5b3bb5 100644 --- a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java +++ b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java @@ -4,11 +4,13 @@ import static org.hamcrest.Matchers.equalTo; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.times; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.Test; +import redis.clients.jedis.conditions.ValueCondition; import redis.clients.jedis.params.GetExParams; import redis.clients.jedis.params.LCSParams; import redis.clients.jedis.params.SetParams; @@ -866,4 +868,153 @@ public void testSubstrBinary() { verify(commandObjects).substr(key, start, end); } + @Test + public void testDigest() { + String key = "key"; + String expectedHex = "0123456789abcdef"; + + when(commandObjects.digest(key)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedHex); + + String result = jedis.digest(key); + + assertThat(result, equalTo(expectedHex)); + + verify(commandExecutor).executeCommand(stringCommandObject); + verify(commandObjects).digest(key); + } + + @Test + public void testDigestBinary() { + byte[] key = "key".getBytes(); + byte[] expectedHex = "fedcba9876543210".getBytes(); + + when(commandObjects.digest(key)).thenReturn(bytesCommandObject); + when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedHex); + + byte[] result = jedis.digest(key); + + assertThat(result, equalTo(expectedHex)); + + verify(commandExecutor).executeCommand(bytesCommandObject); + verify(commandObjects).digest(key); + } + + @Test + public void testDelex() { + String key = "dk"; + long expected = 1L; + + when(commandObjects.delex(key)).thenReturn(longCommandObject); + when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); + + long result = jedis.delex(key); + + assertThat(result, equalTo(expected)); + + verify(commandExecutor).executeCommand(longCommandObject); + verify(commandObjects).delex(key); + } + + @Test + public void testDelexBinary() { + byte[] key = "dk".getBytes(); + long expected = 0L; + + when(commandObjects.delex(key)).thenReturn(longCommandObject); + when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); + + long result = jedis.delex(key); + + assertThat(result, equalTo(expected)); + + verify(commandExecutor).executeCommand(longCommandObject); + verify(commandObjects).delex(key); + } + + @Test + public void testDelexWithParams() { + String key = "dkp"; + ValueCondition cond = ValueCondition.valueEq("v1"); + long expected = 1L; + + when(commandObjects.delex(key, cond)).thenReturn(longCommandObject); + when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); + + long result = jedis.delex(key, cond); + + assertThat(result, equalTo(expected)); + + verify(commandExecutor).executeCommand(longCommandObject); + verify(commandObjects).delex(key, cond); + } + + @Test + public void testDelexWithParamsBinary() { + byte[] key = "dkp".getBytes(); + ValueCondition cond = ValueCondition.digestEq("0123456789abcdef"); + long expected = 0L; + + when(commandObjects.delex(key, cond)).thenReturn(longCommandObject); + when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); + + long result = jedis.delex(key, cond); + + assertThat(result, equalTo(expected)); + + verify(commandExecutor).executeCommand(longCommandObject); + verify(commandObjects).delex(key, cond); + } + + @Test + public void testSetWithParamsIFConditions() { + String key = "k"; + String value = "v"; + ValueCondition c1 = ValueCondition.valueEq("v"); + ValueCondition c2 = ValueCondition.valueNe("x"); + ValueCondition c3 = ValueCondition.digestEq("0123456789abcdef"); + ValueCondition c4 = ValueCondition.digestNe("0123456789abcdef"); + + when(commandObjects.set(key, value, c1)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); + assertThat(jedis.set(key, value, c1), equalTo("OK")); + verify(commandExecutor, times(1)).executeCommand(stringCommandObject); + verify(commandObjects).set(key, value, c1); + + when(commandObjects.set(key, value, c2)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); + assertThat(jedis.set(key, value, c2), equalTo("OK")); + verify(commandExecutor, times(2)).executeCommand(stringCommandObject); + verify(commandObjects).set(key, value, c2); + + when(commandObjects.set(key, value, c3)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); + assertThat(jedis.set(key, value, c3), equalTo("OK")); + verify(commandExecutor, times(3)).executeCommand(stringCommandObject); + verify(commandObjects).set(key, value, c3); + + when(commandObjects.set(key, value, c4)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("OK"); + assertThat(jedis.set(key, value, c4), equalTo("OK")); + verify(commandExecutor, times(4)).executeCommand(stringCommandObject); + verify(commandObjects).set(key, value, c4); + } + + @Test + public void testSetGetWithParamsIFConditions() { + String key = "kg"; + String value = "v"; + ValueCondition cond = ValueCondition.valueEq("v"); + + when(commandObjects.setGet(key, value, cond)).thenReturn(stringCommandObject); + when(commandExecutor.executeCommand(stringCommandObject)).thenReturn("old"); + + String result = jedis.setGet(key, value, cond); + + assertThat(result, equalTo("old")); + + verify(commandExecutor).executeCommand(stringCommandObject); + verify(commandObjects).setGet(key, value, cond); + } + } From cb4a52d5d8a4c2199642c232356637535100594e Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 12:25:32 +0200 Subject: [PATCH 3/6] Remove unconditional delex. --- pom.xml | 16 +++++---- .../redis/clients/jedis/CommandObjects.java | 8 ----- src/main/java/redis/clients/jedis/Jedis.java | 12 ------- .../redis/clients/jedis/UnifiedJedis.java | 10 ------ .../jedis/commands/KeyBinaryCommands.java | 3 -- .../clients/jedis/commands/KeyCommands.java | 3 -- .../jedis/AllKindOfValuesCommandsTest.java | 4 +-- .../AllKindOfValuesCommandsTestBase.java | 5 +-- .../UnifiedJedisStringCommandsTest.java | 34 +------------------ 9 files changed, 16 insertions(+), 79 deletions(-) diff --git a/pom.xml b/pom.xml index 9ae293e949..5ae78cd78b 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,10 @@ 5.13.4 + + + + integration,scenario false @@ -349,7 +353,7 @@ ${maven.surefire.version} ${skipUnitTests} - @{argLine} ${JVM_OPTS} + ${argLine} ${JVM_OPTS} ${redis-hosts} @@ -368,7 +372,7 @@ maven-failsafe-plugin ${maven.surefire.version} - @{failsafeSuffixArgLine} ${JVM_OPTS} + ${failsafeSuffixArgLine} ${JVM_OPTS} ${redis-hosts} @@ -394,7 +398,7 @@ ${skipIntegrationTests} - @{failsafeTaggedArgLine} ${JVM_OPTS} + ${failsafeTaggedArgLine} ${JVM_OPTS} integration scenario,unit @@ -411,7 +415,7 @@ ${skipIntegrationTests} - @{failsafeSuffixArgLine} ${JVM_OPTS} + ${failsafeSuffixArgLine} ${JVM_OPTS} scenario,unit **/*IT.java @@ -429,7 +433,7 @@ ${skipScenarioTests} - @{failsafeScenarioArgLine} ${JVM_OPTS} + ${failsafeScenarioArgLine} ${JVM_OPTS} scenario integration,unit @@ -638,7 +642,7 @@ maven-surefire-plugin ${maven.surefire.version} - @{argLine} ${JVM_OPTS} + ${argLine} ${JVM_OPTS} diff --git a/src/main/java/redis/clients/jedis/CommandObjects.java b/src/main/java/redis/clients/jedis/CommandObjects.java index 6b57d8b4b0..8f9bddd18d 100644 --- a/src/main/java/redis/clients/jedis/CommandObjects.java +++ b/src/main/java/redis/clients/jedis/CommandObjects.java @@ -347,20 +347,12 @@ public final CommandObject del(byte[]... keys) { return new CommandObject<>(commandArguments(DEL).keys((Object[]) keys), BuilderFactory.LONG); } - public final CommandObject delex(String key) { - return new CommandObject<>(commandArguments(Command.DELEX).key(key), BuilderFactory.LONG); - } - public final CommandObject delex(String key, ValueCondition cond) { CommandArguments ca = commandArguments(Command.DELEX).key(key); cond.addTo(ca); return new CommandObject<>(ca, BuilderFactory.LONG); } - public final CommandObject delex(byte[] key) { - return new CommandObject<>(commandArguments(Command.DELEX).key(key), BuilderFactory.LONG); - } - public final CommandObject delex(byte[] key, ValueCondition cond) { CommandArguments ca = commandArguments(Command.DELEX).key(key); cond.addTo(ca); diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 4dfc81f78c..7941a7235d 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -606,12 +606,6 @@ public long del(final byte[] key) { return connection.executeCommand(commandObjects.del(key)); } - @Override - public long delex(final byte[] key) { - checkIsInMultiOrPipeline(); - return connection.executeCommand(commandObjects.delex(key)); - } - /** * This command is very similar to DEL: it removes the specified keys. Just like DEL a key is * ignored if it does not exist. However, the command performs the actual memory reclaiming in a @@ -5228,12 +5222,6 @@ public boolean exists(final String key) { return connection.executeCommand(commandObjects.exists(key)); } - @Override - public long delex(final String key) { - checkIsInMultiOrPipeline(); - return connection.executeCommand(commandObjects.delex(key)); - } - /** * Remove the specified keys. If a given key does not exist no operation is performed for this * key. The command returns the number of keys removed. Time complexity: O(1) diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index 025dbaedfe..6dcd351f87 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -585,11 +585,6 @@ public long del(String key) { return executeCommand(commandObjects.del(key)); } - @Override - public long delex(String key) { - return executeCommand(commandObjects.delex(key)); - } - @Override public long delex(String key, ValueCondition condition) { return executeCommand(commandObjects.delex(key, condition)); @@ -605,11 +600,6 @@ public long unlink(String key) { return executeCommand(commandObjects.unlink(key)); } - @Override - public long delex(byte[] key) { - return executeCommand(commandObjects.delex(key)); - } - @Override public long delex(byte[] key, ValueCondition condition) { return executeCommand(commandObjects.delex(key, condition)); diff --git a/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java index 55a684b01f..6b64a00668 100644 --- a/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/KeyBinaryCommands.java @@ -69,9 +69,6 @@ public interface KeyBinaryCommands { @Experimental long delex(byte[] key, ValueCondition condition); - @Experimental - long delex(byte[] key); - long del(byte[]... keys); long unlink(byte[] key); diff --git a/src/main/java/redis/clients/jedis/commands/KeyCommands.java b/src/main/java/redis/clients/jedis/commands/KeyCommands.java index e426cbb67c..2d6032484e 100644 --- a/src/main/java/redis/clients/jedis/commands/KeyCommands.java +++ b/src/main/java/redis/clients/jedis/commands/KeyCommands.java @@ -431,9 +431,6 @@ public interface KeyCommands { @Experimental long delex(String key, ValueCondition condition); - @Experimental - long delex(String key); - /** * Unlink Command * This command is very similar to {@link KeyCommands#del(String) DEL}: it removes the specified key. diff --git a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java index c71ce4b982..2faadf18f9 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/AllKindOfValuesCommandsTest.java @@ -1184,7 +1184,7 @@ public void reset() { @SinceRedisVersion("8.3.224") public void delexBasicAndConditions() { jedis.set("dk", "v"); - assertEquals(1L, jedis.delex("dk")); + assertEquals(1L, jedis.del("dk")); assertFalse(jedis.exists("dk")); jedis.set("dk", "v1"); @@ -1197,7 +1197,7 @@ public void delexBasicAndConditions() { assertEquals(1L, jedis.delex("dk3", ValueCondition.valueNe("z"))); jedis.del("missing"); - assertEquals(0L, jedis.delex("missing")); + assertEquals(0L, jedis.del("missing")); assertEquals(0L, jedis.delex("missing", ValueCondition.valueNe("anything"))); } } diff --git a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java index 41d720416b..55ecb8a9cb 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/AllKindOfValuesCommandsTestBase.java @@ -969,12 +969,13 @@ public void scanIteration() { } assertEquals(allIn, allScan); } + @Test @SinceRedisVersion("8.3.224") public void delexBasicAndConditions() { // basic jedis.set("dk", "v"); - assertEquals(1L, jedis.delex("dk")); + assertEquals(1L, jedis.del("dk")); assertFalse(jedis.exists("dk")); // IFEQ match @@ -990,7 +991,7 @@ public void delexBasicAndConditions() { // Missing key: regardless of condition, deletion count should be 0 jedis.del("missing"); - assertEquals(0L, jedis.delex("missing")); + assertEquals(0L, jedis.del("missing")); assertEquals(0L, jedis.delex("missing", ValueCondition.valueNe("anything"))); } diff --git a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java index 10fa5b3bb5..bee353c7f8 100644 --- a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java +++ b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java @@ -902,38 +902,6 @@ public void testDigestBinary() { @Test public void testDelex() { - String key = "dk"; - long expected = 1L; - - when(commandObjects.delex(key)).thenReturn(longCommandObject); - when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); - - long result = jedis.delex(key); - - assertThat(result, equalTo(expected)); - - verify(commandExecutor).executeCommand(longCommandObject); - verify(commandObjects).delex(key); - } - - @Test - public void testDelexBinary() { - byte[] key = "dk".getBytes(); - long expected = 0L; - - when(commandObjects.delex(key)).thenReturn(longCommandObject); - when(commandExecutor.executeCommand(longCommandObject)).thenReturn(expected); - - long result = jedis.delex(key); - - assertThat(result, equalTo(expected)); - - verify(commandExecutor).executeCommand(longCommandObject); - verify(commandObjects).delex(key); - } - - @Test - public void testDelexWithParams() { String key = "dkp"; ValueCondition cond = ValueCondition.valueEq("v1"); long expected = 1L; @@ -950,7 +918,7 @@ public void testDelexWithParams() { } @Test - public void testDelexWithParamsBinary() { + public void testDelexBinary() { byte[] key = "dkp".getBytes(); ValueCondition cond = ValueCondition.digestEq("0123456789abcdef"); long expected = 0L; From 4955c17c5cf143827e67c942336e884fe9a03320 Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 14:41:32 +0200 Subject: [PATCH 4/6] Add CAS + Params API --- .../redis/clients/jedis/CommandObjects.java | 32 +++++++++++++++-- src/main/java/redis/clients/jedis/Jedis.java | 24 +++++++++++++ .../redis/clients/jedis/UnifiedJedis.java | 21 ++++++++++++ .../jedis/commands/StringBinaryCommands.java | 13 +++++++ .../jedis/commands/StringCommands.java | 12 +++++++ .../unified/StringValuesCommandsTestBase.java | 34 +++++++++++++++++++ 6 files changed, 133 insertions(+), 3 deletions(-) diff --git a/src/main/java/redis/clients/jedis/CommandObjects.java b/src/main/java/redis/clients/jedis/CommandObjects.java index 8f9bddd18d..def88060c5 100644 --- a/src/main/java/redis/clients/jedis/CommandObjects.java +++ b/src/main/java/redis/clients/jedis/CommandObjects.java @@ -50,7 +50,7 @@ protected RedisProtocol getProtocol() { protected volatile CommandKeyArgumentPreProcessor keyPreProcessor = null; private JedisBroadcastAndRoundRobinConfig broadcastAndRoundRobinConfig = null; - private Lock mapperLock = new ReentrantLock(true); + private Lock mapperLock = new ReentrantLock(true); private volatile JsonObjectMapper jsonObjectMapper; private final AtomicInteger searchDialect = new AtomicInteger(SearchProtocol.DEFAULT_DIALECT); @@ -497,6 +497,32 @@ public final CommandObject set(byte[] key, byte[] value, SetParams param return new CommandObject<>(commandArguments(Command.SET).key(key).add(value).addParams(params), BuilderFactory.STRING); } + public final CommandObject set(String key, String value, SetParams params, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject setGet(String key, String value, SetParams params, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + cond.addTo(ca); + ca.add(Keyword.GET); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject set(byte[] key, byte[] value, SetParams params, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + cond.addTo(ca); + return new CommandObject<>(ca, BuilderFactory.STRING); + } + + public final CommandObject setGet(byte[] key, byte[] value, SetParams params, ValueCondition cond) { + CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + cond.addTo(ca); + ca.add(Keyword.GET); + return new CommandObject<>(ca, BuilderFactory.BINARY); + } + public final CommandObject get(String key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.STRING); } @@ -3024,7 +3050,7 @@ public final CommandObject> xreadGroup(byte[] groupName, byte[] con } public final CommandObject>>> xreadGroupBinary( - byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, + byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) @@ -3039,7 +3065,7 @@ public final CommandObject>>> xre } public final CommandObject>> xreadGroupBinaryAsMap( - byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, + byte[] groupName, byte[] consumer, XReadGroupParams xReadGroupParams, Map.Entry... streams) { CommandArguments args = commandArguments(XREADGROUP) .add(GROUP).add(groupName).add(consumer) diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index 7941a7235d..e2d85437c9 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -498,6 +498,12 @@ public String set(final byte[] key, final byte[] value, final ValueCondition con return connection.executeCommand(commandObjects.set(key, value, condition)); } + @Override + public String set(final byte[] key, final byte[] value, final SetParams params, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.set(key, value, params, condition)); + } + @Override public byte[] setGet(final byte[] key, final byte[] value, final ValueCondition condition) { checkIsInMultiOrPipeline(); @@ -537,6 +543,12 @@ public byte[] setGet(final byte[] key, final byte[] value, final SetParams param return connection.executeCommand(commandObjects.setGet(key, value, params)); } + @Override + public byte[] setGet(final byte[] key, final byte[] value, final SetParams params, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.setGet(key, value, params, condition)); + } + /** * Get the value of key and delete the key. This command is similar to GET, except for the fact * that it also deletes the key on success (if and only if the key's value type is a string). @@ -5132,6 +5144,12 @@ public String set(final String key, final String value, final ValueCondition con return connection.executeCommand(commandObjects.set(key, value, condition)); } + @Override + public String set(final String key, final String value, final SetParams params, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.set(key, value, params, condition)); + } + @Override public String setGet(final String key, final String value, final ValueCondition condition) { checkIsInMultiOrPipeline(); @@ -5159,6 +5177,12 @@ public String setGet(final String key, final String value) { return connection.executeCommand(commandObjects.setGet(key, value)); } + @Override + public String setGet(final String key, final String value, final SetParams params, final ValueCondition condition) { + checkIsInMultiOrPipeline(); + return connection.executeCommand(commandObjects.setGet(key, value, params, condition)); + } + @Override public String setGet(final String key, final String value, final SetParams params) { checkIsInMultiOrPipeline(); diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index 6dcd351f87..8e3fada1c4 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -780,6 +780,16 @@ public String set(String key, String value, ValueCondition condition) { return executeCommand(commandObjects.set(key, value, condition)); } + @Override + public String set(String key, String value, SetParams params, ValueCondition condition) { + return executeCommand(commandObjects.set(key, value, params, condition)); + } + + @Override + public String setGet(String key, String value, SetParams params, ValueCondition condition) { + return executeCommand(commandObjects.setGet(key, value, params, condition)); + } + @Override public String setGet(String key, String value, ValueCondition condition) { return executeCommand(commandObjects.setGet(key, value, condition)); @@ -815,6 +825,11 @@ public String set(byte[] key, byte[] value, SetParams params) { return executeCommand(commandObjects.set(key, value, params)); } + @Override + public String set(byte[] key, byte[] value, SetParams params, ValueCondition condition) { + return executeCommand(commandObjects.set(key, value, params, condition)); + } + @Override public byte[] get(byte[] key) { return executeCommand(commandObjects.get(key)); @@ -850,6 +865,12 @@ public byte[] getDel(byte[] key) { return executeCommand(commandObjects.getDel(key)); } + + @Override + public byte[] setGet(byte[] key, byte[] value, SetParams params, ValueCondition condition) { + return executeCommand(commandObjects.setGet(key, value, params, condition)); + } + @Override public byte[] getEx(byte[] key, GetExParams params) { return executeCommand(commandObjects.getEx(key, params)); diff --git a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java index f9c06a3204..58ef5316d9 100644 --- a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java @@ -24,6 +24,19 @@ public interface StringBinaryCommands extends BitBinaryCommands { @Experimental byte[] setGet(byte[] key, byte[] value, ValueCondition condition); + /** + * Experimental: SET with SetParams and compare-and-* condition. + */ + @Experimental + String set(byte[] key, byte[] value, SetParams params, ValueCondition condition); + + /** + * Experimental: SET+GET with SetParams and compare-and-* condition. + */ + @Experimental + byte[] setGet(byte[] key, byte[] value, SetParams params, ValueCondition condition); + + String set(byte[] key, byte[] value); String set(byte[] key, byte[] value, SetParams params); diff --git a/src/main/java/redis/clients/jedis/commands/StringCommands.java b/src/main/java/redis/clients/jedis/commands/StringCommands.java index d0b5d2e134..b13e4fb234 100644 --- a/src/main/java/redis/clients/jedis/commands/StringCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringCommands.java @@ -65,6 +65,18 @@ public interface StringCommands extends BitCommands { @Experimental String setGet(String key, String value, ValueCondition condition); + /** + * Experimental: SET with SetParams and compare-and-* condition. + */ + @Experimental + String set(String key, String value, SetParams params, ValueCondition condition); + + /** + * Experimental: SET+GET with SetParams and compare-and-* condition. + */ + @Experimental + String setGet(String key, String value, SetParams params, ValueCondition condition); + /** * GetDel Command * Get the value of key and delete the key. This command is similar to GET, except for the fact diff --git a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java index 166bd45478..426a489dc6 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java @@ -406,4 +406,38 @@ public void casCadEndToEndExample_Experimental() { assertEquals(1L, jedis.delex(k, cond2)); assertFalse(jedis.exists(k)); } + + @Test + @SinceRedisVersion("8.3.224") + public void setWithParamsAndIFCondition() { + jedis.del("comb1"); + // missing key: NX + IFNE should set + assertEquals("OK", jedis.set("comb1", "v1", setParams().nx(), ValueCondition.valueNe("x"))); + assertEquals("v1", jedis.get("comb1")); + + // existing key: XX + IFEQ should set + assertEquals("OK", jedis.set("comb1", "v2", setParams().xx(), ValueCondition.valueEq("v1"))); + assertEquals("v2", jedis.get("comb1")); + + // existing key: XX + wrong IFEQ should not set + assertNull(jedis.set("comb1", "no", setParams().xx(), ValueCondition.valueEq("nope"))); + assertEquals("v2", jedis.get("comb1")); + } + + @Test + @SinceRedisVersion("8.3.224") + public void setGetWithParamsAndIFCondition() { + jedis.set("comb2", "v1"); + + // existing key: XX + IFEQ should set and return previous + String prev = jedis.setGet("comb2", "v2", setParams().xx(), ValueCondition.valueEq("v1")); + assertEquals("v1", prev); + assertEquals("v2", jedis.get("comb2")); + + // failing condition: returns current and does not set + prev = jedis.setGet("comb2", "no", setParams().xx(), ValueCondition.valueEq("nope")); + assertEquals("v2", prev); + assertEquals("v2", jedis.get("comb2")); + } + } From 0542991e5b3dd6109833e107e01adb1f7a00eedf Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 16:01:25 +0200 Subject: [PATCH 5/6] Change digest to digestKey --- .../java/redis/clients/jedis/CommandObjects.java | 4 ++-- src/main/java/redis/clients/jedis/Jedis.java | 8 ++++---- .../java/redis/clients/jedis/UnifiedJedis.java | 8 ++++---- .../jedis/commands/StringBinaryCommands.java | 2 +- .../clients/jedis/commands/StringCommands.java | 2 +- .../commands/jedis/StringValuesCommandsTest.java | 8 ++++---- .../unified/StringValuesCommandsTestBase.java | 16 ++++++++-------- .../unified/UnifiedJedisStringCommandsTest.java | 12 ++++++------ 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/main/java/redis/clients/jedis/CommandObjects.java b/src/main/java/redis/clients/jedis/CommandObjects.java index def88060c5..d4a95db8e4 100644 --- a/src/main/java/redis/clients/jedis/CommandObjects.java +++ b/src/main/java/redis/clients/jedis/CommandObjects.java @@ -527,7 +527,7 @@ public final CommandObject get(String key) { return new CommandObject<>(commandArguments(Command.GET).key(key), BuilderFactory.STRING); } - public final CommandObject digest(String key) { + public final CommandObject digestKey(String key) { return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.STRING); } @@ -548,7 +548,7 @@ public final CommandObject getEx(String key, GetExParams params) { return new CommandObject<>(commandArguments(Command.GETEX).key(key).addParams(params), BuilderFactory.STRING); } - public final CommandObject digest(byte[] key) { + public final CommandObject digestKey(byte[] key) { return new CommandObject<>(commandArguments(Command.DIGEST).key(key), BuilderFactory.BINARY); } diff --git a/src/main/java/redis/clients/jedis/Jedis.java b/src/main/java/redis/clients/jedis/Jedis.java index e2d85437c9..842a814835 100644 --- a/src/main/java/redis/clients/jedis/Jedis.java +++ b/src/main/java/redis/clients/jedis/Jedis.java @@ -526,9 +526,9 @@ public byte[] get(final byte[] key) { } @Override - public byte[] digest(final byte[] key) { + public byte[] digestKey(final byte[] key) { checkIsInMultiOrPipeline(); - return connection.executeCommand(commandObjects.digest(key)); + return connection.executeCommand(commandObjects.digestKey(key)); } @Override @@ -5210,9 +5210,9 @@ public String getEx(String key, GetExParams params) { } @Override - public String digest(final String key) { + public String digestKey(final String key) { checkIsInMultiOrPipeline(); - return connection.executeCommand(commandObjects.digest(key)); + return connection.executeCommand(commandObjects.digestKey(key)); } @Override diff --git a/src/main/java/redis/clients/jedis/UnifiedJedis.java b/src/main/java/redis/clients/jedis/UnifiedJedis.java index 8e3fada1c4..db784a714c 100644 --- a/src/main/java/redis/clients/jedis/UnifiedJedis.java +++ b/src/main/java/redis/clients/jedis/UnifiedJedis.java @@ -771,8 +771,8 @@ public String get(String key) { } @Override - public String digest(String key) { - return executeCommand(commandObjects.digest(key)); + public String digestKey(String key) { + return executeCommand(commandObjects.digestKey(key)); } @Override @@ -846,8 +846,8 @@ public byte[] setGet(byte[] key, byte[] value, ValueCondition condition) { } @Override - public byte[] digest(byte[] key) { - return executeCommand(commandObjects.digest(key)); + public byte[] digestKey(byte[] key) { + return executeCommand(commandObjects.digestKey(key)); } @Override diff --git a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java index 58ef5316d9..8f7e6c49c8 100644 --- a/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringBinaryCommands.java @@ -53,7 +53,7 @@ public interface StringBinaryCommands extends BitBinaryCommands { /** Returns the 64-bit XXH3 digest hex (ASCII bytes) of the string value stored at key, or null if missing. */ @Experimental - byte[] digest(byte[] key); + byte[] digestKey(byte[] key); long setrange(byte[] key, long offset, byte[] value); diff --git a/src/main/java/redis/clients/jedis/commands/StringCommands.java b/src/main/java/redis/clients/jedis/commands/StringCommands.java index b13e4fb234..81792e8322 100644 --- a/src/main/java/redis/clients/jedis/commands/StringCommands.java +++ b/src/main/java/redis/clients/jedis/commands/StringCommands.java @@ -110,7 +110,7 @@ public interface StringCommands extends BitCommands { * Returns null if key does not exist. */ @Experimental - String digest(String key); + String digestKey(String key); /** * SetRange Command diff --git a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java index d4631e0350..7076cce20d 100644 --- a/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java +++ b/src/test/java/redis/clients/jedis/commands/jedis/StringValuesCommandsTest.java @@ -44,9 +44,9 @@ public void setAndGet() { @Test @SinceRedisVersion("8.3.224") public void digestSimple() { - assertNull(jedis.digest("missing")); + assertNull(jedis.digestKey("missing")); jedis.set("foo", "bar"); - String hex = jedis.digest("foo"); + String hex = jedis.digestKey("foo"); org.junit.jupiter.api.Assertions.assertNotNull(hex); assertTrue(hex.matches("(?i)[0-9a-f]{16}")); } @@ -268,9 +268,9 @@ public void substr() { @SinceRedisVersion("8.3.224") public void digestBasic() { jedis.del("dg"); - assertNull(jedis.digest("dg")); + assertNull(jedis.digestKey("dg")); jedis.set("dg", "val"); - String hex = jedis.digest("dg"); + String hex = jedis.digestKey("dg"); assertTrue(hex != null && (hex.length() == 16)); } diff --git a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java index 426a489dc6..60b686abd1 100644 --- a/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java +++ b/src/test/java/redis/clients/jedis/commands/unified/StringValuesCommandsTestBase.java @@ -287,9 +287,9 @@ public void lcs() { @SinceRedisVersion("8.3.224") public void digestBasic() { jedis.del("dg"); - assertNull(jedis.digest("dg")); + assertNull(jedis.digestKey("dg")); jedis.set("dg", "val"); - String hex = jedis.digest("dg"); + String hex = jedis.digestKey("dg"); assertTrue(hex != null && (hex.length() == 16)); } @@ -338,11 +338,11 @@ public void setGetWithIFConditions() { @SinceRedisVersion("8.3.224") public void setWithIFDigestConditions() { jedis.set("dk", "abc"); - String dig = jedis.digest("dk"); + String dig = jedis.digestKey("dk"); // IFDEQ matches -> set assertEquals("OK", jedis.set("dk", "def", ValueCondition.digestEq(dig))); - String newDig = jedis.digest("dk"); + String newDig = jedis.digestKey("dk"); assertTrue(newDig != null && newDig.length() == 16); // IFDEQ fails -> no set @@ -371,7 +371,7 @@ public void casCadEndToEndExample() { assertEquals("v1", jedis.get(k)); // 2) Read digest and use it to CAS to v2 - String d1 = jedis.digest(k); + String d1 = jedis.digestKey(k); assertTrue(d1 != null && d1.length() == 16); // Wrong digest must not set @@ -383,7 +383,7 @@ public void casCadEndToEndExample() { assertEquals("v2", jedis.get(k)); // 3) Delete using DELEX guarded by the latest digest - String d2 = jedis.digest(k); + String d2 = jedis.digestKey(k); assertEquals(0L, jedis.delex(k, ValueCondition.digestEq("0000000000000000"))); assertEquals(1L, jedis.delex(k, ValueCondition.digestEq(d2))); assertFalse(jedis.exists(k)); @@ -396,12 +396,12 @@ public void casCadEndToEndExample_Experimental() { assertEquals("OK", jedis.set(k, "v1")); - String d1 = jedis.digest(k); + String d1 = jedis.digestKey(k); ValueCondition cond1 = ValueCondition.digestEq(d1); assertEquals("OK", jedis.set(k, "v2", cond1)); assertEquals("v2", jedis.get(k)); - String d2 = jedis.digest(k); + String d2 = jedis.digestKey(k); ValueCondition cond2 = ValueCondition.digestEq(d2); assertEquals(1L, jedis.delex(k, cond2)); assertFalse(jedis.exists(k)); diff --git a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java index bee353c7f8..e1f6781655 100644 --- a/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java +++ b/src/test/java/redis/clients/jedis/mocked/unified/UnifiedJedisStringCommandsTest.java @@ -873,15 +873,15 @@ public void testDigest() { String key = "key"; String expectedHex = "0123456789abcdef"; - when(commandObjects.digest(key)).thenReturn(stringCommandObject); + when(commandObjects.digestKey(key)).thenReturn(stringCommandObject); when(commandExecutor.executeCommand(stringCommandObject)).thenReturn(expectedHex); - String result = jedis.digest(key); + String result = jedis.digestKey(key); assertThat(result, equalTo(expectedHex)); verify(commandExecutor).executeCommand(stringCommandObject); - verify(commandObjects).digest(key); + verify(commandObjects).digestKey(key); } @Test @@ -889,15 +889,15 @@ public void testDigestBinary() { byte[] key = "key".getBytes(); byte[] expectedHex = "fedcba9876543210".getBytes(); - when(commandObjects.digest(key)).thenReturn(bytesCommandObject); + when(commandObjects.digestKey(key)).thenReturn(bytesCommandObject); when(commandExecutor.executeCommand(bytesCommandObject)).thenReturn(expectedHex); - byte[] result = jedis.digest(key); + byte[] result = jedis.digestKey(key); assertThat(result, equalTo(expectedHex)); verify(commandExecutor).executeCommand(bytesCommandObject); - verify(commandObjects).digest(key); + verify(commandObjects).digestKey(key); } @Test From cbd70f3475613a2fc141e785108dfeff037f8793 Mon Sep 17 00:00:00 2001 From: "aleksandar.todorov" Date: Thu, 6 Nov 2025 16:58:07 +0200 Subject: [PATCH 6/6] Fix arg order in ValueCondition + params cases --- .../java/redis/clients/jedis/CommandObjects.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/redis/clients/jedis/CommandObjects.java b/src/main/java/redis/clients/jedis/CommandObjects.java index d4a95db8e4..f6c81beae6 100644 --- a/src/main/java/redis/clients/jedis/CommandObjects.java +++ b/src/main/java/redis/clients/jedis/CommandObjects.java @@ -498,27 +498,31 @@ public final CommandObject set(byte[] key, byte[] value, SetParams param } public final CommandObject set(String key, String value, SetParams params, ValueCondition cond) { - CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); cond.addTo(ca); + ca.addParams(params); return new CommandObject<>(ca, BuilderFactory.STRING); } public final CommandObject setGet(String key, String value, SetParams params, ValueCondition cond) { - CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); cond.addTo(ca); + ca.addParams(params); ca.add(Keyword.GET); return new CommandObject<>(ca, BuilderFactory.STRING); } public final CommandObject set(byte[] key, byte[] value, SetParams params, ValueCondition cond) { - CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); cond.addTo(ca); + ca.addParams(params); return new CommandObject<>(ca, BuilderFactory.STRING); } public final CommandObject setGet(byte[] key, byte[] value, SetParams params, ValueCondition cond) { - CommandArguments ca = commandArguments(Command.SET).key(key).add(value).addParams(params); + CommandArguments ca = commandArguments(Command.SET).key(key).add(value); cond.addTo(ca); + ca.addParams(params); ca.add(Keyword.GET); return new CommandObject<>(ca, BuilderFactory.BINARY); }