{
  "schema_version": 2,
  "id": "develop/clients/lettuce/failover",
  "title": "Client-side geographic failover",
  "url": "https://redis.io/docs/latest/develop/clients/lettuce/failover/",
  "summary": "Improve reliability using the failover features of Lettuce.",
  "tags": [
    "docs",
    "develop",
    "stack",
    "oss",
    "rs",
    "rc",
    "oss",
    "kubernetes",
    "clients"
  ],
  "last_updated": "2026-09-09T09:47:45+01:00",
  "page_type": "content",
  "content_hash": "f218585784c848c974f8e8a018fa0acc9217bf5afdb9f20a9c95bfaa7537d107",
  "sections": [
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "Lettuce 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 Lettuce 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": "connection-configuration",
      "title": "Connection configuration",
      "role": "content",
      "text": "The 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, Lettuce should fail over to\n`redis-west`.\n\nIn your source file, import the required classes:\n\n[code example]\n\nSupply the weighted endpoints using a `List` of `DatabaseConfig` objects\n(see [Selecting a failover target](https://redis.io/docs/latest/develop/clients/failover#selecting-a-failover-target)\nfor a full description of how the weighted list is used).\nUse the `weight` option in the builder to order the endpoints, with the highest\nweight being tried first.\n\n[code example]\n\nPass the list of `DatabaseConfig` objects to a `MultiDbClient` instance\nduring creation. This class provides the same features as the standard\n`RedisClient` class, but will also handle the connection management and failover transparently.\nLikewise, the `StatefulRedisMultiDbConnection` implements the same interface\nto Redis commands as the standard `StatefulRedisConnection` class.\n\n[code example]"
    },
    {
      "id": "database-configuration",
      "title": "Database configuration",
      "role": "content",
      "text": "Use the `DatabaseConfig` builder to configure each database in the list.\nThe builder provides the following methods:\n\n| Method | Default | Description |\n| --- | --- | --- |\n| `weight()` | `1.0f` | Priority weight for database selection. |\n| `circuitBreakerConfig()` | Default config | `CircuitBreakerConfig` object to configure failure detection. See [Circuit breaker configuration](#circuit-breaker-configuration) below for details. |\n| `healthCheckStrategySupplier()` | `PingStrategy.DEFAULT` | `HealthCheckStrategySupplier` object to configure health checks. See [Health check configuration](#health-check-configuration) below for details. |\n| `clientOptions()` | Default options | Per-database `ClientOptions` configuration. |"
    },
    {
      "id": "client-configuration",
      "title": "Client configuration",
      "role": "content",
      "text": "Use the `MultiDbOptions` builder to supply global options for the multi-database client\nincluding failback configuration:\n\n| Method | Default | Description |\n| --- | --- | --- |\n| `failbackSupported()` | `true` | Enable automatic failback to higher-priority databases. |\n| `failbackCheckInterval()` | `Duration.ofSeconds(120)` | Interval for checking if failed databases have recovered. |\n| `gracePeriod()` | `Duration.ofSeconds(60)` | Time to wait before allowing failback to a recovered database. |\n| `delayInBetweenFailoverAttempts()` | `Duration.ofSeconds(12)` | Delay between failover attempts when no healthy database is available. |\n| `initializationPolicy()` | `MAJORITY_AVAILABLE` | `InitializationPolicy` enum value for connection initialization. |\n\nThe initialization policy is used to decide if the number of healthy databases available\nat startup is sufficient to consider the client initialized and ready to use. The\npolicies are:\n\n- `ALL_AVAILABLE` - All databases must be available.\n- `MAJORITY_AVAILABLE` - A majority of databases must be available.\n- `ONE_AVAILABLE` - At least one database must be available.\n\nThe example below shows how to supply the configuration when you create the client.\n\n[code example]"
    },
    {
      "id": "failover-configuration",
      "title": "Failover configuration",
      "role": "content",
      "text": "The `DatabaseConfig` builder lets you pass configuration objects to specify\nthe behavior of the circuit breaker and health checks. These\nare described in the sections below."
    },
    {
      "id": "circuit-breaker-configuration",
      "title": "Circuit breaker configuration",
      "role": "content",
      "text": "The `CircuitBreakerConfig` builder lets you pass several options to configure\nthe circuit breaker, as shown in the example below (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[code example]\n\nThe builder provides the following methods:\n\n| Method | Default value | Description|\n| --- | --- | --- |\n| `metricsWindowSize()` | `2` | Duration in seconds to keep failures and successes in the sliding window. |\n| `minimumNumberOfFailures()` | `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| `trackedExceptions()` | See description | `Set` of `Throwable` classes that should be considered as failures. The default set contains `RedisConnectionException`, `IOException`, `ConnectException`, `RedisCommandTimeoutException`, and `TimeoutException`. |"
    },
    {
      "id": "failover-events",
      "title": "Failover events",
      "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. Lettuce provides\ntwo `Event` classes that you can subscribe to for this purpose,\n`DatabaseSwitchEvent` and `AllDatabasesUnhealthyEvent`.\n\n#### `DatabaseSwitchEvent`\n\nUse `DatabaseSwitchEvent` to detect when the client switches to a different database.\nThe class implements three methods to get information about the event:\n\n- `getFromDb()` - Returns the `RedisURI` of the database that was previously in use.\n- `getToDb()` - Returns the `RedisURI` of the database that is now in use.\n- `getReason()` - Returns one of the following `SwitchReason` enum values to describe why the switch occurred:\n   - `HEALTH_CHECK` - Database failed health check\n   - `CIRCUIT_BREAKER` - Circuit breaker opened due to failures\n   - `FAILBACK` - Automatic failback to higher-priority database\n   - `FORCED` - Manual switch via `switchTo()` (see [Manual failback](#manual-failback) below for details)\n\nThe example below shows a simple event handler that logs information about the switch.\n\n[code example]\n\n#### `AllDatabasesUnhealthyEvent`\n\nUse `AllDatabasesUnhealthyEvent` to detect when all databases become unhealthy.\nThis class implements two methods to get information about the event:\n\n- `getFailedAttempts()` - Returns the number of consecutive failover attempts that have failed.\n- `getUnhealthyDatabases()` - Returns a `List` of `RedisURI` objects for the unhealthy databases.\n\nThe example below shows a simple event handler that logs information about the unhealthy databases.\n\n[code example]"
    },
    {
      "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`DatabaseConfig` 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` builder are shown in the table below.\nNote that some strategies (including your own custom strategies) may use a\nsubclass of `HealthCheckStrategy.Config` to provide extra options.\n\n| 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 `healthCheckStrategySupplier()` method of the `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",
      "title": "`LagAwareStrategy`",
      "role": "content",
      "text": "`LagAwareStrategy` 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 and use `LagAwareStrategy`. Note that\n`LagAwareStrategy` requires the following dependencies (although they are\nadded automatically with Lettuce):\n\n[code example]\n\nConfigure `LagAwareStrategy` using its configuration builder and then\npass the configured strategy to the `healthCheckStrategySupplier()` method of the `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| Method | Default value | Description|\n| --- | --- | --- |\n| `restApiUri()` | (required) | URI of the Redis Software REST API. |\n| `credentials()` | (required) | Credentials for accessing the REST API. |\n| `extendedCheckEnabled()` | `false` | Enable extended lag checking (this includes lag validation in addition to the standard datapath validation). |\n| `availabilityLagTolerance()` | `Duration.ofMillis(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 `DatabaseConfig`\nbuilder with the `healthCheckStrategySupplier()` method, as shown in the example below.\n\n[code example]"
    },
    {
      "id": "disable-health-checks",
      "title": "Disable health checks",
      "role": "content",
      "text": "To disable health checks completely for a specific database, pass a value of\n`HealthCheckStrategySupplier.NO_HEALTH_CHECK` to the\n`healthCheckStrategySupplier()` method of the `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 `switchTo()` method of\n`MultiDbClient` to select which database to use manually:\n\n[code example]\n\nNote that `switchTo()` 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`switchTo()`."
    },
    {
      "id": "pubsub-and-re-subscription",
      "title": "Pub/Sub and re-subscription",
      "role": "content",
      "text": "`MultiDbClient` supports [Pub/Sub](https://redis.io/docs/latest/develop/pubsub)\nmessaging with automatic re-subscription to channels during failover.\nThis means you don't have to detect failovers and re-subscribe manually:\n\n- **Subscriber failover**: When the active database becomes unhealthy, the\n  subscriber automatically reconnects to the next available database and\n  re-subscribes to the same channels.\n- **Publisher failover**: The publisher switches to the next available\n  database and continues publishing to the same channels.\n\nFor reliable Pub/Sub delivery during failover, use `MultiDbClient` instances\nfor *both* publishers and subscribers.\n\nUnlike the standard commands, which use the connection returned by `connect()`,\nPub/Sub uses a dedicated connection that you obtain with the `connectPubSub()`\nmethod. This returns a `StatefulRedisMultiDbPubSubConnection`, which implements\nthe same interface as the standard `StatefulRedisPubSubConnection`. Register a\n`RedisPubSubListener` (or extend `RedisPubSubAdapter` to override only the\ncallbacks you need) to receive messages, then subscribe to one or more channels:\n\n[code example]\n\nUse a separate Pub/Sub connection to publish messages:\n\n[code example]\n\n> [!NOTE]\n> The only addition over a standard Lettuce Pub/Sub connection is that\n> you obtain the connection with `connectPubSub()` on the `MultiDbClient`. The\n> subscription and publishing API is otherwise identical, but the active\n> subscriptions and registered listeners are automatically migrated to the new\n> database when a failover occurs.\n>\n> Message loss can still occur if the failover events happen in the reverse order,\n> with the publisher failing over to the new database before the subscriber.\n> Messages published during this window may not reach a subscriber that is still\n> connected to the previous database."
    },
    {
      "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, Lettuce will keep trying\nindefinitely to reconnect to a healthy endpoint. The frequency of these attempts\nis configured by the `delayInBetweenFailoverAttempts()` option in the `MultiDbConfig`\nbuilder (see [Client configuration](#client-configuration)) with\na default value of 12 seconds.\n\nNote that you can subscribe to the `AllDatabasesUnhealthyEvent` event to be notified when\nall endpoints become unhealthy (see [Failover events](#failover-events) for more information). Your\nevent handler could include code to close the connection if the indefinite search for\na healthy endpoint is not suitable for your application (see\n[Connection configuration](#connection-configuration) to learn how to close a connection)."
    },
    {
      "id": "troubleshooting",
      "title": "Troubleshooting",
      "role": "errors",
      "text": "This section lists some common problems and their solutions. Note that you can\nuse debug logging for the failover client to get more information when problems occur.\nTo enable this, set the log level to `DEBUG` in your logging configuration:\n\n[code example]"
    },
    {
      "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 Lettuce will check if a\nfailback is possible).\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "connection-configuration-ex0",
      "language": "java",
      "code": "import io.lettuce.core.RedisURI;\nimport io.lettuce.core.failover.DatabaseConfig;\nimport io.lettuce.core.failover.MultiDbClient;\nimport io.lettuce.core.failover.MultiDbOptions;\nimport io.lettuce.core.failover.api.StatefulRedisMultiDbConnection;",
      "section_id": "connection-configuration"
    },
    {
      "id": "connection-configuration-ex1",
      "language": "java",
      "code": "RedisURI eastUri = RedisURI.builder()\n        .withHost(\"redis-east.example.com\")\n        .withPort(6379)\n        .withPassword(\"secret\".toCharArray())\n        .build();\n\nDatabaseConfig east = DatabaseConfig.builder(eastUri)\n        .weight(1.0f)\n        .build();\n\nRedisURI westUri = RedisURI.builder()\n        .withHost(\"redis-west.example.com\")\n        .withPort(6379)\n        .withPassword(\"secret\".toCharArray())\n        .build();\n\nDatabaseConfig west = DatabaseConfig.builder(westUri)\n        .weight(0.5f)\n        .build();\n\nList<DatabaseConfig> databases = Arrays.asList(east, west);",
      "section_id": "connection-configuration"
    },
    {
      "id": "connection-configuration-ex2",
      "language": "java",
      "code": "MultiDbClient client = MultiDbClient.create(databases);\n\n// Connect and use like a regular Redis connection\nStatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Execute commands asynchronously - they go to the highest-weighted\n// healthy database\nconnection.async().set(\"key\", \"value\");\nString value = connection.async().get(\"key\").get();\n\n// Clean up\nconnection.close();\nclient.shutdown();",
      "section_id": "connection-configuration"
    },
    {
      "id": "client-configuration-ex0",
      "language": "java",
      "code": "MultiDbOptions options = MultiDbOptions.builder()\n        .failbackSupported(true)\n        .failbackCheckInterval(Duration.ofSeconds(30))\n        .gracePeriod(Duration.ofSeconds(10))\n        .delayInBetweenFailoverAttempts(Duration.ofSeconds(5))\n        .initializationPolicy(InitializationPolicy.ALL_AVAILABLE)\n        .build();\n\nMultiDbClient client = MultiDbClient.create(Arrays.asList(db1, db2), options);",
      "section_id": "client-configuration"
    },
    {
      "id": "circuit-breaker-configuration-ex0",
      "language": "java",
      "code": "import io.lettuce.core.failover.api.CircuitBreakerConfig;\n\nCircuitBreakerConfig cbConfig = CircuitBreakerConfig.builder()\n        .failureRateThreshold(50.0f)\n        .minimumNumberOfFailures(100)\n        .metricsWindowSize(5)\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .circuitBreakerConfig(cbConfig)\n        .build();",
      "section_id": "circuit-breaker-configuration"
    },
    {
      "id": "failover-events-ex0",
      "language": "java",
      "code": "import io.lettuce.core.failover.event.DatabaseSwitchEvent;\nimport io.lettuce.core.failover.event.SwitchReason;\n\nclient.getResources().eventBus().get()\n        .filter(event -> event instanceof DatabaseSwitchEvent)\n        .cast(DatabaseSwitchEvent.class)\n        .subscribe(event -> log.info(\"Switch: {} -> {} ({})\",\n                event.getFromDb(), event.getToDb(), event.getReason()));",
      "section_id": "failover-events"
    },
    {
      "id": "failover-events-ex1",
      "language": "java",
      "code": "import io.lettuce.core.failover.event.AllDatabasesUnhealthyEvent;\n\nclient.getResources().eventBus().get()\n        .filter(event -> event instanceof AllDatabasesUnhealthyEvent)\n        .cast(AllDatabasesUnhealthyEvent.class)\n        .subscribe(event -> log.warn(\"All databases unhealthy! Attempts: {}, DBs: {}\",\n                event.getFailedAttempts(), event.getUnhealthyDatabases()));",
      "section_id": "failover-events"
    },
    {
      "id": "pingstrategy-default-ex0",
      "language": "java",
      "code": "import io.lettuce.core.failover.health.PingStrategy;\nimport io.lettuce.core.failover.health.HealthCheckStrategy;\n\nHealthCheckStrategy.Config healthConfig = HealthCheckStrategy.Config.builder()\n        .interval(5000)                 // Check every 5 seconds\n        .timeout(1000)                  // 1 second timeout\n        .numProbes(3)                   // 3 probes per check\n        .delayInBetweenProbes(500)      // 500ms between probes\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) ->\n                new PingStrategy(factory, healthConfig))\n        .build();",
      "section_id": "pingstrategy-default"
    },
    {
      "id": "lagawarestrategy-ex0",
      "language": "xml",
      "code": "<dependency>\n    <groupId>io.netty</groupId>\n    <artifactId>netty-codec-http</artifactId>\n</dependency>\n<dependency>\n    <groupId>com.fasterxml.jackson.core</groupId>\n    <artifactId>jackson-databind</artifactId>\n</dependency>",
      "section_id": "lagawarestrategy"
    },
    {
      "id": "lagawarestrategy-ex1",
      "language": "java",
      "code": "import io.lettuce.core.failover.health.LagAwareStrategy;\n\nLagAwareStrategy.Config lagConfig = LagAwareStrategy.Config.builder()\n        .restApiUri(URI.create(\"https://cluster.redis.local:9443\"))\n        .credentials(() -> RedisCredentials.just(\"admin\", \"password\"))\n        .extendedCheckEnabled(true)  // Enable lag-aware checks\n        .availabilityLagTolerance(Duration.ofMillis(100))\n        .build();\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) -> new LagAwareStrategy(lagConfig))\n        .build();",
      "section_id": "lagawarestrategy"
    },
    {
      "id": "custom-health-check-strategy-ex0",
      "language": "java",
      "code": "// Custom strategy supplier\npublic class CustomHealthCheck extends AbstractHealthCheckStrategy {\n\n    public CustomHealthCheck(HealthCheckStrategy.Config config) {\n        super(config);\n    }\n\n    @Override\n    public HealthStatus doHealthCheck(RedisURI endpoint) {\n        // Return HealthStatus.HEALTHY, UNHEALTHY, or UNKNOWN\n        return checkExternalMonitoringSystem(endpoint)\n                ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;\n    }\n}\n\n// Use with healthCheckStrategySupplier()\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier((uri, factory) ->\n                new CustomHealthCheck(HealthCheckStrategy.Config.create()))\n        .build();",
      "section_id": "custom-health-check-strategy"
    },
    {
      "id": "disable-health-checks-ex0",
      "language": "java",
      "code": "import io.lettuce.core.failover.health.HealthCheckStrategySupplier;\n\nDatabaseConfig db = DatabaseConfig.builder(redisUri)\n        .healthCheckStrategySupplier(HealthCheckStrategySupplier.NO_HEALTH_CHECK)\n        .build();",
      "section_id": "disable-health-checks"
    },
    {
      "id": "managing-databases-at-runtime-ex0",
      "language": "java",
      "code": "StatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Add a new database\nRedisURI newDb = RedisURI.create(\"redis://new-server:6379\");\nDatabaseConfig newConfig = DatabaseConfig.builder(newDb)\n        .weight(0.8f)\n        .build();\nconnection.addDatabase(newConfig);\n\n// Remove a database\nconnection.removeDatabase(existingUri);",
      "section_id": "managing-databases-at-runtime"
    },
    {
      "id": "manual-failback-ex0",
      "language": "java",
      "code": "StatefulRedisMultiDbConnection<String, String> connection = client.connect();\n\n// Get current endpoint\nRedisURI current = connection.getCurrentEndpoint();\n\n// Get all available endpoints\nCollection<RedisURI> endpoints = connection.getEndpoints();\n\n// Check if a specific endpoint is healthy\nboolean healthy = connection.isHealthy(targetUri);\n\n// Force switch to a specific endpoint\nconnection.switchTo(targetUri);",
      "section_id": "manual-failback"
    },
    {
      "id": "pubsub-and-re-subscription-ex0",
      "language": "java",
      "code": "import io.lettuce.core.failover.api.StatefulRedisMultiDbPubSubConnection;\nimport io.lettuce.core.pubsub.RedisPubSubAdapter;\n\nStatefulRedisMultiDbPubSubConnection<String, String> pubSubConnection =\n        client.connectPubSub();\n\n// Register a listener to handle incoming messages.\npubSubConnection.addListener(new RedisPubSubAdapter<String, String>() {\n    @Override\n    public void message(String channel, String message) {\n        System.out.printf(\"Received \\\"%s\\\" on channel \\\"%s\\\"%n\", message, channel);\n    }\n});\n\n// Subscribe to one or more channels. If a failover happens, the\n// subscriptions are automatically re-established on the new active database.\npubSubConnection.sync().subscribe(\"news\", \"alerts\");",
      "section_id": "pubsub-and-re-subscription"
    },
    {
      "id": "pubsub-and-re-subscription-ex1",
      "language": "java",
      "code": "StatefulRedisMultiDbPubSubConnection<String, String> publisher =\n        client.connectPubSub();\n\npublisher.sync().publish(\"news\", \"Hello World\");",
      "section_id": "pubsub-and-re-subscription"
    },
    {
      "id": "troubleshooting-ex0",
      "language": "plaintext",
      "code": "logging.level.io.lettuce.core.failover=DEBUG",
      "section_id": "troubleshooting"
    },
    {
      "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\nMultiDbOptions multiConfig = MultiDbOptions.builder()\n        .gracePeriod(5000)                 // Shorter grace period\n        .build();",
      "section_id": "slow-failback-after-recovery"
    }
  ]
}
