diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java
index 8e35cd26cc..bbe52a63f2 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterConnection.java
@@ -21,6 +21,8 @@
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisClusterInfoCache;
+import redis.clients.jedis.RedisClusterClient;
+import redis.clients.jedis.UnifiedJedis;
import redis.clients.jedis.providers.ClusterConnectionProvider;
import java.time.Duration;
@@ -66,6 +68,8 @@
* Uses the native {@link JedisCluster} api where possible and falls back to direct node communication using
* {@link Jedis} where needed.
*
+ * Pipelines and transactions are not supported in cluster mode.
+ *
* This class is not Thread-safe and instances should not be shared across threads.
*
* @author Christoph Strobl
@@ -76,17 +80,32 @@
* @author Pavel Khokhlov
* @author Liming Deng
* @author John Blum
+ * @author Tihomir Mateev
* @since 1.7
*/
@NullUnmarked
-public class JedisClusterConnection implements RedisClusterConnection {
+public class JedisClusterConnection extends JedisConnection implements RedisClusterConnection {
private static final ExceptionTranslationStrategy EXCEPTION_TRANSLATION = new FallbackExceptionTranslationStrategy(
JedisExceptionConverter.INSTANCE);
private final Log log = LogFactory.getLog(getClass());
- private final JedisCluster cluster;
+ private final UnifiedJedis cluster;
+
+ /**
+ * Cluster-safe invoker that only supports direct execution.
+ * Pipelines and transactions are not supported in cluster mode.
+ */
+ private final JedisInvoker clusterInvoker = new JedisInvoker((directFunction, pipelineFunction, converter, nullDefault) -> {
+ try {
+ Object result = directFunction.apply(getCluster());
+ return result != null ? converter.convert(result) : nullDefault.get();
+ } catch (Exception ex) {
+ throw convertJedisAccessException(ex);
+ }
+ });
+
private final JedisClusterGeoCommands geoCommands = new JedisClusterGeoCommands(this);
private final JedisClusterHashCommands hashCommands = new JedisClusterHashCommands(this);
private final JedisClusterHyperLogLogCommands hllCommands = new JedisClusterHyperLogLogCommands(this);
@@ -104,16 +123,16 @@ public class JedisClusterConnection implements RedisClusterConnection {
private final ClusterCommandExecutor clusterCommandExecutor;
private final boolean disposeClusterCommandExecutorOnClose;
- private volatile @Nullable JedisSubscription subscription;
-
/**
- * Create new {@link JedisClusterConnection} utilizing native connections via {@link JedisCluster}.
+ * Create new {@link JedisClusterConnection} utilizing native connections via {@link UnifiedJedis} based {@link RedisClusterClient}.
*
* @param cluster must not be {@literal null}.
*/
- public JedisClusterConnection(@NonNull JedisCluster cluster) {
+ public JedisClusterConnection(@NonNull UnifiedJedis cluster) {
- Assert.notNull(cluster, "JedisCluster must not be null");
+ super(cluster);
+
+ Assert.notNull(cluster, "UnifiedJedis must not be null");
this.cluster = cluster;
@@ -135,18 +154,18 @@ public JedisClusterConnection(@NonNull JedisCluster cluster) {
}
/**
- * Create new {@link JedisClusterConnection} utilizing native connections via {@link JedisCluster} running commands
+ * Create new {@link JedisClusterConnection} utilizing native connections via {@link UnifiedJedis} running commands
* across the cluster via given {@link ClusterCommandExecutor}. Uses {@link JedisClusterTopologyProvider} by default.
*
* @param cluster must not be {@literal null}.
* @param executor must not be {@literal null}.
*/
- public JedisClusterConnection(@NonNull JedisCluster cluster, @NonNull ClusterCommandExecutor executor) {
+ public JedisClusterConnection(@NonNull UnifiedJedis cluster, @NonNull ClusterCommandExecutor executor) {
this(cluster, executor, new JedisClusterTopologyProvider(cluster));
}
/**
- * Create new {@link JedisClusterConnection} utilizing native connections via {@link JedisCluster} running commands
+ * Create new {@link JedisClusterConnection} utilizing native connections via {@link UnifiedJedis} running commands
* across the cluster via given {@link ClusterCommandExecutor} and using the given {@link ClusterTopologyProvider}.
*
* @param cluster must not be {@literal null}.
@@ -154,10 +173,12 @@ public JedisClusterConnection(@NonNull JedisCluster cluster, @NonNull ClusterCom
* @param topologyProvider must not be {@literal null}.
* @since 2.2
*/
- public JedisClusterConnection(@NonNull JedisCluster cluster, @NonNull ClusterCommandExecutor executor,
+ public JedisClusterConnection(@NonNull UnifiedJedis cluster, @NonNull ClusterCommandExecutor executor,
@NonNull ClusterTopologyProvider topologyProvider) {
- Assert.notNull(cluster, "JedisCluster must not be null");
+ super(cluster);
+
+ Assert.notNull(cluster, "UnifiedJedis must not be null");
Assert.notNull(executor, "ClusterCommandExecutor must not be null");
Assert.notNull(topologyProvider, "ClusterTopologyProvider must not be null");
@@ -354,60 +375,6 @@ public void unwatch() {
throw new InvalidDataAccessApiUsageException("UNWATCH is currently not supported in cluster mode");
}
- @Override
- public boolean isSubscribed() {
- JedisSubscription subscription = this.subscription;
- return (subscription != null && subscription.isAlive());
- }
-
- @Override
- public Subscription getSubscription() {
- return this.subscription;
- }
-
- @Override
- public Long publish(byte @NonNull [] channel, byte @NonNull [] message) {
-
- try {
- return this.cluster.publish(channel, message);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void subscribe(@NonNull MessageListener listener, byte @NonNull [] @NonNull... channels) {
-
- if (isSubscribed()) {
- String message = "Connection already subscribed; use the connection Subscription to cancel or add new channels";
- throw new RedisSubscribedConnectionException(message);
- }
- try {
- JedisMessageListener jedisPubSub = new JedisMessageListener(listener);
- subscription = new JedisSubscription(listener, jedisPubSub, channels, null);
- cluster.subscribe(jedisPubSub, channels);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void pSubscribe(@NonNull MessageListener listener, byte @NonNull [] @NonNull... patterns) {
-
- if (isSubscribed()) {
- String message = "Connection already subscribed; use the connection Subscription to cancel or add new channels";
- throw new RedisSubscribedConnectionException(message);
- }
-
- try {
- JedisMessageListener jedisPubSub = new JedisMessageListener(listener);
- subscription = new JedisSubscription(listener, jedisPubSub, null, patterns);
- cluster.psubscribe(jedisPubSub, patterns);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
@Override
public void select(int dbIndex) {
@@ -631,18 +598,8 @@ public ClusterInfo clusterGetClusterInfo() {
return new ClusterInfo(JedisConverters.toProperties(source));
}
- /*
- * Little helpers to make it work
- */
- protected DataAccessException convertJedisAccessException(Exception cause) {
-
- DataAccessException translated = EXCEPTION_TRANSLATION.translate(cause);
-
- return translated != null ? translated : new RedisSystemException(cause.getMessage(), cause);
- }
-
@Override
- public void close() throws DataAccessException {
+ protected void doClose() {
if (!closed && disposeClusterCommandExecutorOnClose) {
try {
@@ -661,7 +618,7 @@ public boolean isClosed() {
}
@Override
- public JedisCluster getNativeConnection() {
+ public UnifiedJedis getNativeConnection() {
return cluster;
}
@@ -723,7 +680,7 @@ protected interface JedisMultiKeyClusterCommandCallback extends MultiKeyClust
@NullMarked
static class JedisClusterNodeResourceProvider implements ClusterNodeResourceProvider {
- private final JedisCluster cluster;
+ private final UnifiedJedis cluster;
private final ClusterTopologyProvider topologyProvider;
private final @Nullable ClusterConnectionProvider connectionHandler;
@@ -733,7 +690,7 @@ static class JedisClusterNodeResourceProvider implements ClusterNodeResourceProv
* @param cluster should not be {@literal null}.
* @param topologyProvider must not be {@literal null}.
*/
- JedisClusterNodeResourceProvider(JedisCluster cluster, ClusterTopologyProvider topologyProvider) {
+ JedisClusterNodeResourceProvider(UnifiedJedis cluster, ClusterTopologyProvider topologyProvider) {
this.cluster = cluster;
this.topologyProvider = topologyProvider;
@@ -767,7 +724,7 @@ public Jedis getResourceForSpecificNode(RedisClusterNode node) {
private @Nullable ConnectionPool getResourcePoolForSpecificNode(RedisClusterNode node) {
- Map clusterNodes = cluster.getClusterNodes();
+ Map clusterNodes = getClusterNodesMap(cluster);
HostAndPort hap = JedisConverters.toHostAndPort(node);
String key = JedisClusterInfoCache.getNodeKey(hap);
@@ -810,7 +767,7 @@ public void returnResourceForSpecificNode(@NonNull RedisClusterNode node, @NonNu
@NullMarked
public static class JedisClusterTopologyProvider implements ClusterTopologyProvider {
- private final JedisCluster cluster;
+ private final UnifiedJedis cluster;
private final long cacheTimeMs;
@@ -821,7 +778,7 @@ public static class JedisClusterTopologyProvider implements ClusterTopologyProvi
*
* @param cluster must not be {@literal null}.
*/
- public JedisClusterTopologyProvider(JedisCluster cluster) {
+ public JedisClusterTopologyProvider(UnifiedJedis cluster) {
this(cluster, Duration.ofMillis(100));
}
@@ -832,9 +789,9 @@ public JedisClusterTopologyProvider(JedisCluster cluster) {
* @param cacheTimeout must not be {@literal null}.
* @since 2.2
*/
- public JedisClusterTopologyProvider(JedisCluster cluster, Duration cacheTimeout) {
+ public JedisClusterTopologyProvider(UnifiedJedis cluster, Duration cacheTimeout) {
- Assert.notNull(cluster, "JedisCluster must not be null");
+ Assert.notNull(cluster, "UnifiedJedis must not be null");
Assert.notNull(cacheTimeout, "Cache timeout must not be null");
Assert.isTrue(!cacheTimeout.isNegative(), "Cache timeout must not be negative");
@@ -852,7 +809,7 @@ public ClusterTopology getTopology() {
}
Map errors = new LinkedHashMap<>();
- List> list = new ArrayList<>(cluster.getClusterNodes().entrySet());
+ List> list = new ArrayList<>(getClusterNodesMap(cluster).entrySet());
Collections.shuffle(list);
@@ -885,7 +842,7 @@ public ClusterTopology getTopology() {
*
* @return {@literal true} to use the cached {@link ClusterTopology}; {@literal false} to fetch a new cluster
* topology.
- * @see #JedisClusterTopologyProvider(JedisCluster, Duration)
+ * @see #JedisClusterTopologyProvider(UnifiedJedis, Duration)
* @since 3.3.4
*/
protected boolean shouldUseCachedValue(@Nullable JedisClusterTopology topology) {
@@ -923,10 +880,43 @@ long getMaxTime() {
}
}
- protected JedisCluster getCluster() {
+ protected UnifiedJedis getCluster() {
return cluster;
}
+ @Override
+ public UnifiedJedis getJedis() {
+ return cluster;
+ }
+
+ /**
+ * Obtain a {@link JedisInvoker} to call Jedis methods on the cluster.
+ *
+ * This invoker only supports direct execution mode. Pipelines and transactions
+ * are not supported in cluster mode.
+ *
+ * @return the {@link JedisInvoker}.
+ * @since 3.5
+ */
+ @Override
+ public JedisInvoker invoke() {
+ return this.clusterInvoker;
+ }
+
+ /**
+ * Obtain a {@link JedisInvoker} for status commands on the cluster.
+ *
+ * In cluster mode, this returns the same invoker as {@link #invoke()} since
+ * pipelines and transactions are not supported.
+ *
+ * @return the {@link JedisInvoker}.
+ * @since 3.5
+ */
+ @Override
+ public JedisInvoker invokeStatus() {
+ return this.clusterInvoker;
+ }
+
protected ClusterCommandExecutor getClusterCommandExecutor() {
return clusterCommandExecutor;
}
@@ -934,4 +924,24 @@ protected ClusterCommandExecutor getClusterCommandExecutor() {
protected ClusterTopologyProvider getTopologyProvider() {
return topologyProvider;
}
+
+ /**
+ * Get cluster nodes map from a {@link UnifiedJedis} instance. This method handles both
+ * {@link JedisCluster} and {@link RedisClusterClient} by invoking the {@code getClusterNodes()}
+ * method via reflection since it's not part of the {@link UnifiedJedis} base class.
+ *
+ * @param cluster the cluster client (either JedisCluster or RedisClusterClient)
+ * @return map of node addresses to connection pools
+ */
+ @SuppressWarnings("unchecked")
+ static Map getClusterNodesMap(UnifiedJedis cluster) {
+ if (cluster instanceof JedisCluster jedisCluster) {
+ return jedisCluster.getClusterNodes();
+ }
+ if (cluster instanceof RedisClusterClient redisClusterClient) {
+ return redisClusterClient.getClusterNodes();
+ }
+ throw new IllegalArgumentException(
+ "Unsupported UnifiedJedis type: " + cluster.getClass().getName() + ". Expected JedisCluster or RedisClusterClient.");
+ }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java
index d32395e799..5b19208cab 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterGeoCommands.java
@@ -15,270 +15,27 @@
*/
package org.springframework.data.redis.connection.jedis;
-import redis.clients.jedis.GeoCoordinate;
-import redis.clients.jedis.args.GeoUnit;
-import redis.clients.jedis.params.GeoRadiusParam;
-import redis.clients.jedis.params.GeoSearchParam;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullUnmarked;
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.geo.Circle;
-import org.springframework.data.geo.Distance;
-import org.springframework.data.geo.GeoResults;
-import org.springframework.data.geo.Metric;
-import org.springframework.data.geo.Point;
+
import org.springframework.data.redis.connection.RedisGeoCommands;
-import org.springframework.data.redis.domain.geo.GeoReference;
-import org.springframework.data.redis.domain.geo.GeoShape;
-import org.springframework.util.Assert;
/**
+ * Cluster {@link RedisGeoCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Tihomir Mateev
* @since 2.0
*/
@NullUnmarked
-class JedisClusterGeoCommands implements RedisGeoCommands {
-
- private final JedisClusterConnection connection;
-
- JedisClusterGeoCommands(JedisClusterConnection connection) {
-
- Assert.notNull(connection, "Connection must not be null");
- this.connection = connection;
- }
-
- @Override
- public Long geoAdd(byte @NonNull [] key, @NonNull Point point, byte @NonNull [] member) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(point, "Point must not be null");
- Assert.notNull(member, "Member must not be null");
-
- try {
- return connection.getCluster().geoadd(key, point.getX(), point.getY(), member);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long geoAdd(byte @NonNull [] key, @NonNull Map memberCoordinateMap) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(memberCoordinateMap, "MemberCoordinateMap must not be null");
-
- Map redisGeoCoordinateMap = new HashMap<>();
- for (byte[] mapKey : memberCoordinateMap.keySet()) {
- redisGeoCoordinateMap.put(mapKey, JedisConverters.toGeoCoordinate(memberCoordinateMap.get(mapKey)));
- }
-
- try {
- return connection.getCluster().geoadd(key, redisGeoCoordinateMap);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long geoAdd(byte @NonNull [] key, @NonNull Iterable<@NonNull GeoLocation> locations) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(locations, "Locations must not be null");
-
- Map redisGeoCoordinateMap = new HashMap<>();
- for (GeoLocation location : locations) {
- redisGeoCoordinateMap.put(location.getName(), JedisConverters.toGeoCoordinate(location.getPoint()));
- }
-
- try {
- return connection.getCluster().geoadd(key, redisGeoCoordinateMap);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Distance geoDist(byte @NonNull [] key, byte @NonNull [] member1, byte @NonNull [] member2) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(member1, "Member1 must not be null");
- Assert.notNull(member2, "Member2 must not be null");
-
- try {
- return JedisConverters.distanceConverterForMetric(DistanceUnit.METERS)
- .convert(connection.getCluster().geodist(key, member1, member2));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Distance geoDist(byte @NonNull [] key, byte @NonNull [] member1, byte @NonNull [] member2,
- @NonNull Metric metric) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(member1, "Member1 must not be null");
- Assert.notNull(member2, "Member2 must not be null");
- Assert.notNull(metric, "Metric must not be null");
-
- GeoUnit geoUnit = JedisConverters.toGeoUnit(metric);
- try {
- return JedisConverters.distanceConverterForMetric(metric)
- .convert(connection.getCluster().geodist(key, member1, member2, geoUnit));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List<@NonNull String> geoHash(byte @NonNull [] key, byte @NonNull [] @NonNull... members) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(members, "Members must not be null");
- Assert.noNullElements(members, "Members must not contain null");
-
- try {
- return JedisConverters.toStrings(connection.getCluster().geohash(key, members));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List<@NonNull Point> geoPos(byte @NonNull [] key, byte @NonNull [] @NonNull... members) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(members, "Members must not be null");
- Assert.noNullElements(members, "Members must not contain null");
-
- try {
- return JedisConverters.geoCoordinateToPointConverter().convert(connection.getCluster().geopos(key, members));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public GeoResults> geoRadius(byte @NonNull [] key, @NonNull Circle within) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(within, "Within must not be null");
-
- try {
- return JedisConverters.geoRadiusResponseToGeoResultsConverter(within.getRadius().getMetric())
- .convert(connection.getCluster().georadius(key, within.getCenter().getX(), within.getCenter().getY(),
- within.getRadius().getValue(), JedisConverters.toGeoUnit(within.getRadius().getMetric())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public GeoResults> geoRadius(byte @NonNull [] key, @NonNull Circle within,
- @NonNull GeoRadiusCommandArgs args) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(within, "Within must not be null");
- Assert.notNull(args, "Args must not be null");
-
- GeoRadiusParam geoRadiusParam = JedisConverters.toGeoRadiusParam(args);
-
- try {
- return JedisConverters.geoRadiusResponseToGeoResultsConverter(within.getRadius().getMetric())
- .convert(connection.getCluster().georadius(key, within.getCenter().getX(), within.getCenter().getY(),
- within.getRadius().getValue(), JedisConverters.toGeoUnit(within.getRadius().getMetric()),
- geoRadiusParam));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public GeoResults> geoRadiusByMember(byte @NonNull [] key, byte @NonNull [] member,
- @NonNull Distance radius) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(member, "Member must not be null");
- Assert.notNull(radius, "Radius must not be null");
-
- GeoUnit geoUnit = JedisConverters.toGeoUnit(radius.getMetric());
- try {
- return JedisConverters.geoRadiusResponseToGeoResultsConverter(radius.getMetric())
- .convert(connection.getCluster().georadiusByMember(key, member, radius.getValue(), geoUnit));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public GeoResults> geoRadiusByMember(byte @NonNull [] key, byte @NonNull [] member,
- @NonNull Distance radius, @NonNull GeoRadiusCommandArgs args) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(member, "Member must not be null");
- Assert.notNull(radius, "Radius must not be null");
- Assert.notNull(args, "Args must not be null");
-
- GeoUnit geoUnit = JedisConverters.toGeoUnit(radius.getMetric());
- redis.clients.jedis.params.GeoRadiusParam geoRadiusParam = JedisConverters.toGeoRadiusParam(args);
-
- try {
- return JedisConverters.geoRadiusResponseToGeoResultsConverter(radius.getMetric())
- .convert(connection.getCluster().georadiusByMember(key, member, radius.getValue(), geoUnit, geoRadiusParam));
-
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long geoRemove(byte @NonNull [] key, byte @NonNull [] @NonNull... members) {
- return connection.zRem(key, members);
- }
-
- @Override
- public GeoResults> geoSearch(byte @NonNull [] key, @NonNull GeoReference reference,
- @NonNull GeoShape predicate, @NonNull GeoSearchCommandArgs args) {
-
- Assert.notNull(key, "Key must not be null");
- GeoSearchParam params = JedisConverters.toGeoSearchParams(reference, predicate, args);
-
- try {
-
- return JedisConverters.geoRadiusResponseToGeoResultsConverter(predicate.getMetric())
- .convert(connection.getCluster().geosearch(key, params));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long geoSearchStore(byte @NonNull [] destKey, byte @NonNull [] key, @NonNull GeoReference reference,
- @NonNull GeoShape predicate, @NonNull GeoSearchStoreCommandArgs args) {
-
- Assert.notNull(destKey, "Destination Key must not be null");
- Assert.notNull(key, "Key must not be null");
- GeoSearchParam params = JedisConverters.toGeoSearchParams(reference, predicate, args);
-
- try {
-
- if (args.isStoreDistance()) {
- return connection.getCluster().geosearchStoreStoreDist(destKey, key, params);
- }
-
- return connection.getCluster().geosearchStore(destKey, key, params);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
+class JedisClusterGeoCommands extends JedisGeoCommands {
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
+ JedisClusterGeoCommands(@NonNull JedisClusterConnection connection) {
+ super(connection);
}
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
index e2c0f46587..1a53e3c548 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java
@@ -15,31 +15,28 @@
*/
package org.springframework.data.redis.connection.jedis;
-import redis.clients.jedis.args.ExpiryOption;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
-import java.util.Set;
-import java.util.concurrent.TimeUnit;
import org.jspecify.annotations.NonNull;
-import org.jspecify.annotations.Nullable;
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.redis.connection.ExpirationOptions;
+import org.jspecify.annotations.NullUnmarked;
+
import org.springframework.data.redis.connection.RedisHashCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanCursor;
import org.springframework.data.redis.core.ScanIteration;
import org.springframework.data.redis.core.ScanOptions;
-import org.springframework.data.redis.core.types.Expiration;
import org.springframework.util.Assert;
/**
* Cluster {@link RedisHashCommands} implementation for Jedis.
+ *
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
*
* @author Christoph Strobl
* @author Mark Paluch
@@ -47,233 +44,16 @@
* @author Tihomir Mateev
* @since 2.0
*/
-class JedisClusterHashCommands implements RedisHashCommands {
-
- private final JedisClusterConnection connection;
+@NullUnmarked
+class JedisClusterHashCommands extends JedisHashCommands {
JedisClusterHashCommands(JedisClusterConnection connection) {
- this.connection = connection;
- }
-
- @Override
- public Boolean hSet(byte[] key, byte[] field, byte[] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return JedisConverters.toBoolean(connection.getCluster().hset(key, field, value));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean hSetNX(byte[] key, byte[] field, byte[] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return JedisConverters.toBoolean(connection.getCluster().hsetnx(key, field, value));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] hGet(byte[] key, byte[] field) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
-
- try {
- return connection.getCluster().hget(key, field);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hMGet(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hmget(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void hMSet(byte[] key, Map hashes) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(hashes, "Hashes must not be null");
-
- try {
- connection.getCluster().hmset(key, hashes);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long hIncrBy(byte[] key, byte[] field, long delta) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
-
- try {
- return connection.getCluster().hincrBy(key, field, delta);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Double hIncrBy(byte[] key, byte[] field, double delta) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
-
- try {
- return connection.getCluster().hincrByFloat(key, field, delta);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte @Nullable [] hRandField(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().hrandfield(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Nullable
- @Override
- public Entry hRandFieldWithValues(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- List> mapEntryList = connection.getCluster().hrandfieldWithValues(key, 1);
- return mapEntryList.isEmpty() ? null : mapEntryList.get(0);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Nullable
- @Override
- public List hRandField(byte[] key, long count) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().hrandfield(key, count);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Nullable
- @Override
- public List> hRandFieldWithValues(byte[] key, long count) {
-
- try {
- return connection.getCluster().hrandfieldWithValues(key, count);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ super(connection);
}
@Override
- public Boolean hExists(byte[] key, byte[] field) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
-
- try {
- return connection.getCluster().hexists(key, field);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long hDel(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hdel(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long hLen(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().hlen(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Set hKeys(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().hkeys(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hVals(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return new ArrayList<>(connection.getCluster().hvals(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Map hGetAll(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().hgetAll(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Cursor> hScan(byte[] key, ScanOptions options) {
+ public Cursor<@NonNull Entry> hScan(byte @NonNull [] key,
+ @NonNull ScanOptions options) {
Assert.notNull(key, "Key must not be null");
@@ -284,191 +64,10 @@ protected ScanIteration> doScan(CursorId cursorId, ScanOpt
ScanParams params = JedisConverters.toScanParams(options);
- ScanResult> result = connection.getCluster().hscan(key, JedisConverters.toBytes(cursorId),
- params);
+ ScanResult> result = getConnection().getJedis().hscan(key,
+ JedisConverters.toBytes(cursorId), params);
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
}
}.open();
}
-
- @Override
- public List hExpire(byte[] key, long seconds, ExpirationOptions.Condition condition, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return connection.getCluster().hexpire(key, seconds, fields);
- }
-
- return connection.getCluster().hexpire(key, seconds, ExpiryOption.valueOf(condition.name()), fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hpExpire(byte[] key, long millis, ExpirationOptions.Condition condition, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return connection.getCluster().hpexpire(key, millis, fields);
- }
-
- return connection.getCluster().hpexpire(key, millis, ExpiryOption.valueOf(condition.name()), fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hExpireAt(byte[] key, long unixTime, ExpirationOptions.Condition condition, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
-
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return connection.getCluster().hexpireAt(key, unixTime, fields);
- }
-
- return connection.getCluster().hexpireAt(key, unixTime, ExpiryOption.valueOf(condition.name()), fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hpExpireAt(byte[] key, long unixTimeInMillis, ExpirationOptions.Condition condition,
- byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
-
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return connection.getCluster().hpexpireAt(key, unixTimeInMillis, fields);
- }
-
- return connection.getCluster().hpexpireAt(key, unixTimeInMillis, ExpiryOption.valueOf(condition.name()), fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hPersist(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hpersist(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hTtl(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().httl(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hTtl(byte[] key, TimeUnit timeUnit, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().httl(key, fields).stream()
- .map(it -> it != null ? timeUnit.convert(it, TimeUnit.SECONDS) : null).toList();
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hpTtl(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hpttl(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hGetDel(byte[] key, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hgetdel(key, fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List hGetEx(byte[] key, @Nullable Expiration expiration, byte[]... fields) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(fields, "Fields must not be null");
-
- try {
- return connection.getCluster().hgetex(key, JedisConverters.toHGetExParams(expiration), fields);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean hSetEx(byte[] key, Map hashes, @NonNull HashFieldSetOption condition,
- @Nullable Expiration expiration) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(hashes, "Fields must not be null");
- Assert.notNull(condition, "Condition must not be null");
-
- try {
- return JedisConverters.toBoolean(
- connection.getCluster().hsetex(key, JedisConverters.toHSetExParams(condition, expiration), hashes));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Nullable
- @Override
- public Long hStrLen(byte[] key, byte[] field) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(field, "Field must not be null");
-
- return connection.getCluster().hstrlen(key, field);
- }
-
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
-
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java
index d33674f06f..f912870a8c 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHyperLogLogCommands.java
@@ -15,7 +15,9 @@
*/
package org.springframework.data.redis.connection.jedis;
-import org.springframework.dao.DataAccessException;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.NullUnmarked;
+
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
import org.springframework.data.redis.connection.RedisHyperLogLogCommands;
@@ -23,51 +25,39 @@
import org.springframework.util.Assert;
/**
+ * Cluster {@link RedisHyperLogLogCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Christoph Strobl
* @author Mark Paluch
+ * @author Tihomir Mateev
* @since 2.0
*/
-class JedisClusterHyperLogLogCommands implements RedisHyperLogLogCommands {
-
- private final JedisClusterConnection connection;
-
- JedisClusterHyperLogLogCommands(JedisClusterConnection connection) {
- this.connection = connection;
- }
-
- @Override
- public Long pfAdd(byte[] key, byte[]... values) {
+@NullUnmarked
+class JedisClusterHyperLogLogCommands extends JedisHyperLogLogCommands {
- Assert.notEmpty(values, "PFADD requires at least one non 'null' value");
- Assert.noNullElements(values, "Values for PFADD must not contain 'null'");
-
- try {
- return connection.getCluster().pfadd(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ JedisClusterHyperLogLogCommands(@NonNull JedisClusterConnection connection) {
+ super(connection);
}
@Override
- public Long pfCount(byte[]... keys) {
+ public Long pfCount(byte @NonNull [] @NonNull... keys) {
Assert.notEmpty(keys, "PFCOUNT requires at least one non 'null' key");
Assert.noNullElements(keys, "Keys for PFCOUNT must not contain 'null'");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
-
- try {
- return connection.getCluster().pfcount(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
-
+ return super.pfCount(keys);
}
+
throw new InvalidDataAccessApiUsageException("All keys must map to same slot for pfcount in cluster mode");
}
@Override
- public void pfMerge(byte[] destinationKey, byte[]... sourceKeys) {
+ public void pfMerge(byte @NonNull [] destinationKey, byte @NonNull [] @NonNull... sourceKeys) {
Assert.notNull(destinationKey, "Destination key must not be null");
Assert.notNull(sourceKeys, "Source keys must not be null");
@@ -76,18 +66,10 @@ public void pfMerge(byte[] destinationKey, byte[]... sourceKeys) {
byte[][] allKeys = ByteUtils.mergeArrays(destinationKey, sourceKeys);
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
- try {
- connection.getCluster().pfmerge(destinationKey, sourceKeys);
- return;
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ super.pfMerge(destinationKey, sourceKeys);
+ return;
}
throw new InvalidDataAccessApiUsageException("All keys must map to same slot for pfmerge in cluster mode");
}
-
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
index dcafdb621c..d0e6f91cca 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java
@@ -15,39 +15,28 @@
*/
package org.springframework.data.redis.connection.jedis;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.args.ExpiryOption;
-import redis.clients.jedis.params.RestoreParams;
+import redis.clients.jedis.commands.JedisBinaryCommands;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
-import java.util.concurrent.TimeUnit;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
-import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
-import org.springframework.data.redis.connection.CompareCondition;
-import org.springframework.data.redis.connection.DataType;
-import org.springframework.data.redis.connection.ExpirationOptions;
import org.springframework.data.redis.connection.RedisClusterNode;
import org.springframework.data.redis.connection.RedisKeyCommands;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.SortParameters;
-import org.springframework.data.redis.connection.ValueEncoding;
-import org.springframework.data.redis.connection.convert.Converters;
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisClusterCommandCallback;
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisMultiKeyClusterCommandCallback;
import org.springframework.data.redis.core.Cursor;
@@ -58,30 +47,29 @@
import org.springframework.util.ObjectUtils;
/**
+ * Cluster {@link RedisKeyCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Christoph Strobl
* @author Mark Paluch
* @author ihaohong
* @author Dan Smith
+ * @author Tihomir Mateev
* @since 2.0
*/
@NullUnmarked
-class JedisClusterKeyCommands implements RedisKeyCommands {
+class JedisClusterKeyCommands extends JedisKeyCommands {
private final JedisClusterConnection connection;
JedisClusterKeyCommands(JedisClusterConnection connection) {
+ super(connection);
this.connection = connection;
}
- @Override
- public Boolean copy(byte @NonNull [] sourceKey, byte @NonNull [] targetKey, boolean replace) {
-
- Assert.notNull(sourceKey, "source key must not be null");
- Assert.notNull(targetKey, "target key must not be null");
-
- return connection.getCluster().copy(sourceKey, targetKey, replace);
- }
-
@Override
public Long del(byte @NonNull [] @NonNull... keys) {
@@ -89,51 +77,26 @@ public Long del(byte @NonNull [] @NonNull... keys) {
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().del(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.del(keys);
}
return (long) connection.getClusterCommandExecutor()
- .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) Jedis::del, Arrays.asList(keys))
+ .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) JedisBinaryCommands::del, Arrays.asList(keys))
.resultsAsList().size();
}
- @Override
- public Boolean delex(byte @NonNull [] key, @NonNull CompareCondition condition) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(condition, "CommandCondition must not be null");
-
- try {
- return JedisConverters
- .toBoolean(connection.getCluster().delex(key, JedisConverters.toCompareCondition(condition)));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
@Override
public Long unlink(byte @NonNull [] @NonNull... keys) {
Assert.notNull(keys, "Keys must not be null");
- return connection. execute("UNLINK", Arrays.asList(keys), Collections.emptyList()).stream()
- .mapToLong(val -> val).sum();
- }
-
- @Override
- public DataType type(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return JedisConverters.toDataType(connection.getCluster().type(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
+ if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
+ return super.unlink(keys);
}
+
+ return connection.getClusterCommandExecutor()
+ .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) JedisBinaryCommands::unlink, Arrays.asList(keys))
+ .resultsAsList().stream().mapToLong(val -> val).sum();
}
@Override
@@ -141,8 +104,13 @@ public Long touch(byte @NonNull [] @NonNull... keys) {
Assert.notNull(keys, "Keys must not be null");
- return connection. execute("TOUCH", Arrays.asList(keys), Collections.emptyList()).stream()
- .mapToLong(val -> val).sum();
+ if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
+ return super.touch(keys);
+ }
+
+ return connection.getClusterCommandExecutor()
+ .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) JedisBinaryCommands::touch, Arrays.asList(keys))
+ .resultsAsList().stream().mapToLong(val -> val).sum();
}
@Override
@@ -161,6 +129,13 @@ public Long touch(byte @NonNull [] @NonNull... keys) {
return keys;
}
+ /**
+ * Get keys matching pattern from specific cluster node.
+ *
+ * @param node must not be {@literal null}.
+ * @param pattern must not be {@literal null}.
+ * @return never {@literal null}.
+ */
public Set keys(@NonNull RedisClusterNode node, byte @NonNull [] pattern) {
Assert.notNull(node, "RedisClusterNode must not be null");
@@ -231,12 +206,18 @@ public byte[] randomKey() {
return null;
}
+ /**
+ * Get a random key from a specific cluster node.
+ *
+ * @param node must not be {@literal null}.
+ * @return the random key or {@literal null}.
+ */
public byte[] randomKey(@NonNull RedisClusterNode node) {
Assert.notNull(node, "RedisClusterNode must not be null");
return connection.getClusterCommandExecutor()
- .executeCommandOnSingleNode((JedisClusterCommandCallback) Jedis::randomBinaryKey, node).getValue();
+ .executeCommandOnSingleNode((JedisClusterCommandCallback) JedisBinaryCommands::randomBinaryKey, node).getValue();
}
@Override
@@ -246,13 +227,8 @@ public void rename(byte @NonNull [] oldKey, byte @NonNull [] newKey) {
Assert.notNull(newKey, "New key must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(oldKey, newKey)) {
-
- try {
- connection.getCluster().rename(oldKey, newKey);
- return;
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ super.rename(oldKey, newKey);
+ return;
}
byte[] value = dump(oldKey);
@@ -271,12 +247,7 @@ public Boolean renameNX(byte @NonNull [] sourceKey, byte @NonNull [] targetKey)
Assert.notNull(targetKey, "Target key must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(sourceKey, targetKey)) {
-
- try {
- return JedisConverters.toBoolean(connection.getCluster().renamenx(sourceKey, targetKey));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.renameNX(sourceKey, targetKey);
}
byte[] value = dump(sourceKey);
@@ -290,192 +261,18 @@ public Boolean renameNX(byte @NonNull [] sourceKey, byte @NonNull [] targetKey)
return Boolean.FALSE;
}
- @Override
- public Boolean expire(byte @NonNull [] key, long seconds, ExpirationOptions.@NonNull Condition condition) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return JedisConverters.toBoolean(connection.getCluster().expire(key, seconds));
- }
-
- return JedisConverters
- .toBoolean(connection.getCluster().expire(key, seconds, ExpiryOption.valueOf(condition.name())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean pExpire(byte @NonNull [] key, long millis, ExpirationOptions.@NonNull Condition condition) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return JedisConverters.toBoolean(connection.getCluster().pexpire(key, millis));
- }
- return JedisConverters
- .toBoolean(connection.getCluster().pexpire(key, millis, ExpiryOption.valueOf(condition.name())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean expireAt(byte @NonNull [] key, long unixTime, ExpirationOptions.@NonNull Condition condition) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return JedisConverters.toBoolean(connection.getCluster().expireAt(key, unixTime));
- }
-
- return JedisConverters
- .toBoolean(connection.getCluster().expireAt(key, unixTime, ExpiryOption.valueOf(condition.name())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean pExpireAt(byte @NonNull [] key, long unixTimeInMillis,
- ExpirationOptions.@NonNull Condition condition) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- if (condition == ExpirationOptions.Condition.ALWAYS) {
- return JedisConverters.toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis));
- }
-
- return JedisConverters
- .toBoolean(connection.getCluster().pexpireAt(key, unixTimeInMillis, ExpiryOption.valueOf(condition.name())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean persist(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return JedisConverters.toBoolean(connection.getCluster().persist(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
@Override
public Boolean move(byte @NonNull [] key, int dbIndex) {
throw new InvalidDataAccessApiUsageException("Cluster mode does not allow moving keys");
}
- @Override
- public Long ttl(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().ttl(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long ttl(byte @NonNull [] key, @NonNull TimeUnit timeUnit) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return Converters.secondsToTimeUnit(connection.getCluster().ttl(key), timeUnit);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long pTtl(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().pttl(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long pTtl(byte @NonNull [] key, @NonNull TimeUnit timeUnit) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return Converters.millisecondsToTimeUnit(connection.getCluster().pttl(key), timeUnit);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] dump(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().dump(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void restore(byte @NonNull [] key, long ttlInMillis, byte @NonNull [] serializedValue, boolean replace) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(serializedValue, "Serialized value must not be null");
-
- RestoreParams restoreParams = RestoreParams.restoreParams();
-
- if (replace) {
- restoreParams = restoreParams.replace();
- }
- try {
- connection.getCluster().restore(key, ttlInMillis, serializedValue, restoreParams);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List sort(byte @NonNull [] key, @Nullable SortParameters params) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().sort(key, JedisConverters.toSortingParams(params));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
@Override
public Long sort(byte @NonNull [] key, @Nullable SortParameters params, byte @NonNull [] storeKey) {
Assert.notNull(key, "Key must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(key, storeKey)) {
- try {
- return connection.getCluster().sort(key, JedisConverters.toSortingParams(params), storeKey);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sort(key, params, storeKey);
}
List sorted = sort(key, params);
@@ -492,56 +289,11 @@ public Long exists(byte @NonNull [] @NonNull... keys) {
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().exists(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.exists(keys);
}
return connection.getClusterCommandExecutor()
- .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) Jedis::exists, Arrays.asList(keys))
+ .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback) JedisBinaryCommands::exists, Arrays.asList(keys))
.resultsAsList().stream().mapToLong(val -> ObjectUtils.nullSafeEquals(val, Boolean.TRUE) ? 1 : 0).sum();
}
-
- @Override
- public ValueEncoding encodingOf(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return JedisConverters.toEncoding(connection.getCluster().objectEncoding(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Duration idletime(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return Converters.secondsToDuration(connection.getCluster().objectIdletime(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long refcount(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().objectRefcount(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
-
- }
-
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java
index d022fa607a..b98911a1b4 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterListCommands.java
@@ -15,17 +15,13 @@
*/
package org.springframework.data.redis.connection.jedis;
-import redis.clients.jedis.args.ListDirection;
-import redis.clients.jedis.params.LPosParams;
-
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jspecify.annotations.NonNull;
import org.jspecify.annotations.NullUnmarked;
-import org.jspecify.annotations.Nullable;
-import org.springframework.dao.DataAccessException;
+
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
import org.springframework.data.redis.connection.RedisListCommands;
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisMultiKeyClusterCommandCallback;
@@ -33,266 +29,29 @@
import org.springframework.util.CollectionUtils;
/**
+ * Cluster {@link RedisListCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Christoph Strobl
* @author Mark Paluch
* @author Jot Zhao
* @author dengliming
+ * @author Tihomir Mateev
* @since 2.0
*/
@NullUnmarked
-class JedisClusterListCommands implements RedisListCommands {
+class JedisClusterListCommands extends JedisListCommands {
private final JedisClusterConnection connection;
JedisClusterListCommands(@NonNull JedisClusterConnection connection) {
+ super(connection);
this.connection = connection;
}
- @Override
- public Long rPush(byte @NonNull [] key, byte @NonNull [] @NonNull... values) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().rpush(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List lPos(byte @NonNull [] key, byte @NonNull [] element, @Nullable Integer rank,
- @Nullable Integer count) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(element, "Element must not be null");
-
- LPosParams params = new LPosParams();
- if (rank != null) {
- params.rank(rank);
- }
-
- try {
-
- if (count != null) {
- return connection.getCluster().lpos(key, element, params, count);
- }
-
- Long value = connection.getCluster().lpos(key, element, params);
- return value != null ? Collections.singletonList(value) : Collections.emptyList();
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long lPush(byte @NonNull [] key, byte @NonNull [] @NonNull... values) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(values, "Values must not be null");
- Assert.noNullElements(values, "Values must not contain null elements");
-
- try {
- return connection.getCluster().lpush(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long rPushX(byte @NonNull [] key, byte @NonNull [] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return connection.getCluster().rpushx(key, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long lPushX(byte @NonNull [] key, byte @NonNull [] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return connection.getCluster().lpushx(key, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long lLen(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().llen(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List lRange(byte @NonNull [] key, long start, long end) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().lrange(key, start, end);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void lTrim(byte @NonNull [] key, long start, long end) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- connection.getCluster().ltrim(key, start, end);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] lIndex(byte @NonNull [] key, long index) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().lindex(key, index);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long lInsert(byte @NonNull [] key, @NonNull Position where, byte @NonNull [] pivot, byte @NonNull [] value) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().linsert(key, JedisConverters.toListPosition(where), pivot, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] lMove(byte @NonNull [] sourceKey, byte @NonNull [] destinationKey, @NonNull Direction from,
- @NonNull Direction to) {
-
- Assert.notNull(sourceKey, "Source key must not be null");
- Assert.notNull(destinationKey, "Destination key must not be null");
- Assert.notNull(from, "From direction must not be null");
- Assert.notNull(to, "To direction must not be null");
-
- try {
- return connection.getCluster().lmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
- ListDirection.valueOf(to.name()));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] bLMove(byte @NonNull [] sourceKey, byte @NonNull [] destinationKey, @NonNull Direction from,
- @NonNull Direction to, double timeout) {
-
- Assert.notNull(sourceKey, "Source key must not be null");
- Assert.notNull(destinationKey, "Destination key must not be null");
- Assert.notNull(from, "From direction must not be null");
- Assert.notNull(to, "To direction must not be null");
-
- try {
- return connection.getCluster().blmove(sourceKey, destinationKey, ListDirection.valueOf(from.name()),
- ListDirection.valueOf(to.name()), timeout);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public void lSet(byte @NonNull [] key, long index, byte @NonNull [] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- connection.getCluster().lset(key, index, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long lRem(byte @NonNull [] key, long count, byte @NonNull [] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return connection.getCluster().lrem(key, count, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] lPop(byte @NonNull [] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().lpop(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List lPop(byte @NonNull [] key, long count) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().lpop(key, (int) count);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] rPop(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().rpop(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List rPop(byte @NonNull [] key, long count) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().rpop(key, (int) count);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
@Override
public List bLPop(int timeout, byte @NonNull [] @NonNull... keys) {
@@ -300,11 +59,7 @@ public byte[] rPop(byte[] key) {
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().blpop(timeout, keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.bLPop(timeout, keys);
}
return connection.getClusterCommandExecutor()
@@ -321,11 +76,7 @@ public byte[] rPop(byte[] key) {
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().brpop(timeout, keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.bRPop(timeout, keys);
}
return connection.getClusterCommandExecutor()
@@ -342,11 +93,7 @@ public byte[] rPopLPush(byte @NonNull [] srcKey, byte @NonNull [] dstKey) {
Assert.notNull(dstKey, "Destination key must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(srcKey, dstKey)) {
- try {
- return connection.getCluster().rpoplpush(srcKey, dstKey);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.rPopLPush(srcKey, dstKey);
}
byte[] val = rPop(srcKey);
@@ -361,11 +108,7 @@ public byte[] bRPopLPush(int timeout, byte @NonNull [] srcKey, byte @NonNull []
Assert.notNull(dstKey, "Destination key must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(srcKey, dstKey)) {
- try {
- return connection.getCluster().brpoplpush(srcKey, dstKey, timeout);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.bRPopLPush(timeout, srcKey, dstKey);
}
List val = bRPop(timeout, srcKey);
@@ -373,11 +116,6 @@ public byte[] bRPopLPush(int timeout, byte @NonNull [] srcKey, byte @NonNull []
lPush(dstKey, val.get(1));
return val.get(1);
}
-
return null;
}
-
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java
index 063f186268..9d3c5edd05 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterScriptingCommands.java
@@ -16,7 +16,6 @@
package org.springframework.data.redis.connection.jedis;
import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisCluster;
import java.util.List;
@@ -25,20 +24,27 @@
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.ClusterCommandExecutor;
import org.springframework.data.redis.connection.RedisScriptingCommands;
-import org.springframework.data.redis.connection.ReturnType;
import org.springframework.util.Assert;
/**
+ * Cluster {@link RedisScriptingCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Mark Paluch
* @author Pavel Khokhlov
+ * @author Tihomir Mateev
* @since 2.0
*/
@NullUnmarked
-class JedisClusterScriptingCommands implements RedisScriptingCommands {
+class JedisClusterScriptingCommands extends JedisScriptingCommands {
private final JedisClusterConnection connection;
JedisClusterScriptingCommands(@NonNull JedisClusterConnection connection) {
+ super(connection);
this.connection = connection;
}
@@ -49,7 +55,7 @@ public void scriptFlush() {
connection.getClusterCommandExecutor()
.executeCommandOnAllNodes((JedisClusterConnection.JedisClusterCommandCallback) Jedis::scriptFlush);
} catch (Exception ex) {
- throw convertJedisAccessException(ex);
+ throw connection.convertJedisAccessException(ex);
}
}
@@ -60,7 +66,7 @@ public void scriptKill() {
connection.getClusterCommandExecutor()
.executeCommandOnAllNodes((JedisClusterConnection.JedisClusterCommandCallback) Jedis::scriptKill);
} catch (Exception ex) {
- throw convertJedisAccessException(ex);
+ throw connection.convertJedisAccessException(ex);
}
}
@@ -76,7 +82,7 @@ public String scriptLoad(byte @NonNull [] script) {
return JedisConverters.toString(multiNodeResult.getFirstNonNullNotEmptyOrDefault(new byte[0]));
} catch (Exception ex) {
- throw convertJedisAccessException(ex);
+ throw connection.convertJedisAccessException(ex);
}
}
@@ -85,46 +91,6 @@ public List scriptExists(@NonNull String @NonNull... scriptShas) {
throw new InvalidDataAccessApiUsageException("ScriptExists is not supported in cluster environment");
}
- @Override
- @SuppressWarnings("unchecked")
- public T eval(byte @NonNull [] script, @NonNull ReturnType returnType, int numKeys,
- byte @NonNull [] @NonNull... keysAndArgs) {
-
- Assert.notNull(script, "Script must not be null");
-
- try {
- return (T) new JedisScriptReturnConverter(returnType).convert(getCluster().eval(script, numKeys, keysAndArgs));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public T evalSha(@NonNull String scriptSha, @NonNull ReturnType returnType, int numKeys,
- byte @NonNull [] @NonNull... keysAndArgs) {
- return evalSha(JedisConverters.toBytes(scriptSha), returnType, numKeys, keysAndArgs);
- }
-
- @Override
- @SuppressWarnings("unchecked")
- public T evalSha(byte @NonNull [] scriptSha, @NonNull ReturnType returnType, int numKeys,
- byte @NonNull [] @NonNull... keysAndArgs) {
-
- Assert.notNull(scriptSha, "Script digest must not be null");
-
- try {
- return (T) new JedisScriptReturnConverter(returnType)
- .convert(getCluster().evalsha(scriptSha, numKeys, keysAndArgs));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- protected RuntimeException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
-
- private JedisCluster getCluster() {
- return connection.getCluster();
- }
+ // eval() and evalSha() are inherited from JedisScriptingCommands
+ // UnifiedJedis handles cluster routing automatically for these commands
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java
index f5834d09a8..574a834b40 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java
@@ -18,14 +18,14 @@
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
-import java.util.List;
import java.util.Set;
-import org.springframework.dao.DataAccessException;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.NullUnmarked;
+
import org.springframework.data.redis.connection.ClusterSlotHashUtil;
import org.springframework.data.redis.connection.RedisSetCommands;
import org.springframework.data.redis.connection.jedis.JedisClusterConnection.JedisMultiKeyClusterCommandCallback;
@@ -39,84 +39,37 @@
import org.springframework.util.Assert;
/**
+ * Cluster {@link RedisSetCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Christoph Strobl
* @author Mark Paluch
* @author Mingi Lee
+ * @author Tihomir Mateev
* @since 2.0
*/
-class JedisClusterSetCommands implements RedisSetCommands {
+@NullUnmarked
+class JedisClusterSetCommands extends JedisSetCommands {
private final JedisClusterConnection connection;
JedisClusterSetCommands(JedisClusterConnection connection) {
+ super(connection);
this.connection = connection;
}
@Override
- public Long sAdd(byte[] key, byte[]... values) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(values, "Values must not be null");
- Assert.noNullElements(values, "Values must not contain null elements");
-
- try {
- return connection.getCluster().sadd(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long sRem(byte[] key, byte[]... values) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(values, "Values must not be null");
- Assert.noNullElements(values, "Values must not contain null elements");
-
- try {
- return connection.getCluster().srem(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] sPop(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().spop(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List sPop(byte[] key, long count) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return new ArrayList<>(connection.getCluster().spop(key, count));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean sMove(byte[] srcKey, byte[] destKey, byte[] value) {
+ public Boolean sMove(byte @NonNull [] srcKey, byte @NonNull [] destKey, byte @NonNull [] value) {
Assert.notNull(srcKey, "Source key must not be null");
Assert.notNull(destKey, "Destination key must not be null");
Assert.notNull(value, "Value must not be null");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(srcKey, destKey)) {
- try {
- return JedisConverters.toBoolean(connection.getCluster().smove(srcKey, destKey, value));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sMove(srcKey, destKey, value);
}
if (connection.keyCommands().exists(srcKey)) {
@@ -128,56 +81,13 @@ public Boolean sMove(byte[] srcKey, byte[] destKey, byte[] value) {
}
@Override
- public Long sCard(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().scard(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean sIsMember(byte[] key, byte[] value) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(value, "Value must not be null");
-
- try {
- return connection.getCluster().sismember(key, value);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List sMIsMember(byte[] key, byte[]... values) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(values, "Value must not be null");
- Assert.noNullElements(values, "Values must not contain null elements");
-
- try {
- return connection.getCluster().smismember(key, values);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Set sInter(byte[]... keys) {
+ public Set sInter(byte @NonNull [] @NonNull... keys) {
Assert.notNull(keys, "Keys must not be null");
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().sinter(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sInter(keys);
}
Collection> resultList = connection.getClusterCommandExecutor()
@@ -209,7 +119,7 @@ public Set sInter(byte[]... keys) {
}
@Override
- public Long sInterStore(byte[] destKey, byte[]... keys) {
+ public Long sInterStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull ... keys) {
Assert.notNull(destKey, "Destination key must not be null");
Assert.notNull(keys, "Source keys must not be null");
@@ -218,11 +128,7 @@ public Long sInterStore(byte[] destKey, byte[]... keys) {
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
- try {
- return connection.getCluster().sinterstore(destKey, keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sInterStore(destKey, keys);
}
Set result = sInter(keys);
@@ -233,17 +139,13 @@ public Long sInterStore(byte[] destKey, byte[]... keys) {
}
@Override
- public Long sInterCard(byte[]... keys) {
+ public Long sInterCard(byte @NonNull [] @NonNull ... keys) {
Assert.notNull(keys, "Keys must not be null");
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().sintercard(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sInterCard(keys);
}
// For multi-slot clusters, calculate intersection cardinality by performing intersection
@@ -252,17 +154,13 @@ public Long sInterCard(byte[]... keys) {
}
@Override
- public Set sUnion(byte[]... keys) {
+ public Set sUnion(byte @NonNull [] @NonNull ... keys) {
Assert.notNull(keys, "Keys must not be null");
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().sunion(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sUnion(keys);
}
Collection> resultList = connection.getClusterCommandExecutor()
@@ -284,7 +182,7 @@ public Set sUnion(byte[]... keys) {
}
@Override
- public Long sUnionStore(byte[] destKey, byte[]... keys) {
+ public Long sUnionStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull ... keys) {
Assert.notNull(destKey, "Destination key must not be null");
Assert.notNull(keys, "Source keys must not be null");
@@ -293,11 +191,7 @@ public Long sUnionStore(byte[] destKey, byte[]... keys) {
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
- try {
- return connection.getCluster().sunionstore(destKey, keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sUnionStore(destKey, keys);
}
Set result = sUnion(keys);
@@ -308,17 +202,13 @@ public Long sUnionStore(byte[] destKey, byte[]... keys) {
}
@Override
- public Set sDiff(byte[]... keys) {
+ public Set sDiff(byte @NonNull [] @NonNull ... keys) {
Assert.notNull(keys, "Keys must not be null");
Assert.noNullElements(keys, "Keys must not contain null elements");
if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) {
- try {
- return connection.getCluster().sdiff(keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sDiff(keys);
}
return KeyUtils.splitKeys(keys, (source, others) -> {
@@ -343,7 +233,7 @@ public Set sDiff(byte[]... keys) {
}
@Override
- public Long sDiffStore(byte[] destKey, byte[]... keys) {
+ public Long sDiffStore(byte @NonNull [] destKey, byte @NonNull [] @NonNull ... keys) {
Assert.notNull(destKey, "Destination key must not be null");
Assert.notNull(keys, "Source keys must not be null");
@@ -352,11 +242,7 @@ public Long sDiffStore(byte[] destKey, byte[]... keys) {
byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys);
if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) {
- try {
- return connection.getCluster().sdiffstore(destKey, keys);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ return super.sDiffStore(destKey, keys);
}
Set diff = sDiff(keys);
@@ -368,47 +254,7 @@ public Long sDiffStore(byte[] destKey, byte[]... keys) {
}
@Override
- public Set sMembers(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().smembers(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public byte[] sRandMember(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().srandmember(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List sRandMember(byte[] key, long count) {
-
- Assert.notNull(key, "Key must not be null");
-
- if (count > Integer.MAX_VALUE) {
- throw new IllegalArgumentException("Count cannot exceed Integer.MAX_VALUE");
- }
-
- try {
- return connection.getCluster().srandmember(key, Long.valueOf(count).intValue());
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Cursor sScan(byte[] key, ScanOptions options) {
+ public Cursor sScan(byte @NonNull [] key, @NonNull ScanOptions options) {
Assert.notNull(key, "Key must not be null");
@@ -418,14 +264,9 @@ public Cursor sScan(byte[] key, ScanOptions options) {
protected ScanIteration doScan(CursorId cursorId, ScanOptions options) {
ScanParams params = JedisConverters.toScanParams(options);
- ScanResult result = connection.getCluster().sscan(key, JedisConverters.toBytes(cursorId), params);
+ ScanResult result = getConnection().getJedis().sscan(key, JedisConverters.toBytes(cursorId), params);
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
}
}.open();
}
-
- private DataAccessException convertJedisAccessException(Exception ex) {
- return connection.convertJedisAccessException(ex);
- }
-
}
diff --git a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java
index 372329d7ed..1f77b49e32 100644
--- a/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java
+++ b/src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterStreamCommands.java
@@ -15,420 +15,27 @@
*/
package org.springframework.data.redis.connection.jedis;
-import static org.springframework.data.redis.connection.jedis.StreamConverters.*;
+import org.jspecify.annotations.NonNull;
+import org.jspecify.annotations.NullUnmarked;
-import redis.clients.jedis.BuilderFactory;
-import redis.clients.jedis.params.XAddParams;
-import redis.clients.jedis.params.XClaimParams;
-import redis.clients.jedis.params.XPendingParams;
-import redis.clients.jedis.params.XReadGroupParams;
-import redis.clients.jedis.params.XReadParams;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.springframework.dao.DataAccessException;
-import org.springframework.data.domain.Range;
-import org.springframework.data.redis.connection.Limit;
import org.springframework.data.redis.connection.RedisStreamCommands;
-import org.springframework.data.redis.connection.RedisStreamCommands.StreamEntryDeletionResult;
-import org.springframework.data.redis.connection.stream.ByteRecord;
-import org.springframework.data.redis.connection.stream.Consumer;
-import org.springframework.data.redis.connection.stream.MapRecord;
-import org.springframework.data.redis.connection.stream.PendingMessages;
-import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
-import org.springframework.data.redis.connection.stream.ReadOffset;
-import org.springframework.data.redis.connection.stream.RecordId;
-import org.springframework.data.redis.connection.stream.StreamInfo;
-import org.springframework.data.redis.connection.stream.StreamOffset;
-import org.springframework.data.redis.connection.stream.StreamReadOptions;
-import org.springframework.util.Assert;
-import redis.clients.jedis.params.XTrimParams;
/**
+ * Cluster {@link RedisStreamCommands} implementation for Jedis.
+ *
+ * This class can be used to override only methods that require cluster-specific handling.
+ *
+ * Pipeline and transaction modes are not supported in cluster mode.
+ *
* @author Dengliming
* @author Jeonggyu Choi
+ * @author Tihomir Mateev
* @since 2.3
*/
-class JedisClusterStreamCommands implements RedisStreamCommands {
-
- private final JedisClusterConnection connection;
-
- JedisClusterStreamCommands(JedisClusterConnection connection) {
- this.connection = connection;
- }
-
- @Override
- public Long xAck(byte[] key, String group, RecordId... recordIds) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.hasText(group, "Group name must not be null or empty");
- Assert.notNull(recordIds, "recordIds must not be null");
-
- try {
- return connection.getCluster().xack(key, JedisConverters.toBytes(group),
- entryIdsToBytes(Arrays.asList(recordIds)));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public RecordId xAdd(MapRecord record, XAddOptions options) {
-
- Assert.notNull(record, "Record must not be null");
- Assert.notNull(record.getStream(), "Stream must not be null");
-
- XAddParams params = StreamConverters.toXAddParams(record.getId(), options);
-
- try {
- return RecordId
- .of(JedisConverters.toString(connection.getCluster().xadd(record.getStream(), record.getValue(), params)));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List xClaimJustId(byte[] key, String group, String newOwner, XClaimOptions options) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(group, "Group must not be null");
- Assert.notNull(newOwner, "NewOwner must not be null");
-
- long minIdleTime = options.getMinIdleTime() == null ? -1L : options.getMinIdleTime().toMillis();
-
- XClaimParams xClaimParams = StreamConverters.toXClaimParams(options);
- try {
-
- List ids = connection.getCluster().xclaimJustId(key, JedisConverters.toBytes(group),
- JedisConverters.toBytes(newOwner), minIdleTime, xClaimParams, entryIdsToBytes(options.getIds()));
-
- List recordIds = new ArrayList<>(ids.size());
- ids.forEach(it -> recordIds.add(RecordId.of(JedisConverters.toString(it))));
-
- return recordIds;
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List xClaim(byte[] key, String group, String newOwner, XClaimOptions options) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(group, "Group must not be null");
- Assert.notNull(newOwner, "NewOwner must not be null");
-
- long minIdleTime = options.getMinIdleTime() == null ? -1L : options.getMinIdleTime().toMillis();
-
- XClaimParams xClaimParams = StreamConverters.toXClaimParams(options);
- try {
- return convertToByteRecord(key, connection.getCluster().xclaim(key, JedisConverters.toBytes(group),
- JedisConverters.toBytes(newOwner), minIdleTime, xClaimParams, entryIdsToBytes(options.getIds())));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long xDel(byte[] key, RecordId... recordIds) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(recordIds, "recordIds must not be null");
-
- try {
- return connection.getCluster().xdel(key, entryIdsToBytes(Arrays.asList(recordIds)));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List xDelEx(byte[] key, XDelOptions options, RecordId... recordIds) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(recordIds, "recordIds must not be null");
-
- try {
- return StreamConverters.toStreamEntryDeletionResults(connection.getCluster().xdelex(key,
- StreamConverters.toStreamDeletionPolicy(options),
- entryIdsToBytes(Arrays.asList(recordIds))));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public List xAckDel(byte[] key, String group, XDelOptions options, RecordId... recordIds) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(group, "Group must not be null");
- Assert.notNull(recordIds, "recordIds must not be null");
-
- try {
- return StreamConverters.toStreamEntryDeletionResults(connection.getCluster().xackdel(key, JedisConverters.toBytes(group),
- StreamConverters.toStreamDeletionPolicy(options),
- entryIdsToBytes(Arrays.asList(recordIds))));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset) {
- return xGroupCreate(key, groupName, readOffset, false);
- }
-
- @Override
- public String xGroupCreate(byte[] key, String groupName, ReadOffset readOffset, boolean mkStream) {
+@NullUnmarked
+class JedisClusterStreamCommands extends JedisStreamCommands {
- Assert.notNull(key, "Key must not be null");
- Assert.hasText(groupName, "Group name must not be null or empty");
- Assert.notNull(readOffset, "ReadOffset must not be null");
-
- try {
- return connection.getCluster().xgroupCreate(key, JedisConverters.toBytes(groupName),
- JedisConverters.toBytes(readOffset.getOffset()), mkStream);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
+ JedisClusterStreamCommands(@NonNull JedisClusterConnection connection) {
+ super(connection);
}
-
- @Override
- public Boolean xGroupDelConsumer(byte[] key, Consumer consumer) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(consumer, "Consumer must not be null");
-
- try {
- return connection.getCluster().xgroupDelConsumer(key, JedisConverters.toBytes(consumer.getGroup()),
- JedisConverters.toBytes(consumer.getName())) != 0L;
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Boolean xGroupDestroy(byte[] key, String groupName) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.hasText(groupName, "Group name must not be null or empty");
-
- try {
- return connection.getCluster().xgroupDestroy(key, JedisConverters.toBytes(groupName)) != 0L;
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public StreamInfo.XInfoStream xInfo(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return StreamInfo.XInfoStream.fromList((List) connection.getCluster().xinfoStream(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public StreamInfo.XInfoGroups xInfoGroups(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return StreamInfo.XInfoGroups.fromList(connection.getCluster().xinfoGroups(key));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public StreamInfo.XInfoConsumers xInfoConsumers(byte[] key, String groupName) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(groupName, "GroupName must not be null");
-
- try {
- return StreamInfo.XInfoConsumers.fromList(groupName,
- connection.getCluster().xinfoConsumers(key, JedisConverters.toBytes(groupName)));
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public Long xLen(byte[] key) {
-
- Assert.notNull(key, "Key must not be null");
-
- try {
- return connection.getCluster().xlen(key);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
- }
-
- @Override
- public PendingMessagesSummary xPending(byte[] key, String groupName) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(groupName, "GroupName must not be null");
-
- byte[] group = JedisConverters.toBytes(groupName);
-
- try {
-
- Object response = connection.getCluster().xpending(key, group);
-
- return StreamConverters.toPendingMessagesSummary(groupName, response);
- } catch (Exception ex) {
- throw convertJedisAccessException(ex);
- }
-
- }
-
- @Override
- @SuppressWarnings("NullAway")
- public PendingMessages xPending(byte[] key, String groupName, XPendingOptions options) {
-
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(groupName, "GroupName must not be null");
-
- Range range = (Range) options.getRange();
- byte[] group = JedisConverters.toBytes(groupName);
-
- try {
-
- XPendingParams pendingParams = StreamConverters.toXPendingParams(options);
- List