The Jedis 'Command timed out' error occurs when a Redis command executed through the Jedis Java client fails to complete within the configured timeout period. This typically indicates slow Redis operations, network latency, or insufficient timeout settings.
The "JedisDataException: Command timed out" error in Jedis means that a Redis command did not return a response within the configured socket timeout period. When Jedis executes a Redis command, it waits for the server to respond. If the response doesn't arrive within the timeout limit (default is 2 seconds), Jedis throws this exception to prevent your application from hanging indefinitely. This error is part of the Jedis exception hierarchy, extending from JedisDataException which handles data-related issues during Redis operations. Unlike JedisConnectionException which indicates connection failures, JedisDataException suggests the connection was established but the command execution took too long. The timeout is controlled by the `socketTimeout` parameter in JedisPooled, JedisPool, or JedisClientConfig. This setting determines the maximum time allowed for reading or writing data while executing a command. When commands take longer than expected due to server load, network issues, or expensive operations, Jedis decides the command has timed out and raises this exception.
If legitimate operations need more time, increase the timeout when creating your Jedis client:
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
// Create client with custom timeout
HostAndPort hostAndPort = new HostAndPort("localhost", 6379);
DefaultJedisClientConfig config = DefaultJedisClientConfig.builder()
.socketTimeoutMillis(5000) // 5 seconds for command execution
.connectionTimeoutMillis(5000) // 5 seconds for connection establishment
.build();
JedisPooled jedis = new JedisPooled(hostAndPort, config);For connection pools:
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(20);
poolConfig.setMaxIdle(10);
JedisPool pool = new JedisPool(
poolConfig,
"localhost",
6379,
5000, // socketTimeout in milliseconds
5000 // connectionTimeout in milliseconds
);Note: Only increase timeout for legitimate long-running operations. Don't mask underlying performance issues.
Properly configure your JedisPool to prevent connection exhaustion:
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
JedisPoolConfig poolConfig = new JedisPoolConfig();
// Maximum number of connections in pool (default: 8)
poolConfig.setMaxTotal(50);
// Maximum idle connections (default: 8)
poolConfig.setMaxIdle(20);
// Minimum idle connections (default: 0)
poolConfig.setMinIdle(5);
// Maximum wait time when pool is exhausted (milliseconds, default: -1 infinite)
poolConfig.setMaxWaitMillis(3000);
// Test connection before borrowing
poolConfig.setTestOnBorrow(true);
// Test idle connections
poolConfig.setTestWhileIdle(true);
JedisPool pool = new JedisPool(poolConfig, "localhost", 6379, 5000);
// Always use try-with-resources to return connections
try (Jedis jedis = pool.getResource()) {
String value = jedis.get("key");
}Monitor pool statistics:
System.out.println("Active: " + pool.getNumActive());
System.out.println("Idle: " + pool.getNumIdle());
System.out.println("Waiters: " + pool.getNumWaiters());Connect to Redis and check for performance issues:
# Connect to Redis CLI
redis-cli -h localhost -p 6379
# Check slow queries (commands taking longer than threshold)
SLOWLOG GET 10
# View current slowlog configuration
CONFIG GET slowlog-log-slower-than
CONFIG GET slowlog-max-len
# Check server statistics
INFO stats
INFO commandstats
# Check memory usage
INFO memory
# Monitor commands in real-time (use sparingly in production)
MONITORIn your Java application:
try (Jedis jedis = pool.getResource()) {
// Get slow log entries
List<Slowlog> slowlogs = jedis.slowlogGet(10);
for (Slowlog log : slowlogs) {
System.out.println("Command: " + log.getArgs());
System.out.println("Duration: " + log.getExecutionTime() + " microseconds");
}
// Get server info
String info = jedis.info("stats");
System.out.println(info);
}Look for:
- Commands consistently taking >1 second
- High memory usage (>80% of maxmemory)
- Many evictions or blocked clients
Avoid commands that block Redis and cause timeouts:
// BAD: KEYS blocks Redis and scans entire keyspace
Set<String> keys = jedis.keys("user:*"); // Avoid in production!
// GOOD: SCAN doesn't block Redis
ScanParams scanParams = new ScanParams()
.match("user:*")
.count(100);
String cursor = ScanParams.SCAN_POINTER_START;
List<String> allKeys = new ArrayList<>();
do {
ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
allKeys.addAll(scanResult.getResult());
cursor = scanResult.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));
System.out.println("Found keys: " + allKeys.size());For large sets and sorted sets:
// BAD: SMEMBERS returns entire set (can timeout on large sets)
Set<String> members = jedis.smembers("large_set");
// GOOD: SSCAN iterates without blocking
ScanParams params = new ScanParams().count(100);
String cursor = ScanParams.SCAN_POINTER_START;
List<String> allMembers = new ArrayList<>();
do {
ScanResult<String> result = jedis.sscan("large_set", cursor, params);
allMembers.addAll(result.getResult());
cursor = result.getCursor();
} while (!cursor.equals(ScanParams.SCAN_POINTER_START));Use pipelining for multiple commands:
Pipeline pipeline = jedis.pipelined();
for (int i = 0; i < 1000; i++) {
pipeline.set("key" + i, "value" + i);
}
pipeline.sync(); // Execute all at onceHandle timeout exceptions gracefully in your application:
import redis.clients.jedis.exceptions.JedisDataException;
public class RedisHelper {
private final JedisPool pool;
private final int maxRetries = 3;
private final int retryDelayMs = 100;
public RedisHelper(JedisPool pool) {
this.pool = pool;
}
public String getWithRetry(String key) throws Exception {
int attempt = 0;
Exception lastException = null;
while (attempt < maxRetries) {
try (Jedis jedis = pool.getResource()) {
return jedis.get(key);
} catch (JedisDataException e) {
if (e.getMessage().contains("Command timed out")) {
lastException = e;
attempt++;
System.err.println("Timeout on attempt " + attempt + "/" + maxRetries);
if (attempt < maxRetries) {
Thread.sleep(retryDelayMs * attempt);
}
} else {
throw e; // Re-throw other JedisDataException types
}
}
}
throw new Exception("Failed after " + maxRetries + " retries", lastException);
}
// Circuit breaker pattern for production
public Optional<String> getSafe(String key) {
try (Jedis jedis = pool.getResource()) {
String value = jedis.get(key);
return Optional.ofNullable(value);
} catch (JedisDataException e) {
System.err.println("Redis timeout: " + e.getMessage());
return Optional.empty(); // Fail gracefully
}
}
}Verify network performance between your application and Redis:
# Test basic connectivity
ping redis-host
# Test Redis-specific latency
redis-cli -h redis-host --latency
# Continuous latency monitoring
redis-cli -h redis-host --latency-history
# Latency distribution
redis-cli -h redis-host --latency-dist
# Network intrinsic latency (measures network + OS)
redis-cli -h redis-host --intrinsic-latency 60In your Java application, measure round-trip time:
try (Jedis jedis = pool.getResource()) {
long start = System.currentTimeMillis();
jedis.ping();
long latency = System.currentTimeMillis() - start;
System.out.println("Redis latency: " + latency + "ms");
if (latency > 50) {
System.err.println("WARNING: High latency detected");
}
}If latency is consistently high:
- Move Redis closer to your application (same data center/availability zone)
- Check for network congestion or firewall rules
- Consider Redis Cluster for geo-distributed deployments
Disk I/O from persistence operations can cause command delays:
# Check current persistence configuration
redis-cli CONFIG GET save
redis-cli CONFIG GET appendonly
redis-cli CONFIG GET appendfsync
# View when last save occurred
redis-cli LASTSAVE
redis-cli INFO persistenceFor cache-only use cases (no durability needed):
# Disable RDB snapshots
redis-cli CONFIG SET save ""
# Disable AOF
redis-cli CONFIG SET appendonly noFor better performance with persistence enabled, edit redis.conf:
# RDB: Save less frequently
save 900 1
save 300 10
save 60 10000
# AOF: Use everysec instead of always
appendfsync everysec
# Don't fsync during background save/rewrite
no-appendfsync-on-rewrite yes
# Automatic AOF rewrite thresholds
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mbMonitor background operations:
try (Jedis jedis = pool.getResource()) {
String info = jedis.info("persistence");
System.out.println(info);
// Check if background save is in progress
if (info.contains("rdb_bgsave_in_progress:1")) {
System.out.println("Warning: Background save in progress");
}
}### Understanding Jedis Timeout Configuration
Jedis provides two distinct timeout settings:
1. `connectionTimeout`: Maximum time to establish a TCP connection to Redis (default: 2000ms)
2. `socketTimeout`: Maximum time to wait for a command response after the connection is established (default: 2000ms)
Both are critical:
- Too short: Legitimate operations fail unnecessarily
- Too long: Application hangs during actual issues
The default 2-second timeout is conservative and may be too short for:
- Complex Lua scripts
- Large data structure operations (SORT, ZRANGE with large ranges)
- Production environments with network variability
### Jedis vs. Other Redis Clients
Timeout behavior comparison:
| Client | Default Timeout | Configuration Method |
|--------|----------------|---------------------|
| Jedis | 2000ms | socketTimeoutMillis() |
| Lettuce | No timeout | .timeout(Duration) |
| Redisson | 3000ms | timeout in config |
Jedis is more conservative by default, which helps catch problems early but may cause false positives.
### Connection Pool vs. Direct Connections
Using JedisPool (recommended for production):
- Reuses connections efficiently
- Handles connection lifecycle
- Provides pool-level monitoring
- Thread-safe by design
Using direct Jedis (not recommended):
// Creates new connection each time (expensive!)
Jedis jedis = new Jedis("localhost", 6379, 5000);
jedis.get("key");
jedis.close();Always use connection pooling in production to avoid:
- Connection overhead on every operation
- Socket exhaustion under load
- Memory leaks from unclosed connections
### Debugging Timeout Issues
Enable Jedis debug logging to see exactly what's happening:
<!-- In logback.xml -->
<logger name="redis.clients.jedis" level="DEBUG" />Or programmatically:
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.SimpleLog;
System.setProperty("org.apache.commons.logging.Log",
"org.apache.commons.logging.impl.SimpleLog");
System.setProperty("org.apache.commons.logging.simplelog.defaultlog", "debug");### Redis Cluster Considerations
Timeouts are more complex in Redis Cluster:
- Commands may be redirected (MOVED, ASK responses)
- Multiple round-trips increase latency
- Cluster topology changes can cause delays
Configure higher timeouts for cluster mode:
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.ConnectionPoolConfig;
Set<HostAndPort> nodes = new HashSet<>();
nodes.add(new HostAndPort("node1", 6379));
nodes.add(new HostAndPort("node2", 6379));
JedisCluster cluster = new JedisCluster(
nodes,
10000, // connectionTimeout
10000, // soTimeout (higher for cluster)
5, // maxAttempts for redirects
poolConfig
);### Production Monitoring Best Practices
Monitor these metrics to prevent timeout issues:
1. Pool metrics:
- Active connections (should be < maxTotal)
- Idle connections (should be > minIdle)
- Wait time (should be near 0)
2. Command latency:
- Track P50, P95, P99 latencies
- Alert on P99 > 1 second
3. Timeout rate:
- Calculate: (timeouts / total commands) × 100
- Alert if > 1%
Example monitoring:
public class RedisMetrics {
private final JedisPool pool;
private final AtomicLong timeouts = new AtomicLong(0);
private final AtomicLong successes = new AtomicLong(0);
public String executeWithMetrics(String key) {
long start = System.nanoTime();
try (Jedis jedis = pool.getResource()) {
String result = jedis.get(key);
successes.incrementAndGet();
return result;
} catch (JedisDataException e) {
if (e.getMessage().contains("Command timed out")) {
timeouts.incrementAndGet();
}
throw e;
} finally {
long duration = (System.nanoTime() - start) / 1_000_000;
// Log or emit to monitoring system
if (duration > 100) {
System.err.println("Slow Redis command: " + duration + "ms");
}
}
}
public double getTimeoutRate() {
long total = timeouts.get() + successes.get();
return total > 0 ? (timeouts.get() * 100.0 / total) : 0;
}
}### Spring Boot Integration
Configure Jedis in Spring Boot applications:
@Configuration
public class RedisConfig {
@Bean
public JedisConnectionFactory jedisConnectionFactory() {
RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
config.setHostName("localhost");
config.setPort(6379);
JedisClientConfiguration.JedisClientConfigurationBuilder builder =
JedisClientConfiguration.builder();
builder.connectTimeout(Duration.ofSeconds(5));
builder.readTimeout(Duration.ofSeconds(5));
JedisConnectionFactory factory = new JedisConnectionFactory(
config,
builder.build()
);
return factory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate() {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(jedisConnectionFactory());
return template;
}
}ERR Unbalanced XREAD list of streams
How to fix "ERR Unbalanced XREAD list" in Redis
ERR syntax error
How to fix "ERR syntax error" in Redis
ConnectionError: Error while reading from socket
ConnectionError: Error while reading from socket in redis-py
ERR unknown command
How to fix ERR unknown command in Redis
Command timed out
How to fix 'Command timed out' in ioredis