@neyugn/agent-kits 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +514 -0
- package/README.vi.md +410 -0
- package/README.zh.md +410 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +422 -0
- package/kits/coder/ARCHITECTURE.md +289 -0
- package/kits/coder/agents/ai-engineer.md +344 -0
- package/kits/coder/agents/backend-specialist.md +270 -0
- package/kits/coder/agents/cloud-architect.md +363 -0
- package/kits/coder/agents/code-reviewer.md +284 -0
- package/kits/coder/agents/data-engineer.md +401 -0
- package/kits/coder/agents/database-specialist.md +251 -0
- package/kits/coder/agents/debugger.md +209 -0
- package/kits/coder/agents/devops-engineer.md +281 -0
- package/kits/coder/agents/documentation-writer.md +296 -0
- package/kits/coder/agents/frontend-specialist.md +298 -0
- package/kits/coder/agents/i18n-specialist.md +348 -0
- package/kits/coder/agents/integration-specialist.md +314 -0
- package/kits/coder/agents/mobile-developer.md +271 -0
- package/kits/coder/agents/multi-tenant-architect.md +281 -0
- package/kits/coder/agents/orchestrator.md +263 -0
- package/kits/coder/agents/performance-analyst.md +327 -0
- package/kits/coder/agents/project-planner.md +277 -0
- package/kits/coder/agents/queue-specialist.md +282 -0
- package/kits/coder/agents/realtime-specialist.md +267 -0
- package/kits/coder/agents/security-auditor.md +253 -0
- package/kits/coder/agents/test-engineer.md +315 -0
- package/kits/coder/agents/ux-researcher.md +388 -0
- package/kits/coder/rules/.cursorrules +287 -0
- package/kits/coder/rules/CLAUDE.md +287 -0
- package/kits/coder/rules/CODEX.md +287 -0
- package/kits/coder/rules/GEMINI.md +287 -0
- package/kits/coder/scripts/checklist.py +318 -0
- package/kits/coder/scripts/kit_status.py +292 -0
- package/kits/coder/scripts/skills_manager.py +243 -0
- package/kits/coder/scripts/verify_all.py +391 -0
- package/kits/coder/skills/accessibility-patterns/SKILL.md +372 -0
- package/kits/coder/skills/accessibility-patterns/scripts/a11y_checker.py +211 -0
- package/kits/coder/skills/ai-rag-patterns/SKILL.md +444 -0
- package/kits/coder/skills/api-patterns/SKILL.md +316 -0
- package/kits/coder/skills/api-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/api-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/api-patterns/scripts/api_validator.py +253 -0
- package/kits/coder/skills/api-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/auth-patterns/SKILL.md +267 -0
- package/kits/coder/skills/aws-patterns/SKILL.md +576 -0
- package/kits/coder/skills/brainstorming/SKILL.md +370 -0
- package/kits/coder/skills/brainstorming/assets/.gitkeep +1 -0
- package/kits/coder/skills/brainstorming/references/deep-dive.md +21 -0
- package/kits/coder/skills/brainstorming/scripts/validate.py +56 -0
- package/kits/coder/skills/clean-code/SKILL.md +240 -0
- package/kits/coder/skills/clean-code/assets/.gitkeep +1 -0
- package/kits/coder/skills/clean-code/references/deep-dive.md +21 -0
- package/kits/coder/skills/clean-code/scripts/lint_runner.py +186 -0
- package/kits/coder/skills/clean-code/scripts/validate.py +56 -0
- package/kits/coder/skills/database-design/SKILL.md +255 -0
- package/kits/coder/skills/database-design/assets/.gitkeep +1 -0
- package/kits/coder/skills/database-design/references/deep-dive.md +21 -0
- package/kits/coder/skills/database-design/scripts/schema_validator.py +272 -0
- package/kits/coder/skills/database-design/scripts/validate.py +56 -0
- package/kits/coder/skills/docker-patterns/SKILL.md +240 -0
- package/kits/coder/skills/documentation-templates/SKILL.md +441 -0
- package/kits/coder/skills/e2e-testing/SKILL.md +457 -0
- package/kits/coder/skills/flutter-patterns/SKILL.md +330 -0
- package/kits/coder/skills/frontend-design/SKILL.md +127 -0
- package/kits/coder/skills/github-actions/SKILL.md +349 -0
- package/kits/coder/skills/gitlab-ci-patterns/SKILL.md +466 -0
- package/kits/coder/skills/graphql-patterns/SKILL.md +558 -0
- package/kits/coder/skills/i18n-localization/SKILL.md +345 -0
- package/kits/coder/skills/i18n-localization/scripts/i18n_checker.py +267 -0
- package/kits/coder/skills/kubernetes-patterns/SKILL.md +357 -0
- package/kits/coder/skills/mermaid-diagrams/SKILL.md +351 -0
- package/kits/coder/skills/mobile-design/SKILL.md +305 -0
- package/kits/coder/skills/monitoring-observability/SKILL.md +458 -0
- package/kits/coder/skills/multi-tenancy/SKILL.md +317 -0
- package/kits/coder/skills/multi-tenancy/assets/.gitkeep +1 -0
- package/kits/coder/skills/multi-tenancy/references/deep-dive.md +21 -0
- package/kits/coder/skills/multi-tenancy/scripts/validate.py +56 -0
- package/kits/coder/skills/nodejs-best-practices/SKILL.md +220 -0
- package/kits/coder/skills/performance-profiling/SKILL.md +333 -0
- package/kits/coder/skills/performance-profiling/assets/.gitkeep +1 -0
- package/kits/coder/skills/performance-profiling/references/deep-dive.md +21 -0
- package/kits/coder/skills/performance-profiling/scripts/validate.py +56 -0
- package/kits/coder/skills/plan-writing/SKILL.md +360 -0
- package/kits/coder/skills/plan-writing/assets/.gitkeep +1 -0
- package/kits/coder/skills/plan-writing/references/deep-dive.md +21 -0
- package/kits/coder/skills/plan-writing/scripts/validate.py +56 -0
- package/kits/coder/skills/postgres-patterns/SKILL.md +361 -0
- package/kits/coder/skills/prompt-engineering/SKILL.md +277 -0
- package/kits/coder/skills/queue-patterns/SKILL.md +359 -0
- package/kits/coder/skills/queue-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/queue-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/queue-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/react-native-patterns/SKILL.md +393 -0
- package/kits/coder/skills/react-patterns/SKILL.md +319 -0
- package/kits/coder/skills/realtime-patterns/SKILL.md +506 -0
- package/kits/coder/skills/realtime-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/realtime-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/realtime-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/redis-patterns/SKILL.md +484 -0
- package/kits/coder/skills/security-fundamentals/SKILL.md +363 -0
- package/kits/coder/skills/security-fundamentals/assets/.gitkeep +1 -0
- package/kits/coder/skills/security-fundamentals/references/deep-dive.md +21 -0
- package/kits/coder/skills/security-fundamentals/scripts/security_scan.py +326 -0
- package/kits/coder/skills/security-fundamentals/scripts/validate.py +56 -0
- package/kits/coder/skills/seo-patterns/SKILL.md +262 -0
- package/kits/coder/skills/seo-patterns/scripts/seo_checker.py +211 -0
- package/kits/coder/skills/systematic-debugging/SKILL.md +478 -0
- package/kits/coder/skills/systematic-debugging/assets/.gitkeep +1 -0
- package/kits/coder/skills/systematic-debugging/references/deep-dive.md +21 -0
- package/kits/coder/skills/systematic-debugging/scripts/validate.py +56 -0
- package/kits/coder/skills/tailwind-patterns/SKILL.md +395 -0
- package/kits/coder/skills/terraform-patterns/SKILL.md +470 -0
- package/kits/coder/skills/testing-patterns/SKILL.md +285 -0
- package/kits/coder/skills/testing-patterns/assets/.gitkeep +1 -0
- package/kits/coder/skills/testing-patterns/references/deep-dive.md +21 -0
- package/kits/coder/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/kits/coder/skills/testing-patterns/scripts/validate.py +56 -0
- package/kits/coder/skills/typescript-patterns/SKILL.md +417 -0
- package/kits/coder/skills/ui-ux-pro-max/SKILL.md +364 -0
- package/kits/coder/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/kits/coder/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/kits/coder/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/kits/coder/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/kits/coder/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/kits/coder/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/kits/coder/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/kits/coder/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/kits/coder/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/core.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/__pycache__/design_system.cpython-314.pyc +0 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/core.py +257 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/design_system.py +488 -0
- package/kits/coder/skills/ui-ux-pro-max/scripts/search.py +76 -0
- package/kits/coder/workflows/.gitkeep +20 -0
- package/kits/coder/workflows/create.md +152 -0
- package/kits/coder/workflows/debug.md +223 -0
- package/kits/coder/workflows/deploy.md +283 -0
- package/kits/coder/workflows/orchestrate.md +243 -0
- package/kits/coder/workflows/plan.md +134 -0
- package/kits/coder/workflows/test.md +237 -0
- package/kits/coder/workflows/ui-ux-pro-max.md +109 -0
- package/package.json +49 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: redis-patterns
|
|
3
|
+
description: Redis caching, pub/sub, sessions, and data structure patterns. Use when implementing caching layers, real-time features, rate limiting, session management, or distributed locking with Redis.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Glob, Grep
|
|
5
|
+
version: 2.0
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Redis Patterns - Caching & Real-Time
|
|
9
|
+
|
|
10
|
+
> **Philosophy:** Redis is not just a cache—it's a data structure server. Use the right structure for the right problem.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## When to Use This Skill
|
|
15
|
+
|
|
16
|
+
| ✅ Use | ❌ Don't Use |
|
|
17
|
+
| ---------------------- | ---------------------------- |
|
|
18
|
+
| Caching strategies | Primary database design |
|
|
19
|
+
| Session management | Complex querying |
|
|
20
|
+
| Rate limiting | Relational data |
|
|
21
|
+
| Pub/Sub messaging | Durable storage requirements |
|
|
22
|
+
| Distributed locking | ACID transactions |
|
|
23
|
+
| Real-time leaderboards | Large document storage |
|
|
24
|
+
| Queue/Job patterns | Complex aggregations |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Core Rules (Non-Negotiable)
|
|
29
|
+
|
|
30
|
+
1. **TTL everything** - Set expiration on all cache keys
|
|
31
|
+
2. **Namespace keys** - Use prefixes: `{app}:{entity}:{id}`
|
|
32
|
+
3. **Atomic operations** - Use built-in commands over multi-step
|
|
33
|
+
4. **Memory monitoring** - Track memory usage with policies
|
|
34
|
+
5. **Connection pooling** - Reuse connections, don't create per request
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Data Structure Decision Tree
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
What's your use case?
|
|
42
|
+
│
|
|
43
|
+
├─ Simple key-value cache?
|
|
44
|
+
│ └─ → STRING with TTL
|
|
45
|
+
│
|
|
46
|
+
├─ Object/document cache?
|
|
47
|
+
│ └─ → HASH (field-level access)
|
|
48
|
+
│
|
|
49
|
+
├─ Unique collection (tags, flags)?
|
|
50
|
+
│ └─ → SET
|
|
51
|
+
│
|
|
52
|
+
├─ Ordered data (leaderboards, timelines)?
|
|
53
|
+
│ └─ → SORTED SET (ZSET)
|
|
54
|
+
│
|
|
55
|
+
├─ Queue/stack (FIFO, LIFO)?
|
|
56
|
+
│ └─ → LIST (LPUSH/RPOP or RPUSH/LPOP)
|
|
57
|
+
│
|
|
58
|
+
├─ Real-time messaging?
|
|
59
|
+
│ └─ → Pub/Sub or Streams
|
|
60
|
+
│
|
|
61
|
+
└─ Counting/existence checking?
|
|
62
|
+
└─ → HyperLogLog (cardinality) or Bloom Filter
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Caching Patterns
|
|
68
|
+
|
|
69
|
+
### Cache-Aside (Lazy Loading)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
async function getUser(userId: string): Promise<User> {
|
|
73
|
+
const cacheKey = `user:${userId}`;
|
|
74
|
+
|
|
75
|
+
// Check cache first
|
|
76
|
+
const cached = await redis.get(cacheKey);
|
|
77
|
+
if (cached) return JSON.parse(cached);
|
|
78
|
+
|
|
79
|
+
// Cache miss: fetch from DB
|
|
80
|
+
const user = await db.users.findById(userId);
|
|
81
|
+
|
|
82
|
+
// Store in cache with TTL
|
|
83
|
+
await redis.setex(cacheKey, 3600, JSON.stringify(user)); // 1 hour
|
|
84
|
+
|
|
85
|
+
return user;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Write-Through
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
async function updateUser(userId: string, data: Partial<User>): Promise<User> {
|
|
93
|
+
// Update DB first
|
|
94
|
+
const user = await db.users.update(userId, data);
|
|
95
|
+
|
|
96
|
+
// Update cache immediately
|
|
97
|
+
const cacheKey = `user:${userId}`;
|
|
98
|
+
await redis.setex(cacheKey, 3600, JSON.stringify(user));
|
|
99
|
+
|
|
100
|
+
return user;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Write-Behind (Async)
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
async function updateUserAsync(
|
|
108
|
+
userId: string,
|
|
109
|
+
data: Partial<User>,
|
|
110
|
+
): Promise<void> {
|
|
111
|
+
const cacheKey = `user:${userId}`;
|
|
112
|
+
|
|
113
|
+
// Update cache immediately
|
|
114
|
+
await redis.hset(cacheKey, data);
|
|
115
|
+
|
|
116
|
+
// Queue DB update
|
|
117
|
+
await redis.lpush("db:updates:user", JSON.stringify({ userId, data }));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Background worker processes queue
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Cache Invalidation
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// Pattern: Event-driven invalidation
|
|
127
|
+
async function onUserUpdated(userId: string): Promise<void> {
|
|
128
|
+
await redis.del(`user:${userId}`);
|
|
129
|
+
await redis.del(`user:${userId}:profile`);
|
|
130
|
+
await redis.del(`user:${userId}:posts`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Pattern: Tag-based invalidation
|
|
134
|
+
async function invalidateByTag(tag: string): Promise<void> {
|
|
135
|
+
const keys = await redis.smembers(`tag:${tag}`);
|
|
136
|
+
if (keys.length > 0) {
|
|
137
|
+
await redis.del(...keys);
|
|
138
|
+
await redis.del(`tag:${tag}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Session Management
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
// Session storage with HASH
|
|
149
|
+
const sessionKey = `session:${sessionId}`;
|
|
150
|
+
|
|
151
|
+
// Create session
|
|
152
|
+
await redis.hset(sessionKey, {
|
|
153
|
+
userId: user.id,
|
|
154
|
+
email: user.email,
|
|
155
|
+
role: user.role,
|
|
156
|
+
createdAt: Date.now(),
|
|
157
|
+
});
|
|
158
|
+
await redis.expire(sessionKey, 86400); // 24 hours
|
|
159
|
+
|
|
160
|
+
// Get session
|
|
161
|
+
const session = await redis.hgetall(sessionKey);
|
|
162
|
+
|
|
163
|
+
// Extend session on activity
|
|
164
|
+
await redis.expire(sessionKey, 86400);
|
|
165
|
+
|
|
166
|
+
// Destroy session
|
|
167
|
+
await redis.del(sessionKey);
|
|
168
|
+
|
|
169
|
+
// Force logout all sessions for user
|
|
170
|
+
const sessionKeys = await redis.keys(`session:*:${userId}`);
|
|
171
|
+
await redis.del(...sessionKeys);
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## Rate Limiting
|
|
177
|
+
|
|
178
|
+
### Fixed Window
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
async function checkRateLimit(
|
|
182
|
+
key: string,
|
|
183
|
+
limit: number,
|
|
184
|
+
windowSec: number,
|
|
185
|
+
): Promise<boolean> {
|
|
186
|
+
const current = await redis.incr(key);
|
|
187
|
+
|
|
188
|
+
if (current === 1) {
|
|
189
|
+
await redis.expire(key, windowSec);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return current <= limit;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Usage: 100 requests per minute
|
|
196
|
+
const allowed = await checkRateLimit(`ratelimit:${userId}:api`, 100, 60);
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Sliding Window (More Accurate)
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
async function slidingWindowRateLimit(
|
|
203
|
+
key: string,
|
|
204
|
+
limit: number,
|
|
205
|
+
windowMs: number,
|
|
206
|
+
): Promise<boolean> {
|
|
207
|
+
const now = Date.now();
|
|
208
|
+
const windowStart = now - windowMs;
|
|
209
|
+
|
|
210
|
+
// Remove old entries
|
|
211
|
+
await redis.zremrangebyscore(key, "-inf", windowStart);
|
|
212
|
+
|
|
213
|
+
// Count current window
|
|
214
|
+
const count = await redis.zcard(key);
|
|
215
|
+
|
|
216
|
+
if (count >= limit) return false;
|
|
217
|
+
|
|
218
|
+
// Add current request
|
|
219
|
+
await redis.zadd(key, now, `${now}:${Math.random()}`);
|
|
220
|
+
await redis.expire(key, Math.ceil(windowMs / 1000));
|
|
221
|
+
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Token Bucket
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
async function tokenBucket(
|
|
230
|
+
key: string,
|
|
231
|
+
capacity: number,
|
|
232
|
+
refillRate: number, // tokens per second
|
|
233
|
+
): Promise<boolean> {
|
|
234
|
+
const now = Date.now();
|
|
235
|
+
const data = await redis.hgetall(key);
|
|
236
|
+
|
|
237
|
+
let tokens = parseFloat(data.tokens) || capacity;
|
|
238
|
+
let lastRefill = parseInt(data.lastRefill) || now;
|
|
239
|
+
|
|
240
|
+
// Refill tokens based on time elapsed
|
|
241
|
+
const elapsed = (now - lastRefill) / 1000;
|
|
242
|
+
tokens = Math.min(capacity, tokens + elapsed * refillRate);
|
|
243
|
+
|
|
244
|
+
if (tokens < 1) return false;
|
|
245
|
+
|
|
246
|
+
// Consume token
|
|
247
|
+
await redis.hset(key, {
|
|
248
|
+
tokens: tokens - 1,
|
|
249
|
+
lastRefill: now,
|
|
250
|
+
});
|
|
251
|
+
await redis.expire(key, 3600);
|
|
252
|
+
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
---
|
|
258
|
+
|
|
259
|
+
## Distributed Locking
|
|
260
|
+
|
|
261
|
+
### Simple Lock (Redlock Pattern)
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
async function acquireLock(key: string, ttlMs: number): Promise<string | null> {
|
|
265
|
+
const lockKey = `lock:${key}`;
|
|
266
|
+
const lockValue = crypto.randomUUID();
|
|
267
|
+
|
|
268
|
+
const acquired = await redis.set(lockKey, lockValue, "PX", ttlMs, "NX");
|
|
269
|
+
|
|
270
|
+
return acquired ? lockValue : null;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
async function releaseLock(key: string, lockValue: string): Promise<boolean> {
|
|
274
|
+
const lockKey = `lock:${key}`;
|
|
275
|
+
|
|
276
|
+
// Lua script for atomic check-and-delete
|
|
277
|
+
const script = `
|
|
278
|
+
if redis.call("get", KEYS[1]) == ARGV[1] then
|
|
279
|
+
return redis.call("del", KEYS[1])
|
|
280
|
+
else
|
|
281
|
+
return 0
|
|
282
|
+
end
|
|
283
|
+
`;
|
|
284
|
+
|
|
285
|
+
const result = await redis.eval(script, 1, lockKey, lockValue);
|
|
286
|
+
return result === 1;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Usage
|
|
290
|
+
const lock = await acquireLock("process:order:123", 30000);
|
|
291
|
+
if (lock) {
|
|
292
|
+
try {
|
|
293
|
+
await processOrder(123);
|
|
294
|
+
} finally {
|
|
295
|
+
await releaseLock("process:order:123", lock);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Pub/Sub Messaging
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
// Publisher
|
|
306
|
+
async function publishEvent(channel: string, event: object): Promise<void> {
|
|
307
|
+
await redis.publish(channel, JSON.stringify(event));
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Subscriber
|
|
311
|
+
const subscriber = redis.duplicate();
|
|
312
|
+
await subscriber.subscribe("orders", "payments");
|
|
313
|
+
|
|
314
|
+
subscriber.on("message", (channel, message) => {
|
|
315
|
+
const event = JSON.parse(message);
|
|
316
|
+
console.log(`[${channel}]`, event);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Publish examples
|
|
320
|
+
await publishEvent("orders", { type: "created", orderId: "123" });
|
|
321
|
+
await publishEvent("payments", { type: "completed", paymentId: "456" });
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Redis Streams (Persistent)
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
// Add to stream
|
|
328
|
+
await redis.xadd(
|
|
329
|
+
"events",
|
|
330
|
+
"*",
|
|
331
|
+
"type",
|
|
332
|
+
"order.created",
|
|
333
|
+
"data",
|
|
334
|
+
JSON.stringify(order),
|
|
335
|
+
);
|
|
336
|
+
|
|
337
|
+
// Consumer group
|
|
338
|
+
await redis.xgroup("CREATE", "events", "workers", "0", "MKSTREAM");
|
|
339
|
+
|
|
340
|
+
// Read as consumer
|
|
341
|
+
const entries = await redis.xreadgroup(
|
|
342
|
+
"GROUP",
|
|
343
|
+
"workers",
|
|
344
|
+
"worker-1",
|
|
345
|
+
"COUNT",
|
|
346
|
+
10,
|
|
347
|
+
"BLOCK",
|
|
348
|
+
5000,
|
|
349
|
+
"STREAMS",
|
|
350
|
+
"events",
|
|
351
|
+
">",
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
// Acknowledge processed
|
|
355
|
+
await redis.xack("events", "workers", entryId);
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Leaderboard Pattern
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Add/Update score
|
|
364
|
+
await redis.zadd("leaderboard:weekly", score, `user:${userId}`);
|
|
365
|
+
|
|
366
|
+
// Get rank (0-indexed)
|
|
367
|
+
const rank = await redis.zrevrank("leaderboard:weekly", `user:${userId}`);
|
|
368
|
+
|
|
369
|
+
// Get top 10
|
|
370
|
+
const top10 = await redis.zrevrange("leaderboard:weekly", 0, 9, "WITHSCORES");
|
|
371
|
+
|
|
372
|
+
// Get score
|
|
373
|
+
const score = await redis.zscore("leaderboard:weekly", `user:${userId}`);
|
|
374
|
+
|
|
375
|
+
// Get nearby ranks
|
|
376
|
+
const myRank = await redis.zrevrank("leaderboard:weekly", `user:${userId}`);
|
|
377
|
+
const nearby = await redis.zrevrange(
|
|
378
|
+
"leaderboard:weekly",
|
|
379
|
+
Math.max(0, myRank - 5),
|
|
380
|
+
myRank + 5,
|
|
381
|
+
"WITHSCORES",
|
|
382
|
+
);
|
|
383
|
+
|
|
384
|
+
// Weekly reset (TTL on key creation)
|
|
385
|
+
await redis.expire("leaderboard:weekly", 604800); // 7 days
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## Memory Management
|
|
391
|
+
|
|
392
|
+
### Eviction Policies
|
|
393
|
+
|
|
394
|
+
| Policy | Use Case |
|
|
395
|
+
| ---------------- | ----------------------------- |
|
|
396
|
+
| `noeviction` | Fail writes when memory full |
|
|
397
|
+
| `allkeys-lru` | General caching (recommended) |
|
|
398
|
+
| `volatile-lru` | Only evict keys with TTL |
|
|
399
|
+
| `allkeys-lfu` | Frequency-based eviction |
|
|
400
|
+
| `allkeys-random` | Random eviction |
|
|
401
|
+
|
|
402
|
+
### Memory Optimization
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# Check memory usage
|
|
406
|
+
redis-cli INFO memory
|
|
407
|
+
|
|
408
|
+
# Analyze key sizes
|
|
409
|
+
redis-cli --bigkeys
|
|
410
|
+
|
|
411
|
+
# Memory usage for specific key
|
|
412
|
+
MEMORY USAGE mykey
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
// Use HASH for objects (more compact than JSON strings)
|
|
417
|
+
// ❌ Less efficient
|
|
418
|
+
await redis.set("user:123", JSON.stringify(user));
|
|
419
|
+
|
|
420
|
+
// ✅ More efficient
|
|
421
|
+
await redis.hset("user:123", user);
|
|
422
|
+
```
|
|
423
|
+
|
|
424
|
+
---
|
|
425
|
+
|
|
426
|
+
## Key Naming Convention
|
|
427
|
+
|
|
428
|
+
```
|
|
429
|
+
{app}:{entity}:{id}:{subkey}
|
|
430
|
+
|
|
431
|
+
Examples:
|
|
432
|
+
- myapp:user:123 - User data
|
|
433
|
+
- myapp:user:123:session - User session
|
|
434
|
+
- myapp:cache:products:456 - Cached product
|
|
435
|
+
- myapp:ratelimit:ip:1.2.3.4 - Rate limit counter
|
|
436
|
+
- myapp:lock:order:789 - Distributed lock
|
|
437
|
+
- myapp:queue:emails - Email queue
|
|
438
|
+
- myapp:leaderboard:weekly - Weekly leaderboard
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
---
|
|
442
|
+
|
|
443
|
+
## Anti-Patterns
|
|
444
|
+
|
|
445
|
+
| ❌ Don't | ✅ Do |
|
|
446
|
+
| ------------------------------- | ------------------------------------- |
|
|
447
|
+
| Store without TTL | Always set expiration |
|
|
448
|
+
| Use KEYS in production | Use SCAN for iteration |
|
|
449
|
+
| Large values (>1MB) | Split or use different storage |
|
|
450
|
+
| Create connection per request | Use connection pooling |
|
|
451
|
+
| Use as primary database | Use as cache/session/queue layer |
|
|
452
|
+
| Block with BLPOP in main thread | Dedicated subscriber connections |
|
|
453
|
+
| Store complex relational data | Use proper database for relations |
|
|
454
|
+
| Ignore memory limits | Configure maxmemory + eviction policy |
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
## Production Checklist
|
|
459
|
+
|
|
460
|
+
Before deployment:
|
|
461
|
+
|
|
462
|
+
- [ ] Connection pooling configured?
|
|
463
|
+
- [ ] Memory limits set (maxmemory)?
|
|
464
|
+
- [ ] Eviction policy appropriate?
|
|
465
|
+
- [ ] TTL on all cache keys?
|
|
466
|
+
- [ ] Key namespacing consistent?
|
|
467
|
+
- [ ] Persistence configured (if needed)?
|
|
468
|
+
- [ ] Replica for high availability?
|
|
469
|
+
- [ ] Monitoring/alerting in place?
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## Related Skills
|
|
474
|
+
|
|
475
|
+
| Need | Skill |
|
|
476
|
+
| --------------------- | ----------------------- |
|
|
477
|
+
| Message queues | `queue-patterns` |
|
|
478
|
+
| Database caching | `database-design` |
|
|
479
|
+
| Real-time patterns | `realtime-patterns` |
|
|
480
|
+
| Performance profiling | `performance-profiling` |
|
|
481
|
+
|
|
482
|
+
---
|
|
483
|
+
|
|
484
|
+
> **Remember:** Redis shines when you use the right data structure for your problem. Don't force every use case into strings—explore sets, sorted sets, and hashes.
|