Skip to content

Commit

Permalink
Merge pull request #43566 from Ladicek/redis-options-update
Browse files Browse the repository at this point in the history
Redis: expose new configuration options from the Vert.x Redis client
  • Loading branch information
Ladicek authored Sep 27, 2024
2 parents 60a9780 + de323cc commit 259ffef
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 17 deletions.
42 changes: 41 additions & 1 deletion docs/src/main/asciidoc/redis-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ The Redis extension can operate in 4 distinct modes:
* Simple client (probably what most users need).
* Sentinel (when working with Redis in High Availability mode).
* Cluster (when working with Redis in Clustered mode).
* Replication (single shard, one node write, multiple read).
* Replication (single shard, one node writes, multiple read).

The connection url is configured with the `quarkus.redis.hosts` (or `quarkus.redis.<name>.hosts`) as follows:

Expand Down Expand Up @@ -183,6 +183,22 @@ The client will obtain the URLs of actual Redis servers (master or replicas, dep
Note that you practically never want to configure `quarkus.redis.role=sentinel`.
This setting means that the Redis client will execute commands directly on one of the sentinel servers, instead of an actual Redis server guarded by the sentinels.

==== Automatic Failover

In the sentinel mode, it is possible to configure automatic failover of _master_ connections:

[source,properties]
----
quarkus.redis.auto-failover=true
----

If enabled, the sentinel client will additionally create a connection to one sentinel node and watch for failover events.
When new master is elected, all connections to the old master are automatically closed and new connections to the new master are created.
Automatic failover makes sense for connections executing regular commands, but not for connections used to subscribe to Redis pub/sub channels.

Note that there is a brief period of time between the old master failing and the new master being elected when the existing connections will temporarily fail all operations.
After the new master is elected, the connections will automatically fail over and start working again.

=== Use the Cluster Mode

When using Redis in cluster mode, you need to pass multiple _host urls_, configure the client type to `cluster` and configure the `replicas` mode:
Expand Down Expand Up @@ -233,6 +249,30 @@ Set the `quarkus.redis.replicas` configuration property to:

Note that replication in Redis is asynchronous, so replica nodes may be lagging behind the master node.

==== Static Topology

In the replication mode, it is possible to reconfigure the Redis client to skip automatic discovery of the topology:

[source,properties]
----
quarkus.redis.topology=static
----

With static topology, the first node in the configuration is assumed to be a _master_ node, while the remaining nodes are assumed to be _replicas_.
The nodes are not verified; it is a responsibility of the application developer to ensure that the static configuration is correct.

Note that automatic discovery of the topology is usually the preferred choice.
Static configuration should only be used when necessary.
One such case is _Amazon Elasticache for Redis (Cluster Mode Disabled)_, where:

* master node should be set to the _primary endpoint_, and
* one replica node should be set to the _reader endpoint_.

WARNING: Note that the reader endpoint of Elasticache for Redis (Cluster Mode Disabled) is a domain name which resolves to a CNAME record that points to one of the replicas.
The CNAME record to which the reader endpoint resolves changes over time.
This form of DNS-based load balancing does not work well with DNS resolution caching and connection pooling.
As a result, some replicas are likely to be underutilized.

=== Connect to Redis Cloud

To connect to redis cloud, you need the following properties:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,14 @@ public static Redis create(String name, Vertx vertx, RedisClientConfig config, T
config.preferredProtocolVersion().ifPresent(options::setPreferredProtocolVersion);
options.setPassword(config.password().orElse(null));
config.poolCleanerInterval().ifPresent(d -> options.setPoolCleanerInterval((int) d.toMillis()));
options.setPoolRecycleTimeout((int) config.poolRecycleTimeout().toMillis());
config.poolRecycleTimeout().ifPresent(d -> options.setPoolRecycleTimeout((int) d.toMillis()));
options.setHashSlotCacheTTL(config.hashSlotCacheTtl().toMillis());

config.role().ifPresent(options::setRole);
options.setType(config.clientType());
config.replicas().ifPresent(options::setUseReplicas);
options.setAutoFailover(config.autoFailover());
config.topology().ifPresent(options::setTopology);

options.setNetClientOptions(toNetClientOptions(config));
configureTLS(name, config, tlsRegistry, options.getNetClientOptions(), hosts);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,13 @@
import io.vertx.redis.client.RedisClientType;
import io.vertx.redis.client.RedisReplicas;
import io.vertx.redis.client.RedisRole;
import io.vertx.redis.client.RedisTopology;

@ConfigGroup
public interface RedisClientConfig {

/**
* The redis hosts to use while connecting to the redis server. Only the cluster and sentinel modes will consider more than
* The Redis hosts to use while connecting to the Redis server. Only the cluster and sentinel modes will consider more than
* 1 element.
* <p>
* The URI provided uses the following schema `redis://[username:password@][host][:port][/database]`
Expand All @@ -41,49 +42,47 @@ public interface RedisClientConfig {
Optional<String> hostsProviderName();

/**
* The maximum delay to wait before a blocking command to redis server times out
* The maximum delay to wait before a blocking command to Redis server times out
*/
@WithDefault("10s")
Duration timeout();

/**
* The redis client type.
* The Redis client type.
* Accepted values are: {@code STANDALONE} (default), {@code CLUSTER}, {@code REPLICATION}, {@code SENTINEL}.
*/
@WithDefault("standalone")
RedisClientType clientType();

/**
* The master name (only considered in HA mode).
* The master name (only considered in the Sentinel mode).
*/
@ConfigDocDefault("mymaster")
Optional<String> masterName();

/**
* The role name (only considered in Sentinel / HA mode).
* The role name (only considered in the Sentinel mode).
* Accepted values are: {@code MASTER}, {@code REPLICA}, {@code SENTINEL}.
*/
@ConfigDocDefault("master")
Optional<RedisRole> role();

/**
* Whether to use replicas nodes (only considered in Cluster mode).
* Whether to use replicas nodes (only considered in Cluster mode and Replication mode).
* Accepted values are: {@code ALWAYS}, {@code NEVER}, {@code SHARE}.
*/
@ConfigDocDefault("never")
Optional<RedisReplicas> replicas();

/**
* The default password for cluster/sentinel connections.
* The default password for Redis connections.
* <p>
* If not set it will try to extract the value from the current default {@code #hosts}.
* If not set, it will try to extract the value from the {@code hosts}.
*/
Optional<String> password();

/**
* The maximum size of the connection pool.
* When working with cluster or sentinel, this value should be at least the total number of cluster members (or
* number of sentinels + 1)
*/
@WithDefault("6")
int maxPoolSize();
Expand All @@ -95,15 +94,16 @@ public interface RedisClientConfig {
int maxPoolWaiting();

/**
* The duration indicating how often should the connection pool cleaner executes.
* The duration indicating how often should the connection pool cleaner execute.
*/
@ConfigDocDefault("30s")
Optional<Duration> poolCleanerInterval();

/**
* The timeout for a connection recycling.
* The timeout for unused connection recycling.
*/
@WithDefault("15")
Duration poolRecycleTimeout();
@WithDefault("3m")
Optional<Duration> poolRecycleTimeout();

/**
* Sets how many handlers is the client willing to queue.
Expand All @@ -115,7 +115,7 @@ public interface RedisClientConfig {
int maxWaitingHandlers();

/**
* Tune how much nested arrays are allowed on a redis response. This affects the parser performance.
* Tune how much nested arrays are allowed on a Redis response. This affects the parser performance.
*/
@WithDefault("32")
int maxNestedArrays();
Expand Down Expand Up @@ -156,6 +156,35 @@ public interface RedisClientConfig {
@WithDefault("1s")
Duration hashSlotCacheTtl();

/**
* Whether automatic failover is enabled. This only makes sense for sentinel clients
* with role of {@code MASTER} and is ignored otherwise.
* <p>
* If enabled, the sentinel client will additionally create a connection to one sentinel node
* and watch for failover events. When new master is elected, all connections to the old master
* are automatically closed and new connections to the new master are created. Automatic failover
* makes sense for connections executing regular commands, but not for connections used to subscribe
* to Redis pub/sub channels.
* <p>
* Note that there is a brief period of time between the old master failing and the new
* master being elected when the existing connections will temporarily fail all operations.
* After the new master is elected, the connections will automatically fail over and
* start working again.
*/
@WithDefault("false")
boolean autoFailover();

/**
* How the Redis topology is obtained. By default, the topology is discovered automatically.
* This is the only mode for the clustered and sentinel client. For replication client,
* topology may be set <em>statically</em>.
* <p>
* In case of a static topology for replication Redis client, the first node in the list
* is considered a <em>master</em> and the remaining nodes in the list are considered <em>replicas</em>.
*/
@ConfigDocDefault("discover")
Optional<RedisTopology> topology();

/**
* TCP config.
*/
Expand Down

0 comments on commit 259ffef

Please sign in to comment.