redis-connections
Redis client and connection guidance covering connection pooling, multiplexing, pipelining, client-side caching with RESP3, avoiding slow commands (KEYS, SMEMBERS, HGETALL), and tuning socket timeouts. Use when configuring a Redis client (redis-py, Jedis, Lettuce, NRedisStack), b
Install
npx skills add https://github.com/redis/agent-skills/tree/main/skills/redis-connections
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install redis-agent-skills@llmmart
git clone https://github.com/redis/agent-skills.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole redis/agent-skills collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
Redis Connections
Client-side guidance for talking to Redis efficiently: how to share connections, how to batch commands, which commands not to call in production, when to turn on client-side caching, and how to set timeouts that fail fast without breaking healthy traffic.
When to apply
- Creating or reviewing a Redis client setup (redis-py, Jedis, Lettuce, go-redis, NRedisStack).
- Making many small Redis calls and wondering where the latency is going.
- Iterating large keyspaces, sets, hashes, or lists.
- Enabling client-side caching for hot keys.
- Tuning connect / read / write timeouts.
1. Pool or multiplex — never one connection per request
The single biggest mistake in Redis client code is opening a new TCP connection for every operation. Always either:
- Pool — keep N persistent connections that the application leases per call (redis-py
ConnectionPool, JedisJedisPooled, go-redis client). - Multiplex — share a single connection across all requests (Lettuce, NRedisStack).
| Style | Used by | Note |
|---|---|---|
| Pool | redis-py, Jedis, go-redis | Each lease blocks if pool exhausted; size the pool to your concurrency |
| Multiplex | Lettuce, NRedisStack | Single connection; cannot carry blocking commands like BLPOP |
# redis-py — connection pool
pool = redis.ConnectionPool(host="localhost", port=6379, max_connections=50)
r = redis.Redis(connection_pool=pool)
See references/pooling.md for Python + Java + Lettuce examples.
2. Pipeline bulk work
For N commands that don't depend on each other's results, send them as a single batch with pipelining. One round-trip instead of N.
pipe = redis.pipeline()
for user_id in user_ids:
pipe.get(f"user:{user_id}")
results = pipe.execute()
Use non-transactional pipelining for performance, and pipeline(transaction=True) only when you actually need atomicity (see redis-core's transactions guidance).
3. Avoid commands that scan everything
Anything that walks the whole keyspace (or a whole large container) blocks the server. Use incremental variants instead.
| Don't | Use |
|---|---|
KEYS pattern |
SCAN cursor loop |
SMEMBERS large_set |
SSCAN |
HGETALL large_hash |
HSCAN |
LRANGE 0 -1 on a huge list |
Paginate (LRANGE 0 100) |
cursor = 0
while True:
cursor, keys = redis.scan(cursor, match="user:*", count=100)
for key in keys:
process(key)
if cursor == 0:
break
Blocking commands (BLPOP, BRPOP, BLMOVE) are different — they intentionally wait for data and are fine for queue consumers, but always pass a timeout, and don't issue them on a multiplexed connection (Lettuce, NRedisStack).
4. Client-side caching for hot keys
For data that's read often and written rarely (config, feature flags, sessions on every request), enable RESP3 client-side caching. The client keeps a local copy and the server invalidates it on writes — saving the round trip for hot reads.
client = redis.Redis(
host="localhost",
port=6379,
protocol=3, # RESP3 is required
cache_config=redis.CacheConfig(max_size=1000),
)
Skip it for write-heavy workloads or data that changes constantly — the invalidation traffic overruns the savings.
See references/client-cache.md.
5. Set explicit timeouts
Defaults vary by client and may be too generous. Pick values that match the application's failure model:
r = redis.Redis(
host="localhost",
socket_connect_timeout=2.0, # fail fast on dead nodes
socket_timeout=5.0, # tune to expected operation time
retry_on_timeout=True,
)
Rule of thumb: connect timeout shorter than read/write timeout. Tight timeouts + retry-on-timeout for latency-sensitive paths; longer timeouts for batch jobs.
References
Files (agent-skills)
-
references
-
blocking.md 1.8 KB
# Avoid Slow Commands in Production Some Redis commands are slow because they scan large datasets. Use incremental alternatives to avoid blocking the server. | Avoid | Use Instead | |-------|-------------| | `KEYS *` | `SCAN` with cursor | | `SMEMBERS` on large sets | `SSCAN` | | `HGETALL` on large hashes | `HSCAN` | | `LRANGE 0 -1` on large lists | Paginate with `LRANGE 0 100` | **Correct:** Use SCAN for iteration. **Python** (redis-py): ```python # Good: Non-blocking iteration cursor = 0 while True: cursor, keys = redis.scan(cursor, match="user:*", count=100) for key in keys: process(key) if cursor == 0: break ``` **Java** (Jedis): ```java import redis.clients.jedis.ScanIteration; import redis.clients.jedis.UnifiedJedis; import java.util.List; try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) { // ScanIteration manages the cursor automatically ScanIteration scan = jedis.scanIteration(10, "user:*", "hash"); while (!scan.isIterationCompleted()) { List<String> result = scan.nextBatch().getResult(); for (String key : result) { process(key); } } } ``` **Incorrect:** Using KEYS in production. **Python** (redis-py): ```python # Bad: Scans all keys, slow on large datasets keys = redis.keys("user:*") ``` **Java** (Jedis): ```java // Bad: Scans all keys, blocks the server Set<String> result = jedis.keys("*"); ``` **Note:** Truly blocking commands (like `BLPOP`, `BRPOP`, `BLMOVE`) that wait indefinitely for data are appropriate for some use cases like job queues, but should be used with timeouts. ```python # Blocking pop with timeout - appropriate for queue consumers result = redis.blpop("task_queue", timeout=5) ``` Reference: [Redis SCAN](https://redis.io/docs/latest/commands/scan/) -
client-cache.md 2 KB
# Use Client-Side Caching for Frequently Read Data Use a connection with client-side caching enabled for any data that will be read frequently but written only occasionally. Client-side caching avoids contacting the server for repeated access to data that has recently been read, reducing network traffic and improving performance. **Correct:** Enable client-side caching with RESP3 protocol for frequently accessed data. **Python** (redis-py): ```python import redis # Enable client-side caching with RESP3 client = redis.Redis( host='localhost', port=6379, protocol=3, # RESP3 required for client-side caching cache_config=redis.CacheConfig(max_size=1000) ) # Cached reads avoid server round-trips value = client.get("frequently:read:key") ``` **Java** (Jedis): ```java import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.CacheConfig; HostAndPort endpoint = new HostAndPort("localhost", 6379); DefaultJedisClientConfig config = DefaultJedisClientConfig .builder() .password("secretPassword") .protocol(RedisProtocol.RESP3) .build(); CacheConfig cacheConfig = CacheConfig.builder().maxSize(1000).build(); UnifiedJedis client = new UnifiedJedis(endpoint, config, cacheConfig); ``` **When to use:** - Configuration data read frequently, updated rarely - User session data accessed on every request - Feature flags or settings checked repeatedly - Any read-heavy workload with low write frequency **When NOT needed:** - Data that changes frequently (cache invalidation overhead outweighs benefits) - Write-heavy workloads - Simple applications where network latency is not a bottleneck - When you need guaranteed real-time consistency **Trade-offs:** - Adds memory overhead on the client - Requires RESP3 protocol - Cache invalidation adds complexity for frequently changing data Reference: [Client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching/) -
pipelining.md 1.1 KB
# Use Pipelining for Bulk Operations Batch multiple commands into a single round trip to reduce network latency. **Correct:** Use pipeline for multiple commands. **Python** (redis-py): ```python # Good: Single round trip for multiple commands pipe = redis.pipeline() for user_id in user_ids: pipe.get(f"user:{user_id}") results = pipe.execute() ``` **Java** (Jedis): ```java import redis.clients.jedis.Pipeline; // Good: Buffer commands and send as single batch Pipeline pipe = (Pipeline) jedis.pipelined(); pipe.set("person:1:name", "Alex"); pipe.set("person:1:rank", "Captain"); pipe.set("person:1:serial", "AB1234"); pipe.sync(); ``` **Incorrect:** Sequential commands in a loop. **Python** (redis-py): ```python # Bad: N round trips results = [] for user_id in user_ids: results.append(redis.get(f"user:{user_id}")) ``` **Java** (Jedis): ```java // Bad: 3 separate round trips jedis.set("person:1:name", "Alex"); jedis.set("person:1:rank", "Captain"); jedis.set("person:1:serial", "AB1234"); ``` Reference: [Redis Pipelining](https://redis.io/docs/latest/develop/use/pipelining/) -
pooling.md 1.9 KB
# Use Connection Pooling or Multiplexing Reuse connections via a pool or multiplexing instead of creating new connections per request. **Correct:** Use a connection pool. **Python** (redis-py): ```python import redis # Good: Connection pool - reuses existing connections pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=50) r = redis.Redis(connection_pool=pool) ``` **Java** (Jedis): ```java import redis.clients.jedis.JedisPooled; // JedisPooled manages a connection pool internally try (JedisPooled jedis = new JedisPooled("redis://localhost:6379")) { jedis.set("testKey", "testValue"); } ``` **Correct:** Use multiplexing (Lettuce, NRedisStack). ```java // Lettuce uses multiplexing by default - single connection handles all traffic RedisClient client = RedisClient.create("redis://localhost:6379"); StatefulRedisConnection<String, String> connection = client.connect(); // All commands share the single connection efficiently connection.sync().set("key", "value"); ``` **Incorrect:** Creating new connections per request. **Python** (redis-py): ```python # Bad: New connection every time def get_user(user_id): r = redis.Redis(host='localhost', port=6379) # Don't do this return r.get(f"user:{user_id}") ``` **Java** (Jedis): ```java // Bad: Creating new client per request public String getUser(String userId) { try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) { return jedis.get("user:" + userId); // Don't do this } } ``` **Pooling vs Multiplexing:** - **Pooling**: Multiple connections shared across requests (redis-py, Jedis, go-redis) - **Multiplexing**: Single connection handles all traffic (NRedisStack, Lettuce) - Multiplexing cannot support blocking commands (BLPOP, etc.) as they would stall all callers Reference: [Connection Pools and Multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing/) -
timeouts.md 1.3 KB
# Configure Connection Timeouts Configure appropriate timeout values to improve your application's connection resilience. While most Redis clients set default timeouts, choosing well-tuned values based on your application's usage patterns leads to better failure recovery. **Correct:** Set timeouts based on your application needs. ```python r = redis.Redis( host='localhost', socket_timeout=5.0, # Read/write timeout - tune based on expected operation time socket_connect_timeout=2.0, # Connection timeout - shorter for fast failure detection retry_on_timeout=True # Automatic retry on timeout ) ``` **Incorrect:** Relying solely on defaults without considering your use case. ```python # Not ideal: Default timeouts may not match your application's needs r = redis.Redis(host='localhost') # For example, if your app needs fast failure detection, # the default timeouts might be too generous ``` **Considerations:** - Set `socket_connect_timeout` shorter than `socket_timeout` for quick connection failure detection - For latency-sensitive apps, use tighter timeouts with retry logic - For batch operations, allow longer timeouts to complete large operations - Consider using health checks alongside timeouts for robust failure handling Reference: [Redis Client Configuration](https://redis.io/docs/latest/develop/clients/)
-
-
SKILL.md 5 KB
--- name: redis-connections description: Redis client and connection guidance covering connection pooling, multiplexing, pipelining, client-side caching with RESP3, avoiding slow commands (KEYS, SMEMBERS, HGETALL), and tuning socket timeouts. Use when configuring a Redis client (redis-py, Jedis, Lettuce, NRedisStack), batching commands for throughput, eliminating per-request connection creation, iterating large keyspaces with SCAN, enabling client-side caching for read-heavy workloads, or setting connect and read timeouts. license: MIT metadata: author: Redis, Inc. version: "0.1.0" --- # Redis Connections Client-side guidance for talking to Redis efficiently: how to share connections, how to batch commands, which commands not to call in production, when to turn on client-side caching, and how to set timeouts that fail fast without breaking healthy traffic. ## When to apply - Creating or reviewing a Redis client setup (redis-py, Jedis, Lettuce, go-redis, NRedisStack). - Making many small Redis calls and wondering where the latency is going. - Iterating large keyspaces, sets, hashes, or lists. - Enabling client-side caching for hot keys. - Tuning connect / read / write timeouts. ## 1. Pool or multiplex — never one connection per request The single biggest mistake in Redis client code is opening a new TCP connection for every operation. Always either: - **Pool** — keep N persistent connections that the application leases per call (redis-py `ConnectionPool`, Jedis `JedisPooled`, go-redis client). - **Multiplex** — share a single connection across all requests (Lettuce, NRedisStack). | Style | Used by | Note | |---|---|---| | Pool | redis-py, Jedis, go-redis | Each lease blocks if pool exhausted; size the pool to your concurrency | | Multiplex | Lettuce, NRedisStack | Single connection; **cannot** carry blocking commands like `BLPOP` | ```python # redis-py — connection pool pool = redis.ConnectionPool(host="localhost", port=6379, max_connections=50) r = redis.Redis(connection_pool=pool) ``` See [references/pooling.md](references/pooling.md) for Python + Java + Lettuce examples. ## 2. Pipeline bulk work For N commands that don't depend on each other's results, send them as a single batch with pipelining. One round-trip instead of N. ```python pipe = redis.pipeline() for user_id in user_ids: pipe.get(f"user:{user_id}") results = pipe.execute() ``` Use **non-transactional** pipelining for performance, and `pipeline(transaction=True)` only when you actually need atomicity (see redis-core's transactions guidance). See [references/pipelining.md](references/pipelining.md). ## 3. Avoid commands that scan everything Anything that walks the whole keyspace (or a whole large container) blocks the server. Use incremental variants instead. | Don't | Use | |---|---| | `KEYS pattern` | `SCAN` cursor loop | | `SMEMBERS large_set` | `SSCAN` | | `HGETALL large_hash` | `HSCAN` | | `LRANGE 0 -1` on a huge list | Paginate (`LRANGE 0 100`) | ```python cursor = 0 while True: cursor, keys = redis.scan(cursor, match="user:*", count=100) for key in keys: process(key) if cursor == 0: break ``` **Blocking commands (`BLPOP`, `BRPOP`, `BLMOVE`) are different** — they intentionally wait for data and are fine for queue consumers, but always pass a timeout, and don't issue them on a multiplexed connection (Lettuce, NRedisStack). See [references/blocking.md](references/blocking.md). ## 4. Client-side caching for hot keys For data that's read often and written rarely (config, feature flags, sessions on every request), enable RESP3 client-side caching. The client keeps a local copy and the server invalidates it on writes — saving the round trip for hot reads. ```python client = redis.Redis( host="localhost", port=6379, protocol=3, # RESP3 is required cache_config=redis.CacheConfig(max_size=1000), ) ``` Skip it for write-heavy workloads or data that changes constantly — the invalidation traffic overruns the savings. See [references/client-cache.md](references/client-cache.md). ## 5. Set explicit timeouts Defaults vary by client and may be too generous. Pick values that match the *application's* failure model: ```python r = redis.Redis( host="localhost", socket_connect_timeout=2.0, # fail fast on dead nodes socket_timeout=5.0, # tune to expected operation time retry_on_timeout=True, ) ``` Rule of thumb: connect timeout shorter than read/write timeout. Tight timeouts + retry-on-timeout for latency-sensitive paths; longer timeouts for batch jobs. See [references/timeouts.md](references/timeouts.md). ## References - [Redis: Connection Pools and Multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing/) - [Redis: Pipelining](https://redis.io/docs/latest/develop/use/pipelining/) - [Redis: SCAN](https://redis.io/docs/latest/commands/scan/) - [Redis: Client-side caching](https://redis.io/docs/latest/develop/clients/client-side-caching/) - [Redis: Clients](https://redis.io/docs/latest/develop/clients/)
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.