Skip to main content
rule

redis — Cursor rule: guidance and installation

This Cursor rule guides Redis key design, memory handling, and cluster-aware client use. It helps keep those practices in view while an agent works on project code.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/redis.mdc
Open on GitHub

What redis does

The rule covers key naming and data layout. It recommends splitting large objects across hashes, streams, or separate keys, and using hashtags to place keys for multi-key operations in the same Redis Cluster slot. It favors key prefixes over numbered databases, which Redis Cluster does not support.

Other guidance addresses connection pools, memory policies, and clients that understand cluster topology. It also describes exponential backoff with jitter for reconnecting and touches on pipelining batch commands. The rule applies to all project files (**/*), so its guidance can affect code beyond Redis-specific files.

What it helps with

  • organize Redis keys with a consistent hierarchy
  • place related keys in one Redis Cluster slot
  • replace numbered databases with key prefixes
  • retry cluster connections with exponential backoff
  • reduce network round-trips for batches

Example prompts for the agent

  • Review this Redis code and suggest a clear hierarchy for its keys.
  • Prepare these related keys for a multi-key operation in Redis Cluster.
  • Check the Redis Cluster reconnect logic and add exponential backoff with jitter.

How to install redis

Manually: put the files in place

.cursor/rules/redis.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about redis

How do I install redis in Cursor?

Add the rule file at .cursor/rules/redis.mdc.

What does the redis rule do?

It guides Redis key design, data layout, connection pooling, memory settings, and cluster-aware client use.

How should keys be named for multi-key operations in Redis Cluster?

Give the related keys the same hashtag in braces, such as {user:1}:profile and {user:1}:orders, so they share a hash slot.

Source text

redis.mdc · CC0-1.0
---
description: This guide provides definitive, actionable best practices for using Redis effectively, focusing on data modeling, performance, security, and cluster-aware client usage to build robust and scalable applications.
globs: **/*
---
# Redis Best Practices

Redis is a powerful, single-threaded, in-memory database. Its efficiency hinges on how you interact with it. Follow these rules to ensure your applications are performant, scalable, and resilient.

## 1. Data Modeling & Key Design

Efficient key design is paramount for performance and memory.

### 1.1. Use Hierarchical Key Naming

Adopt a consistent, hierarchical key naming convention. This improves readability and allows for logical grouping.

❌ BAD
```redis
user:1:name
user:1:email
product:123:details
order:456:items
```

✅ GOOD
```redis
user:1:profile:name
user:1:profile:email
product:123:data
order:456:cart:items
```
*Rationale*: Clearer separation, better organization.

### 1.2. Avoid Large Objects in Single Keys

Do not store multi-megabyte objects in a single key. Break down complex data into hashes or streams. This improves eviction, replication, and memory efficiency.

❌ BAD
```python
# Storing a large JSON object directly as a string
user_data = {"id": 1, "name": "Alice", "preferences": {...large_object...}, "history": [...many_items...]}
redis_client.set(f"user:1:data", json.dumps(user_data))
```

✅ GOOD
```python
# Using a Redis Hash for structured data
redis_client.hset(f"user:1:profile", mapping={
    "id": 1,
    "name": "Alice",
    "email": "[email protected]"
})
# Storing large, evolving data in a stream or separate keys
redis_client.xadd(f"user:1:activity_stream", {"action": "login", "timestamp": time.time()})
```
*Rationale*: Hashes are optimized for field-value pairs, streams for time-series data. This keeps individual values small, aiding Redis's single-threaded nature.

### 1.3. Use Hashtags for Multi-Key Operations in Clusters

When performing multi-key operations (e.g., transactions, Lua scripts) in a Redis Cluster, ensure all involved keys reside on the same hash slot by using hashtags.

❌ BAD
```python
# These keys will likely be on different slots, causing MOVED errors in a cluster
redis_client.mget("user:1:profile", "user:1:orders")
```

✅ GOOD
```python
# Keys with the same hashtag ({...}) are guaranteed to be on the same slot
redis_client.mget("{user:1}:profile", "{user:1}:orders")
```
*Rationale*: Hashtags (`{...}`) force Redis to hash only the content within the braces, ensuring co-location for atomic operations.

### 1.4. Never Use Numbered Databases (`SELECT`)

Numbered databases (`SELECT`) are an anti-pattern. They provide false isolation and are not supported by Redis Cluster.

❌ BAD
```python
redis_client.select(1) # Using database 1
redis_client.set("mykey", "myvalue")
```

✅ GOOD
```python
# Use distinct key prefixes instead of numbered databases
redis_client.set("app:cache:mykey", "myvalue")
redis_client.set("app:sessions:mykey", "myvalue")
```
*Rationale*: Numbered databases share the same underlying instance, meaning operations like `KEYS` on one database block all others. Key prefixes are the correct way to logically separate data.

## 2. Connection & Memory Management

Proper client configuration and instance sizing are critical.

### 2.1. Always Use Connection Pooling

Creating new TCP connections for every Redis command is expensive. Use a connection pool to reuse connections.

❌ BAD
```python
# Creates a new connection for each operation
redis_client = redis.Redis(host='localhost', port=6379)
redis_client.set("key", "value")
redis_client.get("key")
```

✅ GOOD
```python
# Uses a connection pool (default for most clients, but explicitly shown)
pool = redis.ConnectionPool(host='localhost', port=6379, max_connections=10)
redis_client = redis.Redis(connection_pool=pool)
redis_client.set("key", "value")
redis_client.get("key")
```
*Rationale*: Connection pooling significantly reduces overhead and improves latency.

### 2.2. Monitor and Manage Memory Usage

Configure `maxmemory` policies and lazy freeing. Provision instances with sufficient headroom for write spikes.

```redis
# redis.conf
maxmemory 8gb             # Set maximum memory usage
maxmemory-policy allkeys-lru # Evict least recently used keys when memory limit is reached
lazyfree-lazy-eviction yes # Asynchronously free memory during eviction
lazyfree-lazy-expire yes   # Asynchronously free memory during key expiration
lazyfree-lazy-server-del yes # Asynchronously free memory when deleting keys
```
*Rationale*: Proactive memory management prevents OOM errors and maintains predictable performance.

## 3. Cluster-Aware Client Usage

When using Redis Cluster, your client must understand the cluster topology.

### 3.1. Use Cluster-Aware Clients

Always use client libraries specifically designed for Redis Cluster (e.g., `redis-py-cluster`, Lettuce, GLIDE). These clients handle slot mapping and `MOVED` redirections automatically.

❌ BAD
```python
# This client is not cluster-aware and will fail or perform poorly
import redis
client = redis.Redis(host='cluster-node-1', port=6379)
client.set("mykey", "myvalue")
```

✅ GOOD
```python
# Use a cluster-aware client
from redis.cluster import RedisCluster
client = RedisCluster(host='cluster-node-1', port=6379, decode_responses=True)
client.set("mykey", "myvalue")
```
*Rationale*: Cluster-aware clients prevent `MOVED` errors and ensure requests are routed to the correct node, enabling seamless scaling.

### 3.2. Implement Exponential Backoff for Client Discovery

When clients disconnect or need to discover cluster topology, implement exponential backoff with jitter on retries to prevent overwhelming the cluster with `CLUSTER SLOTS` commands.

```python
import time
import random
from redis.cluster import RedisCluster, RedisClusterException

def connect_with_backoff(hosts, max_retries=5):
    for i in range(max_retries):
        try:
            client = RedisCluster(startup_nodes=hosts, decode_responses=True)
            client.ping() # Verify connection
            return client
        except RedisClusterException as e:
            print(f"Connection failed (attempt {i+1}/{max_retries}): {e}")
            sleep_time = (2 ** i) + random.uniform(0, 1) # Exponential backoff with jitter
            time.sleep(sleep_time)
    raise ConnectionError("Failed to connect to Redis Cluster after multiple retries")

# Example usage
startup_nodes = [{"host": "node1", "port": 6379}, {"host": "node2", "port": 6379}]
redis_cluster_client = connect_with_backoff(startup_nodes)
```
*Rationale*: Prevents a thundering herd problem during cluster reconfigurations or outages.

## 4. Query Optimization & Batch Operations

Minimize network round-trips and avoid resource-intensive commands.

### 4.1. Use Pipelining for Batch Operations

Group multiple commands into a single request to reduce network latency.

❌ BAD
```python
# Multiple round-trips
redis_client.set("key1", "value1")
redis_client.set("key2", "value2")
redis_client.get("key1")
redis_client.get("key2")
```

✅ GOOD
```python
# Single round-trip for multiple commands
pipe = redis_client.pipeline()
pipe.set("key1", "value1")
pipe.set("key2", "value2")
pipe.get("key1")
pipe.get("key2")
results = pipe.execute()
```
*Rationale*: Pipelining batches commands, sending them in one go and receiving all results at once, drastically improving throughput.

### 4.2. Avoid `KEYS` in Production

`KEYS` is a blocking, O(N) operation that can halt your Redis server. Use `SCAN` for iterating keys.

❌ BAD
```python
# Blocks the server for large datasets
all_keys = redis_client.keys("user:*")
```

✅ GOOD
```python
# Iterates keys incrementally without blocking
cursor = 0
while True:
    cursor, keys = redis_client.scan(cursor, match="user:*", count=100)
    for key in keys:
        # Process key
        pass
    if cursor == 0:
        break
```
*Rationale*: `SCAN` provides an iterator-like interface, distributing the work over multiple calls and preventing server stalls.

### 4.3. Limit Result Sets for Collections

Avoid fetching entire large lists, sets, or hashes. Use `LRANGE` with limits, `HSCAN`, or `SSCAN`.

❌ BAD
```python
# Fetches all members of a potentially huge set
all_members = redis_client.smembers("my_large_set")
```

✅ GOOD
```python
# Iterates members incrementally
cursor = 0
while True:
    cursor, members = redis_client.sscan(cursor, name="my_large_set", count=100)
    for member in members:
        # Process member
        pass
    if cursor == 0:
        break
```
*Rationale*: Prevents excessive memory usage on both the server and client, and reduces network transfer.

## 5. Security Best Practices

Secure your Redis instances from unauthorized access.

### 5.1. Always Set a Strong Password

Never run a Redis instance without authentication enabled, especially in production.

❌ BAD
```redis
# No password configured, highly insecure
# redis.conf (default)
# requirepass foobared
```

✅ GOOD
```redis
# redis.conf
requirepass your_strong_and_unique_password_here
```
*Rationale*: An unprotected Redis instance is a common target for attackers.

### 5.2. Isolate Redis Instances

Deploy Redis behind a firewall, in a private network, or within a VPC. Only allow access from trusted application servers.

*Rationale*: Reduces the attack surface by preventing direct public access.

## 6. Testing Approaches

Ensure your Redis interactions are robust and performant.

### 6.1. Use a Dedicated Test Redis Instance

For unit and integration tests, spin up a clean Redis instance (e.g., via Docker) to ensure isolated and repeatable tests.

```python
# Example using pytest and a local Redis fixture
import pytest
import redis
import docker

@pytest.fixture(scope="session")
def redis_container():
    client = docker.from_env()
    container = client.containers.run('redis:latest', detach=True, ports={'6379/tcp': 6379})
    time.sleep(2) # Give Redis time to start
    yield container
    container.stop()
    container.remove()

@pytest.fixture(scope="function")
def redis_client(redis_container):
    client = redis.Redis(host='localhost', port=6379, decode_responses=True)
    client.flushdb() # Clear database for each test
    yield client
    client.flushdb()
```
*Rationale*: Prevents test pollution and ensures tests run against a known state.

### 6.2. Conduct Load Testing

Simulate real-world traffic patterns to identify performance bottlenecks and validate scaling strategies. Tools like `memtier_benchmark` are ideal.

```bash
# Example memtier_benchmark command
memtier_benchmark -s localhost -p 6379 -n 100000 -c 50 -t 4 --ratio=1:1
```
*Rationale*: Uncovers issues related to connection limits, memory pressure, and command latency under stress.

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Data and databases →

From the same set240

The whole set →

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor