Why Your Spring Redis Calls Hang: Uncovering the Hidden Connection‑Pool Pitfall
A week‑long API freeze in a sandbox environment was traced to a misconfigured Redis connection pool in a Spring Java application, where threads waited indefinitely for a Jedis resource, and the fix involved setting proper pool timeouts, using RedisCallback for scans, and correctly releasing connections.
Hello, I'm Peng Lei.
In an internal sandbox environment the API became unresponsive for a week, with all requests hanging. Initial attempts to restart the application only provided temporary relief, prompting a deeper investigation.
Initial Investigation
Running top showed the machine was still alive. The next step was to examine JVM thread stacks with jstack. The most resource‑intensive threads were identified, but no obvious errors appeared in the logs or database.
JVM Stack Inspection
Using jstack 12798 | grep 12799 revealed several threads in a LOCK state without any business‑related code.
Redis Connection Issue
Further debugging showed that the threads were blocked while acquiring a Redis connection from the pool. The relevant code in JedisConnectionFactory is:
protected Jedis fetchJedisConnector() {
try {
if (usePool && pool != null) {
return pool.getResource();
}
Jedis jedis = new Jedis(getShardInfo());
jedis.connect();
return jedis;
} catch (Exception ex) {
throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);
}
}When pool.getResource() is called, the thread enters a waiting state:
public T getResource() {
try {
return internalPool.borrowObject();
} catch (Exception e) {
throw new JedisConnectionException("Could not get a resource from the pool", e);
}
}The pool implementation contains logic that, if borrowMaxWaitMillis < 0, will loop indefinitely, causing the observed hang.
Thread‑Level Diagnosis with Arthas
Installing Arthas and running thread revealed many http-nio threads stuck in WAITING state, confirming that all incoming API requests were blocked on the Redis pool.
Further analysis with thread -b (blocked‑by) showed no direct blocker, indicating the issue stemmed from the pool configuration itself.
Fixing the Pool Configuration
Adding a proper maxWaitMillis value to the JedisPoolConfig (e.g., 2000 ms) prevented the infinite wait:
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxWaitMillis(2000);
jedisConnectionFactory.setPoolConfig(config);
jedisConnectionFactory.afterPropertiesSet();After redeploying, the API resumed normal operation, but occasional 500 errors persisted.
Root Cause of the 500 Errors
The stack trace showed RedisConnectionFailureException: Cannot get Jedis connection originating from Spring’s StringRedisTemplate usage:
Cursor c = stringRedisTemplate.getConnectionFactory().getConnection().scan(options);
while (c.hasNext()) { ... }Calling getConnection() directly rents a Redis connection without returning it, leaving the pool exhausted.
Recommended Approach
Instead of accessing the raw connection, use RedisCallback to execute commands and let Spring manage the connection lifecycle:
stringRedisTemplate.execute(new RedisCallback<Cursor>() {
@Override
public Cursor doInRedis(RedisConnection connection) throws DataAccessException {
return connection.scan(options);
}
});When a raw connection is required, release it explicitly:
RedisConnectionUtils.releaseConnection(conn, factory);Avoid using the KEYS command in production and configure the Redis pool with sensible limits to prevent silent deadlocks.
Conclusion
The issue was caused by an improperly configured Redis connection pool and the misuse of StringRedisTemplate.getConnectionFactory().getConnection(). Proper pool timeout settings and using Spring‑managed callbacks resolve the hanging API problem.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Tech Stack
Java backend, microservices, distributed systems, containerized programming, and more.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
