Skip to content

Commit 9d76cea

Browse files
valkirilovclaude
andcommitted
feat(redis-clustering): add spec-compliant skill for cluster and replication
Introduces skills/redis-clustering/ covering the two cluster-* rules from skills/redis-development/rules/ in agentskills.io spec layout: cluster-hash-tags → references/hash-tags.md cluster-read-replicas → references/read-replicas.md SKILL.md frames the skill around the two failure modes that bite most new cluster users: CROSSSLOT errors on multi-key operations (solved by hash tags) and overloading primaries with read traffic (solved by replica reads). Reference files carry the full Python and Java code samples. Additive only: the source cluster-*.md rules under skills/redis-development/ remain in place so the legacy compiled AGENTS.md continues to serve existing plugin consumers unchanged. They are removed in the final cleanup PR alongside the rest of the rules/ tree. Validation: - skill-validator check skills/redis-clustering → passed (0 warnings) - npm run validate → rules + plugin validators green Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 66c796d commit 9d76cea

3 files changed

Lines changed: 197 additions & 0 deletions

File tree

skills/redis-clustering/SKILL.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
name: redis-clustering
3+
description: Redis Cluster and replication guidance covering hash tags for multi-key operations, avoiding CROSSSLOT errors, and reading from replicas to scale read-heavy workloads. Use when designing keys for a sharded Redis Cluster, debugging CROSSSLOT errors on MGET / SDIFF / pipelines, configuring a multi-key transaction in a cluster, or routing reads to replicas for caches, analytics, or dashboards.
4+
license: MIT
5+
metadata:
6+
author: Redis, Inc.
7+
version: "0.1.0"
8+
---
9+
10+
# Redis Clustering
11+
12+
Guidance for designing keys and routing reads in a sharded Redis Cluster (and in standalone primary/replica replication). Covers the two failure modes that bite most new cluster users: `CROSSSLOT` errors on multi-key operations, and overloading primaries with read traffic.
13+
14+
## When to apply
15+
16+
- Designing keys for a Redis Cluster deployment.
17+
- Debugging a `CROSSSLOT` error on `MGET`, `SDIFF`, transactions, or pipelines.
18+
- Implementing transactions / Lua scripts that touch multiple keys.
19+
- Scaling out read traffic without adding shards.
20+
21+
## 1. Hash tags for multi-key operations
22+
23+
Redis Cluster distributes keys across 16,384 slots by hashing the key name. Any command that touches **multiple keys** (`MGET`, `SDIFF`, `SUNIONSTORE`, transactions, pipelines, Lua scripts with multiple `KEYS[]`) requires all keys to live on the **same slot** — otherwise the server returns a `CROSSSLOT` error.
24+
25+
Hash tags force this: the part between `{` and `}` is the only thing hashed for slot assignment, so two keys sharing a hash tag always land together.
26+
27+
```python
28+
# Same slot — multi-key ops work
29+
redis.set("{user:1001}:profile", "...")
30+
redis.set("{user:1001}:settings", "...")
31+
redis.lmove("{user:1001}:pending", "{user:1001}:processed", "LEFT", "RIGHT")
32+
```
33+
34+
```python
35+
# Different keys, no hash tag — CROSSSLOT on multi-key commands in cluster mode
36+
redis.set("user:1001:profile", "...")
37+
redis.set("user:1001:settings", "...")
38+
pipe = redis.pipeline()
39+
pipe.get("user:1001:profile")
40+
pipe.get("user:1001:settings")
41+
pipe.execute() # CROSSSLOT error in cluster
42+
```
43+
44+
Rules of thumb:
45+
46+
- **Use a tag scoped to the meaningful entity**, e.g. `{user:1001}`. Avoid bare `{1001}` — unrelated namespaces (`purchase:{1001}`, `employee:{1001}`) would all collide on the same slot.
47+
- **Only tag where you actually need multi-key ops.** Tagging everything creates hotspots and defeats the point of sharding.
48+
- A single-key command on a hash-tagged key works fine, so adding tags later is incremental — but renaming keys in production is painful, so plan tagging up front for entities you'll group.
49+
50+
See [references/hash-tags.md](references/hash-tags.md).
51+
52+
## 2. Read replicas for read-heavy workloads
53+
54+
If reads dominate writes, route them to replicas to free primary capacity. Works both in Redis Cluster (each shard has 1+ replica) and in standalone primary/replica replication.
55+
56+
```python
57+
# Redis Cluster: enable replica reads on the client
58+
from redis.cluster import RedisCluster
59+
60+
rc = RedisCluster(host="localhost", port=6379, read_from_replicas=True)
61+
rc.set("key", "value") # → primary
62+
value = rc.get("key") # → may be served by a replica
63+
```
64+
65+
For non-cluster setups, point two clients at the right nodes:
66+
67+
```python
68+
primary = Redis(host="primary-host", port=6379)
69+
replica = Redis(host="replica-host", port=6379)
70+
primary.set("key", "value")
71+
value = replica.get("key")
72+
```
73+
74+
The trade-off is consistency: **replicas are eventually consistent**. Don't read your own writes from a replica; don't use replica reads for anything that requires strict freshness (financial balances, idempotency state). Good fits: cache layers, analytics, dashboards, recommendation feeds.
75+
76+
See [references/read-replicas.md](references/read-replicas.md).
77+
78+
## References
79+
80+
- [Redis Cluster spec — hash tags](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#hash-tags)
81+
- [Redis: multi-key operations in cluster](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering/#multikey-operations)
82+
- [Redis: Replication](https://redis.io/docs/latest/operate/oss_and_stack/management/replication/)
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Use Hash Tags for Multi-Key Operations
2+
3+
In Redis Cluster, keys are distributed across slots based on their hash. Use hash tags to ensure keys that must be used together in [multi-key operations](https://redis.io/docs/latest/operate/rs/databases/durability-ha/clustering/#multikey-operations) are on the same slot.
4+
5+
**Correct:** Use hash tags for keys used in multi-key operations.
6+
7+
**Python** (redis-py):
8+
```python
9+
# These keys go to the same slot because {user:1001} is the hash tag
10+
redis.set("{user:1001}:profile", "...")
11+
redis.set("{user:1001}:settings", "...")
12+
redis.set("{user:1001}:cart", "...")
13+
14+
# Now you can use transactions and pipelines
15+
pipe = redis.pipeline()
16+
pipe.get("{user:1001}:profile")
17+
pipe.get("{user:1001}:settings")
18+
pipe.execute()
19+
20+
# Multi-key commands also work
21+
redis.lmove("{user:1001}:pending", "{user:1001}:processed", "LEFT", "RIGHT")
22+
```
23+
24+
**Java** (Jedis):
25+
```java
26+
import redis.clients.jedis.UnifiedJedis;
27+
import java.util.Set;
28+
29+
try (UnifiedJedis jedis = new UnifiedJedis("redis://localhost:6379")) {
30+
// Hash tags ensure keys go to the same slot
31+
jedis.sadd("{bikes:racing}:france", "bike:1", "bike:2", "bike:3");
32+
jedis.sadd("{bikes:racing}:usa", "bike:1", "bike:4");
33+
34+
// Multi-key operation works because of matching hash tags
35+
Set<String> result = jedis.sdiff("{bikes:racing}:france", "{bikes:racing}:usa");
36+
}
37+
```
38+
39+
**Incorrect:** Keys without hash tags that need multi-key operations.
40+
41+
**Python** (redis-py):
42+
```python
43+
# Bad: These may be on different slots
44+
redis.set("user:1001:profile", "...") # No hash tag
45+
redis.set("user:1001:settings", "...")
46+
47+
# This will fail in cluster mode
48+
pipe = redis.pipeline()
49+
pipe.get("user:1001:profile")
50+
pipe.get("user:1001:settings")
51+
pipe.execute() # CROSSSLOT error
52+
```
53+
54+
**Java** (Jedis):
55+
```java
56+
// Bad: No hash tags - keys may be on different slots
57+
jedis.sadd("bikes:racing:france", "bike:1", "bike:2", "bike:3");
58+
jedis.sadd("bikes:racing:usa", "bike:1", "bike:4");
59+
60+
// This will fail in cluster mode with CROSSSLOT error
61+
Set<String> result = jedis.sdiff("bikes:racing:france", "bikes:racing:usa");
62+
```
63+
64+
**Hash tag rules:**
65+
- Only the part between `{` and `}` is hashed for slot assignment
66+
- Use meaningful identifiers like `{user:1001}` not just `{1001}` to avoid unrelated keys (e.g., `purchase:{1001}`, `employee:{1001}`) saturating the same slot
67+
- Use hash tags only where multi-key operations are needed, not as a general habit
68+
69+
Reference: [Redis Cluster Key Distribution](https://redis.io/docs/latest/operate/oss_and_stack/reference/cluster-spec/#hash-tags)
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Use Read Replicas for Read-Heavy Workloads
2+
3+
For read-heavy workloads, distribute reads across replicas to reduce load on primaries.
4+
5+
**Correct:** Configure replica reads in Redis Cluster.
6+
7+
```python
8+
from redis.cluster import RedisCluster
9+
10+
rc = RedisCluster(
11+
host='localhost',
12+
port=6379,
13+
read_from_replicas=True # Distribute reads to replicas
14+
)
15+
16+
# Writes go to primary
17+
rc.set("key", "value")
18+
19+
# Reads can be served by replicas (eventually consistent)
20+
value = rc.get("key")
21+
```
22+
23+
**Correct:** Use replica reads in standalone replication setup.
24+
25+
```python
26+
from redis import Redis
27+
28+
# Connect to primary for writes
29+
primary = Redis(host='primary-host', port=6379)
30+
31+
# Connect to replica for reads
32+
replica = Redis(host='replica-host', port=6379)
33+
34+
# Write to primary
35+
primary.set("key", "value")
36+
37+
# Read from replica (eventually consistent)
38+
value = replica.get("key")
39+
```
40+
41+
**Considerations:**
42+
- Replica reads are eventually consistent
43+
- Don't read from replicas for data that was just written
44+
- Use for read-heavy, slightly-stale-OK workloads (caches, analytics, dashboards)
45+
46+
Reference: [Redis Replication](https://redis.io/docs/latest/operate/oss_and_stack/management/replication/)

0 commit comments

Comments
 (0)