From c1165ea358ffce56d47c24c82bf7ea28256beaa3 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Mon, 13 Oct 2025 15:33:34 +0100 Subject: [PATCH 01/15] DOC-5665 updated failover docs with new API --- content/develop/clients/jedis/failover.md | 291 +++++++++++----------- 1 file changed, 145 insertions(+), 146 deletions(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 4e8fd5ee2..c2271047f 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) @@ -84,7 +83,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 +97,37 @@ 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-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-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 +145,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(1000) // Sliding window size in number of calls + .failureRateThreshold(50.0f) // percentage of failures to trigger circuit breaker + .minNumOfFailures(500) // Minimum number of failures before circuit breaker is tripped + .build()) + .failbackSupported(true) // Enable failback + .failbackCheckInterval(1000) // Check every second the unhealthy database to see if it has recovered + .gracePeriod(10000) // Keep database disabled for 10 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 against 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` | Number of calls to keep 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 +236,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 +274,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 +292,33 @@ 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 `healthCheckStrategySupplier()` 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(); +BiFunction, MultiDbConfig.StrategySupplier> healthCheckStrategySupplier = + (HostAndPort dbHostPort, Supplier credentialsSupplier) -> { + LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(dbHostPort, credentialsSupplier) + .interval(5000) // Check every 5 seconds + .timeout(3000) // 3 second timeout + .extendedCheckEnabled(true) + .build(); - return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig); -}; + 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"); +HostAndPort restEndpoint = new HostAndPort("redis-enterprise-db-fqdn", 9443); +Supplier credentialsSupplier = () -> + new DefaultRedisCredentials("rest-api-user", "pwd"); -MultiClusterClientConfig.StrategySupplier lagawareStrategySupplier = healthCheckStrategySupplier.apply( - restEndpoint, credentialsSupplier); +MultiDbConfig.StrategySupplier lagawareStrategySupplier = healthCheckStrategySupplier.apply( + restEndpoint, credentialsSupplier); -MultiClusterClientConfig.ClusterConfig clusterConfig = - MultiClusterClientConfig.ClusterConfig.builder(hostAndPort, clientConfig) - .healthCheckStrategySupplier(lagawareStrategySupplier) - .build(); +MultiDbConfig.DatabaseConfig dbConfig = + MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig) + .healthCheckStrategySupplier(lagawareStrategySupplier) + .build(); ``` ### Custom health check strategy @@ -301,83 +327,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 +405,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(); ``` From ae9d0e4c1a39954e4def93b42e3964e18e10e840 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Mon, 13 Oct 2025 16:00:21 +0100 Subject: [PATCH 02/15] DOC-5665 updated Jedis version on landing page --- content/develop/clients/jedis/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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' //... } ``` From 07776d2a3bdafaa827ec293e503b224218e5b9ee Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:30:41 +0100 Subject: [PATCH 03/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index c2271047f..4e2c9ecca 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -58,7 +58,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" >}} From 7dd8072d7c2b82253d4dce3f1637df7c92e61405 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:30:52 +0100 Subject: [PATCH 04/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 4e2c9ecca..21e5692ce 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -166,7 +166,7 @@ and [retries](#retry-configuration) (these are explained in more detail below). // Configure circuit breaker for failure detection multiConfig .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() - .slidingWindowSize(1000) // Sliding window size in number of calls + .slidingWindowSize(2) // Sliding window size as a duration in seconds. .failureRateThreshold(50.0f) // percentage of failures to trigger circuit breaker .minNumOfFailures(500) // Minimum number of failures before circuit breaker is tripped .build()) From 85587b285ee316907bf35133b1f5c8ee946c7a2b Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:31:00 +0100 Subject: [PATCH 05/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 21e5692ce..172870d10 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -167,7 +167,7 @@ and [retries](#retry-configuration) (these are explained in more detail below). multiConfig .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder() .slidingWindowSize(2) // Sliding window size as a duration in seconds. - .failureRateThreshold(50.0f) // percentage of failures to trigger circuit breaker + .failureRateThreshold(10.0f) // percentage of failures to trigger circuit breaker .minNumOfFailures(500) // Minimum number of failures before circuit breaker is tripped .build()) .failbackSupported(true) // Enable failback From 9753d5d4383cbe41048a275e1acffe2a595c0426 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:31:07 +0100 Subject: [PATCH 06/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 172870d10..da2a6a10f 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -168,7 +168,7 @@ 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(500) // Minimum number of failures before circuit breaker is tripped + .minNumOfFailures(1000) // Minimum number of failures before circuit breaker is tripped .build()) .failbackSupported(true) // Enable failback .failbackCheckInterval(1000) // Check every second the unhealthy database to see if it has recovered From df7c2c31bcc4ff1ef68ce03f21758607425c1af5 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:31:15 +0100 Subject: [PATCH 07/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index da2a6a10f..361649f3a 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -171,7 +171,7 @@ multiConfig .minNumOfFailures(1000) // Minimum number of failures before circuit breaker is tripped .build()) .failbackSupported(true) // Enable failback - .failbackCheckInterval(1000) // Check every second the unhealthy database to see if it has recovered + .failbackCheckInterval(120000) // Check every 2 minutes the unhealthy database to see if it has recovered .gracePeriod(10000) // Keep database disabled for 10 seconds after it becomes unhealthy // Optional: configure retry settings .commandRetry(MultiDbConfig.RetryConfig.builder() From 0bee643b0d83d5c8e41465a3923becb23fdd58e0 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:31:22 +0100 Subject: [PATCH 08/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 361649f3a..45570dfd6 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -172,7 +172,7 @@ multiConfig .build()) .failbackSupported(true) // Enable failback .failbackCheckInterval(120000) // Check every 2 minutes the unhealthy database to see if it has recovered - .gracePeriod(10000) // Keep database disabled for 10 seconds after it becomes unhealthy + .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) From c93b3e0ef90a40b89225c5f0042036b929ce7055 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Tue, 14 Oct 2025 09:31:33 +0100 Subject: [PATCH 09/15] Apply suggestion from @atakavci Co-authored-by: atakavci --- content/develop/clients/jedis/failover.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 45570dfd6..33e909aee 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -202,7 +202,7 @@ the circuit breaker: | Builder method | Default value | Description| | --- | --- | --- | -| `slidingWindowSize()` | `2` | Number of calls to keep in the sliding window. | +| `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`. | From 845398a7d17680811f634e56747af07754c0031c Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 14 Oct 2025 09:42:09 +0100 Subject: [PATCH 10/15] DOC-5665 applied feedback --- content/develop/clients/jedis/failover.md | 54 +++++++++++------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 33e909aee..feff65d43 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -167,17 +167,17 @@ and [retries](#retry-configuration) (these are explained in more detail below). 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 + .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 the unhealthy database to see if it has recovered - .gracePeriod(60000) // Keep database disabled for 60 seconds after it becomes unhealthy + .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 against wait duration between retries + .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 @@ -297,28 +297,28 @@ builder. ```java BiFunction, MultiDbConfig.StrategySupplier> healthCheckStrategySupplier = - (HostAndPort dbHostPort, Supplier credentialsSupplier) -> { - LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(dbHostPort, credentialsSupplier) - .interval(5000) // Check every 5 seconds - .timeout(3000) // 3 second timeout - .extendedCheckEnabled(true) - .build(); - - return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig); - }; - -// Configure REST API endpoint and credentials -HostAndPort restEndpoint = new HostAndPort("redis-enterprise-db-fqdn", 9443); -Supplier credentialsSupplier = () -> - new DefaultRedisCredentials("rest-api-user", "pwd"); - -MultiDbConfig.StrategySupplier lagawareStrategySupplier = healthCheckStrategySupplier.apply( - restEndpoint, credentialsSupplier); - -MultiDbConfig.DatabaseConfig dbConfig = - MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig) - .healthCheckStrategySupplier(lagawareStrategySupplier) + (HostAndPort dbHostPort, Supplier credentialsSupplier) -> { + LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(dbHostPort, credentialsSupplier) + .interval(5000) // Check every 5 seconds + .timeout(3000) // 3 second timeout + .extendedCheckEnabled(true) .build(); + + return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig); + }; + + // Configure REST API endpoint and credentials + 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(); + MultiDbConfig.StrategySupplier lagAwareStrategySupplier = (hostAndPort, + jedisClientConfig) -> new LagAwareStrategy(lagConfig); ``` ### Custom health check strategy From a8e3dcc74ff4c93ae48d71c6b9aee2debf2387b2 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 14 Oct 2025 10:33:55 +0100 Subject: [PATCH 11/15] DOC-5665 applied feedback --- content/develop/clients/jedis/failover.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index feff65d43..e3a67ebae 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -168,10 +168,10 @@ 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 + .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 + .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() @@ -180,8 +180,8 @@ multiConfig .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 + .fastFailover(true) // Force closing connections to unhealthy database on failover. + .retryOnFailover(false); // Do not retry failed commands during failover. ``` Finally, use the configuration to build the `MultiDbClient`. From 2df267030a5e1015e30e7d7f5dabdd100d66aba4 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Tue, 14 Oct 2025 16:28:30 +0100 Subject: [PATCH 12/15] DOC-5665 implemented feedback --- content/develop/clients/jedis/failover.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index e3a67ebae..28c57fcf8 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -57,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 -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.. +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" >}} From 337709f0b9c6c4c3596fb73596bd005aedb34206 Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:40:47 +0100 Subject: [PATCH 13/15] Apply suggestion from @ggivo Co-authored-by: Ivo Gaydazhiev --- content/develop/clients/jedis/failover.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 28c57fcf8..3efc513a1 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -106,6 +106,11 @@ 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 From f2ab5e5bfa3ea8fbe5a894e43bd846109991f63f Mon Sep 17 00:00:00 2001 From: andy-stark-redis <164213578+andy-stark-redis@users.noreply.github.com> Date: Thu, 16 Oct 2025 14:40:59 +0100 Subject: [PATCH 14/15] Apply suggestion from @ggivo Co-authored-by: Ivo Gaydazhiev --- content/develop/clients/jedis/failover.md | 1 + 1 file changed, 1 insertion(+) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 3efc513a1..1d43732de 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -127,6 +127,7 @@ 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' ``` From 0e596fa3a1e43316fc2b9e7fff2fa4630de553c2 Mon Sep 17 00:00:00 2001 From: Andy Stark Date: Thu, 16 Oct 2025 14:56:49 +0100 Subject: [PATCH 15/15] DOC-5665 updated example based on feedback --- content/develop/clients/jedis/failover.md | 30 +++++++++-------------- 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/content/develop/clients/jedis/failover.md b/content/develop/clients/jedis/failover.md index 1d43732de..47cc0839f 100644 --- a/content/develop/clients/jedis/failover.md +++ b/content/develop/clients/jedis/failover.md @@ -297,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 `MultiDbConfig.DatabaseConfig` +the `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig` builder. ```java -BiFunction, MultiDbConfig.StrategySupplier> healthCheckStrategySupplier = - (HostAndPort dbHostPort, Supplier credentialsSupplier) -> { - LagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder(dbHostPort, credentialsSupplier) - .interval(5000) // Check every 5 seconds - .timeout(3000) // 3 second timeout - .extendedCheckEnabled(true) - .build(); - - return (hostAndPort, jedisClientConfig) -> new LagAwareStrategy(lagConfig); - }; +// Configure REST API endpoint and credentials +HostAndPort restEndpoint = new HostAndPort("redis-enterprise-db-fqdn", 9443); +Supplier credentialsSupplier = () -> new DefaultRedisCredentials("rest-api-user", "pwd"); - // Configure REST API endpoint and credentials - 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 +// 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(); - MultiDbConfig.StrategySupplier lagAwareStrategySupplier = (hostAndPort, - jedisClientConfig) -> new LagAwareStrategy(lagConfig); + +// 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