diff --git a/pom.xml b/pom.xml
index 81102e8008..2da7b4eb0a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,7 @@
1.7.36
1.7.1
2.17.2
- 3.3.1
+ 3.5.0
@@ -89,7 +89,7 @@
org.locationtech.jts
jts-core
- 1.19.0
+ 1.20.0
test
@@ -139,7 +139,7 @@
org.apache.httpcomponents.client5
httpclient5-fluent
- 5.3.1
+ 5.4
test
@@ -240,7 +240,7 @@
maven-javadoc-plugin
- 3.8.0
+ 3.10.0
false
@@ -315,7 +315,7 @@
maven-gpg-plugin
- 3.2.5
+ 3.2.6
--pinentry-mode
diff --git a/src/main/java/redis/clients/jedis/JedisCluster.java b/src/main/java/redis/clients/jedis/JedisCluster.java
index 0b9f5b49d1..db8d17ee15 100644
--- a/src/main/java/redis/clients/jedis/JedisCluster.java
+++ b/src/main/java/redis/clients/jedis/JedisCluster.java
@@ -23,16 +23,38 @@ public class JedisCluster extends UnifiedJedis {
* Default timeout in milliseconds.
*/
public static final int DEFAULT_TIMEOUT = 2000;
+
+ /**
+ * Default amount of attempts for executing a command
+ */
public static final int DEFAULT_MAX_ATTEMPTS = 5;
+ /**
+ * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster.
+ * Here, the default timeout of {@value JedisCluster#DEFAULT_TIMEOUT} ms is being used with {@value JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts.
+ * @param node Node to first connect to.
+ */
public JedisCluster(HostAndPort node) {
this(Collections.singleton(node));
}
+ /**
+ * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster.
+ * Here, the default timeout of {@value JedisCluster#DEFAULT_TIMEOUT} ms is being used with {@value JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts.
+ * @param node Node to first connect to.
+ * @param timeout connection and socket timeout in milliseconds.
+ */
public JedisCluster(HostAndPort node, int timeout) {
this(Collections.singleton(node), timeout);
}
+ /**
+ * Creates a JedisCluster instance. The provided node is used to make the first contact with the cluster.
+ * You can specify the timeout and the maximum attempts.
+ * @param node Node to first connect to.
+ * @param timeout connection and socket timeout in milliseconds.
+ * @param maxAttempts maximum attempts for executing a command.
+ */
public JedisCluster(HostAndPort node, int timeout, int maxAttempts) {
this(Collections.singleton(node), timeout, maxAttempts);
}
@@ -93,14 +115,32 @@ public JedisCluster(HostAndPort node, final JedisClientConfig clientConfig, int
this(Collections.singleton(node), clientConfig, maxAttempts, poolConfig);
}
+ /**
+ * Creates a JedisCluster with multiple entry points.
+ * Here, the default timeout of {@value JedisCluster#DEFAULT_TIMEOUT} ms is being used with {@value JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts.
+ * @param nodes Nodes to connect to.
+ */
public JedisCluster(Set nodes) {
this(nodes, DEFAULT_TIMEOUT);
}
+ /**
+ * Creates a JedisCluster with multiple entry points.
+ * Here, the default timeout of {@value JedisCluster#DEFAULT_TIMEOUT} ms is being used with {@value JedisCluster#DEFAULT_MAX_ATTEMPTS} maximum attempts.
+ * @param nodes Nodes to connect to.
+ * @param timeout connection and socket timeout in milliseconds.
+ */
public JedisCluster(Set nodes, int timeout) {
this(nodes, DefaultJedisClientConfig.builder().timeoutMillis(timeout).build());
}
+ /**
+ * Creates a JedisCluster with multiple entry points.
+ * You can specify the timeout and the maximum attempts.
+ * @param nodes Nodes to connect to.
+ * @param timeout connection and socket timeout in milliseconds.
+ * @param maxAttempts maximum attempts for executing a command.
+ */
public JedisCluster(Set nodes, int timeout, int maxAttempts) {
this(nodes, DefaultJedisClientConfig.builder().timeoutMillis(timeout).build(), maxAttempts);
}
@@ -186,6 +226,19 @@ public JedisCluster(Set clusterNodes, JedisClientConfig clientConfi
Duration.ofMillis((long) clientConfig.getSocketTimeoutMillis() * maxAttempts));
}
+ /**
+ * Creates a JedisCluster with multiple entry points.
+ * You can specify the timeout and the maximum attempts.
+ *
+ * Additionally, you are free to provide a {@link JedisClientConfig} instance.
+ * You can use the {@link DefaultJedisClientConfig#builder()} builder pattern to customize your configuration, including socket timeouts,
+ * username and passwords as well as SSL related parameters.
+ *
+ * @param clusterNodes Nodes to connect to.
+ * @param clientConfig Client configuration parameters.
+ * @param maxAttempts maximum attempts for executing a command.
+ * @param maxTotalRetriesDuration Maximum time used for reconnecting.
+ */
public JedisCluster(Set clusterNodes, JedisClientConfig clientConfig, int maxAttempts,
Duration maxTotalRetriesDuration) {
this(new ClusterConnectionProvider(clusterNodes, clientConfig), maxAttempts, maxTotalRetriesDuration,
@@ -216,6 +269,11 @@ public JedisCluster(Set clusterNodes, JedisClientConfig clientConfi
maxAttempts, maxTotalRetriesDuration, clientConfig.getRedisProtocol());
}
+ // Uses a fetched connection to process protocol. Should be avoided if possible.
+ public JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
+ super(provider, maxAttempts, maxTotalRetriesDuration);
+ }
+
private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration,
RedisProtocol protocol) {
super(provider, maxAttempts, maxTotalRetriesDuration, protocol);
@@ -268,15 +326,20 @@ private JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Durati
super(provider, maxAttempts, maxTotalRetriesDuration, protocol, clientSideCache);
}
- // Uses a fetched connection to process protocol. Should be avoided if possible.
- public JedisCluster(ClusterConnectionProvider provider, int maxAttempts, Duration maxTotalRetriesDuration) {
- super(provider, maxAttempts, maxTotalRetriesDuration);
- }
-
+ /**
+ * Returns all nodes that were configured to connect to in key-value pairs ({@link Map}).
+ * Key is the HOST:PORT and the value is the connection pool.
+ * @return the map of all connections.
+ */
public Map getClusterNodes() {
return ((ClusterConnectionProvider) provider).getNodes();
}
+ /**
+ * Returns the connection for one of the 16,384 slots.
+ * @param slot the slot to retrieve the connection for.
+ * @return connection of the provided slot. {@code close()} of this connection must be called after use.
+ */
public Connection getConnectionFromSlot(int slot) {
return ((ClusterConnectionProvider) provider).getConnectionFromSlot(slot);
}
diff --git a/src/main/java/redis/clients/jedis/providers/MultiClusterPooledConnectionProvider.java b/src/main/java/redis/clients/jedis/providers/MultiClusterPooledConnectionProvider.java
index eb443bca1e..097bf636e0 100644
--- a/src/main/java/redis/clients/jedis/providers/MultiClusterPooledConnectionProvider.java
+++ b/src/main/java/redis/clients/jedis/providers/MultiClusterPooledConnectionProvider.java
@@ -24,6 +24,7 @@
import redis.clients.jedis.*;
import redis.clients.jedis.MultiClusterClientConfig.ClusterConfig;
import redis.clients.jedis.annots.Experimental;
+import redis.clients.jedis.annots.VisibleForTesting;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisValidationException;
import redis.clients.jedis.util.Pool;
@@ -299,6 +300,11 @@ public Cluster getCluster() {
return multiClusterMap.get(activeMultiClusterIndex);
}
+ @VisibleForTesting
+ public Cluster getCluster(int multiClusterIndex) {
+ return multiClusterMap.get(multiClusterIndex);
+ }
+
public CircuitBreaker getClusterCircuitBreaker() {
return multiClusterMap.get(activeMultiClusterIndex).getCircuitBreaker();
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSAddParams.java b/src/main/java/redis/clients/jedis/timeseries/TSAddParams.java
index 0a9713cefb..487b5301a2 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSAddParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSAddParams.java
@@ -5,6 +5,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -125,4 +126,36 @@ public void addParams(CommandArguments args) {
labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue()));
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSAddParams that = (TSAddParams) o;
+ return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff &&
+ Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 &&
+ Objects.equals(retentionPeriod, that.retentionPeriod) &&
+ encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) &&
+ duplicatePolicy == that.duplicatePolicy && onDuplicate == that.onDuplicate &&
+ Objects.equals(labels, that.labels);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(retentionPeriod);
+ result = 31 * result + Objects.hashCode(encoding);
+ result = 31 * result + Objects.hashCode(chunkSize);
+ result = 31 * result + Objects.hashCode(duplicatePolicy);
+ result = 31 * result + Objects.hashCode(onDuplicate);
+ result = 31 * result + Boolean.hashCode(ignore);
+ result = 31 * result + Long.hashCode(ignoreMaxTimediff);
+ result = 31 * result + Double.hashCode(ignoreMaxValDiff);
+ result = 31 * result + Objects.hashCode(labels);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSAlterParams.java b/src/main/java/redis/clients/jedis/timeseries/TSAlterParams.java
index 50ba9723ac..2a65cc74d9 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSAlterParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSAlterParams.java
@@ -6,6 +6,7 @@
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -106,4 +107,33 @@ public void addParams(CommandArguments args) {
labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue()));
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSAlterParams that = (TSAlterParams) o;
+ return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff &&
+ Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 &&
+ Objects.equals(retentionPeriod, that.retentionPeriod) &&
+ Objects.equals(chunkSize, that.chunkSize) &&
+ duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(retentionPeriod);
+ result = 31 * result + Objects.hashCode(chunkSize);
+ result = 31 * result + Objects.hashCode(duplicatePolicy);
+ result = 31 * result + Boolean.hashCode(ignore);
+ result = 31 * result + Long.hashCode(ignoreMaxTimediff);
+ result = 31 * result + Double.hashCode(ignoreMaxValDiff);
+ result = 31 * result + Objects.hashCode(labels);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSArithByParams.java b/src/main/java/redis/clients/jedis/timeseries/TSArithByParams.java
index 1bc3df1c55..7703816d25 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSArithByParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSArithByParams.java
@@ -5,6 +5,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -117,4 +118,36 @@ public void addParams(CommandArguments args) {
labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue()));
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSArithByParams> that = (TSArithByParams>) o;
+ return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff &&
+ Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 &&
+ Objects.equals(timestamp, that.timestamp) &&
+ Objects.equals(retentionPeriod, that.retentionPeriod) &&
+ encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) &&
+ duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(timestamp);
+ result = 31 * result + Objects.hashCode(retentionPeriod);
+ result = 31 * result + Objects.hashCode(encoding);
+ result = 31 * result + Objects.hashCode(chunkSize);
+ result = 31 * result + Objects.hashCode(duplicatePolicy);
+ result = 31 * result + Boolean.hashCode(ignore);
+ result = 31 * result + Long.hashCode(ignoreMaxTimediff);
+ result = 31 * result + Double.hashCode(ignoreMaxValDiff);
+ result = 31 * result + Objects.hashCode(labels);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSCreateParams.java b/src/main/java/redis/clients/jedis/timeseries/TSCreateParams.java
index 0611383d4d..c9c5face72 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSCreateParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSCreateParams.java
@@ -5,6 +5,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -121,4 +122,34 @@ public void addParams(CommandArguments args) {
labels.entrySet().forEach((entry) -> args.add(entry.getKey()).add(entry.getValue()));
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSCreateParams that = (TSCreateParams) o;
+ return ignore == that.ignore && ignoreMaxTimediff == that.ignoreMaxTimediff &&
+ Double.compare(ignoreMaxValDiff, that.ignoreMaxValDiff) == 0 &&
+ Objects.equals(retentionPeriod, that.retentionPeriod) &&
+ encoding == that.encoding && Objects.equals(chunkSize, that.chunkSize) &&
+ duplicatePolicy == that.duplicatePolicy && Objects.equals(labels, that.labels);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(retentionPeriod);
+ result = 31 * result + Objects.hashCode(encoding);
+ result = 31 * result + Objects.hashCode(chunkSize);
+ result = 31 * result + Objects.hashCode(duplicatePolicy);
+ result = 31 * result + Boolean.hashCode(ignore);
+ result = 31 * result + Long.hashCode(ignoreMaxTimediff);
+ result = 31 * result + Double.hashCode(ignoreMaxValDiff);
+ result = 31 * result + Objects.hashCode(labels);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSGetParams.java b/src/main/java/redis/clients/jedis/timeseries/TSGetParams.java
index eb35cf1eae..3525b0e18f 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSGetParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSGetParams.java
@@ -27,4 +27,22 @@ public void addParams(CommandArguments args) {
args.add(LATEST);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSGetParams that = (TSGetParams) o;
+ return latest == that.latest;
+ }
+
+ @Override
+ public int hashCode() {
+ return Boolean.hashCode(latest);
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSMGetParams.java b/src/main/java/redis/clients/jedis/timeseries/TSMGetParams.java
index c7d08ffde2..c4272c04c3 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSMGetParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSMGetParams.java
@@ -4,6 +4,7 @@
import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.SELECTED_LABELS;
import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.WITHLABELS;
+import java.util.Arrays;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -55,4 +56,26 @@ public void addParams(CommandArguments args) {
}
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSMGetParams that = (TSMGetParams) o;
+ return latest == that.latest && withLabels == that.withLabels &&
+ Arrays.equals(selectedLabels, that.selectedLabels);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Boolean.hashCode(latest);
+ result = 31 * result + Boolean.hashCode(withLabels);
+ result = 31 * result + Arrays.hashCode(selectedLabels);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSMRangeParams.java b/src/main/java/redis/clients/jedis/timeseries/TSMRangeParams.java
index 182c90814b..cafe091a3a 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSMRangeParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSMRangeParams.java
@@ -7,6 +7,8 @@
import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*;
import static redis.clients.jedis.util.SafeEncoder.encode;
+import java.util.Arrays;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -260,4 +262,50 @@ public void addParams(CommandArguments args) {
args.add(GROUPBY).add(groupByLabel).add(REDUCE).add(groupByReduce);
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSMRangeParams that = (TSMRangeParams) o;
+ return latest == that.latest && withLabels == that.withLabels &&
+ bucketDuration == that.bucketDuration && empty == that.empty &&
+ Objects.equals(fromTimestamp, that.fromTimestamp) &&
+ Objects.equals(toTimestamp, that.toTimestamp) &&
+ Arrays.equals(filterByTimestamps, that.filterByTimestamps) &&
+ Arrays.equals(filterByValues, that.filterByValues) &&
+ Arrays.equals(selectedLabels, that.selectedLabels) &&
+ Objects.equals(count, that.count) && Arrays.equals(align, that.align) &&
+ aggregationType == that.aggregationType &&
+ Arrays.equals(bucketTimestamp, that.bucketTimestamp) &&
+ Arrays.equals(filters, that.filters) &&
+ Objects.equals(groupByLabel, that.groupByLabel) &&
+ Objects.equals(groupByReduce, that.groupByReduce);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(fromTimestamp);
+ result = 31 * result + Objects.hashCode(toTimestamp);
+ result = 31 * result + Boolean.hashCode(latest);
+ result = 31 * result + Arrays.hashCode(filterByTimestamps);
+ result = 31 * result + Arrays.hashCode(filterByValues);
+ result = 31 * result + Boolean.hashCode(withLabels);
+ result = 31 * result + Arrays.hashCode(selectedLabels);
+ result = 31 * result + Objects.hashCode(count);
+ result = 31 * result + Arrays.hashCode(align);
+ result = 31 * result + Objects.hashCode(aggregationType);
+ result = 31 * result + Long.hashCode(bucketDuration);
+ result = 31 * result + Arrays.hashCode(bucketTimestamp);
+ result = 31 * result + Boolean.hashCode(empty);
+ result = 31 * result + Arrays.hashCode(filters);
+ result = 31 * result + Objects.hashCode(groupByLabel);
+ result = 31 * result + Objects.hashCode(groupByReduce);
+ return result;
+ }
}
diff --git a/src/main/java/redis/clients/jedis/timeseries/TSRangeParams.java b/src/main/java/redis/clients/jedis/timeseries/TSRangeParams.java
index bab70dcbd6..402e2b88e6 100644
--- a/src/main/java/redis/clients/jedis/timeseries/TSRangeParams.java
+++ b/src/main/java/redis/clients/jedis/timeseries/TSRangeParams.java
@@ -7,6 +7,8 @@
import static redis.clients.jedis.timeseries.TimeSeriesProtocol.TimeSeriesKeyword.*;
import static redis.clients.jedis.util.SafeEncoder.encode;
+import java.util.Arrays;
+import java.util.Objects;
import redis.clients.jedis.CommandArguments;
import redis.clients.jedis.params.IParams;
@@ -205,4 +207,40 @@ public void addParams(CommandArguments args) {
}
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ TSRangeParams that = (TSRangeParams) o;
+ return latest == that.latest && bucketDuration == that.bucketDuration && empty == that.empty &&
+ Objects.equals(fromTimestamp, that.fromTimestamp) &&
+ Objects.equals(toTimestamp, that.toTimestamp) &&
+ Arrays.equals(filterByTimestamps, that.filterByTimestamps) &&
+ Arrays.equals(filterByValues, that.filterByValues) &&
+ Objects.equals(count, that.count) && Arrays.equals(align, that.align) &&
+ aggregationType == that.aggregationType &&
+ Arrays.equals(bucketTimestamp, that.bucketTimestamp);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hashCode(fromTimestamp);
+ result = 31 * result + Objects.hashCode(toTimestamp);
+ result = 31 * result + Boolean.hashCode(latest);
+ result = 31 * result + Arrays.hashCode(filterByTimestamps);
+ result = 31 * result + Arrays.hashCode(filterByValues);
+ result = 31 * result + Objects.hashCode(count);
+ result = 31 * result + Arrays.hashCode(align);
+ result = 31 * result + Objects.hashCode(aggregationType);
+ result = 31 * result + Long.hashCode(bucketDuration);
+ result = 31 * result + Arrays.hashCode(bucketTimestamp);
+ result = 31 * result + Boolean.hashCode(empty);
+ return result;
+ }
}
diff --git a/src/test/java/io/redis/examples/CmdsGenericExample.java b/src/test/java/io/redis/examples/CmdsGenericExample.java
new file mode 100644
index 0000000000..c43364f105
--- /dev/null
+++ b/src/test/java/io/redis/examples/CmdsGenericExample.java
@@ -0,0 +1,113 @@
+// EXAMPLE: cmds_generic
+// REMOVE_START
+package io.redis.examples;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+// REMOVE_END
+// HIDE_START
+import redis.clients.jedis.UnifiedJedis;
+import redis.clients.jedis.args.ExpiryOption;
+// HIDE_END
+
+// HIDE_START
+public class CmdsGenericExample {
+ @Test
+ public void run() {
+ UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
+
+ //REMOVE_START
+ // Clear any keys here before using them in tests.
+
+ //REMOVE_END
+// HIDE_END
+
+ // STEP_START del
+ String delResult1 = jedis.set("key1", "Hello");
+ System.out.println(delResult1); // >>> OK
+
+ String delResult2 = jedis.set("key2", "World");
+ System.out.println(delResult2); // >>> OK
+
+ long delResult3 = jedis.del("key1", "key2", "key3");
+ System.out.println(delResult3); // >>> 2
+ // STEP_END
+
+ // Tests for 'del' step.
+ // REMOVE_START
+ Assert.assertEquals("OK", delResult1);
+ Assert.assertEquals("OK", delResult2);
+ Assert.assertEquals(2, delResult3);
+ // REMOVE_END
+
+
+ // STEP_START expire
+ String expireResult1 = jedis.set("mykey", "Hello");
+ System.out.println(expireResult1); // >>> OK
+
+ long expireResult2 = jedis.expire("mykey", 10);
+ System.out.println(expireResult2); // >>> 1
+
+ long expireResult3 = jedis.ttl("mykey");
+ System.out.println(expireResult3); // >>> 10
+
+ String expireResult4 = jedis.set("mykey", "Hello World");
+ System.out.println(expireResult4); // >>> OK
+
+ long expireResult5 = jedis.ttl("mykey");
+ System.out.println(expireResult5); // >>> -1
+
+ long expireResult6 = jedis.expire("mykey", 10, ExpiryOption.XX);
+ System.out.println(expireResult6); // >>> 0
+
+ long expireResult7 = jedis.ttl("mykey");
+ System.out.println(expireResult7); // >>> -1
+
+ long expireResult8 = jedis.expire("mykey", 10, ExpiryOption.NX);
+ System.out.println(expireResult8); // >>> 1
+
+ long expireResult9 = jedis.ttl("mykey");
+ System.out.println(expireResult9); // >>> 10
+ // STEP_END
+
+ // Tests for 'expire' step.
+ // REMOVE_START
+ Assert.assertEquals("OK", expireResult1);
+ Assert.assertEquals(1, expireResult2);
+ Assert.assertEquals(10, expireResult3);
+ Assert.assertEquals("OK", expireResult4);
+ Assert.assertEquals(-1, expireResult5);
+ Assert.assertEquals(0, expireResult6);
+ Assert.assertEquals(-1, expireResult7);
+ Assert.assertEquals(1, expireResult8);
+ Assert.assertEquals(10, expireResult9);
+ jedis.del("mykey");
+ // REMOVE_END
+
+
+ // STEP_START ttl
+ String ttlResult1 = jedis.set("mykey", "Hello");
+ System.out.println(ttlResult1); // >>> OK
+
+ long ttlResult2 = jedis.expire("mykey", 10);
+ System.out.println(ttlResult2); // >>> 1
+
+ long ttlResult3 = jedis.ttl("mykey");
+ System.out.println(ttlResult3); // >>> 10
+ // STEP_END
+
+ // Tests for 'ttl' step.
+ // REMOVE_START
+ Assert.assertEquals("OK", ttlResult1);
+ Assert.assertEquals(1, ttlResult2);
+ Assert.assertEquals(10, ttlResult3);
+ jedis.del("mykey");
+ // REMOVE_END
+
+// HIDE_START
+
+ }
+}
+// HIDE_END
+
diff --git a/src/test/java/io/redis/examples/CmdsSortedSetExample.java b/src/test/java/io/redis/examples/CmdsSortedSetExample.java
new file mode 100644
index 0000000000..19a5c21b8e
--- /dev/null
+++ b/src/test/java/io/redis/examples/CmdsSortedSetExample.java
@@ -0,0 +1,480 @@
+// EXAMPLE: cmds_sorted_set
+// REMOVE_START
+package io.redis.examples;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+// REMOVE_END
+// HIDE_START
+// HIDE_END
+import java.util.HashMap;
+import java.util.Map;
+import java.util.List;
+import redis.clients.jedis.UnifiedJedis;
+import redis.clients.jedis.params.ZRangeParams;
+import redis.clients.jedis.resps.Tuple;
+
+// HIDE_START
+public class CmdsSortedSetExample {
+ @Test
+ public void run() {
+ UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
+
+ //REMOVE_START
+ // Clear any keys here before using them in tests.
+ jedis.del("myzset");
+ //REMOVE_END
+// HIDE_END
+
+
+ // STEP_START bzmpop
+
+ // STEP_END
+
+ // Tests for 'bzmpop' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START bzpopmax
+
+ // STEP_END
+
+ // Tests for 'bzpopmax' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START bzpopmin
+
+ // STEP_END
+
+ // Tests for 'bzpopmin' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zadd
+ Map zAddExampleParams = new HashMap<>();
+ zAddExampleParams.put("one", 1.0);
+ long zAddResult1 = jedis.zadd("myzset", zAddExampleParams);
+ System.out.println(zAddResult1); // >>> 1
+
+ zAddExampleParams.clear();
+ zAddExampleParams.put("uno", 1.0);
+ long zAddResult2 = jedis.zadd("myzset", zAddExampleParams);
+ System.out.println(zAddResult2); // >>> 1
+
+ zAddExampleParams.clear();
+ zAddExampleParams.put("two", 2.0);
+ zAddExampleParams.put("three", 3.0);
+ long zAddResult3 = jedis.zadd("myzset", zAddExampleParams);
+ System.out.println(zAddResult3); // >>> 2
+
+ List zAddResult4 = jedis.zrangeWithScores("myzset", new ZRangeParams(0, -1));
+
+ for (Tuple item: zAddResult4) {
+ System.out.println("Element: " + item.getElement() + ", Score: " + item.getScore());
+ }
+ // >>> Element: one, Score: 1.0
+ // >>> Element: uno, Score: 1.0
+ // >>> Element: two, Score: 2.0
+ // >>> Element: three, Score: 3.0
+ // STEP_END
+
+ // Tests for 'zadd' step.
+ // REMOVE_START
+ Assert.assertEquals(1, zAddResult1);
+ Assert.assertEquals(1, zAddResult2);
+ Assert.assertEquals(2, zAddResult3);
+ Assert.assertEquals(new Tuple("one", 1.0), zAddResult4.get(0));
+ Assert.assertEquals(new Tuple("uno", 1.0), zAddResult4.get(1));
+ Assert.assertEquals(new Tuple("two", 2.0), zAddResult4.get(2));
+ Assert.assertEquals(new Tuple("three", 3.0), zAddResult4.get(3));
+ jedis.del("myzset");
+ // REMOVE_END
+
+
+ // STEP_START zcard
+
+ // STEP_END
+
+ // Tests for 'zcard' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zcount
+
+ // STEP_END
+
+ // Tests for 'zcount' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zdiff
+
+ // STEP_END
+
+ // Tests for 'zdiff' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zdiffstore
+
+ // STEP_END
+
+ // Tests for 'zdiffstore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zincrby
+
+ // STEP_END
+
+ // Tests for 'zincrby' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zinter
+
+ // STEP_END
+
+ // Tests for 'zinter' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zintercard
+
+ // STEP_END
+
+ // Tests for 'zintercard' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zinterstore
+
+ // STEP_END
+
+ // Tests for 'zinterstore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zlexcount
+
+ // STEP_END
+
+ // Tests for 'zlexcount' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zmpop
+
+ // STEP_END
+
+ // Tests for 'zmpop' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zmscore
+
+ // STEP_END
+
+ // Tests for 'zmscore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zpopmax
+
+ // STEP_END
+
+ // Tests for 'zpopmax' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zpopmin
+
+ // STEP_END
+
+ // Tests for 'zpopmin' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrandmember
+
+ // STEP_END
+
+ // Tests for 'zrandmember' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrange1
+ Map zRangeExampleParams1 = new HashMap<>();
+ zRangeExampleParams1.put("one", 1.0);
+ zRangeExampleParams1.put("two", 2.0);
+ zRangeExampleParams1.put("three", 3.0);
+ long zRangeResult1 = jedis.zadd("myzset", zRangeExampleParams1);
+ System.out.println(zRangeResult1); // >>> 3
+
+ List zRangeResult2 = jedis.zrange("myzset", new ZRangeParams(0, -1));
+ System.out.println(String.join(", ", zRangeResult2)); // >>> one, two, three
+
+ List zRangeResult3 = jedis.zrange("myzset", new ZRangeParams(2, 3));
+ System.out.println(String.join(", ", zRangeResult3)); // >> three
+
+ List zRangeResult4 = jedis.zrange("myzset", new ZRangeParams(-2, -1));
+ System.out.println(String.join(", ", zRangeResult4)); // >> two, three
+ // STEP_END
+
+ // Tests for 'zrange1' step.
+ // REMOVE_START
+ Assert.assertEquals(3, zRangeResult1);
+ Assert.assertEquals("one, two, three", String.join(", ", zRangeResult2));
+ Assert.assertEquals("three", String.join(", ", zRangeResult3));
+ Assert.assertEquals("two, three", String.join(", ", zRangeResult4));
+ jedis.del("myzset");
+ // REMOVE_END
+
+
+ // STEP_START zrange2
+ Map zRangeExampleParams2 = new HashMap<>();
+ zRangeExampleParams2.put("one", 1.0);
+ zRangeExampleParams2.put("two", 2.0);
+ zRangeExampleParams2.put("three", 3.0);
+ long zRangeResult5 = jedis.zadd("myzset", zRangeExampleParams2);
+ System.out.println(zRangeResult5); // >>> 3
+
+ List zRangeResult6 = jedis.zrangeWithScores("myzset", new ZRangeParams(0, 1));
+
+ for (Tuple item: zRangeResult6) {
+ System.out.println("Element: " + item.getElement() + ", Score: " + item.getScore());
+ }
+ // >>> Element: one, Score: 1.0
+ // >>> Element: two, Score: 2.0
+ // STEP_END
+
+ // Tests for 'zrange2' step.
+ // REMOVE_START
+ Assert.assertEquals(3, zRangeResult5);
+ Assert.assertEquals(new Tuple("one", 1.0), zRangeResult6.get(0));
+ Assert.assertEquals(new Tuple("two", 2.0), zRangeResult6.get(1));
+ jedis.del("myzset");
+ // REMOVE_END
+
+
+ // STEP_START zrange3
+ Map zRangeExampleParams3 = new HashMap<>();
+ zRangeExampleParams3.put("one", 1.0);
+ zRangeExampleParams3.put("two", 2.0);
+ zRangeExampleParams3.put("three", 3.0);
+ long zRangeResult7 = jedis.zadd("myzset", zRangeExampleParams3);
+ System.out.println(zRangeResult7); // >>> 3
+
+ List zRangeResult8 = jedis.zrangeByScore("myzset", "(1", "+inf", 1, 1);
+ System.out.println(String.join(", ", zRangeResult8)); // >>> three
+ // STEP_END
+
+ // Tests for 'zrange3' step.
+ // REMOVE_START
+ Assert.assertEquals(3, zRangeResult7);
+ Assert.assertEquals("three", String.join(", ", zRangeResult8));
+ jedis.del("myzset");
+ // REMOVE_END
+
+
+ // STEP_START zrangebylex
+
+ // STEP_END
+
+ // Tests for 'zrangebylex' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrangebyscore
+
+ // STEP_END
+
+ // Tests for 'zrangebyscore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrangestore
+
+ // STEP_END
+
+ // Tests for 'zrangestore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrank
+
+ // STEP_END
+
+ // Tests for 'zrank' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrem
+
+ // STEP_END
+
+ // Tests for 'zrem' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zremrangebylex
+
+ // STEP_END
+
+ // Tests for 'zremrangebylex' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zremrangebyrank
+
+ // STEP_END
+
+ // Tests for 'zremrangebyrank' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zremrangebyscore
+
+ // STEP_END
+
+ // Tests for 'zremrangebyscore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrevrange
+
+ // STEP_END
+
+ // Tests for 'zrevrange' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrevrangebylex
+
+ // STEP_END
+
+ // Tests for 'zrevrangebylex' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrevrangebyscore
+
+ // STEP_END
+
+ // Tests for 'zrevrangebyscore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zrevrank
+
+ // STEP_END
+
+ // Tests for 'zrevrank' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zscan
+
+ // STEP_END
+
+ // Tests for 'zscan' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zscore
+
+ // STEP_END
+
+ // Tests for 'zscore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zunion
+
+ // STEP_END
+
+ // Tests for 'zunion' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+ // STEP_START zunionstore
+
+ // STEP_END
+
+ // Tests for 'zunionstore' step.
+ // REMOVE_START
+
+ // REMOVE_END
+
+
+// HIDE_START
+ }
+}
+// HIDE_END
+
diff --git a/src/test/java/io/redis/examples/CmdsStringExample.java b/src/test/java/io/redis/examples/CmdsStringExample.java
new file mode 100644
index 0000000000..a1ba0281e5
--- /dev/null
+++ b/src/test/java/io/redis/examples/CmdsStringExample.java
@@ -0,0 +1,48 @@
+// EXAMPLE: cmds_string
+// REMOVE_START
+package io.redis.examples;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+// REMOVE_END
+// HIDE_START
+import redis.clients.jedis.UnifiedJedis;
+// HIDE_END
+
+// HIDE_START
+public class CmdsStringExample {
+ @Test
+ public void run() {
+ UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379");
+
+ //REMOVE_START
+ // Clear any keys here before using them in tests.
+ jedis.del("mykey");
+ //REMOVE_END
+// HIDE_END
+
+ // STEP_START incr
+ String incrResult1 = jedis.set("mykey", "10");
+ System.out.println(incrResult1); // >>> OK
+
+ long incrResult2 = jedis.incr("mykey");
+ System.out.println(incrResult2); // >>> 11
+
+ String incrResult3 = jedis.get("mykey");
+ System.out.println(incrResult3); // >>> 11
+ // STEP_END
+
+ // Tests for 'incr' step.
+ // REMOVE_START
+ Assert.assertEquals("OK", incrResult1);
+ Assert.assertEquals(11, incrResult2);
+ Assert.assertEquals("11", incrResult3);
+ jedis.del("mykey");
+ // REMOVE_END
+
+// HIDE_START
+ }
+}
+// HIDE_END
+
diff --git a/src/test/java/redis/clients/jedis/EndpointConfig.java b/src/test/java/redis/clients/jedis/EndpointConfig.java
index 42a44a3c47..68f927be0e 100644
--- a/src/test/java/redis/clients/jedis/EndpointConfig.java
+++ b/src/test/java/redis/clients/jedis/EndpointConfig.java
@@ -31,6 +31,10 @@ public HostAndPort getHostAndPort() {
return JedisURIHelper.getHostAndPort(endpoints.get(0));
}
+ public HostAndPort getHostAndPort(int index) {
+ return JedisURIHelper.getHostAndPort(endpoints.get(index));
+ }
+
public String getPassword() {
return password;
}
diff --git a/src/test/java/redis/clients/jedis/scenario/ActiveActiveFailoverTest.java b/src/test/java/redis/clients/jedis/scenario/ActiveActiveFailoverTest.java
new file mode 100644
index 0000000000..587988b447
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/scenario/ActiveActiveFailoverTest.java
@@ -0,0 +1,187 @@
+package redis.clients.jedis.scenario;
+
+import io.github.resilience4j.circuitbreaker.CircuitBreakerConfig;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import redis.clients.jedis.*;
+import redis.clients.jedis.providers.MultiClusterPooledConnectionProvider;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
+
+import static org.junit.Assert.*;
+
+public class ActiveActiveFailoverTest {
+ private static final Logger log = LoggerFactory.getLogger(ActiveActiveFailoverTest.class);
+
+ private static EndpointConfig endpoint;
+
+ private final FaultInjectionClient faultClient = new FaultInjectionClient();
+
+ @BeforeClass
+ public static void beforeClass() {
+ try {
+ ActiveActiveFailoverTest.endpoint = HostAndPorts.getRedisEndpoint("re-active-active");
+ } catch (IllegalArgumentException e) {
+ log.warn("Skipping test because no Redis endpoint is configured");
+ org.junit.Assume.assumeTrue(false);
+ }
+ }
+
+ @Test
+ public void testFailover() {
+
+ MultiClusterClientConfig.ClusterConfig[] clusterConfig = new MultiClusterClientConfig.ClusterConfig[2];
+
+ JedisClientConfig config = endpoint.getClientConfigBuilder()
+ .socketTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS)
+ .connectionTimeoutMillis(RecommendedSettings.DEFAULT_TIMEOUT_MS).build();
+
+ clusterConfig[0] = new MultiClusterClientConfig.ClusterConfig(endpoint.getHostAndPort(0),
+ config, RecommendedSettings.poolConfig);
+ clusterConfig[1] = new MultiClusterClientConfig.ClusterConfig(endpoint.getHostAndPort(1),
+ config, RecommendedSettings.poolConfig);
+
+ MultiClusterClientConfig.Builder builder = new MultiClusterClientConfig.Builder(clusterConfig);
+
+ builder.circuitBreakerSlidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED);
+ builder.circuitBreakerSlidingWindowSize(1); // SLIDING WINDOW SIZE IN SECONDS
+ builder.circuitBreakerSlidingWindowMinCalls(1);
+ builder.circuitBreakerFailureRateThreshold(10.0f); // percentage of failures to trigger circuit breaker
+
+ builder.retryWaitDuration(10);
+ builder.retryMaxAttempts(1);
+ builder.retryWaitDurationExponentialBackoffMultiplier(1);
+
+ class FailoverReporter implements Consumer {
+
+ String currentClusterName = "not set";
+
+ boolean failoverHappened = false;
+
+ Instant failoverAt = null;
+
+ public String getCurrentClusterName() {
+ return currentClusterName;
+ }
+
+ @Override
+ public void accept(String clusterName) {
+ this.currentClusterName = clusterName;
+ log.info(
+ "\n\n====FailoverEvent=== \nJedis failover to cluster: {}\n====FailoverEvent===\n\n",
+ clusterName);
+
+ failoverHappened = true;
+ failoverAt = Instant.now();
+ }
+ }
+
+ MultiClusterPooledConnectionProvider provider = new MultiClusterPooledConnectionProvider(
+ builder.build());
+ FailoverReporter reporter = new FailoverReporter();
+ provider.setClusterFailoverPostProcessor(reporter);
+ provider.setActiveMultiClusterIndex(1);
+
+ UnifiedJedis client = new UnifiedJedis(provider);
+
+ AtomicLong retryingThreadsCounter = new AtomicLong(0);
+ AtomicLong failedCommandsAfterFailover = new AtomicLong(0);
+ AtomicReference lastFailedCommandAt = new AtomicReference<>();
+
+ // Start thread that imitates an application that uses the client
+ MultiThreadedFakeApp fakeApp = new MultiThreadedFakeApp(client, (UnifiedJedis c) -> {
+
+ long threadId = Thread.currentThread().getId();
+
+ int attempt = 0;
+ int maxTries = 500;
+ int retryingDelay = 5;
+ while (true) {
+ try {
+ Map executionInfo = new HashMap() {{
+ put("threadId", String.valueOf(threadId));
+ put("cluster", reporter.getCurrentClusterName());
+ }};
+ client.xadd("execution_log", StreamEntryID.NEW_ENTRY, executionInfo);
+
+ if (attempt > 0) {
+ log.info("Thread {} recovered after {} ms. Threads still not recovered: {}", threadId,
+ attempt * retryingDelay, retryingThreadsCounter.decrementAndGet());
+ }
+
+ break;
+ } catch (JedisConnectionException e) {
+
+ if (reporter.failoverHappened) {
+ long failedCommands = failedCommandsAfterFailover.incrementAndGet();
+ lastFailedCommandAt.set(Instant.now());
+ log.warn(
+ "Thread {} failed to execute command after failover. Failed commands after failover: {}",
+ threadId, failedCommands);
+ }
+
+ if (attempt == 0) {
+ long failedThreads = retryingThreadsCounter.incrementAndGet();
+ log.warn("Thread {} failed to execute command. Failed threads: {}", threadId,
+ failedThreads);
+ }
+ try {
+ Thread.sleep(retryingDelay);
+ } catch (InterruptedException ie) {
+ throw new RuntimeException(ie);
+ }
+ if (++attempt == maxTries) throw e;
+ }
+ }
+ return true;
+ }, 18);
+ fakeApp.setKeepExecutingForSeconds(30);
+ Thread t = new Thread(fakeApp);
+ t.start();
+
+ HashMap params = new HashMap<>();
+ params.put("bdb_id", endpoint.getBdbId());
+ params.put("rlutil_command", "pause_bdb");
+
+ FaultInjectionClient.TriggerActionResponse actionResponse = null;
+
+ try {
+ log.info("Triggering bdb_pause");
+ actionResponse = faultClient.triggerAction("execute_rlutil_command", params);
+ } catch (IOException e) {
+ fail("Fault Injection Server error:" + e.getMessage());
+ }
+
+ log.info("Action id: {}", actionResponse.getActionId());
+ fakeApp.setAction(actionResponse);
+
+ try {
+ t.join();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+
+ ConnectionPool pool = provider.getCluster(1).getConnectionPool();
+
+ log.info("First connection pool state: active: {}, idle: {}", pool.getNumActive(),
+ pool.getNumIdle());
+ log.info("Full failover time: {} s",
+ Duration.between(reporter.failoverAt, lastFailedCommandAt.get()).getSeconds());
+
+ assertEquals(0, pool.getNumActive());
+ assertTrue(fakeApp.capturedExceptions().isEmpty());
+
+ client.close();
+ }
+
+}
diff --git a/src/test/java/redis/clients/jedis/scenario/FakeApp.java b/src/test/java/redis/clients/jedis/scenario/FakeApp.java
index 7e505862a2..4ea6ffc1f9 100644
--- a/src/test/java/redis/clients/jedis/scenario/FakeApp.java
+++ b/src/test/java/redis/clients/jedis/scenario/FakeApp.java
@@ -12,18 +12,18 @@
public class FakeApp implements Runnable {
- private static final Logger log = LoggerFactory.getLogger(FakeApp.class);
+ protected static final Logger log = LoggerFactory.getLogger(FakeApp.class);
public void setKeepExecutingForSeconds(int keepExecutingForSeconds) {
this.keepExecutingForSeconds = keepExecutingForSeconds;
}
- private int keepExecutingForSeconds = 60;
+ protected int keepExecutingForSeconds = 60;
- private FaultInjectionClient.TriggerActionResponse actionResponse = null;
- private final UnifiedJedis client;
- private final ExecutedAction action;
- private List exceptions = new ArrayList<>();
+ protected FaultInjectionClient.TriggerActionResponse actionResponse = null;
+ protected final UnifiedJedis client;
+ protected final ExecutedAction action;
+ protected List exceptions = new ArrayList<>();
@FunctionalInterface
public interface ExecutedAction {
diff --git a/src/test/java/redis/clients/jedis/scenario/MultiThreadedFakeApp.java b/src/test/java/redis/clients/jedis/scenario/MultiThreadedFakeApp.java
new file mode 100644
index 0000000000..4c03fc2cf2
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/scenario/MultiThreadedFakeApp.java
@@ -0,0 +1,48 @@
+package redis.clients.jedis.scenario;
+
+import redis.clients.jedis.UnifiedJedis;
+import redis.clients.jedis.exceptions.JedisConnectionException;
+
+import java.time.Duration;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
+public class MultiThreadedFakeApp extends FakeApp {
+
+ private final ExecutorService executorService;
+
+ public MultiThreadedFakeApp(UnifiedJedis client, FakeApp.ExecutedAction action, int numThreads) {
+ super(client, action);
+ this.executorService = Executors.newFixedThreadPool(numThreads);
+ }
+
+ @Override
+ public void run() {
+ log.info("Starting FakeApp");
+
+ int checkEachSeconds = 5;
+ int timeoutSeconds = 120;
+
+ while (actionResponse == null || !actionResponse.isCompleted(
+ Duration.ofSeconds(checkEachSeconds), Duration.ofSeconds(keepExecutingForSeconds),
+ Duration.ofSeconds(timeoutSeconds))) {
+ try {
+ executorService.submit(() -> action.run(client));
+ } catch (JedisConnectionException e) {
+ log.error("Error executing action", e);
+ exceptions.add(e);
+ }
+ }
+
+ executorService.shutdown();
+
+ try {
+ if (!executorService.awaitTermination(keepExecutingForSeconds, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
+ } catch (InterruptedException e) {
+ log.error("Error waiting for executor service to terminate", e);
+ }
+ }
+}