{"id":"failover","title":"Client-side geographic failover","url":"https:\/\/redis.io\/docs\/latest\/develop\/clients\/jedis\/failover\/","summary":"Improve reliability using the failover features of Jedis.","tags":["docs","develop","stack","oss","rs","rc","oss","kubernetes","clients"],"last_updated":"2026-05-05T09:23:06-05:00","page_type":"content","content_hash":"21fc8a02eddfac54f3d8d274d3ff6484209ae551fcc648123534cb81833ece9e","sections":[{"id":"overview","title":"Overview","role":"overview","text":"Jedis supports [Client-side geographic failover](https:\/\/en.wikipedia.org\/wiki\/Failover)\nto improve the availability of connections to Redis databases. This page explains\nhow to configure Jedis for failover. For an overview of the concepts,\nsee the main [Client-side geographic failover](https:\/\/redis.io\/docs\/latest\/develop\/clients\/failover) page."},{"id":"failover-configuration","title":"Failover configuration","role":"content","text":"Jedis uses the [resilience4j](https:\/\/resilience4j.readme.io\/docs\/getting-started)\nlibrary to detect connection problems using a\n[circuit breaker design pattern](https:\/\/en.wikipedia.org\/wiki\/Circuit_breaker_design_pattern).\n\nThe example below shows a simple case with a list of two servers,\n`redis-east` and `redis-west`, where `redis-east` is the preferred\ntarget. If `redis-east` fails, Jedis should fail over to\n`redis-west`.\n\nJedis v6 supported failover\/failback using a special\n`UnifiedJedis` constructor. You should update existing code to use\nthe approach shown below for Jedis v7 and later.\n\n\nFirst, add the `resilience4j` dependencies to your project. If you\nare using [Maven](https:\/\/maven.apache.org\/), add the following\ndependencies to your `pom.xml` file:\n\n[code example]\n\nIf you are using [Gradle](https:\/\/gradle.org\/), add the following\ndependencies to your `build.gradle` file:\n\n[code example]\n\nIn your source file, create some simple configuration for the client and\n[connection pool](https:\/\/redis.io\/docs\/latest\/develop\/clients\/jedis\/connect#connect-with-a-connection-pool),\nas you would for a standard connection.\n\n[code example]\n\nSupply the weighted list of endpoints using the `MultiDbConfig` builder\n(see [Selecting a failover target](https:\/\/redis.io\/docs\/latest\/develop\/clients\/failover#selecting-a-failover-target) for a full description of how\nthe weighted list is used).\nUse the `weight` option to order the endpoints, with the highest\nweight being tried first.\n\n[code example]\n\nThe builder lets you add several options to configure the\n[circuit breaker](#circuit-breaker-configuration) behavior\nand [retries](#retry-configuration) (these are explained in more detail below).\n\n[code example]\n\nFinally, use the configuration to build the `MultiDbClient`.\n\n[code example]\n\nLike `UnifiedJedis`, `MultiDbClient` implements the usual Redis commands,\nbut will also handle the connection management and failover transparently."},{"id":"circuit-breaker-configuration","title":"Circuit breaker configuration","role":"content","text":"The `MultiDbConfig.CircuitBreakerConfig` builder lets you pass several options to configure\nthe circuit breaker (see [Detecting connection problems](https:\/\/redis.io\/docs\/latest\/develop\/clients\/failover#detecting-connection-problems) for more information on how the\ncircuit breaker works):\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `slidingWindowSize()` | `2` | Duration in seconds to keep failures and successes in the sliding window. |\n| `minNumOfFailures()` | `1000` | Minimum number of failures that must occur before the circuit breaker is tripped. |\n| `failureRateThreshold()` | `10.0f` | Percentage of failures to trigger the circuit breaker. |\n| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures. By default, it includes just `JedisConnectionException`. |\n| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for failure rate calculation. |"},{"id":"retry-configuration","title":"Retry configuration","role":"content","text":"The `MultiDbConfig.RetryConfig` builder has the following options to configure retries:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `maxAttempts()` | `3` | Maximum number of retry attempts (including the initial call). Set to `1` to disable retries. |\n| `waitDuration()` | `500` | Initial number of milliseconds to wait between retry attempts. |\n| `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. |\n| `includedExceptionList()` | See description | `List` of `Throwable` classes that should be considered as failures to be retried. By default, it includes just `JedisConnectionException`. |\n| `ignoreExceptionList()` | `null` | `List` of `Throwable` classes that should be ignored for retry. |"},{"id":"general-failover-configuration","title":"General failover configuration","role":"content","text":"The `MultiDbConfig` builder also has the following options to configure general failover behavior:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `maxNumFailoverAttempts()` | `10` | Number of attempts to fail over to a new endpoint before giving up. |\n| `delayInBetweenFailoverAttempts()` | `12000` | Time interval in milliseconds between successive failover attempts. |\n| `gracePeriod()` | `60000` | Time interval in milliseconds to keep the unhealthy endpoint disabled even if it recovers (this prevents rapid oscillation between endpoints when intermittent faults occur). |\n| `fastFailover()` | `false` | If true, existing connections to an unhealthy endpoint are forced to close immediately when a failover occurs, otherwise the connections are closed gracefully. Forcefully closing connections can make failover faster, but it might also cause in-flight operations to fail. |\n| `retryOnFailover()` | `false` | If true, commands that fail during a failover are automatically retried on the replacement endpoint. |"},{"id":"failover-callbacks","title":"Failover callbacks","role":"content","text":"You may want to take some custom action when a failover occurs.\nFor example, you could log a warning, increment a metric, \nor externally persist the cluster connection state.\n\nYou can provide a custom failover action using a class that\nimplements `java.util.function.Consumer`. Place\nthe custom action in the `accept()` method, as shown in the example below.\n\n[code example]\n\nUse the `databaseSwitchListener()` method of the `MultiDbClient` builder to\nregister your custom action:\n\n[code example]\n\nYour `accept()` method is now called whenever a failover occurs.\n\nSince `Consumer` is a functional interface, you can also use a lambda\nexpression to supply the custom action directly.\n\n[code example]"},{"id":"failback-configuration","title":"Failback configuration","role":"content","text":"The `MultiDbConfig` builder lets you configure the failback behavior using the following options:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `failbackSupported()` | `true` | Enable\/disable failback (it is enabled by default). |\n| `failbackCheckInterval()` | `120000` | Interval in milliseconds to check if the unhealthy database has recovered. |"},{"id":"health-check-configuration","title":"Health check configuration","role":"content","text":"Each health check consists of one or more separate \"probes\", each of which is a simple\ntest (such as a [`PING`](https:\/\/redis.io\/docs\/latest\/commands\/ping) command) to determine if the database is available. The results of the separate probes are combined\nusing a configurable policy to determine if the database is healthy.\n\nThere are several strategies available for health checks that you can deploy using the\n`MultiDbConfig` builder. Each strategy is a class that implements the `HealthCheckStrategy` \ninterface. Use the constructor of a `HealthCheckStrategy` implementation to pass\na `HealthCheckStrategy.Config` object to configure the health check behavior.\nThe methods of the base `HealthCheckStrategy.Config` builder are shown below.\nNote that some strategies (including your own custom strategies) may use a\nsubclass of `HealthCheckStrategy.Config` to provide extra options.\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `interval()` | `1000` | Interval in milliseconds between health checks. |\n| `timeout()` | `1000` | Timeout in milliseconds for health check requests. |\n| `numProbes()` | `3` | Number of probes to perform during each health check. |\n| `delayInBetweenProbes()` | `100` | Delay in milliseconds between probes during a health check. |\n| `policy()` | `ProbingPolicy.BuiltIn.ALL_SUCCESS` | Policy to determine if the database is healthy based on the probe results. The options are `ALL_SUCCESS` (all probes must succeed), `ANY_SUCCESS` (at least one probe must succeed), and `MAJORITY_SUCCESS` (majority of probes must succeed). |\n\nThe sections below explain the available strategies in more detail."},{"id":"pingstrategy-default","title":"`PingStrategy` (default)","role":"content","text":"The default strategy, `PingStrategy`, periodically sends a Redis\n[`PING`](https:\/\/redis.io\/docs\/latest\/commands\/ping) command\nand checks that it gives the expected response. Any unexpected response\nor exception indicates an unhealthy server. Although `PingStrategy` is\nvery simple, it is a good basic approach for most Redis deployments.\n\nAlthough `PingStrategy` is the default, you can still activate it\nexplicitly using the `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder. Use this approach if you want to configure the default\n`PingStrategy` with custom options, as shown in the example below.\n\n[code example]"},{"id":"lagawarestrategy-preview","title":"`LagAwareStrategy` (preview)","role":"content","text":"`LagAwareStrategy` (currently in preview) is designed specifically for\nRedis Software [Active-Active](https:\/\/redis.io\/docs\/latest\/operate\/rs\/databases\/active-active)\ndeployments. It uses the Redis Software REST API to check database availability\nand can also optionally check replication lag.\n\n`LagAwareStrategy` determines the health of the server using the\n[REST API](https:\/\/redis.io\/docs\/latest\/operate\/rs\/references\/rest-api). The example\nbelow shows how to configure `LagAwareStrategy` and activate it using\nthe `healthCheckStrategy()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder.\n\n[code example]\n\nThe `LagAwareStrategy.Config` builder has the following options in\naddition to the standard options provided by `HealthCheckStrategy.Config`:\n\n| Builder method | Default value | Description|\n| --- | --- | --- |\n| `sslOptions()` | `null` | Standard SSL options for connecting to the REST API. |\n| `extendedCheckEnabled()` | `false` | Enable extended lag checking (this includes lag validation in addition to the standard datapath validation). |\n| `availabilityLagTolerance()` | `100` | Maximum lag tolerance in milliseconds for extended lag checking. |"},{"id":"custom-health-check-strategy","title":"Custom health check strategy","role":"content","text":"You can supply your own custom health check strategy by\nimplementing the `HealthCheckStrategy` interface. For example, you might\nuse this to integrate with external monitoring tools or to implement\nchecks that are specific to your application. The example below\nshows a simple custom strategy. Pass your custom strategy implementation to the `MultiDbConfig.DatabaseConfig`\nbuilder with the `healthCheckStrategySupplier()` method.\n\n[code example]"},{"id":"disable-health-checks","title":"Disable health checks","role":"content","text":"To disable health checks completely, use the\n`healthCheckEnabled()` method of the `MultiDbConfig.DatabaseConfig`\nbuilder:\n\n[code example]"},{"id":"managing-databases-at-runtime","title":"Managing databases at runtime","role":"content","text":"Although you will typically configure all databases during the initial connection, you can also modify the configuration at runtime. The example below shows how to add and remove database endpoints.\n\n[code example]"},{"id":"manual-failback","title":"Manual failback","role":"content","text":"By default, the failback mechanism runs health checks on all servers in the\nweighted list and selects the highest-weighted server that is\nhealthy. However, you can also use the `setActiveDatabase()` method of\n`MultiDbClient` to select which database to use manually:\n\n[code example]\n\nNote that `setActiveDatabase()` is thread-safe.\n\nIf you decide to implement manual failback, you will need a way for external\nsystems to trigger this method in your application. For example, if your application\nexposes a REST API, you might consider creating a REST endpoint to call\n`setActiveDatabase()`."},{"id":"behavior-when-all-endpoints-are-unhealthy","title":"Behavior when all endpoints are unhealthy","role":"content","text":"In the extreme case where no endpoint is healthy, a command will throw a\n`JedisTemporarilyNotAvailableException`. This indicates that the client is periodically\nchecking to see if any endpoint becomes healthy again. The number of\ntimes it will keep checking is configured by the `maxNumFailoverAttempts()` option in\nthe `MultiDbConfig` builder and\nthe delay between attempts is configured by the `delayInBetweenFailoverAttempts()` option (see [General failover configuration](#general-failover-configuration)). With the default settings,\n`maxNumFailoverAttempts` * `delayInBetweenFailoverAttempts`  gives a period of 120 seconds to find\na healthy endpoint.\n\nYou can still keep retrying commands after a `JedisTemporarilyNotAvailableException` is thrown (for example,\nyou could add this exception to the `includedExceptionList`, as described\nin the [Retry configuration](https:\/\/redis.io\/docs\/latest\/#retry-configuration) section). However, if the number of\nfailover attempts exceeds the value set by `maxNumFailoverAttempts()`, commands will throw a `JedisPermanentlyNotAvailableException`. Note that this is intended to notify your app\nthat the problem is likely to be persistent, but it *doesn't* mean that Jedis will stop trying\nto connect to a healthy endpoint if one becomes available."},{"id":"troubleshooting","title":"Troubleshooting","role":"errors","text":"This section lists some common problems and their solutions."},{"id":"excessive-or-constant-health-check-failures","title":"Excessive or constant health check failures","role":"content","text":"If all health checks fail, you should first rule out authentication\nproblems with the Redis server and also make sure there are no persistent\nnetwork connectivity problems. If you still see frequent or constant\nfailures, try increasing the timeout for health checks and the\ninterval between them:\n\n[code example]"},{"id":"slow-failback-after-recovery","title":"Slow failback after recovery","role":"content","text":"If failback is too slow after a server recovers, you can try\nincreasing the frequency of health checks and reducing the grace\nperiod before failback is attempted (the grace period is the\nminimum time after a failover before Jedis will check if a\nfailback is possible).\n\n[code example]"}],"examples":[{"id":"failover-configuration-ex0","language":"xml","code":"<dependency>\n    <groupId>io.github.resilience4j<\/groupId>\n    <artifactId>resilience4j-all<\/artifactId>\n    <version>1.7.1<\/version>\n<\/dependency>\n<dependency>\n    <groupId>io.github.resilience4j<\/groupId>\n    <artifactId>resilience4j-circuitbreaker<\/artifactId>\n    <version>1.7.1<\/version>\n<\/dependency>\n<dependency>\n    <groupId>io.github.resilience4j<\/groupId>\n    <artifactId>resilience4j-retry<\/artifactId>\n    <version>1.7.1<\/version>\n<\/dependency>","section_id":"failover-configuration"},{"id":"failover-configuration-ex1","language":"bash","code":"compileOnly 'io.github.resilience4j:resilience4j-resilience4j-all:1.7.1'\ncompileOnly 'io.github.resilience4j:resilience4j-circuitbreaker:1.7.1'\ncompileOnly 'io.github.resilience4j:resilience4j-retry:1.7.1'","section_id":"failover-configuration"},{"id":"failover-configuration-ex2","language":"java","code":"JedisClientConfig config = DefaultJedisClientConfig.builder().user(\"<username>\").password(\"<password>\")\n    .socketTimeoutMillis(5000).connectionTimeoutMillis(5000).build();\n\nConnectionPoolConfig poolConfig = new ConnectionPoolConfig();\npoolConfig.setMaxTotal(8);\npoolConfig.setMaxIdle(8);\npoolConfig.setMinIdle(0);\npoolConfig.setBlockWhenExhausted(true);\npoolConfig.setMaxWait(Duration.ofSeconds(1));\npoolConfig.setTestWhileIdle(true);\npoolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(1));","section_id":"failover-configuration"},{"id":"failover-configuration-ex3","language":"java","code":"HostAndPort east = new HostAndPort(\"redis-east.example.com\", 14000);\nHostAndPort west = new HostAndPort(\"redis-west.example.com\", 14000);\n\nMultiDbConfig.Builder multiConfig = MultiDbConfig.builder()\n        .database(DatabaseConfig.builder(east, config).connectionPoolConfig(poolConfig).weight(1.0f).build())\n        .database(DatabaseConfig.builder(west, config).connectionPoolConfig(poolConfig).weight(0.5f).build());","section_id":"failover-configuration"},{"id":"failover-configuration-ex4","language":"java","code":"\/\/ Configure circuit breaker for failure detection\nmultiConfig\n        .failureDetector(MultiDbConfig.CircuitBreakerConfig.builder()\n                .slidingWindowSize(2)        \/\/ Sliding window size as a duration in seconds.\n                .failureRateThreshold(10.0f)    \/\/ Percentage of failures to trigger circuit breaker.\n                .minNumOfFailures(1000)          \/\/ Minimum number of failures before circuit breaker is tripped.\n                .build())\n        .failbackSupported(true)                \/\/ Enable failback.\n        .failbackCheckInterval(120000)          \/\/ Check every 2 minutes to see if the unhealthy database has recovered.\n        .gracePeriod(60000)                     \/\/ Keep database disabled for 60 seconds after it becomes unhealthy.\n        \/\/ Optional: configure retry settings\n        .commandRetry(MultiDbConfig.RetryConfig.builder()\n                .maxAttempts(3)                  \/\/ Maximum number of retry attempts (including the initial call)\n                .waitDuration(500)               \/\/ Number of milliseconds to wait between retry attempts.\n                .exponentialBackoffMultiplier(2) \/\/ Exponential backoff factor multiplied by the wait duration between retries.\n                .build())\n        \/\/ Optional: configure fast failover\n        .fastFailover(true)                       \/\/ Force closing connections to unhealthy database on failover.\n        .retryOnFailover(false);                  \/\/ Do not retry failed commands during failover.","section_id":"failover-configuration"},{"id":"failover-configuration-ex5","language":"java","code":"MultiDbClient multiDbClient = MultiDbClient.builder()\n        .multiDbConfig(multiConfig.build())\n        .build();","section_id":"failover-configuration"},{"id":"failover-callbacks-ex0","language":"java","code":"import org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\nimport java.util.function.Consumer;\n\npublic class FailoverReporter implements Consumer<DatabaseSwitchEvent> {\n\n    @Override\n    public void accept(DatabaseSwitchEvent e) {\n        System.out.println(\"Jedis failover to database: \" + e.getDatabaseName() + \" due to \" + e.getReason());\n    }\n}","section_id":"failover-callbacks"},{"id":"failover-callbacks-ex1","language":"java","code":"FailoverReporter reporter = new FailoverReporter();\nMultiDbClient client = MultiDbClient.builder()\n        .databaseSwitchListener(reporter)\n        .build();","section_id":"failover-callbacks"},{"id":"failover-callbacks-ex2","language":"java","code":"MultiDbClient client = MultiDbClient.builder()\n        .databaseSwitchListener(\n            event -> System.out.println(\"Switched to: \" + event.getEndpoint())\n        )\n        .build();","section_id":"failover-callbacks"},{"id":"pingstrategy-default-ex0","language":"java","code":"MultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategy(new PingStrategy(PingStrategy.Config.builder()\n                        .interval(5000) \/\/ Check every 5 seconds\n                        .timeout(3000) \/\/ 3 second timeout\n                        .numProbes(5) \/\/ 5 probes per check\n                        .delayInBetweenProbes(100) \/\/ 100ms delay between probes\n                        .build()))\n                .build();","section_id":"pingstrategy-default"},{"id":"lagawarestrategy-preview-ex0","language":"java","code":"\/\/ Configure REST API endpoint and credentials\nHostAndPort restEndpoint = new HostAndPort(\"redis-enterprise-db-fqdn\", 9443);\nSupplier<RedisCredentials> credentialsSupplier = () -> new DefaultRedisCredentials(\"rest-api-user\", \"pwd\");\n\n\/\/ Build a single LagAwareStrategy based on REST endpoint and credentials\nLagAwareStrategy.Config lagConfig = LagAwareStrategy.Config\n        .builder(restEndpoint, credentialsSupplier)\n        .interval(5000) \/\/ Check every 5 seconds\n        .timeout(3000) \/\/ 3 second timeout\n        .extendedCheckEnabled(true)\n        .build();\n\n\/\/ Configure a database to use lag-aware health check\nMultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategy(new LagAwareStrategy(lagConfig))\n                .build();","section_id":"lagawarestrategy-preview"},{"id":"custom-health-check-strategy-ex0","language":"java","code":"\/\/ Custom strategy supplier\nMultiDbConfig.StrategySupplier customStrategy =\n        (hostAndPort, jedisClientConfig) -> {\n            \/\/ Return your custom HealthCheckStrategy implementation\n            return new MyCustomHealthCheckStrategy(hostAndPort, jedisClientConfig);\n        };\n\nMultiDbConfig.DatabaseConfig dbConfig =\n        MultiDbConfig.DatabaseConfig.builder(hostAndPort, clientConfig)\n                .healthCheckStrategySupplier(customStrategy)\n                .weight(1.0f)\n                .build();","section_id":"custom-health-check-strategy"},{"id":"disable-health-checks-ex0","language":"java","code":"MultiDbConfig.DatabaseConfig dbConfig = MultiDbConfig.DatabaseConfig.builder(east, config)\n    .healthCheckEnabled(false) \/\/ Disable health checks entirely\n    .build();","section_id":"disable-health-checks"},{"id":"managing-databases-at-runtime-ex0","language":"java","code":"HostAndPort other = new HostAndPort(\"redis-south.example.com\", 14000);\n\n\/\/ Create the database config as you would for the initial connection.\nclient.addDatabase(DatabaseConfig.builder(other, config)\n        \/\/ ...\n        .weight(0.5f)\n        .build()\n);\n\n\/\/ Remove the database from the failover set.\nclient.removeDatabase(other);","section_id":"managing-databases-at-runtime"},{"id":"manual-failback-ex0","language":"java","code":"\/\/ The `setActiveDatabase()` method receives the `Endpoint` (eg,`HostAndPort`)\n\/\/ of the cluster to switch to.\nclient.setActiveDatabase(west);","section_id":"manual-failback"},{"id":"excessive-or-constant-health-check-failures-ex0","language":"java","code":"HealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(5000)                 \/\/ Less frequent checks\n    .timeout(2000)                  \/\/ More generous timeout\n    .build();","section_id":"excessive-or-constant-health-check-failures"},{"id":"slow-failback-after-recovery-ex0","language":"java","code":"\/\/ Faster recovery configuration\nHealthCheckStrategy.Config config = HealthCheckStrategy.Config.builder()\n    .interval(1000)                    \/\/ More frequent checks\n    .build();\n\n\/\/ Adjust failback timing\nMultiDbConfig multiConfig = MultiDbConfig.builder()\n        .gracePeriod(5000)                 \/\/ Shorter grace period\n        .build();","section_id":"slow-failback-after-recovery"}]}