Repository navigation
DOC-7226 Move rate-limiter, leaderboard, session-store and cache-aside Jedis use cases to RedisClient - #4388
Merged
Merged
Conversation
Rate limiter, leaderboard, session store and cache-aside (java-jedis) now take a RedisClient instead of a JedisPool and call commands on it directly. The getResource() try-with-resources blocks are gone, the demo servers build the client with RedisClient.create(host, port) and check it with ping(), and the pages' snippets, prose and Maven/Gradle versions move to Jedis 8.0.2. Each page's javac/java commands now list the six jars Jedis 8.0.2 needs at runtime. With a pool, jedis.multi() and jedis.pipelined() ran on the connection the try block already held. On a RedisClient each one borrows its own pooled connection and only close() returns it, so every transaction and pipeline is now in try-with-resources. cache-aside updateField needed a real redesign, not a rename. WATCH is per-connection, and UnifiedJedis has no watch(). It now opens jedis.transaction(false), calls tx.watch(), then reads existence through the same transaction (tx.exists(key).get()) before tx.multi(). The first port read existence with jedis.exists() while the transaction held a connection; that passed at maxTotal=64 and deadlocked at the default maxTotal=8 (jstack: 40 threads parked in GenericObjectPool.borrowObject at the exists line). Verification, each demo compiled with -Xlint:deprecation (0 warnings) against Jedis 8.0.2 and run against redis:8.10 on a scratch port: rate limiter 10 allowed then denied and 200 concurrent requests; leaderboard 300 concurrent increments land exactly (400), trim and pipeline hydration work; session store full login/refresh/increment/ttl/logout cycle, 300 concurrent increments exact; cache-aside stampede x50 does one primary read. A driver calling RedisCache.updateField under contention (12 updaters, 6 invalidators, 2 HLEN samplers) gave 0 orphan single-field hashes at maxTotal 64, 8 and 2, while a no-WATCH control produced thousands, so the sampler can see the failure. The pool is back to 0 active afterward in every run. The page commands themselves were extracted from each _index.md and run literally in a clean directory holding only the six jars; all four compile and serve. Constraint: never call the client while a transaction or pipeline is open; each holds a pooled connection and a second borrow can deadlock a small pool Constraint: every multi()/pipelined() result on a RedisClient must be closed (try-with-resources) or its connection leaks Rejected: Lua script for updateField (atomic, no retry loop) because the Go, redis-py and Lettuce pages all teach WATCH/MULTI/EXEC for this path and this page lists those commands Learned: RedisClient.transaction(false) accepts reads before multi(); they run immediately on that connection and Response.get() returns at once Learned: the cache-aside demo's /update endpoint writes the primary and invalidates, so it never calls RedisCache.updateField; test that method with a driver, not HTTP Learned: Jedis 8.0.2 runtime jars are jedis, commons-pool2, slf4j-api, gson, json and redis-authx-core (its pom's compile scope). A smaller set appears to work until a class that needs the rest loads (JedisPubSub needs redis-authx-core), so test the classpath with the feature that loads it Learned: before this change the pages' jar-only or partial classpaths could not run either (commons-pool2 and slf4j-api were missing), on Jedis 5.x too Ticket: DOC-7226 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Contributor
Contributor
🧠 Redis MemoryFound 5 related items from repository history (5 new this commit):
Memory updated at 129008f |
Contributor
This was referenced Oct 30, 2026
andy-stark-redis
added a commit
that referenced
this pull request
Oct 30, 2026
…streaming Jedis use cases to RedisClient The prefetch-cache, time-series-dashboard, pub-sub and streaming java-jedis use cases now take a RedisClient instead of a JedisPool. As in #4388, the getResource() blocks are gone, pipelines and MULTI transactions are in try-with-resources, the 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. Three of the directories used APIs that RedisClient lacks, so they needed more than a rename. Time-series-dashboard reached for jedis.getConnection() and wrote raw commands to it; it now calls jedis.executeCommand(new CommandArguments(cmd).addObjects(args)), because the obvious swap, UnifiedJedis.sendCommand(ProtocolCommand, String...), is deprecated in Jedis 8. Pub-sub used Jedis.pubsubChannels, pubsubNumSub and pubsubNumPat, which exist only on the legacy Jedis class, so they are now executeCommand(new CommandObject<>(new CommandArguments(Protocol.Command.PUBSUB).add(Protocol.Keyword.X), BuilderFactory.Y)). Streaming's raw XAUTOCLAIM reply moves from sendCommand to executeCommand, and the deprecated StreamEntryID.UNRECEIVED_ENTRY becomes XREADGROUP_UNDELIVERED_ENTRY; the page's claim that the typed xautoclaim drops the deleted-ids slot still holds in 8.0.2, so the hand parsing stays. Each pub-sub Subscription no longer owns a Jedis. It borrows its own Connection from the shared client with jedis.getPool().getResource() and runs listener.proceed(connection, ...) (or proceedWithPatterns) on a worker thread, which is what UnifiedJedis.subscribe does internally; the thread returns the connection in a finally block. It does not call jedis.subscribe(), because that borrows its connection internally and leaves close() no way to break a stuck subscription. close() sends UNSUBSCRIBE and, if the thread has not exited a second later, disconnects the connection, as the pre-migration code did with its own Jedis. The sharded pub/sub sentence now names RedisClusterClient as the class that exposes spublish and ssubscribe. Each demo compiled with -Xlint:deprecation with 0 warnings against Jedis 8.0.2 and ran against Redis 8.10. Prefetch: bulk load, the sync-worker transaction, add, delete and 200 concurrent mixed calls. Time series: snapshot parsing matches the original on Jedis 5.2.0 (types, and min <= avg <= max in every bucket), plus 200 concurrent snapshots. Pub/sub: exact and pattern delivery counts were exact (203 = 3 + 200), PUBSUB introspection was correct, and 150 subscribe/unsubscribe cycles ran against a pool of 64. With a TCP proxy frozen to make a half-open link, close() left 1 connection checked out before the force-disconnect and 0 after. Streaming: the original (5.2.0) and migrated builds gave identical output for produce, consume, crash, pending, XAUTOCLAIM, add and remove worker, trim and replay; only the final concurrent-load tail differs, which is timing. Each page's javac and java commands were extracted and run in a clean directory holding only the six jars. Constraint: RedisClient has no pubsubChannels, pubsubNumSub, pubsubNumPat or getConnection(), and its sendCommand is deprecated; use executeCommand with CommandArguments or CommandObject Constraint: each pub-sub Subscription holds one pooled connection for its whole life, so maxTotal must cover subscribers plus command traffic Constraint: UnifiedJedis.subscribe() borrows its connection internally; code that must break a stuck subscription has to borrow the Connection itself and call JedisPubSub.proceed(connection, ...) 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: the streaming demo's crash button drops the next N deliveries, so a test must crash before producing or nothing is ever pending Learned: to reproduce a stuck unsubscribe, freeze a TCP proxy between the client and Redis; killing either end makes subscribe() throw and hides the bug Ticket: DOC-7226 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Moves four
java-jedisuse cases fromJedisPooltoRedisClient(Jedis 8.0.2): code, snippets, versions and run commands.multi()/pipelined()is now in try-with-resources, because each borrows its own pooled connection on aRedisClient.updateFieldneeded a redesign:UnifiedJedishas nowatch(), so it usestransaction(false)and reads existence through the same transaction. Reading through the client deadlocked the default pool of 8 under load.Verified: 0 deprecation warnings; demos run against Redis 8.10; 300 concurrent increments exact;
updateFieldcontention test shows no orphan hashes at pool sizes 64, 8 and 2 (a no-WATCH control does); page commands run in a clean directory. Not run: Hugo build.Part of DOC-7226.
🤖 Generated with Claude Code
Note
Medium Risk
Connection lifecycle and WATCH/MULTI usage changed across demos; cache-aside field updates were redesigned to avoid pool deadlocks under contention.
Overview
Updates the cache-aside, leaderboard, rate-limiter, and session-store Java Jedis examples to Jedis 8.0.2 and
RedisClientinstead ofJedisPool/ per-requestJedisborrows. Demo servers connect withRedisClient.create()andping(), pass a shared client into helpers, andclose()on shutdown.Redis access is simplified to calls on the pooled client;
multi(),pipelined(), and WATCH flows useAbstractTransaction/Pipelinein try-with-resources so borrowed connections are released. Docs and compile/run snippets now list the full Jedis 8 runtime classpath (not a singlejedis.jar).The notable behavior change is cache-aside
updateField: it usesjedis.transaction(false)withwatchandexistson the same transaction connection, avoiding pool deadlocks when a held transaction would otherwise trigger a second connection from the client.Medium risk: connection-pooling semantics changed; cache-aside optimistic updates were reworked for
RedisClientAPIs.Reviewed by Cursor Bugbot for commit 129008f. Bugbot is set up for automated code reviews on this repo. Configure here.