Clients & Client-Side Caching
|
This content was generated with the assistance of AI and should be verified against the official Redis documentation before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Examples on this page target Redis Open Source 8.10 (8.10.2) with the Jedis 8 and Lettuce 7.7 client
libraries, connecting to the same Bookshop keyspace used throughout this section (bookshop:book:<isbn> and
friends).
Choosing a client
Jedis 8 is a synchronous, blocking client built around RedisClient — the API that, from Jedis 7.2.0
onward, replaces the older JedisPool/JedisPooled entry points. These are still present, and UnifiedJedis
remains RedisClient’s base class. Jedis 8 also provides `RedisClusterClient and RedisSentinelClient for
cluster and Sentinel topologies. It suits code that already runs synchronously end to end: a servlet-style
request handler, a batch job, or any call site that would otherwise have to bridge back to blocking I/O anyway. Lettuce 7.7 is built on Netty and is
asynchronous and reactive first, with a synchronous facade (.sync()) layered on top of the same connection — it suits reactive stacks, high connection-concurrency services, and is Spring Boot’s default Redis client
(spring-boot-starter-data-redis pulls in Lettuce, not Jedis) when Spring Data Redis is on the classpath. See
Jedis guide and
Lettuce guide.
Redis also ships officially supported clients for other languages — named here for completeness, not elaborated on this Java-focused page: redis-py (Python), node-redis (Node.js), go-redis (Go), and NRedisStack (.NET).
Pools, timeouts, and reliability
Jedis: pooling and timeouts
From Jedis 7.2.0, RedisClient pools connections internally — pass it a ConnectionPoolConfig (a wrapper
around Apache Commons Pool2’s GenericObjectPoolConfig) to size the pool, and a DefaultJedisClientConfig
to set the socket and connection timeouts:
package com.example.bookshop;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.RedisClient;
import redis.clients.jedis.RedisProtocol;
import redis.clients.jedis.csc.CacheConfig;
import redis.clients.jedis.ConnectionPoolConfig;
import java.time.Duration;
public class BookshopJedisConfig {
public RedisClient bookshopRedisClient() {
ConnectionPoolConfig poolConfig = new ConnectionPoolConfig();
poolConfig.setMaxTotal(16);
poolConfig.setMaxIdle(16);
poolConfig.setMinIdle(2);
poolConfig.setBlockWhenExhausted(true);
poolConfig.setMaxWait(Duration.ofSeconds(1));
poolConfig.setTestWhileIdle(true);
poolConfig.setTimeBetweenEvictionRuns(Duration.ofSeconds(30));
DefaultJedisClientConfig clientConfig = DefaultJedisClientConfig.builder()
.protocol(RedisProtocol.RESP3)
.socketTimeoutMillis(2000)
.connectionTimeoutMillis(2000)
.user("bookshop-app")
.password("changeit")
.build();
return RedisClient.builder()
.hostAndPort(new HostAndPort("redis.internal.bookshop.example", 6379))
.clientConfig(clientConfig)
.poolConfig(poolConfig)
.build();
}
}
This derives from Connect to the server (Jedis) and Jedis production usage.
RESP3 (set with .protocol(RedisProtocol.RESP3) above) is required for both push-based invalidation messages
(client-side caching, below) and for richer typed replies in general, and is the protocol Jedis and Lettuce
both default new connections toward on modern Redis servers. Retries after a dropped connection are the
caller’s responsibility with Jedis — a lost connection surfaces as JedisConnectionException, and production
code wraps the call in a bounded retry loop with backoff, requesting a fresh pooled connection on each
attempt. See
Retrying
a command after a connection failure.
For TLS, build a JedisClientConfig with an SslOptions truststore/keystore alongside the ACL user/
password; for reading from replicas or coordinating failover, point RedisClient at a Sentinel- or
cluster-aware variant (RedisSentinelClient/RedisClusterClient) rather than a single HostAndPort, so the
client discovers the current primary (and, for Sentinel, can be pointed at replicas for read traffic) instead
of hardcoding one node. See
Connect
with TLS (Jedis) and ACL.
Lettuce: ClientOptions, timeouts, and reconnection
Lettuce multiplexes many logical connections over one physical connection by default, so "pooling" in the
Jedis sense is less often needed — a single StatefulRedisConnection handles concurrent callers safely.
ClientOptions (plus SocketOptions for TCP-level settings) configures timeouts, keepalive, and reconnect
behavior:
package com.example.bookshop;
import io.lettuce.core.ClientOptions;
import io.lettuce.core.RedisClient;
import io.lettuce.core.RedisURI;
import io.lettuce.core.SocketOptions;
import io.lettuce.core.TimeoutOptions;
import java.time.Duration;
public class BookshopLettuceConfig {
public RedisClient bookshopRedisClient() {
RedisURI redisUri = RedisURI.Builder
.redis("redis.internal.bookshop.example", 6379)
.withAuthentication("bookshop-app", "changeit".toCharArray())
.withTimeout(Duration.ofSeconds(2))
.build();
RedisClient client = RedisClient.create(redisUri);
SocketOptions socketOptions = SocketOptions.builder()
.connectTimeout(Duration.ofSeconds(2))
.keepAlive(true)
.build();
client.setOptions(ClientOptions.builder()
.socketOptions(socketOptions)
.timeoutOptions(TimeoutOptions.enabled(Duration.ofSeconds(2)))
.autoReconnect(true)
.disconnectedBehavior(ClientOptions.DisconnectedBehavior.DEFAULT)
.build());
return client;
}
}
This derives from Lettuce production usage
and the Lettuce project. autoReconnect(true) (the default) gives
at-least-once command execution — Lettuce reconnects automatically and replays commands that were queued
when the connection dropped; autoReconnect(false) switches to at-most-once, discarding queued commands
instead. RedisURI also carries TLS (.withSsl(true)) and ACL credentials
(.withAuthentication(user, password)) the same way it carries the timeout above. For reading from replicas,
StatefulRedisConnection.sync()/.async() accept a ReadFrom setting (for example ReadFrom.REPLICA_PREFERRED)
so read-only commands can be routed off the primary; for failover, RedisClient reacts to Sentinel or Cluster
topology changes on its own once connected through RedisSentinelClient semantics via RedisURI.Builder.sentinel(…)
or a RedisClusterClient, redirecting subsequent commands to the newly elected primary. See
Lettuce production usage.
Client-side caching
Client-side caching lets the client keep a local copy of recently read keys and skip the round trip to Redis
on a cache hit. The server enables it per connection with CLIENT TRACKING ON: in the default (non-broadcast)
mode, Redis tracks only the keys that connection actually read and sends an invalidation message — over the
same RESP3 connection, or redirected to another connection under RESP2/Pub-Sub — the moment any client writes
to one of them; the receiving client then evicts that key from its local cache rather than serving stale data
on the next read. Broadcast mode (CLIENT TRACKING ON BCAST [PREFIX prefix …]) instead sends invalidation
messages for every key matching a subscribed prefix, regardless of whether that connection ever read it — useful when many clients share the same small set of hot prefixes and per-key tracking overhead isn’t worth
it. See Client-side caching introduction
and CLIENT TRACKING.
Jedis (v5.2.0+, so all of Jedis 8) supports the default tracking mode out of the box through CacheConfig;
broadcast and opt-in/opt-out modes are server-side features Jedis does not currently implement on top of.
Lettuce supports client-side caching through its io.lettuce.core.support.caching package, which wires up
tracking and invalidation handling around a pluggable local cache (for example a ConcurrentHashMap).
Jedis client-side caching, scoped to the Bookshop bookshop:cache:book:<isbn> read path:
package com.example.bookshop;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.RedisClient;
import redis.clients.jedis.RedisProtocol;
import redis.clients.jedis.csc.Cache;
import redis.clients.jedis.csc.CacheConfig;
public class BookshopCachingBookRepository {
private final RedisClient client;
public BookshopCachingBookRepository() {
DefaultJedisClientConfig config = DefaultJedisClientConfig.builder()
.protocol(RedisProtocol.RESP3)
.build();
CacheConfig cacheConfig = CacheConfig.builder()
.maxSize(5_000)
.build();
this.client = RedisClient.builder()
.hostAndPort(new HostAndPort("redis.internal.bookshop.example", 6379))
.clientConfig(config)
.cacheConfig(cacheConfig)
.build();
}
public String findTitle(String isbn) {
// First call per isbn hits Redis and populates the cache; later calls
// for the same isbn are served locally until the key is invalidated.
return client.hget("bookshop:book:" + isbn, "title");
}
public void evict(String isbn) {
Cache cache = client.getCache();
cache.deleteByRedisKey("bookshop:book:" + isbn);
}
}
This derives from Connect using client-side caching (Jedis).
key now tracked by server App->>Redis: HGET bookshop:book:9780000000001 title Note over App,Redis: Served from local cache,
no round trip Writer->>Redis: HSET bookshop:book:9780000000001 title "..." Redis-->>App: Invalidation push message Note over App: Local cache entry evicted App->>Redis: HGET bookshop:book:9780000000001 title Redis-->>App: Fresh value, re-cached
Object mapping and migration tooling
Redis OM (Object Mapping) layers repository- and annotation-based mapping on top of a client — for the
Bookshop domain, Java entities that map onto the bookshop:book:* hashes/JSON documents used elsewhere in
this section, backed by the same search index covered in
Query Engine: Indexing & Data Modeling. Its full treatment, together with Spring
Data Redis repositories and Lua-backed custom queries, lives in
Spring Boot: Spring Data Redis, Redis OM & Custom Queries.
For one-off or recurring bulk migration into or out of Redis — for example seeding the Bookshop keyspace from a relational export, or replicating between two Redis deployments — RIOT (Redis Input/Output Tool) is the dedicated command-line tool: it reads from and writes to Redis alongside files, databases, and other Redis instances, without hand-rolling a client-side import script. See RIOT.
References
-
Redis clients — the official client index across languages.
-
Connect to the server (Jedis) — pooling, TLS, and client-side caching connection setup.
-
Jedis production usage — pooling, retries, timeouts, and exception handling checklist.
-
Lettuce production usage — timeouts, reconnection, and reliability checklist.
-
Client-side caching introduction — tracking, invalidation, and per-client support matrix.
-
CLIENT TRACKING — default vs. broadcast mode.
-
ACL — users, passwords, and permissions used by both clients' credentials.
-
RIOT.