Skip to content

Commit 20ea67a

Browse files
authored
[automatic failover] Implement ping health check (CAE-1687) (#3564)
* add Ping strategy * add PingStrategyIntegrationTests add integration test * health checks refactored (inject DatabaseConnectionProvider instead ClientOptions Inject DatabaseConnectionProvider into HealthCheckStrategySupplier's. Injecting per DB connection factory allows reuse of MultiDB client resources - ClientOptions no longer propagated to HealthCheckStrategySupplier - HealthCheckStrategySupplier refactored to use DatabaseConnectionProvider * clean up - renamed DatabaseConnectionProvider -> DatabaseRawConnectionFactory - api docs updated * format * Fix sporadic test failures - Shared TestClientResources shutdown during tests, caused subsequent test to fail. * clean up - rename internal vars * clean up - add unit test - remove unused HealthCheckStrategySupplier DEFAULT_WITH_PROVIDER
1 parent f152db6 commit 20ea67a

File tree

7 files changed

+697
-7
lines changed

7 files changed

+697
-7
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package io.lettuce.core.failover;
2+
3+
import io.lettuce.core.RedisURI;
4+
import io.lettuce.core.api.StatefulRedisConnection;
5+
6+
/**
7+
* Factory interface to obtain direct {@link StatefulRedisConnection connections} to Redis database nodes. Connections created
8+
* by this factory are raw connections without CircuitBreaker counting, health checks, or other management features.
9+
*
10+
* @author Ivo Gaydazhiev
11+
* @since 7.2
12+
*/
13+
@FunctionalInterface
14+
public interface DatabaseRawConnectionFactory {
15+
16+
/**
17+
* Creates a new bare connection to the specified database endpoint. The connection is created without CircuitBreaker
18+
* counting, health checks, or other management features.
19+
*
20+
* @param endpoint the Redis URI of the database endpoint
21+
* @return a new stateful Redis connection to the specified endpoint
22+
*/
23+
StatefulRedisConnection<?, ?> connectToDatabase(RedisURI endpoint);
24+
25+
}

src/main/java/io/lettuce/core/failover/MultiDbClientImpl.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class MultiDbClientImpl extends RedisClient implements MultiDbClient {
4747

4848
private final Map<RedisURI, DatabaseConfig> databaseConfigs;
4949

50+
private final DatabaseRawConnectionFactory databaseRawConnectionFactory = new DatabaseRawConnectionFactoryImpl();
51+
5052
MultiDbClientImpl(Collection<DatabaseConfig> databaseConfigs) {
5153
this(null, databaseConfigs);
5254
}
@@ -125,7 +127,7 @@ private <K, V> RedisDatabase<StatefulRedisConnection<K, V>> createRedisDatabase(
125127
HealthCheck healthCheck;
126128
if (config.getHealthCheckStrategySupplier() != null) {
127129
HealthCheckStrategy hcStrategy = config.getHealthCheckStrategySupplier().get(config.getRedisURI(),
128-
connection.getOptions());
130+
databaseRawConnectionFactory);
129131
healthCheck = healthStatusManager.add(uri, hcStrategy);
130132
} else {
131133
healthCheck = null;
@@ -184,8 +186,9 @@ private <K, V> RedisDatabase<StatefulRedisPubSubConnection<K, V>> createRedisDat
184186

185187
HealthCheck healthCheck;
186188
if (config.getHealthCheckStrategySupplier() != null) {
189+
187190
HealthCheckStrategy hcStrategy = config.getHealthCheckStrategySupplier().get(config.getRedisURI(),
188-
connection.getOptions());
191+
databaseRawConnectionFactory);
189192
healthCheck = healthStatusManager.add(uri, hcStrategy);
190193
} else {
191194
healthCheck = null;
@@ -262,4 +265,13 @@ private void waitForInitialHealthyDatabase(StatusTracker statusTracker,
262265
throw new RedisConnectionException("All configured databases are unhealthy.");
263266
}
264267

268+
private class DatabaseRawConnectionFactoryImpl implements DatabaseRawConnectionFactory {
269+
270+
@Override
271+
public StatefulRedisConnection<?, ?> connectToDatabase(RedisURI endpoint) {
272+
return MultiDbClientImpl.this.connect(endpoint);
273+
}
274+
275+
}
276+
265277
}

src/main/java/io/lettuce/core/failover/health/HealthCheckStrategySupplier.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package io.lettuce.core.failover.health;
22

3-
import io.lettuce.core.ClientOptions;
43
import io.lettuce.core.RedisURI;
4+
import io.lettuce.core.failover.DatabaseRawConnectionFactory;
55

66
/**
77
* Supplier for health check strategies.
@@ -18,6 +18,6 @@ public interface HealthCheckStrategySupplier {
1818
* @param clientOptions the client options
1919
* @return the health check strategy
2020
*/
21-
HealthCheckStrategy get(RedisURI redisURI, ClientOptions clientOptions);
21+
HealthCheckStrategy get(RedisURI redisURI, DatabaseRawConnectionFactory connectionProvider);
2222

2323
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package io.lettuce.core.failover.health;
2+
3+
import io.lettuce.core.RedisURI;
4+
import io.lettuce.core.api.StatefulRedisConnection;
5+
import io.lettuce.core.failover.DatabaseRawConnectionFactory;
6+
7+
public class PingStrategy implements HealthCheckStrategy {
8+
9+
private final DatabaseRawConnectionFactory connectionFactory;
10+
11+
private final HealthCheckStrategy.Config config;
12+
13+
public PingStrategy(RedisURI redisURI, DatabaseRawConnectionFactory connectionFactory) {
14+
this(redisURI, connectionFactory, HealthCheckStrategy.Config.create());
15+
}
16+
17+
public PingStrategy(RedisURI redisURI, DatabaseRawConnectionFactory connectionFactory, HealthCheckStrategy.Config config) {
18+
this.connectionFactory = connectionFactory;
19+
this.config = config;
20+
}
21+
22+
@Override
23+
public int getInterval() {
24+
return config.getInterval();
25+
}
26+
27+
@Override
28+
public int getTimeout() {
29+
return config.getTimeout();
30+
}
31+
32+
@Override
33+
public int getNumProbes() {
34+
return config.getNumProbes();
35+
}
36+
37+
@Override
38+
public ProbingPolicy getPolicy() {
39+
return config.getPolicy();
40+
}
41+
42+
@Override
43+
public int getDelayInBetweenProbes() {
44+
return config.getDelayInBetweenProbes();
45+
}
46+
47+
@Override
48+
public HealthStatus doHealthCheck(RedisURI endpoint) {
49+
try (StatefulRedisConnection<?, ?> connection = connectionFactory.connectToDatabase(endpoint)) {
50+
51+
if (connection == null) {
52+
return HealthStatus.UNHEALTHY;
53+
}
54+
55+
return "PONG".equals(connection.sync().ping()) ? HealthStatus.HEALTHY : HealthStatus.UNHEALTHY;
56+
} catch (Exception e) {
57+
return HealthStatus.UNHEALTHY;
58+
}
59+
}
60+
61+
@Override
62+
public void close() {
63+
// No resources to close
64+
}
65+
66+
public static final HealthCheckStrategySupplier DEFAULT = PingStrategy::new;
67+
68+
}

src/test/java/io/lettuce/core/failover/DatabaseEndpointCallbackTests.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,6 @@ void setUp() {
5454

5555
@AfterEach
5656
void tearDown() {
57-
if (clientResources != null) {
58-
FastShutdown.shutdown(clientResources);
59-
}
6057
if (endpoint != null) {
6158
endpoint.close();
6259
}

0 commit comments

Comments
 (0)