redis-lists-sets
Master Redis Lists and Sets - queues, stacks, unique collections, set operations, and real-world implementation patterns
When & Why to Use This Skill
This Claude skill provides comprehensive expertise in Redis Lists and Sets, enabling developers to implement high-performance data structures and production-ready patterns. It covers everything from basic command execution to complex architectural designs like reliable message queues, priority systems, and social graph operations, ensuring optimal performance and scalability in backend applications.
Use Cases
- Architecting reliable message queues and background task processing systems using LMOVE and blocking pop operations to ensure zero data loss.
- Implementing real-time analytics and tracking unique user interactions (e.g., daily unique visitors) using high-performance Redis Set cardinality operations.
- Developing complex social features such as follower lists, mutual friend discovery, and interest-based recommendations using Set intersections and unions.
- Building efficient tagging and filtering systems for content management platforms to allow rapid multi-criteria searches.
- Optimizing database performance by applying LTRIM for capped collections and using SSCAN to handle large datasets without blocking the server.
| name | redis-lists-sets |
|---|---|
| description | Master Redis Lists and Sets - queues, stacks, unique collections, set operations, and real-world implementation patterns |
| sasmp_version | "1.3.0" |
| bonded_agent | 02-redis-data-structures |
| bond_type | PRIMARY_BOND |
| version | "2.1.0" |
| last_updated | "2025-01" |
| type | integer |
| required | false |
| pattern | "^[a-zA-Z0-9:_-]+$" |
| enum | [push, pop, range, members, add, remove, set_ops] |
| default | 30 |
| max | 300 |
| max_retries | 3 |
| backoff_strategy | exponential |
| backoff_base_ms | 100 |
Redis Lists and Sets Skill
Lists Overview
Redis Lists are linked lists of string values, perfect for queues and stacks.
List Commands
# Push operations
LPUSH key value [value ...] # Push to head - O(1) per element
RPUSH key value [value ...] # Push to tail - O(1) per element
# Pop operations
LPOP key [count] # Pop from head - O(N)
RPOP key [count] # Pop from tail - O(N)
BLPOP key [key ...] timeout # Blocking pop - O(1)
BRPOP key [key ...] timeout # Blocking pop from tail
# Range operations
LRANGE key start stop # Get range - O(S+N)
LINDEX key index # Get by index - O(N)
LLEN key # Get length - O(1)
# Manipulation
LMOVE source dest LEFT|RIGHT LEFT|RIGHT
LINSERT key BEFORE|AFTER pivot value
LSET key index value # Set by index
LTRIM key start stop # Trim list
LPOS key element # Find position (Redis 6.0.6+)
Sets Overview
Redis Sets are unordered collections of unique strings.
Set Commands
# Basic operations
SADD key member [member ...] # Add members - O(N)
SREM key member [member ...] # Remove members - O(N)
SMEMBERS key # Get all members - O(N)
SISMEMBER key member # Check membership - O(1)
SMISMEMBER key member [member ...] # Multi-check (Redis 6.2+)
SCARD key # Get cardinality - O(1)
# Random operations
SRANDMEMBER key [count] # Random members
SPOP key [count] # Pop random members
# Set operations
SINTER key [key ...] # Intersection - O(N*M)
SUNION key [key ...] # Union - O(N)
SDIFF key [key ...] # Difference - O(N)
SINTERSTORE dest key [key ...] # Store intersection
SUNIONSTORE dest key [key ...] # Store union
SDIFFSTORE dest key [key ...] # Store difference
# Scanning
SSCAN key cursor [MATCH pattern] [COUNT count]
Production Patterns
Pattern 1: Reliable Message Queue
# Producer
RPUSH queue:tasks '{"id":1,"action":"process","retry":0}'
# Consumer with reliability (move to processing)
LMOVE queue:tasks queue:processing LEFT RIGHT
# After processing complete
LREM queue:processing 1 '{"id":1,"action":"process","retry":0}'
# Dead letter queue for failures
RPUSH queue:dlq '{"id":1,"action":"process","error":"timeout"}'
Pattern 2: Priority Queue
# High priority
LPUSH queue:tasks:high '{"priority":"high"}'
# Normal priority
RPUSH queue:tasks:normal '{"priority":"normal"}'
# Consumer checks high first
BLPOP queue:tasks:high queue:tasks:normal 30
Pattern 3: Unique Visitors Tracking
# Track unique visitors per day
SADD visitors:2024-01-15 "user:123"
SADD visitors:2024-01-15 "user:456"
# Count unique
SCARD visitors:2024-01-15
# Weekly unique visitors (union)
SUNIONSTORE visitors:week:3 visitors:2024-01-15 visitors:2024-01-16 visitors:2024-01-17
SCARD visitors:week:3
# Set TTL for automatic cleanup
EXPIRE visitors:2024-01-15 604800 # 7 days
Pattern 4: Tag System
# Add tags to items
SADD item:123:tags "redis" "database" "cache"
SADD item:456:tags "redis" "nosql"
# Find items with specific tag
SADD tag:redis:items "item:123" "item:456"
# Find items with ALL tags (intersection)
SINTER tag:redis:items tag:database:items
# Find items with ANY tag (union)
SUNION tag:redis:items tag:nosql:items
Pattern 5: Social Features
# User follows
SADD user:123:following "user:456" "user:789"
SADD user:456:followers "user:123"
# Mutual friends
SINTER user:123:following user:456:following
# Friend suggestions (friends of friends)
SDIFF user:456:following user:123:following
Command Complexity
| Command | Complexity | Notes |
|---|---|---|
| LPUSH/RPUSH | O(1) | Per element |
| LPOP/RPOP | O(N) | N = count |
| LRANGE | O(S+N) | S = start offset |
| LINDEX | O(N) | N = index |
| LLEN | O(1) | Stored metadata |
| SADD/SREM | O(N) | N = members |
| SISMEMBER | O(1) | Hash lookup |
| SMEMBERS | O(N) | Returns all |
| SINTER | O(N*M) | Smallest set first |
Assets
queue-config.yaml- Queue configuration templatereliable-queue.lua- Atomic queue operations
Scripts
queue-consumer.py- Python queue consumer example
References
LIST_SET_PATTERNS.md- Common patterns guide
Troubleshooting Guide
Common Issues & Solutions
1. WRONGTYPE Error
WRONGTYPE Operation against a key holding the wrong kind of value
Cause: Using list command on set or vice versa
Diagnosis:
TYPE mykey # Check actual type
Prevention: Use consistent naming conventions (e.g., list:*, set:*)
2. Blocking Operation Timeout
# Returns nil after timeout
BLPOP queue:empty 5
Cause: No elements in list within timeout
Fix:
- Increase timeout for low-traffic queues
- Check if producers are running
- Use non-blocking LPOP with retry logic
3. Large Set Operations Blocking
# Can block for seconds on large sets
SMEMBERS huge:set # 1M+ members
Fix:
# Use SSCAN instead
SSCAN huge:set 0 COUNT 1000
4. Memory Issues with Long Lists
Cause: Unbounded list growth
Fix:
# Cap list length
LTRIM queue:logs 0 9999 # Keep last 10000
# Or use RPUSH with LTRIM atomically
MULTI
RPUSH queue:logs "entry"
LTRIM queue:logs -10000 -1
EXEC
Debug Checklist
□ Key exists? (EXISTS key)
□ Correct type? (TYPE key)
□ List/Set not empty? (LLEN/SCARD)
□ Blocking timeout appropriate?
□ Memory usage acceptable? (MEMORY USAGE key)
□ Consumer running?
□ Producer running?
Performance Considerations
| Scenario | Issue | Solution |
|---|---|---|
| LRANGE 0 -1 | Full list scan | Paginate with LRANGE |
| SMEMBERS large set | High memory | Use SSCAN |
| Many small lists | Memory overhead | Consolidate or use Streams |
| SINTER large sets | CPU spike | Pre-compute or cache |
Error Codes Reference
| Code | Name | Description | Recovery |
|---|---|---|---|
| LS001 | WRONGTYPE | Type mismatch | Check TYPE, fix key |
| LS002 | EMPTY | List/Set empty | Check producers |
| LS003 | INDEX_OOB | Index out of bounds | Validate index |
| LS004 | TIMEOUT | Blocking timeout | Increase timeout |
| LS005 | MEMORY | Large collection | LTRIM or SSCAN |
Test Template
# test_redis_lists_sets.py
import redis
import pytest
@pytest.fixture
def r():
return redis.Redis(decode_responses=True)
class TestLists:
def test_queue_pattern(self, r):
r.delete("test:queue")
r.rpush("test:queue", "task1", "task2")
assert r.llen("test:queue") == 2
assert r.lpop("test:queue") == "task1"
r.delete("test:queue")
def test_stack_pattern(self, r):
r.delete("test:stack")
r.lpush("test:stack", "a", "b", "c")
assert r.lpop("test:stack") == "c" # LIFO
r.delete("test:stack")
def test_lrange(self, r):
r.delete("test:list")
r.rpush("test:list", *range(10))
assert len(r.lrange("test:list", 0, 4)) == 5
r.delete("test:list")
class TestSets:
def test_unique_members(self, r):
r.delete("test:set")
r.sadd("test:set", "a", "b", "a") # Duplicate ignored
assert r.scard("test:set") == 2
r.delete("test:set")
def test_set_operations(self, r):
r.delete("test:set1", "test:set2")
r.sadd("test:set1", "a", "b", "c")
r.sadd("test:set2", "b", "c", "d")
assert r.sinter("test:set1", "test:set2") == {"b", "c"}
assert r.sunion("test:set1", "test:set2") == {"a", "b", "c", "d"}
assert r.sdiff("test:set1", "test:set2") == {"a"}
r.delete("test:set1", "test:set2")
def test_membership(self, r):
r.delete("test:set")
r.sadd("test:set", "member1")
assert r.sismember("test:set", "member1") == 1
assert r.sismember("test:set", "nonexistent") == 0
r.delete("test:set")