diff --git a/content/develop/clients/jedis/_index.md b/content/develop/clients/jedis/_index.md
index 733608f18..10399bfa4 100644
--- a/content/develop/clients/jedis/_index.md
+++ b/content/develop/clients/jedis/_index.md
@@ -34,7 +34,7 @@ To include `Jedis` as a dependency in your application, edit the dependency file
redis.clients
jedis
- 6.1.0
+ 7.0.0
```
@@ -46,7 +46,7 @@ To include `Jedis` as a dependency in your application, edit the dependency file
}
//...
dependencies {
- implementation 'redis.clients:jedis:6.1.0'
+ implementation 'redis.clients:jedis:7.0.0'
//...
}
```
diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md
index 4e8fd5ee2..47cc0839f 100644
--- a/content/develop/clients/jedis/failover.md
+++ b/content/develop/clients/jedis/failover.md
@@ -13,7 +13,6 @@ description: Improve reliability using the failover/failback features of Jedis.
linkTitle: Failover/failback
title: Failover and failback
weight: 50
-draft: true
---
Jedis supports [failover and failback](https://en.wikipedia.org/wiki/Failover)
@@ -58,8 +57,7 @@ the command a few times.)
The status of the attempted command calls is kept in a "sliding window", which
is simply a buffer where the least recent item is dropped as each new
-one is added. The buffer can be configured to have a fixed number of items or to
-be based on a time window.
+one is added. The buffer can be configured to have a fixed number of failures and/or a failure ratio (specified as a percentage), both based on a time window.
{{< image filename="images/failover/failover-sliding-window.svg" alt="Sliding window of recent connection attempts" >}}
@@ -84,7 +82,7 @@ failing back to that server even if it is not optimal.
Jedis periodically runs a "health check" on each server to see if it has recovered.
The health check can be as simple as
-sending a Redis [`ECHO`]({{< relref "/commands/echo" >}}) command and ensuring
+sending a Redis [`PING`]({{< relref "/commands/ping" >}}) command and ensuring
that it gives the expected response.
You can also configure Jedis to run health checks on the current target
@@ -98,7 +96,43 @@ The example below shows a simple case with a list of two servers,
target. If `redis-east` fails, Jedis should fail over to
`redis-west`.
-First, create some simple configuration for the client and
+{{< note >}}Jedis v6 supported failover/failback using a special
+`UnifiedJedis` constructor. You should update existing code to use
+the approach shown below for Jedis v7 and later.
+{{< /note >}}
+
+First, add the `resilience4j` dependencies to your project. If you
+are using [Maven](https://maven.apache.org/), add the following
+dependencies to your `pom.xml` file:
+
+```xml
+
+ io.github.resilience4j
+ resilience4j-all
+ 1.7.1
+
+
+ io.github.resilience4j
+ resilience4j-circuitbreaker
+ 1.7.1
+
+
+ io.github.resilience4j
+ resilience4j-retry
+ 1.7.1
+
+```
+
+If you are using [Gradle](https://gradle.org/), add the following
+dependencies to your `build.gradle` file:
+
+```bash
+compileOnly 'io.github.resilience4j:resilience4j-resilience4j-all:1.7.1'
+compileOnly 'io.github.resilience4j:resilience4j-circuitbreaker:1.7.1'
+compileOnly 'io.github.resilience4j:resilience4j-retry:1.7.1'
+```
+
+In your source file, create some simple configuration for the client and
[connection pool]({{< relref "/develop/clients/jedis/connect#connect-with-a-connection-pool" >}}),
as you would for a standard connection.
@@ -116,94 +150,80 @@ poolConfig.setTestWhileIdle(true);
poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));
```
-Supply the weighted list of endpoints as an array of `ClusterConfig`
-objects. Use the basic configuration objects created above and
-use the `weight` option to order the endpoints, with the highest
+Supply the weighted list of endpoints using the `MultiDbConfig` builder.
+Use the `weight` option to order the endpoints, with the highest
weight being tried first.
```java
-MultiClusterClientConfig.ClusterConfig[] clusterConfigs = new MultiClusterClientConfig.ClusterConfig[2];
-
HostAndPort east = new HostAndPort("redis-east.example.com", 14000);
-clusterConfigs[0] = ClusterConfig.builder(east, config)
- .connectionPoolConfig(poolConfig)
- .weight(1.0f)
- .build();
-
HostAndPort west = new HostAndPort("redis-west.example.com", 14000);
-clusterConfigs[1] = ClusterConfig.builder(west, config)
- .connectionPoolConfig(poolConfig)
- .weight(0.5f)
- .build();
+
+MultiDbConfig.Builder multiConfig = MultiDbConfig.builder()
+ .database(DatabaseConfig.builder(east, config).connectionPoolConfig(poolConfig).weight(1.0f).build())
+ .database(DatabaseConfig.builder(west, config).connectionPoolConfig(poolConfig).weight(0.5f).build());
```
-Pass the `clusterConfigs` array when you create the `MultiClusterClientConfig` builder.
The builder lets you add several options to configure the
[circuit breaker](#circuit-breaker-configuration) behavior
and [retries](#retry-configuration) (these are explained in more detail below).
```java
-MultiClusterClientConfig.Builder builder = new MultiClusterClientConfig.Builder(clusterConfigs);
-
-builder.circuitBreakerSlidingWindowSize(10); // Sliding window size in number of calls
-builder.circuitBreakerSlidingWindowMinCalls(1);
-builder.circuitBreakerFailureRateThreshold(50.0f); // percentage of failures to trigger circuit breaker
-
-builder.failbackSupported(true); // Enable failback
-builder.failbackCheckInterval(1000); // Check every second the unhealthy cluster to see if it has recovered
-builder.gracePeriod(10000); // Keep cluster disabled for 10 seconds after it becomes unhealthy
-
-// Optional: configure retry settings
-builder.retryMaxAttempts(3); // Maximum number of retry attempts (including the initial call)
-builder.retryWaitDuration(500); // Number of milliseconds to wait between retry attempts
-builder.retryWaitDurationExponentialBackoffMultiplier(2); // Exponential backoff factor multiplied against wait duration between retries
-
-// Optional: configure fast failover
-builder.fastFailover(true); // Force closing connections to unhealthy cluster on failover
-builder.retryOnFailover(false); // Do not retry failed commands during failover
+// Configure circuit breaker for failure detection
+multiConfig
+ .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder()
+ .slidingWindowSize(2) // Sliding window size as a duration in seconds.
+ .failureRateThreshold(10.0f) // Percentage of failures to trigger circuit breaker.
+ .minNumOfFailures(1000) // Minimum number of failures before circuit breaker is tripped.
+ .build())
+ .failbackSupported(true) // Enable failback.
+ .failbackCheckInterval(120000) // Check every 2 minutes to see if the unhealthy database has recovered.
+ .gracePeriod(60000) // Keep database disabled for 60 seconds after it becomes unhealthy.
+ // Optional: configure retry settings
+ .commandRetry(MultiDbConfig.RetryConfig.builder()
+ .maxAttempts(3) // Maximum number of retry attempts (including the initial call)
+ .waitDuration(500) // Number of milliseconds to wait between retry attempts.
+ .exponentialBackoffMultiplier(2) // Exponential backoff factor multiplied by the wait duration between retries.
+ .build())
+ // Optional: configure fast failover
+ .fastFailover(true) // Force closing connections to unhealthy database on failover.
+ .retryOnFailover(false); // Do not retry failed commands during failover.
```
-Finally, build the `MultiClusterClientConfig` and use it to create a `MultiClusterPooledConnectionProvider`. You can now pass this to
-the standard `UnifiedJedis` constructor to establish the client connection
-(see [Basic connection]({{< relref "/develop/clients/jedis/connect#basic-connection" >}})
-for an example).
+Finally, use the configuration to build the `MultiDbClient`.
```java
-MultiClusterPooledConnectionProvider provider = new MultiClusterPooledConnectionProvider(builder.build());
-
-UnifiedJedis jedis = new UnifiedJedis(provider);
+MultiDbClient multiDbClient = MultiDbClient.builder()
+ .multiDbConfig(multiConfig.build())
+ .build();
```
-When you use the `UnifiedJedis` instance, Jedis will handle the connection
-management and failover transparently.
+Like `UnifiedJedis`, `MultiDbClient` implements the usual Redis commands,
+but will also handle the connection management and failover transparently.
### Circuit breaker configuration
-The `MultiClusterClientConfig` builder lets you pass several options to configure
+The `MultiDbConfig.CircuitBreakerConfig` builder lets you pass several options to configure
the circuit breaker:
| Builder method | Default value | Description|
| --- | --- | --- |
-| `circuitBreakerSlidingWindowType()` | `COUNT_BASED` | Type of sliding window. `COUNT_BASED` uses a sliding window based on the number of calls, while `TIME_BASED` uses a sliding window based on time. |
-| `circuitBreakerSlidingWindowSize()` | `100` | Size of the sliding window (this is the number of calls for a `COUNT_BASED` window or time in seconds, for a `TIME_BASED` window). |
-| `circuitBreakerSlidingWindowMinCalls()` | `10` | Minimum number of calls required (per sliding window period) before the circuit breaker will start calculating the error rate or slow call rate. |
-| `circuitBreakerFailureRateThreshold()` | `50.0f` | Percentage of failures to trigger the circuit breaker. |
-| `circuitBreakerSlowCallRateThreshold()` | `100.0f` | Percentage of slow calls to trigger the circuit breaker. |
-| `circuitBreakerSlowCallDurationThreshold()` | `60000` | Duration in milliseconds after which a call is considered slow. |
-| `circuitBreakerIncludedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures. By default, it includes just `JedisConnectionException`. |
-| `circuitBreakerIgnoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for failure rate calculation. |
+| `slidingWindowSize()` | `2` | Duration in seconds to keep failures and successes in the sliding window. |
+| `minNumOfFailures()` | `1000` | Minimum number of failures that must occur before the circuit breaker is tripped. |
+| `failureRateThreshold()` | `10.0f` | Percentage of failures to trigger the circuit breaker. |
+| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures. By default, it includes just `JedisConnectionException`. |
+| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for failure rate calculation. |
### Retry configuration
-The `MultiClusterClientConfig` builder has the following options to configure retries:
+The `MultiDbConfig.RetryConfig` builder has the following options to configure retries:
| Builder method | Default value | Description|
| --- | --- | --- |
-| `retryMaxAttempts()` | `3` | Maximum number of retry attempts (including the initial call). |
-| `retryWaitDuration()` | `500` | Initial number of milliseconds to wait between retry attempts. |
-| `retryWaitDurationExponentialBackoffMultiplier()` | `2` | [Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) factor multiplied by the wait duration between retries. For example, with a wait duration of 1 second and a multiplier of 2, the retries would occur after 1s, 2s, 4s, 8s, 16s, and so on. |
-| `retryIncludedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures to be retried. By default, it includes just `JedisConnectionException`. |
-| `retryIgnoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for retry. |
+| `maxAttempts()` | `3` | Maximum number of retry attempts (including the initial call). Set to `1` to disable retries. |
+| `waitDuration()` | `500` | Initial number of milliseconds to wait between retry attempts. |
+| `exponentialBackoffMultiplier()` | `2` | [Exponential backoff](https://en.wikipedia.org/wiki/Exponential_backoff) factor multiplied by the wait duration between retries. For example, with a wait duration of 1 second and a multiplier of 2, the retries would occur after 1s, 2s, 4s, 8s, 16s, and so on. |
+| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures to be retried. By default, it includes just `JedisConnectionException`. |
+| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for retry. |
### Failover callbacks
@@ -221,26 +241,37 @@ import org.slf4j.LoggerFactory;
import java.util.function.Consumer;
-public class FailoverReporter implements Consumer {
+public class FailoverReporter implements Consumer {
@Override
- public void accept(ClusterSwitchEventArgs e) {
- Logger logger = LoggerFactory.getLogger(FailoverReporter.class);
- logger.warn("Jedis failover to cluster: " + e.getClusterName() + " due to " + e.getReason());
+ public void accept(DatabaseSwitchEvent e) {
+ System.out.println("Jedis failover to database: " + e.getDatabaseName() + " due to " + e.getReason());
}
}
```
-Then, pass an instance of your class to your `MultiPooledConnectionProvider`
-to enable the action (see [Failover configuration](#failover-configuration)
-above for an example of creating the `MultiPooledConnectionProvider`).
+Use the `databaseSwitchListener()` method of the `MultiDbClient` builder to
+register your custom action:
```java
FailoverReporter reporter = new FailoverReporter();
-provider.setClusterSwitchListener(reporter);
+MultiDbClient client = MultiDbClient.builder()
+ .databaseSwitchListener(reporter)
+ .build();
```
-The `accept()` method is now called whenever a failover occurs.
+Your `accept()` method is now called whenever a failover occurs.
+
+Since `Consumer` is a functional interface, you can also use a lambda
+expression to supply the custom action directly.
+
+```java
+MultiDbClient client = MultiDbClient.builder()
+ .databaseSwitchListener(
+ event -> System.out.println("Switched to: " + event.getEndpoint())
+ )
+ .build();
+```
## Health check configuration
@@ -248,12 +279,12 @@ There are several strategies available for health checks that you can configure
`MultiClusterClientConfig` builder. The sections below explain these strategies
in more detail.
-### `EchoStrategy` (default)
+### `PingStrategy` (default)
-The default strategy, `EchoStrategy`, periodically sends a Redis
-[`ECHO`]({{< relref "/commands/echo" >}}) command
+The default strategy, `PingStrategy`, periodically sends a Redis
+[`PING`]({{< relref "/commands/ping" >}}) command
and checks that it gives the expected response. Any unexpected response
-or exception indicates an unhealthy server. Although `EchoStrategy` is
+or exception indicates an unhealthy server. Although `PingStrategy` is
very simple, it is a good basic approach for most Redis deployments.
### `LagAwareStrategy` (preview)
@@ -266,33 +297,27 @@ and can also optionally check replication lag.
`LagAwareStrategy` determines the health of the server using the
[REST API]({{< relref "/operate/rs/references/rest-api" >}}). The example
below shows how to configure `LagAwareStrategy` and activate it using
-the `healthCheckStrategySupplier()` method of the `MultiClusterClientConfig.ClusterConfig`
+the `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig`
builder.
```java
-BiFunction, MultiClusterClientConfig.StrategySupplier> healthCheckStrategySupplier =
-(HostAndPort clusterHostPort, Supplier credentialsSupplier) -> {
- LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(clusterHostPort, credentialsSupplier)
- .interval(5000) // Check every 5 seconds
- .timeout(3000) // 3 second timeout
- .extendedCheckEnabled(true) // Check replication lag
- .build();
-
- return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig);
-};
-
// Configure REST API endpoint and credentials
-Endpoint restEndpoint = new HostAndPort("redis-enterprise-cluster-fqdn", 9443);
-Supplier credentialsSupplier = () ->
- new DefaultRedisCredentials("rest-api-user", "pwd");
-
-MultiClusterClientConfig.StrategySupplier lagawareStrategySupplier = healthCheckStrategySupplier.apply(
- restEndpoint, credentialsSupplier);
-
-MultiClusterClientConfig.ClusterConfig clusterConfig =
- MultiClusterClientConfig.ClusterConfig.builder(hostAndPort, clientConfig)
- .healthCheckStrategySupplier(lagawareStrategySupplier)
+HostAndPort restEndpoint = new HostAndPort("redis-enterprise-db-fqdn", 9443);
+Supplier credentialsSupplier = () -> new DefaultRedisCredentials("rest-api-user", "pwd");
+
+// Build a single LagAwareStrategy based on REST endpoint and credentials
+LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config
+ .builder(restEndpoint, credentialsSupplier)
+ .interval(5000) // Check every 5 seconds
+ .timeout(3000) // 3 second timeout
+ .extendedCheckEnabled(true)
.build();
+
+// Configure a database to use lag-aware health check
+MultiDbConfig.DatabaseConfig dbConfig =
+ MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)
+ .healthCheckStrategy(new LagAwareStrategy(lagConfig))
+ .build();
```
### Custom health check strategy
@@ -301,83 +326,55 @@ You can supply your own custom health check strategy by
implementing the `HealthCheckStrategy` interface. For example, you might
use this to integrate with external monitoring tools or to implement
checks that are specific to your application. The example below
-shows a simple custom strategy. Pass your custom strategy implementation to the `MultiClusterClientConfig.ClusterConfig`
+shows a simple custom strategy. Pass your custom strategy implementation to the `MultiDbConfig.DatabaseConfig`
builder with the `healthCheckStrategySupplier()` method.
```java
-MultiClusterClientConfig.StrategySupplier pingStrategy = (hostAndPort, jedisClientConfig) -> {
- return new HealthCheckStrategy() {
- @Override
- public int getInterval() {
- return 1000; // Check every second
- }
-
- @Override
- public int getTimeout() {
- return 500; // 500ms timeout
- }
-
- @Override
- public int minConsecutiveSuccessCount() {
- return 1; // Single success required
- }
-
- @Override
- public HealthStatus doHealthCheck(Endpoint endpoint) {
- try (UnifiedJedis jedis = new UnifiedJedis(hostAndPort, jedisClientConfig)) {
- String result = jedis.ping();
- return "PONG".equals(result) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;
- } catch (Exception e) {
- return HealthStatus.UNHEALTHY;
- }
- }
-
- @Override
- public void close() {
- // Cleanup resources if needed
- }
- };
-};
-
-MultiClusterClientConfig.ClusterConfig clusterConfig =
- MultiClusterClientConfig.ClusterConfig.builder(hostAndPort, clientConfig)
- .healthCheckStrategySupplier(pingStrategy)
- .build();
+// Custom strategy supplier
+MultiDbConfig.StrategySupplier customStrategy =
+ (hostAndPort, jedisClientConfig) -> {
+ // Return your custom HealthCheckStrategy implementation
+ return new MyCustomHealthCheckStrategy(hostAndPort, jedisClientConfig);
+ };
+
+MultiDbConfig.DatabaseConfig dbConfig =
+ MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)
+ .healthCheckStrategySupplier(customStrategy)
+ .weight(1.0f)
+ .build();
```
### Disable health checks
To disable health checks completely, use the
-`healthCheckEnabled()` method of the `MultiClusterClientConfig.ClusterConfig`
+`healthCheckEnabled()` method of the `MultiDbConfig.DatabaseConfig`
builder:
```java
-MultiClusterClientConfig.ClusterConfig clusterConfig =
- MultiClusterClientConfig.ClusterConfig.builder(hostAndPort, clientConfig)
- .healthCheckEnabled(false) // Disable health checks entirely
- .build();
+MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(east, config)
+ .healthCheckEnabled(false) // Disable health checks entirely
+ .build();
```
## Manual failback
By default, the failback mechanism runs health checks on all servers in the
weighted list and selects the highest-weighted server that is
-healthy. However, you can also use the `setActiveCluster()` method of
-`MultiClusterPooledConnectionProvider` to select which cluster to use
-manually:
+healthy. However, you can also use the `setActiveDatabase()` method of
+`MultiDbClient` to select which database to use manually:
```java
-// The `setActiveCluster()` method receives the `HostAndPort` of the
+// The `setActiveDatabase()` method receives the `HostAndPort` of the
// cluster to switch to.
-provider.setActiveCluster(west);
+client.setActiveDatabase(west);
```
-Note that `setActiveCluster()` is thread-safe.
+Note that `setActiveDatabase()` is thread-safe.
If you decide to implement manual failback, you will need a way for external
systems to trigger this method in your application. For example, if your application
exposes a REST API, you might consider creating a REST endpoint to call
-`setActiveCluster()`.
+`setActiveDatabase()`.
## Troubleshooting
@@ -407,12 +404,13 @@ minimum time after a failover before Jedis will check if a
failback is possible).
```java
+// Faster recovery configuration
HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()
.interval(1000) // More frequent checks
.build();
// Adjust failback timing
-MultiClusterClientConfig multiConfig = new MultiClusterClientConfig.Builder(clusterConfigs)
- .gracePeriod(5000) // Shorter grace period
- .build();
+MultiDbConfig multiConfig = MultiDbConfig.builder()
+ .gracePeriod(5000) // Shorter grace period
+ .build();
```