Playball Logo

Command Palette

Search for a command to run...

목차 열기

Kafka 이벤트 메시징 / Caffeine 캐싱

PlayBall은 서비스 간 비동기 메시징으로 Apache Kafka 3.7.1을 사용하며, DB 부하를 흡수하기 위해 Caffeine 로컬 캐시와 Redis 분산 캐시를 계층적으로 활용합니다.

Kafka를 채택한 맥락(MSA 현황, EDA 전환 동기, Payment 분리/DB 스키마 분리 로드맵)은 별도 문서 MSA · EDA 전환 (Kafka 채택)에 정리되어 있습니다. 이 페이지는 토픽·설정·캐싱 구성 같은 운영 디테일에 집중합니다.


Kafka 이벤트 메시징

토픽 구성

토픽파티션ProducerConsumer용도
payment-completed3Order-CoreSeat결제 완료 시 좌석 BLOCKED → SOLD 전환
order-cancelled3Order-CoreSeat주문 취소 시 좌석 SOLD → AVAILABLE 복원
bank-transfer-expired3Order-CoreSeat무통장 입금 기한 만료 시 좌석 복원
user-blocked3Auth-GuardOrder-Core유저 차단 시 활성 주문 UNDER_REVIEW 처리

신뢰성 정책

  • Acks = all: 모든 replica 확인 후 응답
  • Replication factor = 1, Log retention = 72h
  • 재시도: 실패 시 3회 재시도 (1초 간격)
  • DLT (Dead Letter Topic): 3회 재시도 후에도 실패하면 {토픽}.DLT로 전송
  • @TransactionalEventListener(phase = AFTER_COMMIT): DB 커밋 이후 이벤트 발행으로 정합성 보장

이벤트 흐름 예시

1. 결제 완료
Order-Core
  ├─ Payment 저장 (PAID)
  ├─ Order 상태 전환 (PAYMENT_PENDING → PAID)
  ├─ [AFTER_COMMIT] Kafka publish
  └─ payload: { orderId, matchSeatIds: [123, 124, 125], paymentMethod }
        ▼
Kafka topic: payment-completed
        ▼
Seat (Consumer: seat-service)
  └─ @KafkaListener → MatchSeat.saleStatus: BLOCKED → SOLD

2. 유저 차단
AI 방어 서버
  └─ POST /internal/users/{userId}/block
        ▼
Auth-Guard
  ├─ User.status = BLOCKED
  ├─ [AFTER_COMMIT] Kafka publish
  └─ payload: { userId, occurredAt }
        ▼
Kafka topic: user-blocked
        ▼
Order-Core (Consumer: order-core-notification)
  └─ 해당 userId의 PAID 주문 → UNDER_REVIEW

Caffeine 캐싱

Caffeine이란?

Caffeine은 Java용 고성능 인-메모리 로컬 캐시 라이브러리입니다. Guava Cache의 개선 버전으로, Google 엔지니어 Ben Manes가 만들었으며 현재 Java 생태계에서 사실상 표준 로컬 캐시입니다.

핵심 개념: "로컬 캐시"

[요청] → Spring Application (JVM 메모리 안에 HashMap 같은 저장소)
          ↓ 있으면 바로 반환 (sub-ms)
          ↓ 없으면 DB/Redis 조회 후 저장

JVM 프로세스 자신의 힙 메모리에 데이터를 보관.
네트워크 홉이 없으니 접근 시간이 나노~마이크로초 단위.

Redis vs Caffeine 비교

항목Caffeine (로컬)Redis (원격)
저장 위치JVM 힙 메모리별도 서버
접근 시간< 1μs0.5~2ms (+네트워크)
용량JVM 힙 한도 내GB~TB 가능
인스턴스 간 공유각자 따로공유됨
일관성노드별 달라질 수 있음단일 소스
장애 격리JVM과 운명공유Redis 다운 시 영향

Caffeine의 강점

  1. 매우 빠른 구현 — W-TinyLFU 교체 알고리즘으로 LRU보다 높은 hit rate, Lock-free에 가까운 동시성
  2. 풍부한 만료 정책expireAfterWrite, expireAfterAccess, refreshAfterWrite, recordStats
  3. Spring Cache와 자연스러운 통합@Cacheable 어노테이션만 붙이면 끝

PlayBall에서의 Caffeine 활용

@Cacheable(cacheNames = "match-exists", key = "#matchId", unless = "!#result")
public boolean exists(Long matchId) {
    return matchRepository.existsById(matchId);
}

# application.yaml
spring:
  cache:
    type: caffeine
    cache-names: match-exists
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=10m,recordStats
  • maximumSize=1000: 최대 1000개 캐싱 (초과 시 LFU eviction)
  • expireAfterWrite=10m: 쓰기 후 10분 만료
  • recordStats: /actuator/metrics/cache.gets로 hit/miss 통계

주의할 점

  1. 멀티 인스턴스 시 캐시 불일치 — 자주 바뀌는 데이터엔 부적합
  2. JVM 힙 사용 — 너무 큰 객체/많은 수를 담으면 GC 압박
  3. 재시작 시 휘발 — 프로세스 재시작하면 캐시는 비어서 시작 (cold start)

한 줄 요약: "Redis보다 수백 배 빠른, 프로세스 내부 메모리에 데이터를 보관하는 스마트한 HashMap"


PlayBall 캐싱 맵

서비스Caffeine (로컬)Redis (분산)
Auth-Guarduser-by-id (10분), auth-me (30초)
Queuematch-for-queue (1분)
Seatmatch-exists, match-detail (10분), section-all, blocks-by-section-ids (1시간)seat-groups-response (5초)
Order-Corematch-detail (10분)user-by-id (10분), matches-list-response (30초)