feat: Add SentinelInstanceClient for direct Sentinel node administration#4463
feat: Add SentinelInstanceClient for direct Sentinel node administration#4463ggivo wants to merge 6 commits into
Conversation
🛡️ Jit Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
Introduce a new UnifiedJedis-based client for connecting directly to Redis
Sentinel instances and executing Sentinel administrative commands.
Changes:
- Add SentinelInstanceClient interface
* Extends SentinelNodeCommands for Sentinel operations
* Extends AutoCloseable for resource management
* Provides direct access to all Sentinel administrative commands
- Add RedisSentinelInstanceClient implementation
* Extends UnifiedJedis for consistent client architecture
* Implements SentinelInstanceClient interface
* Provides builder pattern via StandaloneClientBuilder
* Supports Sentinel administrative commands (SENTINEL FAILOVER,
SENTINEL MONITOR, SENTINEL MASTERS, SENTINEL REPLICAS, etc.)
- Add SentinelNodeCommands interface
* Add ping() method for Sentinel health checks
* extends from SentinelCommands for Sentinel administrative commands
- Enhance RedisSentinelClient
* Add getAvailableSentinel() method to obtain a connected Sentinel instance toward one of used sentinels
* Uses ping() for health checking when selecting available Sentinel
- Add integration tests
* RedisSentinelInstanceClientIT
* RedisSentinelClientIT
This client enables direct interaction with Sentinel instances for:
- Monitoring master/replica configurations
- Triggering manual failovers
- Managing Sentinel monitoring configurations
- Querying Sentinel cluster state
- Health checking Sentinel instances
Example usage:
try (SentinelInstanceClient client = RedisSentinelInstanceClient.builder()
.hostAndPort("localhost", 26379)
.build()) {
List<Map<String, String>> masters = client.sentinelMasters();
client.sentinelFailover("mymaster");
}
// Or get an available Sentinel from RedisSentinelClient
try (RedisSentinelClient client = RedisSentinelClient.builder()
.masterName("mymaster")
.sentinels(sentinels)
.build();
SentinelInstanceClient sentinel = client.getAvailableSentinel()) {
String myId = sentinel.sentinelMyId();
}
Related to: CAE-2561
b2cce48 to
7791fa8
Compare
Test Results 307 files + 2 307 suites +2 11m 57s ⏱️ + 3m 4s Results for commit 21042b5. ± Comparison against base commit fef7829. This pull request removes 1 and adds 44 tests. Note that renamed tests count towards both.♻️ This comment has been updated with latest results. |
tishun
left a comment
There was a problem hiding this comment.
The second thing I commented got me very confused
| * @see SentinelNodeCommands | ||
| * @since 7.5.0 | ||
| */ | ||
| public interface SentinelInstanceClient extends SentinelNodeCommands, AutoCloseable { |
There was a problem hiding this comment.
Maybe we should consider this from the perspective of what common interface we would want to have used instead of the UnifiedJedis.
If we want to aim at something like RedisConnection that implements the Redis*Commands (or returns them) we might consider using here SentinelConnection? Or SentinelManagementConnection?
Not sure. tbh we can also think about SentinelManagementClient - if we want to follow the RedisClient / RedisClusterClient / RedisSentinelClient.
There was a problem hiding this comment.
I also don't like current naming.
RedisSentinelClient makes most sense to me but is already used.
Probably SentinelManagementClient and actual implementation could be RedisSentinelManagementClient
| import static redis.clients.jedis.Protocol.Command.SENTINEL; | ||
| import static redis.clients.jedis.Protocol.SentinelKeyword.*; | ||
|
|
||
| public final class RedisSentinelInstanceClient extends UnifiedJedis |
There was a problem hiding this comment.
Why do we implement the UnifiedJedis?
This is very different than what I was thinking we are doing...
| <include>**/resps/LibraryInfoTest.java</include> | ||
| <include>**/*Matchers.java</include> | ||
| <include>**/*TestUtil.java</include> | ||
| <include>**/sentinel/*.java</include> |
There was a problem hiding this comment.
Formatter glob pattern misses sentinel/api subdirectory files
Low Severity
The formatter include pattern **/sentinel/*.java only matches Java files directly inside a sentinel/ directory, but the new SentinelInstanceClient.java and SentinelNodeCommands.java files are in sentinel/api/. The Ant-style glob * does not cross directory boundaries, so these files won't be covered by the formatter validation. The pattern needs to be **/sentinel/**/*.java to include subdirectories.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
| ConnectionProvider connectionProvider, CommandObjects commandObjects, | ||
| RedisProtocol redisProtocol, Cache cache) { | ||
| super(commandExecutor, connectionProvider, commandObjects, redisProtocol, cache); | ||
| } |
There was a problem hiding this comment.
Extending UnifiedJedis exposes inappropriate Redis commands
Medium Severity
RedisSentinelInstanceClient extends UnifiedJedis, inheriting all standard Redis data commands (SET, GET, HSET, etc.) that are not valid on a Sentinel node. While the SentinelInstanceClient interface only exposes sentinel commands, the implementation carries unnecessary weight and any internal code or reflection-based access could invoke unsupported operations. A composition-based approach (wrapping a connection) would be more appropriate than inheritance here.


Introduce a new UnifiedJedis-based client for connecting directly to Redis Sentinel instances and executing Sentinel administrative commands.
Changes:
Add SentinelInstanceClient interface
Add RedisSentinelInstanceClient implementation
Add SentinelNodeCommands interface
Enhance RedisSentinelClient
Add integration tests
This client enables direct interaction with Sentinel instances for:
Public API Summary
New Interfaces
SentinelInstanceClient- Client interface for direct Sentinel instance operations (extendsSentinelNodeCommands,AutoCloseable)SentinelNodeCommands- Sentinel command interface with methods:ping()- Health checksentinelMyId()- Get Sentinel IDsentinelMasters()/sentinelMaster(String)- Query masterssentinelReplicas(String)/sentinelSentinels(String)- Query replicas/sentinelssentinelFailover(String)- Trigger failoversentinelMonitor(...)/sentinelRemove(String)- Manage monitoringsentinelSet(...)/sentinelCkquorum(String)/sentinelFlushConfig()/sentinelReset(String)- ConfigurationNew Implementation
RedisSentinelInstanceClient- Concrete implementation withbuilder()methodEnhanced API
RedisSentinelClient.getAvailableSentinel()- Returns a connectedSentinelInstanceClientfrom the configured Sentinel setExample usage:
Note
Medium Risk
Introduces new public Sentinel client interfaces and new connection logic (
getAvailableSentinel) that opens/validates sentinel connections, which could affect Sentinel-mode users if misconfigured or if resource handling differs.Overview
Adds a new direct-to-Sentinel client surface:
sentinel.api.SentinelInstanceClient/SentinelNodeCommandsand an internalRedisSentinelInstanceClientimplementation to execute Sentinel admin commands (e.g.,SENTINEL MASTERS,MASTER,REPLICAS,FAILOVER) over a standalone connection.Enhances
RedisSentinelClientto retain aSentinelConfigurationand exposegetAvailableSentinel(), which iterates configured sentinels,PINGs for health, and returns the first workingSentinelInstanceClient(else throwsJedisConnectionException).Extends response decoding with
BuilderFactory.STRING_MAP_LISTfor Sentinel replies, relaxesSentinelClientBuilderfield visibility for reuse, updates/adjusts Sentinel integration tests, and expands formatter includes to cover new Sentinel-related sources/tests.Written by Cursor Bugbot for commit 21042b5. This will update automatically on new commits. Configure here.