When connecting to Redis version 6 or above with an ACL configured, your ACL user needs to at least have permissions to run the ECHO command. We run this command to verify that we have a valid connection to the Redis service.
Because there are lots of different ways to configure redis, StackExchange.Redis offers a rich configuration model, which is invoked when calling Connect (or ConnectAsync):
var conn = ConnectionMultiplexer.Connect(configuration);
The configuration here can be either:
- a
ConfigurationOptionsinstance - a
stringrepresenting the configuration
The latter is basically a tokenized form of the former.
Basic Configuration Strings
The simplest configuration example is just the host name:
var conn = ConnectionMultiplexer.Connect("localhost");
This will connect to a single server on the local machine using the default redis port (6379). Additional options are simply appended (comma-delimited). Ports are represented with a colon (:) as is usual. Configuration options include an = after the name. For example:
var conn = ConnectionMultiplexer.Connect("redis0:6380,redis1:6380,allowAdmin=true");
If you specify a serviceName in the connection string, it will trigger sentinel mode. This example will connect to a sentinel server on the local machine
using the default sentinel port (26379), discover the current primary server for the myprimary service and return a managed connection
pointing to that primary server that will automatically be updated if the primary changes:
var conn = ConnectionMultiplexer.Connect("localhost,serviceName=myprimary");
abortConnect applies to sentinel connections the same way it applies to everything else. With abortConnect=true (the default), Connect
throws if the sentinels cannot be reached or do not know the service. With abortConnect=false, Connect returns a multiplexer even then:
it is not connected, operations fail with a connection exception that names the problem, and it connects itself to the primary once the
sentinels become reachable and name one. If you want startup to fail when the sentinels are unavailable, keep abortConnect=true.
A sentinel-managed connection tracks failovers through several signals: the +switch-master announcement, a reconnect poll whenever the
connection to the primary is lost, and - because the announcement is delivered over pub/sub and can be missed - a periodic check that
notices when the node last named as primary is still connected but now reports itself as a replica (a failover where nothing disconnected,
such as a primary demoted in place). In that last case the client re-asks sentinel for the current primary and follows its answer, even to
a node at an address it has never seen; while sentinel still names the demoted node (sentinel itself has not yet failed over), the client
keeps asking without tearing down or reconfiguring the healthy connections.
An overview of mapping between the string and ConfigurationOptions representation is shown below, but you can switch between them trivially:
ConfigurationOptions options = ConfigurationOptions.Parse(configString);
or:
string configString = options.ToString();
A common usage is to store the basic details in a string, and then apply specific details at runtime:
string configString = GetRedisConfiguration();
var options = ConfigurationOptions.Parse(configString);
options.ClientName = GetAppName(); // only known at runtime
options.AllowAdmin = true;
conn = ConnectionMultiplexer.Connect(options);
Microsoft Azure Redis example with password
var conn = ConnectionMultiplexer.Connect("contoso5.redis.cache.windows.net,ssl=true,password=...");
Configuration Options
The ConfigurationOptions object has a wide range of properties, all of which are fully documented in intellisense. Some of the more common options to use include:
| Configuration string | ConfigurationOptions |
Default | Meaning |
|---|---|---|---|
| abortConnect={bool} | AbortOnConnectFail |
true (false on Azure) |
If true, Connect will not create a connection while no servers are available |
| allowAdmin={bool} | AllowAdmin |
false |
Enables a range of commands that are considered risky |
| channelPrefix={string} | ChannelPrefix |
null |
Optional channel prefix for all pub/sub operations |
| checkCertificateRevocation={bool} | CheckCertificateRevocation |
true |
A Boolean value that specifies whether the certificate revocation list is checked during authentication. |
| connectRetry={int} | ConnectRetry |
3 |
The number of times to repeat connect attempts during initial Connect |
| connectTimeout={int} | ConnectTimeout |
5000 |
Timeout (ms) for connect operations |
| configChannel={string} | ConfigurationChannel |
__Booksleeve_MasterChanged |
Broadcast channel name for communicating configuration changes |
| configCheckSeconds={int} | ConfigCheckSeconds |
60 |
Time (seconds) between re-checks of each connected server’s replication role, via INFO replication; also acts as a keep-alive for interactive sockets. Not a topology re-read: see topologyRefreshSeconds |
| topologyRefreshSeconds={int} | TopologyRefreshSeconds |
1800 |
Time (seconds) between unprompted topology re-reads, or 0 to never do so. Jittered by up to 30 seconds. |
| defaultDatabase={int} | DefaultDatabase |
null |
Default database index, from 0 to databases - 1 |
| keepAlive={int} | KeepAlive |
-1 |
Time (seconds) at which to send a message to help keep sockets alive (60 sec default) |
| tcpKeepAlive={bool} | TcpKeepAlive |
true |
Enables TCP keep-alive when appropriate (endpoint- and platform-dependent) |
| name={string} | ClientName |
null |
Identification for the connection within redis |
| password={string} | Password |
null |
Password for the redis server |
| sentinelPassword={string} | SentinelPassword |
null |
Optional password to authenticate with Sentinel servers (falls back to password if not provided) |
| user={string} | User |
null |
User for the redis server (for use with ACLs on redis 6 and above) |
| sentinelUser={string} | SentinelUser |
null |
Optional username to authenticate with Sentinel servers (falls back to user if not provided) |
| proxy={proxy type} | Proxy |
Proxy.None |
Type of proxy in use (if any); for example “twemproxy/envoyproxy” |
| resolveDns={bool} | ResolveDns |
false |
Specifies that DNS resolution should be explicit and eager, rather than implicit |
| serviceName={string} | ServiceName |
null |
Used for connecting to a sentinel primary service |
| ssl={bool} | Ssl |
false |
Specifies that SSL encryption should be used |
| sslHost={string} | SslHost |
null |
Enforces a particular SSL host identity on the server’s certificate |
| sslProtocols={enum} | SslProtocols |
null |
Ssl/Tls versions supported when using an encrypted connection. Use ‘|’ to provide multiple values. |
| syncTimeout={int} | SyncTimeout |
5000 |
Time (ms) to allow for synchronous operations |
| asyncTimeout={int} | AsyncTimeout |
SyncTimeout |
Time (ms) to allow for asynchronous operations |
| tiebreaker={string} | TieBreaker |
__Booksleeve_TieBreak |
Key to use for selecting a server in an ambiguous primary scenario |
| version={string} | DefaultVersion |
(7.4 in AMR, else 6.0) |
Redis version level (useful when the server does not make this available) |
| tunnel={string} | Tunnel |
null |
Tunnel for connections (use http:{proxy url} for “connect”-based proxy server) |
| setlib={bool} | SetClientLibrary |
true |
Whether to attempt to use CLIENT SETINFO to set the library name/version on the connection |
| protocol={string} | Protocol |
null |
Redis protocol to use; see section below |
| sharedSubscriptionConnection={bool} | SharedSubscriptionConnection |
false |
Under RESP3, share the interactive connection with pub/sub rather than using a dedicated one; see section below |
| highIntegrity={bool} | HighIntegrity |
false |
High integrity (incurs overhead) sequence checking on every command; see section below |
| defaults={string} | Defaults |
null |
Selects a named defaults provider; see section below |
| maintNotifications={string} | MaintenanceNotifications |
disabled |
Whether to ask servers for maintenance notifications; see section below |
| maintRelaxedTimeout={int} | MaintenanceRelaxedTimeout |
10 |
Seconds that command timeouts are relaxed to during announced maintenance; see section below |
| maintRelaxedWindowMax={int} | MaintenanceRelaxedWindowMax |
30 |
Seconds a relaxed window may last at most; see section below |
| maintPostEventRelaxed={int} | MaintenancePostEventRelaxedDuration |
20 |
Seconds timeouts stay relaxed after maintenance completes; see section below |
Additional code-only options:
- LoggerFactory (
ILoggerFactory) - Default:null- The logger to use for connection events (not per command), e.g. connection log, disconnects, reconnects, server errors.
- ReconnectRetryPolicy (
IReconnectRetryPolicy) - Default:ReconnectRetryPolicy = ExponentialRetry(ConnectTimeout / 2);- Determines how often a multiplexer will try to reconnect after a failure
- BacklogPolicy - Default:
BacklogPolicy = BacklogPolicy.Default;- Determines how commands will be queued (or not) during a disconnect, for sending when it’s available again
- BeforeSocketConnect - Default:
null- Allows modifying a
Socketbefore connecting (for advanced scenarios)
- Allows modifying a
- SslClientAuthenticationOptions (
netcoreapp3.1/net5.0and higher) - Default:null- Allows specifying exact options for SSL/TLS authentication against a server (e.g. cipher suites, protocols, etc.) - overrides all other SSL configuration options. This is a
Func<string, SslClientAuthenticationOptions>which receives the host (orSslHostif set) to get the options for. Ifnullis returned from theFunc, it’s the same as this property not being set at all when connecting.
- Allows specifying exact options for SSL/TLS authentication against a server (e.g. cipher suites, protocols, etc.) - overrides all other SSL configuration options. This is a
- SocketManager - Default:
SocketManager.Shared:- The thread pool to use for scheduling work to and from the socket connected to Redis, one of…
SocketManager.Shared: Use a shared dedicated thread pool for all multiplexers (defaults to 10 threads) - best balance for most scenarios.SocketManager.ThreadPool: Use the build-in .NET thread pool for scheduling. This can perform better for very small numbers of cores or with large apps on large machines that need to use more than 10 threads (total, across all multiplexers) under load. Important: this option isn’t the default because it’s subject to thread pool growth/starvation and if for example synchronous calls are waiting on a redis command to come back to unblock other threads, stalls/hangs can result. Use with caution, especially if you have sync-over-async work in play.
- The thread pool to use for scheduling work to and from the socket connected to Redis, one of…
- HighIntegrity - Default:
false- This enables sending a sequence check command after every single command sent to Redis. This is an opt-in option that incurs overhead to add this integrity check which isn’t in the Redis protocol (RESP2/3) itself. The impact on this for a given workload depends on the number of commands, size of payloads, etc. as to how proportionately impactful it will be - you should test with your workloads to assess this.
- This is especially relevant if your primary use case is all strings (e.g. key/value caching) where the protocol would otherwise not error.
- Intended for cases where network drops (e.g. bytes from the Redis stream, not packet loss) are suspected and integrity of responses is critical.
- HeartbeatConsistencyChecks - Default:
false- Allows always sending keepalive checks even if a connection isn’t idle. This trades extra commands (per
HeartbeatInterval- default 1 second) to check the network stream for consistency. If any data was lost, the result won’t be as expected and the connection will be terminated ASAP. This is a check to react to any data loss at the network layer as soon as possible.
- Allows always sending keepalive checks even if a connection isn’t idle. This trades extra commands (per
- HeartbeatInterval - Default:
1000ms- Allows running the heartbeat more often which importantly includes timeout evaluation for async commands. For example if you have a 50ms async command timeout, we’re only actually checking it during the heartbeat (once per second by default), so it’s possible 50-1050ms pass before we notice it timed out. If you want more fidelity in that check and to observe that a server failed faster, you can lower this to run the heartbeat more often to achieve that.
- Note: heartbeats are not free and that’s why the default is 1 second. There is additional overhead to running this more often simply because it does some work each time it fires.
- LibraryName - Default:
SE.Redis(unless aDefaultOptionsProviderspecifies otherwise)- The library name to use with
CLIENT SETINFOwhen setting the library name/version on the connection
- The library name to use with
- IncludeDetailInExceptions - Default:
true- Whether exceptions include identifiable details (key names, and additional
.Dataannotations)
- Whether exceptions include identifiable details (key names, and additional
- Defaults (
DefaultOptionsProvider) - Default: resolved from the configured endpoints- The provider that supplies default values for options that have not been set explicitly (for example, recognized cloud endpoints can apply tuned defaults)
- IncludePerformanceCountersInExceptions - Default:
false- Whether exceptions include performance counter details (CPU usage, etc); note that this can be problematic on some platforms
- RequestBufferPool (
MemoryPool<byte>) - Default:null- The buffer pool to use when buffering requests; when
null, a shared default pool is used
- The buffer pool to use when buffering requests; when
- ResponseBufferPool (
MemoryPool<byte>) - Default:null- The buffer pool to use when buffering responses (and for allocating
Lease<byte>results); whennull, a shared default pool is used
- The buffer pool to use when buffering responses (and for allocating
- CircuitBreaker (
CircuitBreaker) - Default:null- Client-side geographic failover. A per-connection circuit breaker that passively observes the outcome of normal traffic and tears the connection down when it becomes unstable. When the connection is a member of a connection group, this flows in from
MultiGroupOptions.CircuitBreakerif not set explicitly. See Client-side geographic failover
- Client-side geographic failover. A per-connection circuit breaker that passively observes the outcome of normal traffic and tears the connection down when it becomes unstable. When the connection is a member of a connection group, this flows in from
- HealthCheck (
HealthCheck) - Default:null- Client-side geographic failover. An active health check used when the connection is a member of a connection group; when
null, the group-levelMultiGroupOptions.HealthCheckis used. See Client-side geographic failover
- Client-side geographic failover. An active health check used when the connection is a member of a connection group; when
Tokens in the configuration string are comma-separated; any without an = sign are assumed to be redis server endpoints. Endpoints without an explicit port will use 6379 if ssl is not enabled, and 6380 if ssl is enabled.
Tokens starting with $ are taken to represent command maps, for example: $config=cfg.
Obsolete Configuration Options
These options are parsed in connection strings for backwards compatibility (meaning they do not error as invalid), but no longer have any effect.
| Configuration string | ConfigurationOptions |
Previous Default | Previous Meaning |
|---|---|---|---|
| responseTimeout={int} | ResponseTimeout |
SyncTimeout |
Time (ms) to decide whether the socket is unhealthy |
| writeBuffer={int} | WriteBuffer |
4096 |
Size of the output buffer |
Automatic and Manual Configuration
In many common scenarios, StackExchange.Redis will automatically configure a lot of settings, including the server type and version, connection timeouts, and primary/replica relationships. Sometimes, though, the commands for this have been disabled on the redis server. In this case, it is useful to provide more information:
ConfigurationOptions config = new ConfigurationOptions
{
EndPoints =
{
{ "redis0", 6379 },
{ "redis1", 6380 }
},
CommandMap = CommandMap.Create(new HashSet<string>
{ // EXCLUDE a few commands
"INFO", "CONFIG", "CLUSTER",
"PING", "ECHO", "CLIENT"
}, available: false),
KeepAlive = 180,
DefaultVersion = new Version(2, 8, 8),
Password = "changeme"
};
Which is equivalent to the command string:
redis0:6379,redis1:6380,keepAlive=180,version=2.8.8,$CLIENT=,$CLUSTER=,$CONFIG=,$ECHO=,$INFO=,$PING=
Renaming Commands
A slightly unusual feature of redis is that you can disable and/or rename individual commands. As per the previous example, this is done via the CommandMap, but instead of passing a HashSet<string> to Create() (to indicate the available or unavailable commands), you pass a Dictionary<string,string>. All commands not mentioned in the dictionary are assumed to be enabled and not renamed. A null or blank value records that the command is disabled. For example:
var commands = new Dictionary<string,string> {
{ "info", null }, // disabled
{ "select", "use" }, // renamed to SQL equivalent for some reason
};
var options = new ConfigurationOptions {
// ...
CommandMap = CommandMap.Create(commands),
// ...
}
The above is equivalent to (in the connection string):
$INFO=,$SELECT=use
Redis Server Permissions
If the user you’re connecting to Redis with is limited, it still needs to have certain commands enabled for the StackExchange.Redis to succeed in connecting. The client uses:
AUTHto authenticateCLIENTto set the client nameINFOto understand server topology/settingsECHOfor heartbeat.- (Optional)
SUBSCRIBEto observe change events - (Optional)
CONFIGto get/understand settings - (Optional)
CLUSTERto get cluster nodes - (Optional)
SENTINELonly for Sentinel servers - (Optional)
GETto determine tie breakers - (Optional)
SET(only ifINFOis disabled) to see if we’re writable
For example, a common very minimal configuration ACL on the server (non-cluster) would be:
-@all +@pubsub +@read +echo +info
Note that if you choose to disable access to the above commands, it needs to be done via the CommandMap and not only the ACL on the server (otherwise we’ll attempt the command and fail the handshake). Also, if any of the these commands are disabled, some functionality may be diminished or broken.
twemproxy
twemproxy is a tool that allows multiple redis instances to be used as though it were a single server, with inbuilt sharding and fault tolerance (much like redis cluster, but implemented separately). The feature-set available to Twemproxy is reduced. To avoid having to configure this manually, the Proxy option can be used:
var options = new ConfigurationOptions
{
EndPoints = { "my-server" },
Proxy = Proxy.Twemproxy
};
##envoyproxy
Envoyproxy is a tool that allows to front a redis cluster with a set of proxies, with inbuilt discovery and fault tolerance. The feature-set available to Envoyproxy is reduced. To avoid having to configure this manually, the Proxy option can be used:
var options = new ConfigurationOptions+{
EndPoints = { "my-proxy1", "my-proxy2", "my-proxy3" },
Proxy = Proxy.Envoyproxy
};
Tiebreakers and Configuration Change Announcements
Normally StackExchange.Redis will resolve primary/replica nodes automatically. However, if you are not using a management tool such as redis-sentinel or redis cluster, there is a chance that occasionally you will get multiple primary nodes (for example, while resetting a node for maintenance it may reappear on the network as a primary). To help with this, StackExchange.Redis can use the notion of a tie-breaker - which is only used when multiple primaries are detected (not including redis cluster, where multiple primaries are expected). For compatibility with BookSleeve, this defaults to the key named "__Booksleeve_TieBreak" (always in database 0). This is used as a crude voting mechanism to help determine the preferred primary, so that work is routed correctly.
Likewise, when the configuration is changed (especially the primary/replica configuration), it will be important for connected instances to make themselves aware of the new situation (via INFO, CONFIG, etc - where available). StackExchange.Redis does this by automatically subscribing to a pub/sub channel upon which such notifications may be sent. For similar reasons, this defaults to "__Booksleeve_MasterChanged".
Both options can be customized or disabled (set to ""), via the .ConfigurationChannel and .TieBreaker configuration properties.
These settings are also used by the IServer.MakeMaster() method, which can set the tie-breaker in the database and broadcast the configuration change message. The configuration message can also be used separately to primary/replica changes simply to request all nodes to refresh their configurations, via the ConnectionMultiplexer.PublishReconfigure method.
The configuration channel is subscribed to on every server, under both RESP2 and RESP3 - on the subscription connection, or on the interactive connection when it is shared with pub/sub (SharedSubscriptionConnection); this is enough to make the server treat that connection as a pub/sub client. Like any other pub/sub channel, it has the ChannelPrefix (if one is configured) applied - both when subscribing to it and when the library publishes to it - so a client with channelPrefix=app1- listens on app1-__Booksleeve_MasterChanged. Two consequences follow:
- If you announce a change yourself rather than through the library (for example
PUBLISH app1-__Booksleeve_MasterChanged "*"fromredis-cli), publish to the prefixed name, once for each distinct prefix in use; clients with a different prefix will not hear it. A publish to the unprefixed name is only heard by clients that have no prefix. - If you use ACLs that restrict channels, the user needs access to the prefixed channel (for example
&app1-__Booksleeve_MasterChanged), in addition to any channels the application itself uses.
The configuration channel is a convenience for prompt notification. Where the server can report the change itself (for example the -MOVED responses from a redis cluster), the library will refresh without it.
Refreshing the topology after repeated connect failures
An endpoint that refuses every connection is evidence that what the client believes about the deployment may
be wrong, so after three consecutive failed connection attempts to the same endpoint, the client re-reads
the topology - the same refresh a MOVED or a configuration announcement would have caused, jittered and
coalesced in the same way.
This closes a real gap rather than a theoretical one. Every other path that re-reads the topology needs somebody else to notice first: a redirect from a reachable node, a peer’s configuration announcement, or a maintenance notification. The internal flag that drives a refresh-on-failure is only set once a connection has been established, so an endpoint that has never connected - because it was replaced while the client was running, or was already gone at startup - could be retried indefinitely with nobody to say otherwise. Measured in the field: a client dialled three removed nodes for around 37 hours.
The re-read is rate-limited to configCheckSeconds (default 60), deliberately reusing the knob that already
means “how often may we re-read configuration” rather than adding one. That restraint is what makes it safe:
a permanently dead endpoint, times a retry loop, times every client in a fleet would otherwise be a great
many topology reads, so a dead endpoint prompts at most one re-read per interval until something changes.
Note that configCheckSeconds on its own is not a periodic topology refresh - it drives an
INFO replication on an established connection, which is a replication-role check. This is the path that
notices an endpoint nobody can reach.
…and the backstop for one nobody can fault
Repeated connect failures cover an endpoint that refuses or never finishes a handshake. What they cannot cover is an endpoint that is reachable, answers a handshake, and is no longer part of the deployment: a re-bound port now serving something else produces no failure, no redirect, and nothing announced, so no event-driven path asks the question.
topologyRefreshSeconds is the answer to that, and only that: every 30 minutes by default, the client
re-reads the topology whether or not anything appears to be wrong. Two things keep it cheap. The interval is
long, and each client picks its own phase within a 30-second jitter on every cycle, so a fleet started
together does not stay in step. Set it to 0 to turn it off.
It is deliberately a backstop rather than the mechanism. Topology is normally learned from something happening - a redirect, an announcement, a maintenance notification, a connection failing - and those react in seconds where this reacts in minutes.
ReconnectRetryPolicy
StackExchange.Redis automatically tries to reconnect in the background when the connection is lost for any reason. It keeps retrying until the connection has been restored. It would use ReconnectRetryPolicy to decide how long it should wait between the retries. ReconnectRetryPolicy can be exponential (default), linear or a custom retry policy.
Examples:
config.ReconnectRetryPolicy = new ExponentialRetry(5000); // defaults maxDeltaBackoff to 10000 ms
//retry# retry to re-connect after time in milliseconds
//1 a random value between 5000 and 5500
//2 a random value between 5000 and 6050
//3 a random value between 5000 and 6655
//4 a random value between 5000 and 8053
//5 a random value between 5000 and 10000, since maxDeltaBackoff was 10000 ms
//6 a random value between 5000 and 10000
config.ReconnectRetryPolicy = new LinearRetry(5000);
//retry# retry to re-connect after time in milliseconds
//1 5000
//2 5000
//3 5000
//4 5000
//5 5000
//6 5000
Defaults providers
Some settings have sensible values that depend on what you are connecting to rather than on what you want, so
the library keeps them in a provider and consults it for anything you have not set explicitly. Providers are
normally chosen automatically by looking at the endpoints - an *.redis.cache.windows.net host selects the
Azure provider, and so on - and you can select one explicitly instead:
myserver:6379,defaults=enterprise
The names are azure, amr (Azure Managed Redis), rediscloud and enterprise (a self-managed Redis
Enterprise deployment). The last one exists because it cannot be detected: a self-managed cluster has whatever
DNS its operator gave it, so there is nothing to recognize. It is also the right choice for a hosted deployment
reached behind private DNS, a CNAME or a proxy, where the endpoint no longer looks like what it is.
A provider chosen explicitly appears in ToString(); one that was merely inferred from the endpoints does not,
since writing it out would turn a guess into a decision. Custom providers (assigned in code, via
ConfigurationOptions.Defaults, or registered with DefaultOptionsProvider.AddProvider) work exactly as
before; they can additionally be named in a configuration string if they override Name.
Maintenance notifications
Server-native maintenance notifications - smart client handoffs, also called hitless upgrades - are configured with the keys below; ServerMaintenanceEvent is the guide to what they do.
Redis Enterprise and Redis Cloud can warn a connected client before a disruptive event - a shard migration, a failover, or an endpoint being replaced - so the client can act ahead of it rather than discover it by way of a broken connection. This requires RESP3, and the client asks for it per connection:
| Mode | Behaviour |
|---|---|
disabled |
Never ask. The default, because most servers have never heard of the request. |
auto |
Ask, and carry on if the server refuses or the connection ends up RESP2. Safe against a mixture of servers, and what most callers want. |
enabled |
Required: ask, and reject the connection unless notifications are live. Only point this at a deployment you know supports them. |
Note that enabled means required, not merely on - it is the cross-client name for that mode, and it will
refuse a connection that cannot deliver notifications, including any RESP2 connection. The amr, rediscloud
and enterprise providers select auto for you, so on those deployments you need not set anything.
While a disruption has been announced, command timeouts are relaxed - raised to maintRelaxedTimeout, never
lowered, so a caller with a more generous timeout keeps it. The window ends when the server says the disruption
finished, and then stays relaxed for maintPostEventRelaxed longer, because completion is exactly when every
other client that received the same notification comes back. maintRelaxedWindowMax bounds a window whose
closing notification never arrives. Only command timeouts are affected: keep-alive and connection-failure
detection are untouched, so a server that dies mid-maintenance is still noticed on the usual schedule.
These durations are in seconds, unlike every other timeout here, because those are the units the cross-client specification names for them.
Receiving a notification raises ConnectionMultiplexer.ServerMaintenanceEvent with a PushMaintenanceEvent,
and a timeout or connection fault that happened during an announced disruption carries a MaintenanceType
saying so.
Redis protocol
RESP3 is a newer protocol (available on v6 servers and above) which allows (among other changes) pub/sub messages to be communicated on the same connection - which can be
desirable in servers with a large number of clients; under RESP2, pub/sub requires a separate connection to the server. The library still uses a separate pub/sub connection under
RESP3 unless SharedSubscriptionConnection is enabled: the server applies its (much tighter) pubsub output-buffer limits to any connection with an active subscription, which
can get a shared connection - and every command in flight on it - closed. Before enabling it, check that the server’s pubsub limits (CONFIG GET client-output-buffer-limit)
are suitable for your largest replies, not just your pub/sub messages; see RESP3 for details.
The protocol handshake needs to happen very early in the connection, so the library only attempts RESP3 when it has reason to expect it to work.
The library determines whether to use RESP3 by:
- The
HELLOcommand has been disabled: RESP2 is used - A protocol other than
resp3or3is specified: RESP2 is used - A protocol of
resp3or3is specified: RESP3 is attempted (with fallback if it fails) - Otherwise: RESP3 is attempted if
defaultVersion(6.0 unless overridden) is v6 or above
Note that HELLO is issued either way: HELLO 2 when staying on RESP2. The reply tells us the server version, replication role, mode (standalone/sentinel/cluster) and connection
identifier, none of which we would otherwise know without INFO or CONFIG GET - both of which are in the @dangerous ACL category, and are commonly restricted. The role and mode
are only taken from the interactive connection (the subscription connection does no discovery), and a reported mode of cluster only leads to cluster routing once a slot map has
been obtained from CLUSTER SLOTS/CLUSTER NODES; a proxy that announces a cluster but refuses those commands is routed as a single standalone server. HELLO itself is
in the @connection category. If you need to prevent it (for example when talking to a proxy that does not understand it), disable it at the command-map level with $hello= in the
configuration string - or specify a defaultVersion below 6.0, since HELLO did not exist before then.