$npx -y skills add redis/agent-skills --skill redis-connectionsRedis 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
| 1 | # Redis Connections |
| 2 | |
| 3 | 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. |
| 4 | |
| 5 | ## When to apply |
| 6 | |
| 7 | - Creating or reviewing a Redis client setup (redis-py, Jedis, Lettuce, go-redis, NRedisStack). |
| 8 | - Making many small Redis calls and wondering where the latency is going. |
| 9 | - Iterating large keyspaces, sets, hashes, or lists. |
| 10 | - Enabling client-side caching for hot keys. |
| 11 | - Tuning connect / read / write timeouts. |
| 12 | |
| 13 | ## 1. Pool or multiplex — never one connection per request |
| 14 | |
| 15 | The single biggest mistake in Redis client code is opening a new TCP connection for every operation. Always either: |
| 16 | |
| 17 | - **Pool** — keep N persistent connections that the application leases per call (redis-py `ConnectionPool`, Jedis `JedisPooled`, go-redis client). |
| 18 | - **Multiplex** — share a single connection across all requests (Lettuce, NRedisStack). |
| 19 | |
| 20 | | Style | Used by | Note | |
| 21 | |---|---|---| |
| 22 | | Pool | redis-py, Jedis, go-redis | Each lease blocks if pool exhausted; size the pool to your concurrency | |
| 23 | | Multiplex | Lettuce, NRedisStack | Single connection; **cannot** carry blocking commands like `BLPOP` | |
| 24 | |
| 25 | ```python |
| 26 | # redis-py — connection pool |
| 27 | pool = redis.ConnectionPool(host="localhost", port=6379, max_connections=50) |
| 28 | r = redis.Redis(connection_pool=pool) |
| 29 | ``` |
| 30 | |
| 31 | See [references/pooling.md](references/pooling.md) for Python + Java + Lettuce examples. |
| 32 | |
| 33 | ## 2. Pipeline bulk work |
| 34 | |
| 35 | 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. |
| 36 | |
| 37 | ```python |
| 38 | pipe = redis.pipeline() |
| 39 | for user_id in user_ids: |
| 40 | pipe.get(f"user:{user_id}") |
| 41 | results = pipe.execute() |
| 42 | ``` |
| 43 | |
| 44 | Use **non-transactional** pipelining for performance, and `pipeline(transaction=True)` only when you actually need atomicity (see redis-core's transactions guidance). |
| 45 | |
| 46 | See [references/pipelining.md](references/pipelining.md). |
| 47 | |
| 48 | ## 3. Avoid commands that scan everything |
| 49 | |
| 50 | Anything that walks the whole keyspace (or a whole large container) blocks the server. Use incremental variants instead. |
| 51 | |
| 52 | | Don't | Use | |
| 53 | |---|---| |
| 54 | | `KEYS pattern` | `SCAN` cursor loop | |
| 55 | | `SMEMBERS large_set` | `SSCAN` | |
| 56 | | `HGETALL large_hash` | `HSCAN` | |
| 57 | | `LRANGE 0 -1` on a huge list | Paginate (`LRANGE 0 100`) | |
| 58 | |
| 59 | ```python |
| 60 | cursor = 0 |
| 61 | while True: |
| 62 | cursor, keys = redis.scan(cursor, match="user:*", count=100) |
| 63 | for key in keys: |
| 64 | process(key) |
| 65 | if cursor == 0: |
| 66 | break |
| 67 | ``` |
| 68 | |
| 69 | **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). |
| 70 | |
| 71 | See [references/blocking.md](references/blocking.md). |
| 72 | |
| 73 | ## 4. Client-side caching for hot keys |
| 74 | |
| 75 | 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. |
| 76 | |
| 77 | ```python |
| 78 | client = redis.Redis( |
| 79 | host="localhost", |
| 80 | port=6379, |
| 81 | protocol=3, # RESP3 is required |
| 82 | cache_config=redis.CacheConfig(max_size=1000), |
| 83 | ) |
| 84 | ``` |
| 85 | |
| 86 | Skip it for write-heavy workloads or data that changes constantly — the invalidation traffic overruns the savings. |
| 87 | |
| 88 | See [references/client-cache.md](references/client-cache.md). |
| 89 | |
| 90 | ## 5. Set explicit timeouts |
| 91 | |
| 92 | Defaults vary by client and may be too generous. Pick values that match the *application's* failure model: |
| 93 | |
| 94 | ```python |
| 95 | r = redis.Redis( |
| 96 | host="localhost", |
| 97 | socket_connect_timeout=2.0, # fail fast on dead nodes |
| 98 | socket_timeout=5.0, # tune to expected operation time |
| 99 | retry_on_timeout=True, |
| 100 | ) |
| 101 | ``` |
| 102 | |
| 103 | Rule of thumb: connect timeout shorter than read/write timeout. Tight timeouts + retry-on-timeout for latency-sensitive paths; longer timeouts for batch jobs. |
| 104 | |
| 105 | See [references/timeouts.md](references/timeouts.md). |
| 106 | |
| 107 | ## References |
| 108 | |
| 109 | - [Redis: Connection Pools and Multiplexing](https://redis.io/docs/latest/develop/clients/pools-and-muxing/) |
| 110 | - [Redis: Pipelining](https://redis.io/docs/latest/develop/use/pipelining/) |
| 111 | - [Redis: SCAN](https://redis.io/docs/latest/commands/scan/) |
| 112 | - [Redis: Client-side caching](https://redis.io/docs/ |