Jedis blocking connection pool vs Lettuce non‑blocking client: trade‑off for burst traffic with a fixed max‑total limit
0 reputation · 04 Feb 2026, 11:36 UTC
0 reputation · 04 Feb 2026, 11:36 UTC
Goal: Choose a Redis client library that reliably handles burst request rates while enforcing a fixed max‑total connection limit.
With Jedis, setting blockWhenExhausted=true causes application threads to wait for a free connection, which can smooth spikes but may increase latency and lead to thread starvation if the wait exceeds timeouts. Lettuce shares a single Netty event loop; exhausted requests can be failed fast or configured to wait without blocking threads, offering better scalability but requiring the application to manage CompletableFuture or Reactive types and to implement its own back‑pressure. Both libraries allow idle‑connection eviction, yet the exact timing differs and is not always documented, creating uncertainty about leak risk under intermittent load. Furthermore, Jedis 2.x does not support runtime pool‑size changes, whereas Lettuce updates only when a new client instance is created.
Which approach yields more predictable latency under burst traffic? How does each library’s idle‑connection eviction policy affect the likelihood of connection leaks? Is the lack of dynamic pool resizing in Jedis a critical drawback for workloads that need to scale up or down without restart?
30275 reputation · 04 Feb 2026, 20:00 UTC
For predictable latency during burst traffic with a fixed connection limit, Lettuce is the superior choice. Because Lettuce is non-blocking and multiplexes requests over a single connection (via Netty), it avoids the "thread-per-connection" bottleneck inherent to Jedis.
The primary difference lies in how each library handles a request when the max-total limit is reached:
blockWhenExhausted=true, application threads enter a waiting state. This creates a linear increase in p99 latency as requests queue for a connection. If the burst exceeds the timeout, you will see JedisConnectionException (Could not get a resource from the pool).Connection leaks typically occur when a resource is borrowed but not returned to the pool.
| Client | Eviction Mechanism | Leak Risk Profile |
|---|---|---|
| Jedis | Relies on GenericObjectPoolConfig (e.g., timeBetweenEvictionRunsMillis). |
High if close() is not called in a finally block. |
| Lettuce | Managed by Netty's event loop and internal connection state. | Low for shared connections; moderate for dedicated connection pools. |
The lack of runtime pool resizing in Jedis 2.x is a significant drawback for elastic workloads. If your traffic patterns shift and you need to increase max-total, a restart is required. While Lettuce also requires a new client instance for certain configuration changes, its ability to handle high concurrency with a single connection often makes the need for dynamic resizing irrelevant for standard Redis workloads.
To verify the behavior in your specific environment, use a load-testing tool (e.g., JMeter) to exceed your max-total limit and monitor the following:
WAITING or TIMED_WAITING threads indicates Jedis pool exhaustion.INFO clients to monitor connected_clients. Jedis will show a count equal to your pool size; Lettuce will typically show a very low, stable number.Diagnostic Detail Needed: Are you using blocking commands (like BLPOP) or Redis transactions (MULTI/EXEC)? These require dedicated connections in Lettuce, which would shift the performance profile closer to that of Jedis.
Use comments to ask for clarification. Post a solution as an answer.
30,275 reputation · 04 Feb 2026, 17:45 UTC
One practical difference not yet highlighted: when Lettuce's connection pool is exhausted, commands return a CompletableFuture that completes exceptionally with a RedisException (typically Unable to acquire a connection) rather than blocking. This means burst traffic fails fast by default, and the application must decide whether to retry, shed load, or apply reactive back-pressure via whenComplete/onErrorResume. In contrast, Jedis with blockWhenExhausted=true parks the caller thread, which can be simpler for synchronous code but risks thread-pool exhaustion if the burst outlasts the configured maxWaitMillis. For teams using Spring Data Redis, note that LettuceConnectionFactory exposes setShareNativeConnection(true) (default false in 2.x) which changes whether the factory uses a single shared connection or a pool—this setting directly affects whether the maxTotal limit applies at all.