Master + Replica vs 단일 — BullMQ 발송 시스템 패턴 분석
2026 베스트 프랙티스 + Rinda 실측 데이터 기반 권장
한 줄 결론
Master + Replica + Sentinel 패턴은 BullMQ 발송 시스템에 over-engineering. 단일 인스턴스 + AOF + EBS snapshot 으로 충분하며, BullMQ 의 멱등성·재시도 정책이 1-2분 downtime 을 흡수합니다. Sentinel HA 의 ~30초 자동 failover 보다 메모리 2배·컨테이너 4개·race-condition 위험 cost 가 큽니다. alpha 도 beta 처럼 단일로 일관화 권장.
1. 현재 상태
| 환경 | 구조 | 평가 |
|---|---|---|
| alpha (dev/test) | master + replica + Sentinel x3 (quorum=2) | HA, 운영 복잡 |
| beta (prod) | master 1개 + AOF + restart:always | 단순, BullMQ 적합 |
역구조 — prod(beta)가 single, dev(alpha)가 HA. 일반적으로 prod 가 더 강한 가용성이어야 하지만, BullMQ 시나리오에서는 단일이 더 안전. 일관성도 깨짐.
2. 2026 패턴 비교
| 패턴 | 장점 | 단점 | BullMQ 적합 |
|---|---|---|---|
| 단일 + AOF + EBS snapshot | 단순, 메모리 1배, 운영 복잡도 ↓ | RTO ~1-2분 (manual restart) | ★★★★★ BullMQ 공식 권장 |
| Sentinel HA (alpha 현재) | 자동 failover ~30s | 메모리 2배, 컨테이너 4개, split-brain 위험, race condition | ★★☆☆☆ over-engineering |
| Redis Cluster (sharded) | horizontal scale | BullMQ 비호환 (Lua multi-key, hash tag 강제) | ★☆☆☆☆ 비권장 |
| Managed (ElastiCache/Upstash) | 자동 backup/maintenance | cost ($50-300/월) | ★★★★☆ |
| Dragonfly (Redis 호환 멀티스레드) | 25배 throughput, 메모리 효율 | edge command 일부 차이 | ★★★★☆ |
3. BullMQ 컨텍스트에서 Sentinel 의 함정
-
Lock + Stalled job race condition — failover 동안
bull:*:stalled-checklock 이 끊김 → 두 worker 가 같은 job 처리 → 이메일 중복 발송 위험 (멱등성 강제 필수) -
evalshascript cache loss — replica → master promote 시 script SHA 사라짐 →NOSCRIPT에러 + 재로드 overhead - AOF rewrite + replica sync 충돌 — AOF rewrite 중 replica sync 가 delay → 일시적 lag
- 오늘 발생한 OOM 은 HA 와 무관 — 메모리 한도 자체가 부족했던 것 (4G → 8G 해결). Sentinel 이 막아주지 않음.
4. 2026 베스트 프랙티스 (영업 메일 발송 시스템)
Redis 공식 + BullMQ 공식 가이드 종합:
✅ 단일 Redis 인스턴스 --appendonly yes --appendfsync everysec # 1초 데이터 유실 허용 --maxmemory 8gb # 또는 워크로드 기반 --maxmemory-policy noeviction --save 900 1 # 보조 RDB snapshot restart: always # docker restart on OOM oom_score_adj: -900 # OOM killer 보호 mem_swappiness: 0 # swap I/O 절대 금지 memswap_limit == mem_limit # swap 영역 0 ✅ EBS gp3 snapshot 1시간 cron # 디스크 레벨 backup ✅ BullMQ idempotent job design # at-least-once 보장 ✅ Worker auto-reconnect # Redis restart 동안 재연결
근거: BullMQ 공식 문서 — "A single Redis instance is sufficient for most use cases". 영업 메일은 retry-safe (멱등 키 강제), 1-2분 downtime 허용. Sentinel split-brain 위험 > 자동 failover 가치.
5. 권장 액션
P0 단기 현재 변경 안정화
- 적용 완료:
maxmemory 4G → 8G, 컨테이너5G → 10G,mem_swappiness: 0,memswap_limit == mem_limit - 1-2일 모니터 — OOM 재발 없는지, 메모리 추세 확인
P1 중기 (별도 PR)
- alpha 의 redis-replica + Sentinel x3 제거 → beta 와 같은 single 구조로 통일
- BullMQ retention 강화 —
sequence-emailcompleted 1000 → 100, repeatable scheduler pruning 확대 (현재 796개 stale) - 다중 SES 계정 라운드로빈 fix — pool 확장 후 기존 enrollment 재할당 로직 추가. 현재 56만 delayed jobs 적체의 근본 원인.
P2 장기
- ElastiCache 또는 Dragonfly 검토 — 운영 부담 ↓ + 성능 ↑
- BullMQ → 분산 큐 (Kafka / SQS) 마이그레이션 검토 (트래픽 10배 시)
6. 오늘 OOM 사고 정리
| 측정 | 값 |
|---|---|
| Redis used / max | 4.00 GB / 4.00 GB (100%) |
| maxmemory-policy | noeviction — 가득 차면 새 write 거부 |
bull:sequence-email:* keys | 582,744 |
bull:sequence-email:delayed ZSET | 562,956 jobs (≈3.5 GB) |
lead-on-demand-enrich:wait | 68,759 적체 |
| repeatable scheduler | 796 (정상 ~10) |
배포 실패 원인은 우리 PR 과 무관 — Redis 가 며칠 동안 100% full 상태였고, zero-downtime deploy 의 새 worker 시동 시 BullMQ Lua script enqueue 가 OOM 거부 → worker error → healthcheck 240s timeout.