â ã¹ãã«äžèŠ§ã«æ»ã

redis-patterns
by eco2-team
ð± ìŽìœììœ(Eco²) BE
â 0ðŽ 0ð
2026幎1æ25æ¥
SKILL.md
name: redis-patterns description: Redis íšíŽ ê°ìŽë. Cache-Aside, Rate Limiting, Pub/Sub, Streams 구í ì ì°žì¡°. "redis", "cache", "rate limit", "pubsub", "streams" í€ìëë¡ ížëŠ¬ê±°.
Redis Patterns Guide
Eco² Redis ì¬ì© íšíŽ
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
â Redis Usage in Eco² â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ€
â â
â Cache-Aside (L1) â
â ââ LangGraph Checkpoint ìºì â
â ââ Intent Classification ìºì â
â ââ Session State ìºì â
â â
â Rate Limiting â
â ââ API ìì² ì í (Sliding Window) â
â â
â Pub/Sub â
â ââ SSE ì€ìê° ìŽë²€íž (sse:events:{job_id}) â
â â
â Streams â
â ââ ìŽë²€íž ë²íŒ ({domain}:events:{shard}) â
â ââ State KV ({domain}:state:{job_id}) â
â â
â Human-in-the-Loop â
â ââ ì
ë ¥ ìì² (input:request:{job_id}) â
â ââ ìížìì© ìí (interaction:state:{job_id}) â
â â
âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ
죌ì íšíŽ
1. Cache-Aside
async def get_with_cache(
key: str,
fetch_fn: Callable[[], Awaitable[T]],
ttl: int = 3600,
) -> T:
"""Cache-Aside íšíŽ"""
# 1. ìºì ì¡°í
cached = await redis.get(key)
if cached:
return deserialize(cached)
# 2. ì볞 ì¡°í
data = await fetch_fn()
# 3. ìºì ì ì¥
await redis.setex(key, ttl, serialize(data))
return data
2. Rate Limiting (Sliding Window)
async def check_rate_limit(
key: str,
limit: int,
window_seconds: int,
) -> bool:
"""Sliding Window Rate Limit"""
now = time.time()
window_start = now - window_seconds
pipe = redis.pipeline()
pipe.zremrangebyscore(key, 0, window_start) # ì€ëë êž°ë¡ ì ê±°
pipe.zadd(key, {str(now): now}) # íì¬ ìì² ì¶ê°
pipe.zcard(key) # ìì² ì íìž
pipe.expire(key, window_seconds)
_, _, count, _ = await pipe.execute()
return count <= limit
3. Pub/Sub
async def publish_event(job_id: str, event: dict) -> None:
"""ìŽë²€íž ë°í"""
channel = f"sse:events:{job_id}"
await redis.publish(channel, json.dumps(event))
async def subscribe_events(job_id: str) -> AsyncIterator[dict]:
"""ìŽë²€íž 구ë
"""
pubsub = redis.pubsub()
await pubsub.subscribe(f"sse:events:{job_id}")
async for message in pubsub.listen():
if message["type"] == "message":
yield json.loads(message["data"])
4. Streams (Consumer Group)
async def consume_stream(
stream: str,
group: str,
consumer: str,
) -> AsyncIterator[tuple[str, dict]]:
"""Consumer Group êž°ë° ì€ížëŠŒ ìë¹"""
while True:
messages = await redis.xreadgroup(
groupname=group,
consumername=consumer,
streams={stream: ">"},
block=5000,
count=100,
)
for stream_name, events in messages:
for event_id, data in events:
yield event_id, data
await redis.xack(stream_name, group, event_id)
Reference Files
- Cache-Aside: See cache-aside.md
- Rate Limiting: See rate-limiting.md
- Pub/Sub & Streams: See messaging.md
Redis ìžì€íŽì€ ë¶ëЬ
Eco² íŽë¬ì€í° Redis ìë¹ì€
| ìžì€íŽì€ | K8s ìë¹ì€ | ì©ë |
|---|---|---|
| Streams | rfr-streams-redis.redis.svc.cluster.local:6379 | Streams, State KV (ìì) |
| Pub/Sub | rfr-pubsub-redis.redis.svc.cluster.local:6379 | ì€ìê° ì ì¡ (íë°) |
# ConfigMap í겜ë³ì ìì
CHAT_WORKER_REDIS_STREAMS_URL: redis://rfr-streams-redis.redis.svc.cluster.local:6379/0
CHAT_WORKER_REDIS_PUBSUB_URL: redis://rfr-pubsub-redis.redis.svc.cluster.local:6379/0
ìžì€íŽì€ë³ í¹ì±
rfr-streams-redis: # Streams, State KV (ìì)
- AOF íì±í
- Checkpoint ë°ìŽí°
- Consumer Group
rfr-pubsub-redis: # Pub/Sub (íë°)
- ì€ìê° ì ì¡ ì ì©
- SSE Gateway 구ë
ã¹ã³ã¢
ç·åã¹ã³ã¢
60/100
ãªããžããªã®åè³ªææšã«åºã¥ãè©äŸ¡
âSKILL.md
SKILL.mdãã¡ã€ã«ãå«ãŸããŠãã
+20
âLICENSE
ã©ã€ã»ã³ã¹ãèšå®ãããŠãã
+10
â説ææ
100æå以äžã®èª¬æããã
0/10
â人æ°
GitHub Stars 100以äž
0/15
âæè¿ã®æŽ»å
3ã¶æä»¥å ã«æŽæ°ããã
0/10
âãã©ãŒã¯
10å以äžãã©ãŒã¯ãããŠãã
0/5
âIssue管ç
ãªãŒãã³Issueã50æªæº
+5
âèšèª
ããã°ã©ãã³ã°èšèªãèšå®ãããŠãã
+5
âã¿ã°
1ã€ä»¥äžã®ã¿ã°ãèšå®ãããŠãã
0/5
ã¬ãã¥ãŒ
ð¬
ã¬ãã¥ãŒæ©èœã¯è¿æ¥å ¬éäºå®ã§ã