Databases 9 min read

Why My Spring API Stalled: Debugging Redis Connection Pool Blocking

A Spring‑based service repeatedly hung because Redis connections were never returned to the pool, leading to thread starvation; the article walks through the investigation using top, jstack, Arthas, and code analysis, then shows the proper way to use RedisCallback and release connections to prevent the deadlock.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why My Spring API Stalled: Debugging Redis Connection Pool Blocking

In an internal sandbox environment the API became unresponsive for a week, with all requests timing out. Restarting the application temporarily restored service, but the issue recurred more frequently.

Initial checks showed no errors in the database or Redis, and no special logs, so the team suspected the sandbox machine itself. Running top showed normal system metrics, so they inspected the JVM stack to find the most resource‑consuming threads.

Using jstack they observed many threads stuck in a lock state. Further debugging revealed that the Redis connection pool was the bottleneck. The method fetchJedisConnector in JedisConnectionFactory obtains a connection from the pool:

protected Jedis fetchJedisConnector() {
    try {
        if (usePool && pool != null) {
            return pool.getResource();
        }
        Jedis jedis = new Jedis(getShardInfo());
        // force initialization (see Jedis issue #82)
        jedis.connect();
        return jedis;
    } catch (Exception ex) {
        throw new RedisConnectionFailureException("Cannot get Jedis connection", ex);
    }
}

The pool’s getResource method eventually calls borrowObject, which contains a loop that waits indefinitely when borrowMaxWaitMillis < 0:

while (p == null) {
    if (blockWhenExhausted) {
        p = this.idleObjects.pollFirst();
        if (p == null) {
            create = true;
            p = this.create();
        }
        if (p == null) {
            if (borrowMaxWaitMillis < 0L) {
                p = (PooledObject) this.idleObjects.takeFirst();
            } else {
                waitTime = System.currentTimeMillis();
                p = (PooledObject) this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
                waitTime = System.currentTimeMillis() - waitTime;
            }
        }
        if (p == null) {
            throw new NoSuchElementException("Timeout waiting for idle object");
        }
    }
}

Because the configuration omitted maxWaitMillis, the pool waited forever, causing all HTTP‑nio threads to block. The team used Alibaba’s Arthas tool to inspect threads and confirmed many http-nio-8083-exec threads were in WAITING state.

Further analysis showed that code like stringRedisTemplate.getConnectionFactory().getConnection() fetched a Redis connection but never released it, leaving the connection in a non‑idle state:

Cursor c = stringRedisTemplate.getConnectionFactory().getConnection().scan(options);
while (c.hasNext()) {
    // ...
}

The proper approach is to execute Redis commands within a RedisCallback and explicitly release the connection:

stringRedisTemplate.execute(new RedisCallback() {
    @Override
    public Cursor doInRedis(RedisConnection connection) throws DataAccessException {
        return connection.scan(options);
    }
});

RedisConnectionUtils.releaseConnection(conn, factory);

After updating the pool configuration (e.g., config.setMaxWaitMillis(2000)) and refactoring the code to use the callback pattern, the API stopped hanging. The article also advises against using the KEYS command in production and recommends proper pool sizing.

top output
top output
jstack thread dump
jstack thread dump
Arthas thread view
Arthas thread view
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

debuggingjavaperformanceredisspringConnection PoolJedis
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.