Real-Time Stream Processing

이벤트 스트림을 실시간으로 처리하여 저지연 분석과 피처 계산을 수행하는 시스템


핵심 개념

실시간 스트림 처리는 연속적으로 도착하는 이벤트를 처리하여 집계, 변환, 알림 등을 수행하는 패턴이다. Apache Flink, Apache Spark Structured Streaming, Kafka Streams 등이 대표적이며, 2026년에는 스트리밍-배치 통합(unified engine)과 ML 워크로드 특화 엔진이 주요 트렌드다.

Spark Real-Time Mode (4.1)

Apache Spark 4.1은 마이크로배치와 저지연 사이의 트레이드오프를 해소하는 Real-Time Mode를 도입했다:

  • 장시간 에포크 + 경계 체크포인팅: 에포크를 길게 유지하면서 경계에서만 체크포인트
  • 동시 map-reduce 스테이지: 파이프라인 병렬성 확보
  • 논블로킹 연산자: 결과를 버퍼링 없이 연속 방출
  • Spark의 리니지 기반 장애 복구를 유지하면서 ETL과 저지연 사기 탐지를 단일 엔진으로 통합

Volga — Rust 기반 ML 피처 엔진

실시간 ML 피처 계산에 특화된 새로운 엔진:

  • 기술 스택: Rust + Apache DataFusion(SQL) + Apache Arrow(컬럼나) + SlateDB(상태 저장)
  • 3중 실행 모드: Streaming(실시간 업데이트) + Batch(학습 데이터) + Request(서빙)
  • 컴퓨트-스토리지 분리: SlateDB가 S3 위에서 동작하여 독립 스케일링
  • ML 특화 윈도우 함수: top-k, categorical aggregation, tiling(월~년 단위 장기 윈도우)
  • 아키텍처 비교: Flink보다 ML 집계에 특화, Chronon보다 단일 바이너리로 단순, OpenMLDB보다 쓰기 경로 분리가 명확

Dataflow 모델 핵심 요소

  • 워터마크: bounded out-of-orderness 가정으로 이벤트 시간 진행을 추적
  • 체크포인팅: Chandy-Lamport 스냅샷 알고리즘 기반 분산 체크포인팅
  • Exactly-once: Kafka 오프셋 기반 정확한 리플레이 보장

트레이드오프

엔진장점단점
Spark Real-Time기존 Spark 에코시스템 활용, 통합 엔진새 기능이라 성숙도 미검증
Flink성숙한 스트리밍, 풍부한 커넥터ML 특화 함수 부족, 별도 서빙 레이어 필요
VolgaML 피처에 특화, 단일 바이너리초기 단계, 에코시스템 부족

Table API가 우아하고 선언적이지만 대규모 조인에서 심각한 상태 관리 문제를 일으킬 수 있다:

문제

  • Table API 4개 조인 체이닝 시 상태가 “추가”가 아닌 “곱셈”으로 증가
  • 235GB 상태 → CPU 고갈, 11분 스냅샷, 크래시 루프, 1시간 SLA 위협

해결: MultiStreamJoinProcessor

  • RocksDB에 SKU별 enrichment 상태 1개 인스턴스만 유지하는 단일 KeyedProcessFunction
  • SQL 쿼리 최적화 포기 대신 수동 스트림 처리로 중복성 제거
  • 결과: 상태 -76%, 스냅샷 시간 -77%, CPU 안정화, AWS 비용 -13%

교훈

  • “4개 조인을 체이닝하면 상태가 추가되는 것이 아니라 곱해진다”
  • Flink 2.1이 동일 문제를 해결하는 Multi-Way Join Operator 도입으로 결정 사후 검증

트레이드오프

엔진장점단점
Spark Real-Time기존 Spark 에코시스템 활용, 통합 엔진새 기능이라 성숙도 미검증
Flink Table API선언적, SQL 친화적복잡 조인에서 상태 기하급수적 증가 위험
Flink DataStream상태 세밀 제어 가능수동 구현, 더 많은 코드 필요
VolgaML 피처에 특화, 단일 바이너리초기 단계, 에코시스템 부족

AutoMQ — S3 기반 Kafka 호환 스트리밍

Kafka의 로컬 디스크 복제를 S3 공유 스토리지로 대체:

  • 3계층: ElasticLog → S3Stream(WAL, 업로드, 캐싱) → KRaft 기반 메타데이터 컨트롤 플레인
  • 무상태 브로커: 장애 시 데이터 복사 없이 epoch fencing만으로 복구
  • WAL 배칭: 250ms 또는 8MB 임계값으로 레이턴시-처리량 균형
  • 트레이드오프: 복제 스토리지 3x 제거, 단 약간 높은 쓰기 레이턴시와 S3 요청 비용

Apache Pinot — 실시간 Upsert 컴팩션

SegmentRefreshTask로 append-only 스토리지의 버전 누적 문제 해결:

  • 높은 obsolete 비율의 세그먼트 선택 → 유효 문서만 추출 → 머지 → 원자적 교체
  • 쿼리 무중단: 새 세그먼트가 빈 bitmap으로 즉시 가시화
  • RocksDB 메타데이터 스토어로 메모리 10x 감소
  • 스토리지 비용 2-10x 절감, 10억+ 프라이머리 키 관리

Kafka KIP-848 — 차세대 컨슈머 리밸런스

기존 Kafka 컨슈머 리밸런스의 근본적 문제를 서버 사이드 조정으로 해결하는 새 프로토콜:

기존 문제

  • 두꺼운 클라이언트: 버그 수정에 모든 컨슈머 업데이트 필요
  • 글로벌 동기화 장벽: 단일 오동작 멤버가 전체 그룹 리밸런스 방해
  • Fake rebalance: 상태 전파 혼동으로 메트릭 해석 어려움

새 아키텍처

  • 3중 에포크: Group Epoch(그룹 변경), Assignment Epoch(목표 할당), Member Epoch(개별 수렴)
  • 선언적 할당: 점진적 업데이트 대신 목표 상태를 선언, 코디네이터가 수렴
  • ConsumerGroupHeartbeat RPC: JoinGroup/SyncGroup/Heartbeat를 단일 RPC로 대체
  • 이벤트 루프 코디네이터: __consumer_offsets 파티션별 복제 상태 머신

현황 (Kafka 4.0+)

  • 서버 사이드 assignor (range, uniform) GA
  • 무중단 롤링 업그레이드 지원 (혼합 프로토콜)
  • Kafka Streams는 KIP-1071로 별도 프로토콜

Interval-Aware 캐싱 — Netflix Druid 최적화

롤링 윈도우 대시보드에서 반복 쿼리를 제거하는 캐싱 프록시:

  • Druid 쿼리를 1분 단위 버킷으로 분해, 캐시된 부분은 Cassandra에서 서빙
  • 지수적 TTL로 시간 경과에 따른 캐시 효율 극대화
  • 결과: 82% 부분 캐시 히트율, 33% Druid 쿼리 감소, 66% P90 레이턴시 개선

연관 개념


Source: Breaking the Microbatch Barrier, Volga - A Rust Rewrite of a Real-Time ML Engine, Zalando - Why We Ditched Flink Table API Joins - Cutting State by 75%, AutoMQ Shared Storage Architecture Deep Dive, Consistent Scalable Compaction for Real-Time Upserts in Apache Pinot, KIP-848 Next Generation Consumer Rebalance Protocol, Stop Answering the Same Question Twice