Frequently Asked Questions¶
I’m seeing RedisCommandTimeoutException
¶
Symptoms:
RedisCommandTimeoutException
with a stack trace like:
io.lettuce.core.RedisCommandTimeoutException: Command timed out after 1 minute(s)
at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51)
at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:114)
at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69)
at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
at com.sun.proxy.$Proxy94.set(Unknown Source)
Diagnosis:
-
Check the debug log (log level
DEBUG
orTRACE
for the loggerio.lettuce.core.protocol
) -
Take a Thread dump to investigate Thread activity
-
Investigate Lettuce usage, specifically for
setAutoFlushCommands(false)
calls -
Do you use a custom
RedisCodec
?
Cause:
Command timeouts are caused by the fact that a command was not completed within the configured timeout. Timeouts may be caused for various reasons:
-
Redis server has crashed/network partition happened and your Redis service didn’t recover within the configured timeout
-
Command was not finished in time. This can happen if your Redis server is overloaded or if the connection is blocked by a command (e.g.
BLPOP 0
, long-running Lua script). See also gives. -
Configured timeout does not match Redis’s performance.
-
If you block the
EventLoop
(e.g. calling blocking methods in aRedisFuture
callback or in a Reactive pipeline). That can easily happen when calling Redis commands in a Pub/Sub listener or aRedisConnectionStateListener
. -
If you manually control the flushing behavior of commands (
setAutoFlushCommands(true/false)
), you should have a good reason to do so. In multi-threaded environments, race conditions may easily happen, and commands are not flushed. Updating a missing or misplacedflushCommands()
call might solve the problem. -
If you’re using a custom
RedisCodec
that can fail during encoding, this will desynchronize the protocol state.
Action:
Check for the causes above. If the configured timeout does not match
your Redis latency characteristics, consider increasing the timeout.
Never block the EventLoop
from your code. Make sure that your
RedisCodec
doesn’t fail on encode.
blpop(Duration.ZERO, …)
gives RedisCommandTimeoutException
¶
Symptoms:
Calling blpop
, brpop
or any other blocking command followed by
RedisCommandTimeoutException
with a stack trace like:
io.lettuce.core.RedisCommandTimeoutException: Command timed out after 1 minute(s)
at io.lettuce.core.ExceptionFactory.createTimeoutException(ExceptionFactory.java:51)
at io.lettuce.core.LettuceFutures.awaitOrCancel(LettuceFutures.java:114)
at io.lettuce.core.FutureSyncInvocationHandler.handleInvocation(FutureSyncInvocationHandler.java:69)
at io.lettuce.core.internal.AbstractInvocationHandler.invoke(AbstractInvocationHandler.java:80)
at com.sun.proxy.$Proxy94.set(Unknown Source)
Cause:
The configured command timeout applies without considering command-specific timeouts.
Action:
There are various options:
-
Configure a higher default timeout.
-
Consider a timeout that meets the default timeout when calling blocking commands.
-
Configure
TimeoutOptions
with a customTimeoutSource
TimeoutOptions timeoutOptions = TimeoutOptions.builder().timeoutSource(new TimeoutSource() {
@Override
public long getTimeout(RedisCommand<?, ?, ?> command) {
if (command.getType() == CommandType.BLPOP) {
return TimeUnit.MILLISECONDS.toNanos(CommandArgsAccessor.getFirstInteger(command.getArgs()));
}
// -1 indicates fallback to the default timeout
return -1;
}
}).build();
Note that commands that timed out may block the connection until either the timeout exceeds or Redis sends a response.
Excessive Memory Usage or RedisException
while disconnected¶
Symptoms:
RedisException
with one of the following messages:
io.lettuce.core.RedisException: Request queue size exceeded: n. Commands are not accepted until the queue size drops.
io.lettuce.core.RedisException: Internal stack size exceeded: n. Commands are not accepted until the stack size drops.
Or excessive memory allocation.
Diagnosis:
-
Check Redis connectivity
-
Inspect memory usage
Cause:
Lettuce auto-reconnects by default to Redis to minimize service disruption. Commands issued while there’s no Redis connection are buffered and replayed once the server connection is reestablished. By default, the queue is unbounded which can lead to memory exhaustion.
Action:
You can configure disconnected behavior and the request queue size
through ClientOptions
for your workload profile. See Client
Options for further reference.
Performance Degradation using the Reactive API with a single connection¶
Symptoms:
Performance degradation when using the Reactive API with a single connection (i.e. non-pooled connection arrangement).
Diagnosis:
- Inspect Thread affinity of reactive signals
Cause:
Netty’s threading model assigns a single Thread to each connection which
makes I/O for a single Channel
effectively single-threaded. With a
significant computation load and without further thread switching, the
system leverages a single thread and therefore leads to contention.
Action:
You can configure signal multiplexing for the reactive API through
ClientOptions
by enabling publishOnScheduler(true)
. See Client
Options for further reference. Alternatively, you can
configure Scheduler
on each result stream through
publishOn(Scheduler)
. Note that the asynchronous API features the same
behavior and you might want to use then…Async(…)
, run…Async(…)
,
apply…Async(…)
, or handleAsync(…)
methods along with an Executor
object.