From 388512056a43eee7d9bd3b65bb54c498f7e101b7 Mon Sep 17 00:00:00 2001
From: Andy Stark
Date: Fri, 30 Oct 2026 12:02:49 +0000
Subject: [PATCH 1/2] DOC-7226 Move four more Jedis use cases from JedisPool to
RedisClient
Prefetch cache, time-series dashboard, pub/sub and streaming (java-jedis) take a
RedisClient instead of a JedisPool. As in the first batch, getResource() blocks
are gone, pipelines and MULTI transactions are in try-with-resources, demo
servers build the client with RedisClient.create or RedisClient.builder() plus a
ConnectionPoolConfig where they size the pool, and each page's Maven/Gradle
version, prose and javac/java commands move to Jedis 8.0.2 with the six runtime
jars.
The three directories that used APIs RedisClient lacks needed more than a rename:
- time-series-dashboard reached for jedis.getConnection() and wrote raw
commands to it. It now goes through jedis.executeCommand(new
CommandArguments(cmd).addObjects(args)). UnifiedJedis.sendCommand(ProtocolCommand,
String...) is the obvious swap, but Jedis 8 deprecates it, which would trade one
deprecated call for another.
- pub-sub used Jedis.pubsubChannels/pubsubNumSub/pubsubNumPat, which exist only on
the legacy Jedis class. They are now executeCommand(new CommandObject<>(
new CommandArguments(Protocol.Command.PUBSUB).add(Protocol.Keyword.X), BuilderFactory.Y)).
Each Subscription no longer owns a Jedis; its worker thread calls
jedis.subscribe(listener, ...) on the shared client, which borrows and holds a
pooled connection until unsubscribe. The "close the Jedis to force subscribe()
to return" fallback is removed because closing the shared client would be wrong.
- streaming's XAUTOCLAIM raw-reply path moves from sendCommand to executeCommand,
and StreamEntryID.UNRECEIVED_ENTRY (deprecated) becomes
XREADGROUP_UNDELIVERED_ENTRY. The page's claim that the typed xautoclaim drops
the deleted-ids slot still holds in 8.0.2 (checked with javap), so the hand
parsing stays.
Verification, compiled with -Xlint:deprecation (0 warnings) against Jedis 8.0.2 and
run on redis:8.10. Prefetch: bulk load, sync-worker transaction, add, delete, 200
concurrent mixed calls. Time series: snapshot parsing matches the original on
Jedis 5.2.0 (types and min<=avg<=max), 200 concurrent snapshots. Pub/sub: exact and
pattern delivery counts exact (203 = 3 + 200), PUBSUB introspection correct,
150 subscribe/unsubscribe cycles against a pool of 64 (a leak would hang by ~60).
Streaming: the same scenario on the original (5.2.0) and migrated builds gives
identical output for produce, consume, crash, pending=3, XAUTOCLAIM claimed=3,
add/remove worker, trim and replay; only the final concurrent-load tail differs,
which is timing. Every page's javac/java commands were extracted and run
literally in a clean directory with only the six jars; all four compile and serve.
Constraint: RedisClient has no pubsubChannels/NumSub/NumPat, getConnection(), or non-deprecated sendCommand; use executeCommand(CommandArguments) or executeCommand(CommandObject)
Constraint: a subscribe()/psubscribe() on a RedisClient holds one pooled connection for the subscription's life, so maxTotal must cover subscribers plus command traffic
Learned: JedisPubSub needs redis.clients.authentication.core.Token (redis-authx-core) at class-load time, so a three-jar classpath serves every other demo and fails only pub/sub
Learned: an unrelated JVM left deadlocked from an earlier probe held 8 pooled connections and inflated every later "clients: N" count; check pgrep before reading connection counts
Learned: the streaming "crash" button drops the next N deliveries, so a scenario must crash before producing or nothing is ever pending
Ticket: DOC-7226
Co-Authored-By: Claude Sonnet 5.5
---
.../prefetch-cache/java-jedis/DemoServer.java | 19 +-
.../java-jedis/PrefetchCache.java | 97 ++++------
.../prefetch-cache/java-jedis/_index.md | 33 ++--
.../pub-sub/java-jedis/DemoServer.java | 19 +-
.../pub-sub/java-jedis/RedisPubSubHub.java | 89 ++++-----
.../use-cases/pub-sub/java-jedis/_index.md | 54 +++---
.../streaming/java-jedis/DemoServer.java | 26 +--
.../streaming/java-jedis/EventStream.java | 180 ++++++++----------
.../use-cases/streaming/java-jedis/_index.md | 112 ++++++-----
.../java-jedis/DemoServer.java | 12 +-
.../java-jedis/RedisTimeSeriesStore.java | 72 +++----
.../java-jedis/_index.md | 8 +-
12 files changed, 325 insertions(+), 396 deletions(-)
diff --git a/content/develop/use-cases/prefetch-cache/java-jedis/DemoServer.java b/content/develop/use-cases/prefetch-cache/java-jedis/DemoServer.java
index 186f7cf455..e06ba288c6 100644
--- a/content/develop/use-cases/prefetch-cache/java-jedis/DemoServer.java
+++ b/content/develop/use-cases/prefetch-cache/java-jedis/DemoServer.java
@@ -1,8 +1,7 @@
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
-import redis.clients.jedis.JedisPool;
-import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.RedisClient;
import java.io.IOException;
import java.io.InputStream;
@@ -28,9 +27,9 @@
* without ever falling back to the primary on the read path.
*
* {@code
- * javac -cp jedis-5.1.2.jar:slf4j-api-2.0.13.jar \
+ * javac -cp jedis-8.0.2.jar:commons-pool2-2.13.1.jar:slf4j-api-1.7.36.jar:gson-2.14.0.jar:json-20260719.jar:redis-authx-core-0.1.1-beta2.jar \
* PrefetchCache.java MockPrimaryStore.java SyncWorker.java DemoServer.java
- * java -cp .:jedis-5.1.2.jar:slf4j-api-2.0.13.jar \
+ * java -cp .:jedis-8.0.2.jar:commons-pool2-2.13.1.jar:slf4j-api-1.7.36.jar:gson-2.14.0.jar:json-20260719.jar:redis-authx-core-0.1.1-beta2.jar \
* DemoServer --port 8785 --redis-host localhost --redis-port 6379
* }
*/
@@ -39,7 +38,7 @@ public class DemoServer {
private static PrefetchCache cache;
private static MockPrimaryStore primary;
private static SyncWorker sync;
- private static JedisPool jedisPool;
+ private static RedisClient jedis;
public static void main(String[] args) {
String host = "127.0.0.1";
@@ -79,14 +78,14 @@ public static void main(String[] args) {
}
try {
- jedisPool = new JedisPool(new JedisPoolConfig(), redisHost, redisPort);
- jedisPool.getResource().close();
+ jedis = RedisClient.create(redisHost, redisPort);
+ jedis.ping();
} catch (Exception e) {
System.err.printf("Failed to connect to Redis at %s:%d: %s%n", redisHost, redisPort, e.getMessage());
System.exit(1);
}
- cache = new PrefetchCache(jedisPool, cachePrefix, ttlSeconds);
+ cache = new PrefetchCache(jedis, cachePrefix, ttlSeconds);
primary = new MockPrimaryStore(primaryLatencyMs);
sync = new SyncWorker(primary, cache);
@@ -121,12 +120,12 @@ public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
sync.stop();
server.stop(0);
- jedisPool.close();
+ jedis.close();
}));
} catch (IOException e) {
System.err.println("Failed to start server: " + e.getMessage());
sync.stop();
- jedisPool.close();
+ jedis.close();
System.exit(1);
}
}
diff --git a/content/develop/use-cases/prefetch-cache/java-jedis/PrefetchCache.java b/content/develop/use-cases/prefetch-cache/java-jedis/PrefetchCache.java
index e323d5c5ee..c207685036 100644
--- a/content/develop/use-cases/prefetch-cache/java-jedis/PrefetchCache.java
+++ b/content/develop/use-cases/prefetch-cache/java-jedis/PrefetchCache.java
@@ -6,10 +6,9 @@
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.AbstractTransaction;
import redis.clients.jedis.Pipeline;
-import redis.clients.jedis.Transaction;
+import redis.clients.jedis.RedisClient;
import redis.clients.jedis.params.ScanParams;
import redis.clients.jedis.resps.ScanResult;
@@ -33,7 +32,7 @@ public class PrefetchCache {
public static final String DEFAULT_PREFIX = "cache:category:";
public static final int DEFAULT_TTL_SECONDS = 3600;
- private final JedisPool pool;
+ private final RedisClient jedis;
private final String prefix;
private final int ttlSeconds;
@@ -45,18 +44,18 @@ public class PrefetchCache {
private double syncLagMsTotal;
private long syncLagSamples;
- public PrefetchCache(JedisPool pool) {
- this(pool, DEFAULT_PREFIX, DEFAULT_TTL_SECONDS);
+ public PrefetchCache(RedisClient jedis) {
+ this(jedis, DEFAULT_PREFIX, DEFAULT_TTL_SECONDS);
}
- public PrefetchCache(JedisPool pool, String prefix, int ttlSeconds) {
- if (pool == null) {
- throw new IllegalArgumentException("pool is required");
+ public PrefetchCache(RedisClient jedis, String prefix, int ttlSeconds) {
+ if (jedis == null) {
+ throw new IllegalArgumentException("jedis is required");
}
if (ttlSeconds < 1) {
throw new IllegalArgumentException("ttlSeconds must be at least 1 second");
}
- this.pool = pool;
+ this.jedis = jedis;
this.prefix = (prefix == null || prefix.isEmpty()) ? DEFAULT_PREFIX : prefix;
this.ttlSeconds = ttlSeconds;
}
@@ -96,8 +95,7 @@ public Result(Map record, boolean hit, double redisLatencyMs) {
*/
public int bulkLoad(Iterable