entries = jedis.zrangeWithScores(
+ key,
+ ZRangeParams.zrangeParams(start, end).rev()
+ );
+ return hydrateEntries(entries, start + 1);
}
```
-Jedis operations are synchronous and thread-safe when you use `JedisPool`, which manages a pool of connections internally.
+Jedis operations are synchronous and thread-safe when you use `RedisClient`, which manages a pool of connections internally.
## Metadata design
@@ -217,10 +214,12 @@ A local demo server ([source](https://github.com/redis/docs/blob/main/content/de
```bash
# Compile
-javac -cp jedis-5.2.0.jar RedisLeaderboard.java DemoServer.java
+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 \
+ RedisLeaderboard.java DemoServer.java
# Run the demo server
-java -cp .:jedis-5.2.0.jar DemoServer
+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
```
The demo provides an interactive web interface where you can:
diff --git a/content/develop/use-cases/rate-limiter/java-jedis/DemoServer.java b/content/develop/use-cases/rate-limiter/java-jedis/DemoServer.java
index ae22edcac0..8bdbc2cf99 100644
--- a/content/develop/use-cases/rate-limiter/java-jedis/DemoServer.java
+++ b/content/develop/use-cases/rate-limiter/java-jedis/DemoServer.java
@@ -13,8 +13,7 @@
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
-import redis.clients.jedis.JedisPool;
-import redis.clients.jedis.JedisPoolConfig;
+import redis.clients.jedis.RedisClient;
import java.io.IOException;
import java.io.InputStream;
@@ -28,7 +27,7 @@
public class DemoServer {
private static TokenBucket limiter;
- private static JedisPool jedisPool;
+ private static RedisClient jedis;
private static int capacity = 10;
private static int refillRate = 1;
private static double refillInterval = 1.0;
@@ -55,8 +54,8 @@ public static void main(String[] args) {
// Initialize Redis connection
try {
- jedisPool = new JedisPool(new JedisPoolConfig(), redisHost, redisPort);
- jedisPool.getResource().close(); // Test connection
+ jedis = RedisClient.create(redisHost, redisPort);
+ jedis.ping(); // Test connection
System.out.printf("✓ Connected to Redis at %s:%d%n", redisHost, redisPort);
} catch (Exception e) {
System.err.printf("✗ Failed to connect to Redis: %s%n", e.getMessage());
@@ -65,7 +64,7 @@ public static void main(String[] args) {
}
// Initialize the rate limiter
- limiter = new TokenBucket(capacity, refillRate, refillInterval, jedisPool);
+ limiter = new TokenBucket(capacity, refillRate, refillInterval, jedis);
// Start the server
try {
@@ -82,7 +81,7 @@ public static void main(String[] args) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
System.out.println("\n✓ Server stopped");
- jedisPool.close();
+ jedis.close();
server.stop(0);
}));
} catch (IOException e) {
@@ -136,7 +135,7 @@ public void handle(HttpExchange exchange) throws IOException {
refillInterval = Double.parseDouble(params.get("refill_interval"));
}
- limiter = new TokenBucket(capacity, refillRate, refillInterval, jedisPool);
+ limiter = new TokenBucket(capacity, refillRate, refillInterval, jedis);
json = String.format(
"{\"success\":true,\"config\":{\"capacity\":%d,\"refill_rate\":%d,\"refill_interval\":%.1f}}",
diff --git a/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java b/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java
index 37ec32f833..2dd3db76a5 100644
--- a/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java
+++ b/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java
@@ -4,8 +4,7 @@
import java.util.Collections;
import java.util.List;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
import redis.clients.jedis.exceptions.JedisNoScriptException;
/**
@@ -20,8 +19,8 @@
*
* Example usage:
* {@code
- * JedisPool pool = new JedisPool("localhost", 6379);
- * TokenBucket limiter = new TokenBucket(10, 1.0, 1.0, pool);
+ * RedisClient jedis = RedisClient.create("localhost", 6379);
+ * TokenBucket limiter = new TokenBucket(10, 1.0, 1.0, jedis);
* RateLimitResult result = limiter.allow("user:123");
* if (result.allowed()) {
* System.out.println("Request allowed. " + result.remaining() + " tokens remaining.");
@@ -85,7 +84,7 @@ public record RateLimitResult(boolean allowed, double remaining) {}
private final int capacity;
private final double refillRate;
private final double refillInterval;
- private final JedisPool jedisPool;
+ private final RedisClient jedis;
private String scriptSha;
private boolean scriptLoaded;
@@ -95,13 +94,13 @@ public record RateLimitResult(boolean allowed, double remaining) {}
* @param capacity maximum number of tokens in the bucket (default: 10)
* @param refillRate number of tokens added per refill interval (default: 1.0)
* @param refillInterval time in seconds between refills (default: 1.0)
- * @param jedisPool Jedis connection pool for Redis operations
+ * @param jedis pooled Jedis client for Redis operations
*/
- public TokenBucket(int capacity, double refillRate, double refillInterval, JedisPool jedisPool) {
+ public TokenBucket(int capacity, double refillRate, double refillInterval, RedisClient jedis) {
this.capacity = capacity;
this.refillRate = refillRate;
this.refillInterval = refillInterval;
- this.jedisPool = jedisPool;
+ this.jedis = jedis;
this.scriptSha = sha1Hex(TOKEN_BUCKET_SCRIPT);
this.scriptLoaded = false;
}
@@ -109,10 +108,10 @@ public TokenBucket(int capacity, double refillRate, double refillInterval, Jedis
/**
* Creates a token bucket with default parameters (capacity=10, refillRate=1.0, refillInterval=1.0).
*
- * @param jedisPool Jedis connection pool for Redis operations
+ * @param jedis pooled Jedis client for Redis operations
*/
- public TokenBucket(JedisPool jedisPool) {
- this(10, 1.0, 1.0, jedisPool);
+ public TokenBucket(RedisClient jedis) {
+ this(10, 1.0, 1.0, jedis);
}
/**
@@ -138,15 +137,13 @@ public RateLimitResult allow(String key) {
);
List result;
- try (Jedis jedis = jedisPool.getResource()) {
- try {
- // Try EVALSHA first (faster if script is cached)
- result = (List) jedis.evalsha(scriptSha, keys, args);
- } catch (JedisNoScriptException e) {
- // Script not in cache, fall back to EVAL and reload
- result = (List) jedis.eval(TOKEN_BUCKET_SCRIPT, keys, args);
- scriptLoaded = false;
- }
+ try {
+ // Try EVALSHA first (faster if script is cached)
+ result = (List) jedis.evalsha(scriptSha, keys, args);
+ } catch (JedisNoScriptException e) {
+ // Script not in cache, fall back to EVAL and reload
+ result = (List) jedis.eval(TOKEN_BUCKET_SCRIPT, keys, args);
+ scriptLoaded = false;
}
boolean allowed = result.get(0) == 1L;
@@ -157,10 +154,8 @@ public RateLimitResult allow(String key) {
/** Ensures the Lua script is loaded into Redis. */
private void ensureScriptLoaded() {
if (!scriptLoaded) {
- try (Jedis jedis = jedisPool.getResource()) {
- scriptSha = jedis.scriptLoad(TOKEN_BUCKET_SCRIPT);
- scriptLoaded = true;
- }
+ scriptSha = jedis.scriptLoad(TOKEN_BUCKET_SCRIPT);
+ scriptLoaded = true;
}
}
diff --git a/content/develop/use-cases/rate-limiter/java-jedis/_index.md b/content/develop/use-cases/rate-limiter/java-jedis/_index.md
index fac7fbb8ba..54de2ab9c3 100644
--- a/content/develop/use-cases/rate-limiter/java-jedis/_index.md
+++ b/content/develop/use-cases/rate-limiter/java-jedis/_index.md
@@ -26,7 +26,7 @@ Rate limiting is a critical technique for controlling the rate at which operatio
* Ensuring fair resource allocation across multiple clients
* Throttling background jobs or batch operations
-The **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Java implementation using the [`Jedis`](/content/develop/clients/jedis/_index.md) client library, taking advantage of Java's `try-with-resources` for connection management, `JedisPool` for connection pooling, and checked exceptions for error handling.
+The **token bucket algorithm** is a popular rate limiting approach that allows bursts of traffic while maintaining an average rate limit over time. This guide covers the Java implementation using the [`Jedis`](/content/develop/clients/jedis/_index.md) client library, taking advantage of the `RedisClient` class for connection pooling and checked exceptions for error handling.
## How it works
@@ -126,14 +126,14 @@ Add the Jedis dependency to your project:
redis.clients
jedis
- 5.2.0
+ 8.0.2
```
* If you use **Gradle**:
```groovy
- implementation 'redis.clients:jedis:5.2.0'
+ implementation 'redis.clients:jedis:8.0.2'
```
## Using the Java class
@@ -142,15 +142,15 @@ The `TokenBucket` class provides a thread-safe interface for rate limiting
([source](https://github.com/redis/docs/blob/main/content/develop/use-cases/rate-limiter/java-jedis/TokenBucket.java)):
```java
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
public class Main {
public static void main(String[] args) {
- // Create a Redis connection pool
- JedisPool jedisPool = new JedisPool("localhost", 6379);
+ // Create a pooled Redis client
+ RedisClient jedis = RedisClient.create("localhost", 6379);
// Create a rate limiter: 10 requests per second
- TokenBucket limiter = new TokenBucket(10, 1, 1.0, jedisPool);
+ TokenBucket limiter = new TokenBucket(10, 1, 1.0, jedis);
// Check if a request should be allowed
TokenBucket.RateLimitResult result = limiter.allow("user:123");
@@ -166,7 +166,7 @@ public class Main {
}
```
-Jedis operations are synchronous and thread-safe when using `JedisPool`, which manages a pool of connections internally. The `allow()` method returns a `RateLimitResult` record containing both the decision and the remaining token count.
+Jedis operations are synchronous and thread-safe when using `RedisClient`, which manages a pool of connections internally. The `allow()` method returns a `RateLimitResult` record containing both the decision and the remaining token count.
### Configuration parameters
@@ -205,7 +205,7 @@ The `TokenBucket` class is thread-safe. You can share a single instance across y
```java
// Create a shared limiter instance
-TokenBucket limiter = new TokenBucket(10, 1, 1.0, jedisPool);
+TokenBucket limiter = new TokenBucket(10, 1, 1.0, jedis);
// Safe to call from multiple threads
ExecutorService executor = Executors.newFixedThreadPool(10);
@@ -240,10 +240,12 @@ A demonstration HTTP server is included to show the rate limiter in action
```bash
# Compile
-javac -cp jedis-5.2.0.jar TokenBucket.java DemoServer.java
+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 \
+ TokenBucket.java DemoServer.java
# Run the demo server
-java -cp .:jedis-5.2.0.jar DemoServer
+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
```
The demo provides an interactive web interface where you can:
@@ -262,7 +264,7 @@ It's common to include rate limit information in HTTP response headers:
```java
int capacity = 10;
double refillInterval = 1.0;
-TokenBucket limiter = new TokenBucket(capacity, 1, refillInterval, jedisPool);
+TokenBucket limiter = new TokenBucket(capacity, 1, refillInterval, jedis);
TokenBucket.RateLimitResult result = limiter.allow("user:" + userId);
@@ -290,8 +292,8 @@ public class RateLimitFilter implements Filter {
@Override
public void init(FilterConfig config) {
- JedisPool jedisPool = new JedisPool("localhost", 6379);
- limiter = new TokenBucket(10, 1, 1.0, jedisPool);
+ RedisClient jedis = RedisClient.create("localhost", 6379);
+ limiter = new TokenBucket(10, 1, 1.0, jedis);
}
@Override
@@ -361,8 +363,7 @@ Simplest algorithm — one key per window, one `EVAL` round trip.
```java
import java.util.List;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
public class FixedWindowLimiter {
@@ -387,16 +388,14 @@ public class FixedWindowLimiter {
public record Decision(boolean allowed, long retryAfterMs) {}
@SuppressWarnings("unchecked")
- public static Decision allow(JedisPool pool, String key, int limit, int windowSeconds) {
- try (Jedis jedis = pool.getResource()) {
- List result = (List) jedis.eval(
- SCRIPT,
- List.of(key),
- List.of(String.valueOf(limit), String.valueOf(windowSeconds)));
-
- boolean allowed = result.get(0) == 1L;
- return new Decision(allowed, allowed ? 0L : result.get(1));
- }
+ public static Decision allow(RedisClient jedis, String key, int limit, int windowSeconds) {
+ List result = (List) jedis.eval(
+ SCRIPT,
+ List.of(key),
+ List.of(String.valueOf(limit), String.valueOf(windowSeconds)));
+
+ boolean allowed = result.get(0) == 1L;
+ return new Decision(allowed, allowed ? 0L : result.get(1));
}
}
```
@@ -413,8 +412,7 @@ Provides a true rolling window with no boundary bursts.
import java.util.List;
import java.util.UUID;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
public class SlidingWindowLogLimiter {
@@ -450,17 +448,15 @@ public class SlidingWindowLogLimiter {
public record Decision(boolean allowed, long retryAfterMs) {}
@SuppressWarnings("unchecked")
- public static Decision allow(JedisPool pool, String key, int limit, int windowSeconds) {
- try (Jedis jedis = pool.getResource()) {
- List result = (List) jedis.eval(
- SCRIPT,
- List.of(key),
- List.of(String.valueOf(limit),
- String.valueOf(windowSeconds),
- UUID.randomUUID().toString()));
-
- return new Decision(result.get(0) == 1L, result.get(1));
- }
+ public static Decision allow(RedisClient jedis, String key, int limit, int windowSeconds) {
+ List result = (List) jedis.eval(
+ SCRIPT,
+ List.of(key),
+ List.of(String.valueOf(limit),
+ String.valueOf(windowSeconds),
+ UUID.randomUUID().toString()));
+
+ return new Decision(result.get(0) == 1L, result.get(1));
}
}
```
@@ -478,8 +474,7 @@ to the same slot in Redis Cluster.
```java
import java.util.List;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
public class SlidingWindowCounterLimiter {
@@ -515,15 +510,13 @@ public class SlidingWindowCounterLimiter {
""";
@SuppressWarnings("unchecked")
- public static boolean allow(JedisPool pool, String key, int limit, int windowSeconds) {
- try (Jedis jedis = pool.getResource()) {
- List result = (List) jedis.eval(
- SCRIPT,
- List.of("{" + key + "}"),
- List.of(String.valueOf(limit), String.valueOf(windowSeconds)));
-
- return result.get(0) == 1L;
- }
+ public static boolean allow(RedisClient jedis, String key, int limit, int windowSeconds) {
+ List result = (List) jedis.eval(
+ SCRIPT,
+ List.of("{" + key + "}"),
+ List.of(String.valueOf(limit), String.valueOf(windowSeconds)));
+
+ return result.get(0) == 1L;
}
}
```
@@ -540,8 +533,7 @@ policing variant — requests are allowed or denied instantly with no delay.
```java
import java.util.List;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
public class LeakyBucketLimiter {
@@ -586,15 +578,13 @@ public class LeakyBucketLimiter {
public record Decision(boolean allowed, long retryAfterMs) {}
@SuppressWarnings("unchecked")
- public static Decision allow(JedisPool pool, String key, int capacity, double leakRate) {
- try (Jedis jedis = pool.getResource()) {
- List result = (List) jedis.eval(
- SCRIPT,
- List.of(key),
- List.of(String.valueOf(capacity), String.valueOf(leakRate)));
-
- return new Decision(result.get(0) == 1L, result.get(1));
- }
+ public static Decision allow(RedisClient jedis, String key, int capacity, double leakRate) {
+ List result = (List) jedis.eval(
+ SCRIPT,
+ List.of(key),
+ List.of(String.valueOf(capacity), String.valueOf(leakRate)));
+
+ return new Decision(result.get(0) == 1L, result.get(1));
}
}
```
diff --git a/content/develop/use-cases/session-store/java-jedis/DemoServer.java b/content/develop/use-cases/session-store/java-jedis/DemoServer.java
index aae2f1cda7..77623eba6f 100644
--- a/content/develop/use-cases/session-store/java-jedis/DemoServer.java
+++ b/content/develop/use-cases/session-store/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;
@@ -20,14 +19,14 @@
* Usage:
*
* {@code
- * javac -cp jedis-5.2.0.jar RedisSessionStore.java DemoServer.java
- * java -cp .:jedis-5.2.0.jar DemoServer --port 8080 --redis-host localhost --redis-port 6379
+ * 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 RedisSessionStore.java DemoServer.java
+ * 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 8080 --redis-host localhost --redis-port 6379
* }
*/
public class DemoServer {
private static RedisSessionStore sessionStore;
- private static JedisPool jedisPool;
+ private static RedisClient jedis;
public static void main(String[] args) {
int port = 8080;
@@ -51,9 +50,9 @@ public static void main(String[] args) {
}
try {
- jedisPool = new JedisPool(new JedisPoolConfig(), redisHost, redisPort);
- jedisPool.getResource().close();
- sessionStore = new RedisSessionStore(jedisPool);
+ jedis = RedisClient.create(redisHost, redisPort);
+ jedis.ping();
+ sessionStore = new RedisSessionStore(jedis);
} catch (Exception e) {
System.err.printf("Failed to connect to Redis at %s:%d: %s%n", redisHost, redisPort, e.getMessage());
System.exit(1);
@@ -73,7 +72,7 @@ public static void main(String[] args) {
System.out.printf("Session store demo listening on http://localhost:%d%n", port);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
- jedisPool.close();
+ jedis.close();
server.stop(0);
}));
} catch (IOException e) {
diff --git a/content/develop/use-cases/session-store/java-jedis/RedisSessionStore.java b/content/develop/use-cases/session-store/java-jedis/RedisSessionStore.java
index 9c4ff168ed..006722d1a3 100644
--- a/content/develop/use-cases/session-store/java-jedis/RedisSessionStore.java
+++ b/content/develop/use-cases/session-store/java-jedis/RedisSessionStore.java
@@ -7,8 +7,7 @@
import java.util.Map;
import java.util.Set;
-import redis.clients.jedis.Jedis;
-import redis.clients.jedis.JedisPool;
+import redis.clients.jedis.RedisClient;
import redis.clients.jedis.Pipeline;
import redis.clients.jedis.Response;
@@ -30,22 +29,22 @@ public class RedisSessionStore {
private static final DateTimeFormatter TIMESTAMP_FORMATTER =
DateTimeFormatter.ofPattern("uuuu-MM-dd'T'HH:mm:ssXXX");
- private final JedisPool jedisPool;
+ private final RedisClient jedis;
private final String prefix;
private final int ttl;
- public RedisSessionStore(JedisPool jedisPool, String prefix, int ttl) {
- if (jedisPool == null) {
- throw new IllegalArgumentException("jedisPool is required");
+ public RedisSessionStore(RedisClient jedis, String prefix, int ttl) {
+ if (jedis == null) {
+ throw new IllegalArgumentException("jedis is required");
}
- this.jedisPool = jedisPool;
+ this.jedis = jedis;
this.prefix = prefix == null || prefix.isEmpty() ? "session:" : prefix;
this.ttl = normalizeTtl(ttl);
}
- public RedisSessionStore(JedisPool jedisPool) {
- this(jedisPool, "session:", 1800);
+ public RedisSessionStore(RedisClient jedis) {
+ this(jedis, "session:", 1800);
}
private int normalizeTtl(Integer value) {
@@ -105,8 +104,7 @@ public String createSession(Map data, Integer ttlOverride) {
payload.put("last_accessed_at", now);
payload.put("session_ttl", String.valueOf(sessionTtl));
- try (Jedis jedis = jedisPool.getResource()) {
- Pipeline pipeline = jedis.pipelined();
+ try (Pipeline pipeline = jedis.pipelined()) {
pipeline.hset(key, payload);
pipeline.expire(key, sessionTtl);
pipeline.sync();
@@ -116,31 +114,28 @@ public String createSession(Map data, Integer ttlOverride) {
}
public Integer getConfiguredTtl(String sessionId) {
- try (Jedis jedis = jedisPool.getResource()) {
- String storedTtl = jedis.hget(sessionKey(sessionId), "session_ttl");
- if (storedTtl == null) {
- return null;
- }
-
- return normalizeTtl(Integer.parseInt(storedTtl));
+ String storedTtl = jedis.hget(sessionKey(sessionId), "session_ttl");
+ if (storedTtl == null) {
+ return null;
}
+
+ return normalizeTtl(Integer.parseInt(storedTtl));
}
public Map getSession(String sessionId, boolean refreshTtl) {
String key = sessionKey(sessionId);
- try (Jedis jedis = jedisPool.getResource()) {
- Map session = jedis.hgetAll(key);
- if (!isValidSession(session)) {
- return null;
- }
+ Map session = jedis.hgetAll(key);
+ if (!isValidSession(session)) {
+ return null;
+ }
- if (!refreshTtl) {
- return session;
- }
+ if (!refreshTtl) {
+ return session;
+ }
- int sessionTtl = normalizeTtl(Integer.valueOf(session.get("session_ttl")));
- Pipeline pipeline = jedis.pipelined();
+ int sessionTtl = normalizeTtl(Integer.valueOf(session.get("session_ttl")));
+ try (Pipeline pipeline = jedis.pipelined()) {
pipeline.hset(key, "last_accessed_at", timestamp());
pipeline.expire(key, sessionTtl);
Response