데이터플로우 그래프
- 데이터플로우 프로그램
- 데이터 처리 연산 사이로 데이터가 어떻게 흐르는지(flow) 기술
- 방향성 그래프(directed graph)
- 데이터플로우 프로그램을 표현하는 일반적 방법
- 노드(node)
- 연산자(operator)라 부르고 계산 표현
- 연산자 : 데이터플로우 애플리케이션의 기본 기능 단위
- 입력으로 들어온 데이터를 소비하고 어떤 계산을 수행한 후 그 결과를 다음처리에서 사용할 수 있도록 출력으로 내보냄
- 데이터 소스(data source) : 입력이 없는 연산자
- 싱크(sink) : 출력이 없는 연산자
- 논리적(logical) 데이터플로우 그래프 : 개념적 수준에서 바라본 계산 로직의 모습
- 그림 2-1. 지속적으로 해시 태그를 카운트하는 논리적인 데이터플로우 그래프
- 논리적 데이터플로우 그래프를 실행하려면 논리적 데이터플로우 그래프를 물리적 데이터플로우로 변환해야 함
- 물리적 데이터플로우 : 프로그램을 어떻게 실행해야 하는지 자세히 지정 (ex. 분산 처리 엔진 사용 시 각 연산자는 각각 다른 물리적 장비에서 돌아가는 여러 병렬 태스크가 됨)
- 그림 2-2. 해시 태그를 카운트하는 물리적 데이터플로우 그래프(노드는 태스크 표현)
- '해시 태그 추출'과 '카운트' 연산자는 두 개의 병렬 연산 태스크를 갖고 있으며, 각 태스크는 입력 데이터 분할해 연산 수행
- 에지(edge)
데이터플로우 프로그래밍 소개
- 데이터플로우 프로그램
- 데이터 처리 연산 사이로 데이터가 어떻게 흐르는지(flow) 기술
- 방향성 그래프(directed graph)
- 데이터플로우 프로그램을 표현하는 일반적 방법
- 노드(node)
- 연산자(operator)라 부르고 계산 표현
- 연산자 : 데이터플로우 애플리케이션의 기본 기능 단위
- 입력으로 들어온 데이터를 소비하고 어떤 계산을 수행한 후 그 결과를 다음처리에서 사용할 수 있도록 출력으로 내보냄
- 데이터 소스(data source) : 입력이 없는 연산자
- 싱크(sink) : 출력이 없는 연산자
- 논리적(logical) 데이터플로우 그래프 : 개념적 수준에서 바라본 계산 로직의 모습
- 그림 2-1. 지속적으로 해시 태그를 카운트하는 논리적인 데이터플로우 그래프
- 데이터 병렬화와 태스크 병렬화
- 데이터플로우 그래프의 병렬화 방식
- 데이터 병렬화(data parallelism) : 동일 연산 수행하는 태스크를 각 입력 파티션에 할당해 병렬로 실행
- 대용량 데이터 처리의 부하를 여러 장비로 분산시킬 수 있어서 매우 유용
- 태스크 병렬화(task parallelism) : 같거나 다른 데이터에 여러 연산을 수행하는 태스크 할당
- 클러스터의 계산 자원을 좀 더 효율적으로 사용 가능
- 데이터 교환 전략
- 데이터 교환 전략(data exchange strategy) : 물리적 데이터플로우 그래프에서 어떤 태스크로 레코드를 할당할지 정의
- 그림 2-3. 데이터 교환 전략
- 전진(Forward) 전략 : 한 태스크로 들어온 데이터를 다른 태스크쪽으로 내보냄
- 두 태스크가 동일한 물리적 장비에 할당된다면(보통 태스크 스케줄러가 결정), 이 교환 전략에서는 네트워크 통신 없이도 데이터 교환 가능
- 브로드캐스트(Broadcast) 전략 : 모든 레코드를 연산자의 모든 병렬 태스크로 내보냄
- 데이터를 복제하고 네트워크 연산자를 사용하므로 비용이 다소 비싼 편
- 키 기반(Key-based) 전략 : 데이터를 키 기준으로 모아 같은 키 값을 가진 데이터는 같은 태스크에 모이도록 보장
- 그림 2-2의 '해시태그 추출'의 출력데이터를 같은 키(해시태그)를 가진 데이터끼리 모으면 카운트 연산자 태스크는 각 해시태그의 등장 횟수 정확히 계산 가능
- 랜덤(Random) 전략 : 각 계산 태스크의 부하를 균등하게 분산시키고자 모든 연산자 태스크로 데이터를 균등하게 분배
병렬 스트림 처리
데이터 스트림 : 무한 이벤트 순서열(unbounded sequence of events)
데이터 스트림의 이벤트는 모니터링 데이터, 센서 측정, 신용카드 거래, 기상 관측소의 관찰 기록, 온라인 사용자 상호작용, 웹 검색등을 표현 가능
지연과 처리율
- 성능 측정할 때의 요소
- 배치 어플리케이션
- 보통 잡의 총 실행 시간이나 사용 중인 처리 엔진이 입력을 읽고 결과를 쓰는 데 얼마나 많은 시간을 소모하는지 측정
- 스트리밍 애플리케이션
- 지속적으로 실행되고 입력이 무한이므로 전체 실행시간은 중요하지 않음
- 대신 빠른 속도로 이벤트를 처리하면서 최대한 빠르게 계산 결과 제공해야 함
- 성능 요건 - 지연(latency)과 처리율(throughput)
- 지연
- 이벤트를 처리하는 데 얼마나 많은 시간이 걸리는지 나타냄
- 기본적으로 이벤트 수신과 이벤트 처리 결과(효과)를 출력해서 볼 때까지의 경과 시간을 의미
- 데이터 스트리밍에서 지연은 밀리초 같은 시간으로 측정. 애플리케이션에 따라 평균 지연 최대 지연, 또는 퍼센타일(percentile) 지연에 관심을 가질 수 있음
- ex. 평균 지연 10밀리초 : 이벤트를 처리하는 데 평균 10밀리초가 걸림
- ex. 95번째 퍼센타일 지연 값이 10밀리초 : 이벤트의 95%를 처리하는데 10밀리 초가 걸림
- 평균값은 처리 지연의 실제 분포값을 가려 문제 숨길 수 있음
- 짧은 지연 보장은 사기 감지(fraud detection), 시스템 알람, 네트워크 모니터링, 엄격한 서비스 수준 협약(service level agreements)과 같은 여러 스트리밍 애플리케이션에서 아주 중요
- 짧은 지연은 스트림 처리의 핵심 특징이며, 짧은 지연 덕분에 실시간(real-time) 애플리케이션의 구현 가능
- 아파치 플링크 같은 최신 스트림 처리기는 몇 밀리초 단위의 지연 제공 가능
- 이벤트가 시스템에 도착하자마자 처리, 지연은 각 이벤트를 처리할 때 소모한 실제 작업 시간 반영
- 처리율
- 시스템의 처리량을 측정하는 메트릭. 단위 시간당 얼마나 많은 이벤트를 처리할 수 있는지 알려줌
- 단위 시간당 처리한 이벤트 개수나 또는 연산 호출 횟수 측정
- 처리율은 이벤트 도착 비율에 의존한다는 것을 명심해야함.(낮은 처리율 =/= 나쁜 성능)
- 최대 처리율 : 시스템이 최대 부하에 다다랐을 때 성능의 한계
- 배압(backpressure) : 시스템이 처리할 수 있는 비율보다 높게 데이터를 계속 받으면 버퍼링도 불가능하게 돼 데이터를 잃을 수 있는 상황
- 지연과 처리율
- 지연과 처리율은 독립적인 메트릭이 아님
- 지연과 처리율에 영향을 주는 요소
- 부하가 없다면 지연은 최적 - 처리율 증가
- 이벤트를 처리하는 데 걸리는 시간 증가 - 처리율 감소
- 짧은 지연과 높은 성능 가지려면 (짧은 지연이 처리율도 높일 수 있음)
- 이벤트 처리 속도 증가 (최대 부하에서 처리율 높아짐)
- 스트림 처리 파이프라인의 병렬화 (같은 시간에 더 많은 이벤트 처리 가능)
데이터 스트림 연산
- 스트림 처리 엔진은 보통 인입, 변환, 출력에서 사용할 수 있는 여러 기본 연산 제공
- 스트리밍 애플리케이션 로직을 구현할 때 데이터플로우 처리 그래프에서 이런 연산 조합해 사용 가능
- 상태가 없는 연산
- 어떤 내부 상태도 유지하지 않기 때문에 이벤트에 대한 처리가 과거의 다른 이벤트에 의존하지 않으며 아무런 이력도 유지하지 않음
- 이벤트는 서로 독립적이고 도착하는 순서에 의존하지 않으므로 병렬화 쉬움
- 장애 발생 시 단순히 마지막에 처리했던 지점부터 재시작해 처리 이어나감
- 상태가 있는 연산
- 이전에 받은 이벤트의 정보 유지. 새로 들어온 이벤트는 이 상태를 갱신하고, 미래의 이벤트는 이 상태를 이벤트 처리 로직에서 사용
- 상태가 있는 스트림 처리 애플리케이션은 상태를 효과적으로 분할하고 장애가 발생할 때 안정적으로 복구해야 하므로 병렬화와 내고장성을 유지하는 것이 큰 도전 과제
- 내고장성 : 시스템의 일부가 고장이 나도 전체에는 영향을 주지 않고, 항상 시스템의 정상 작동을 유지하는 능력
- 데이터 인입(ingestion)과 방출(egress)
- 스트림 처리기가 외부 시스템과 통신하는 연산
- 데이터 인입
- 외부 소스에서 원시(raw) 데이터를 가져와 스트림 처리에 맞는 형식으로 변환하는 연산
- 데이터 소스(data source) : 데이터를 가져오는 로직을 구현한 연산자. TCP 소켓, 파일, 카프카 토픽, 또는 센서 데이터에서 데이터 가져올 수 있음
- 데이터 방출
- 데이터를 수신할 외부 시스템의 소비 형태에 맞게 데이터 형식을 변환해 내보내는 연산
- 데이터 싱크(data sink) : 데이터 방출을 수행하는 연산자. 파일, 데이터베이스, 메시지 큐, 모니터링 서비스의 엔드포인트 등
- 변환 연산(transformation)
- 단일 경로(single path)연산으로, 각 이벤트 독립적으로 처리
- 이벤트를 하나씩 소비하면서 각 이벤트 데이터에 변환 연산을 적용한 후 변환된 이벤트를 새 출력 스트림으로 내보냄
- 변환 로직은 연산자 안이나 사용자 정의 함수로 구현해 제공 가능
- 그림 2-4. 각 인입 이벤트를 더 어둡게 변환하는 함수를 포함하는 스트림 연산
- 연산자는 여러 입력스트림에서 이벤트 받아 여러 출력 스트림 생성 가능
- 연산자는 하나의 스트림을 여러 스트림으로 나누거나 여러 스트림을 하나의 스트림으로 변환하는 등 데이터플로우의 그래프 구조를 변경할 수 있음
- 롤링 집계 연산(Rolling aggregation)
- 합계(sum), 최솟값(min), 최댓값(max)과 같은 집계 연산으로, 이벤트가 들어올 때마다 계속해서 상태 갱신
- 집계 연산은 상태가 있으며 새로 들어온 이벤트와 현재 상태를 결합해 집계 값을 계산
- 집계 값을 효과적으로 계산하기 위해 결합 법칙과 교환 법칙이 성립해야 함 (성립 안할 시 모든 이벤트 스트림 이력 저장 필요)
- 그림 2-5. 롤링 최솟값 집계 연산
- 현재 최솟값 유지 및 인입되는 이벤트마다 최솟값을 계산해 현재 상태 갱신
- 윈도우 연산
- 결과를 계산할 때 일정량의 이벤트를 모아 보관하고 있어야 하는 연산 존재 (스트림 조인(join)이나 평균 함수와 같은 전체 집계 연산(holistic aggregate))
- 무한 스트림에서 효과적으로 수행하려면 각 연산은 자신의 상태에 보관 가능한 수준으로 데이터양 제한 필요
- 실제 값(집계와 같은 값)을 구하는 것 외에도 윈도우를 이용하면 스트림에서 의미 있는 질의 할 수 있음
- 가장 최근의 데이터만 관심있거나 단일 집계로 줄이면 시간에 따라 변하는 데이터의 다양한 정보 잃을 수 있음
- 무한 이벤트 스트림에서 버킷(bucket)이라 부르는 이벤트의 유한 집합을 지속적으로 생성하고, 이 유한집합에 어떤 연산을 수행할 수 있게 해줌
- 윈도우 정책
- 언제 새 버킷을 생성하고, 어떤 이벤트를 어떤 버킷에 할당하며, 언제 버킷의 내용을 평가할 것인지 결정.
- 윈도우 내용 평가 시점은 트리거(trigger)조건을 기반으로 결정. 트리거 조건 충족 시 버킷의 내용을 평가 함수로 전달해 각 버킷 이벤트에 연산 로직 적용
- 시간(ex. 마지막 5초 안에 받은 이벤트)이나 개수(ex.마지막 100개 이벤트), 또는 데이터의 속성을 기준으로 삼기 가능
- 일반적인 윈도우 시멘틱(semantic) 종류
- 텀블링 윈도우(Tumbling Window)
- 고정 길이에 서로 겹치지 않는(중첩되지 않는) 버킷으로 이벤트 할당.
- 이벤트가 윈도우 경계를 넘으면 처리하고자 버킷의 모든 이벤트를 평가 함수로 보냄
- 개수 기반(count-based) 텀블링 윈도우
- 윈도우가 트리거링 되기 전에 얼마나 많은 이벤트를 모아야하는지를 정의
- 그림 2-6. 개수 기반 텀블링 윈도우
- 시간 기반(time-based) 텀블링 윈도우
- 이벤트를 어떤 버킷으로 버퍼링할지를 시간 간격으로 정의
- 그림 2-7. 시간 기반 텀블링 윈도우
- 슬라이딩 윈도우(Sliding Window)
- 서로 겹치는(중첩되는) 고정 길이의 버킷으로 이벤트 할당. 두 버킷에 포함되는 이벤트도 있을 수 있음
- 길이와 슬라이드(slide)로 정의. 슬라이드 값은 새로운 버킷이 생성될 때까지의 간격 정의
- 그림 2-8. 네 개의 이벤트 길이와 세 개의 이벤트 슬라이드로 정의한 개수 기반 슬라이딩 윈도우
- 세션 윈도우(Session Window)
- 텀블링이나 슬라이딩 윈도우로 적용할 수 없는 현실 세계 시나리오에서 유용
- ex. 온라인 사용자 행동 분석 애플리케이션 - 동일 기간에 발생한 사용자 활동이나 세션 대상으로 이벤트 함께 모음
- 세션이 종료됐다고 판단할 수있는 비활동 시간을 세션 격차(session gap)라는 값으로 정의해서 이벤트들을 세션으로 그룹화
- 그림 2-9. 세션 윈도우
- 병렬 텀블링 윈도우
- 스트림을 여러 논리적 스트림으로 분할해서 병렬 윈도우(parallel window)로 정의 가능
- ex. 서로 다른 센서 장비에서 데이터를 받을 때 스트림을 센서 식별자로 그룹핑해서 윈도우 계산 적용
- 그림 2-10. 길이가 2인 개수 기반 병렬 텀블링 윈도우
- 스트림 처리의 두 가지 핵심 개념인 시간과 상태 관리에 밀접한 연관 있음
- 윈도우 연산의 진정한 가치는 빠른 분석 결과 이상
- 네트워크 통신 채널 같은 현실 세계 시스템은 완벽하지 않음, 스트림 데이터는 지연되거나 순서가 바뀌어 도착 가능
- 이런 상황에서 스트리밍 처리가 정확하고 결정적인 결과를 어떻게 생성하는지 반드시 이해해야 함
- 이벤트 발생순으로 이벤트를 처리하는 애플리케이션은 이벤트 발생순으로 이력을 따라가며 이벤트 처리해야 함
- 오프라인 분석이나 이벤트 시간 여행 분석(event time travel analysis) 가능해짐
- 장애 대비 상태 보호 필요 - 모든 윈도우 종류는 결과 생성 전 데이터 버퍼링 필요
- 스트리밍 애플리케이션은 상태 관리 꼭 필요
- 장애 상황에서 상태를 안정적으로 복구해 어떤 일이 벌어지더라도 스트리밍 애플리케이션이 정확한 결과를 낼 수 있다는 확신 필요
시간 시멘틱
- 스트리밍 처리에서 1분
- 매분 어떤 결과를 지속적으로 계산하는 스트리밍 애플리케이션을 가정해보자
- 이 스트리밍 애플리케이션 문맥에서 1분이 의미하는 것은 실제 무엇일까?
- 지하철에서 온라인 게임 플레이 시 네트워크가 끊겼다 안끊겼다 하는 상황 가정
- 그림 2-11. 지하철에서 플레이한 온라인 모바일 게임 이벤트를 받는 애플리케이션은 네트워크가 끊길 때 시간 차이를 경험하게 됨. 그러나 이벤트는 핸드폰에 버퍼링돼 있고 네트워크가 다시 연결되면 애플리케이션으로 버퍼링한 이벤트를 보낸다.
- 이벤트를 받은 시간이 아닌, 이벤트가 실제 발생한 시간이 연산자 시멘틱에 어떤 영향을 미치는지 보여주는 단순 시나리오
- 1분 안의 이벤트양을 정의하는 것은 처리 시간이 아닌 데이터 자체의 실제 시간
- 처리 시간(processing time)이나 이벤트 시간(event time)을 이용해 연산 수행
- 처리 시간
- 스트림 처리 연산자가 실행 중인 장비의 시계에서 측정한 로컬 시간
- 로컬 시간을 기준으로 일정 시간 동안 윈도우 연산자에 도달한 모든 이벤트는 처리 시간 윈도우(processing-time window)안으로 들어감
- 그림 2-12. 앨리스 핸드폰 네트워크가 끊긴 후에도 처리 시간 윈도우의 시간은 계속 흘러감
- 이벤트 시간
- 이벤트가 실제 발생한 시간. 이벤트 내용 안에 포함된 타임스탬프(timestamp)를 기반으로 함
- 타임스탬프는 보통 스트리밍 처리 파이프라인에 도착하기 전에(ex. 이벤트 생성 시간) 생성돼 이벤트 데이터 안에 존재
- 이벤트 시간 윈도우는 이벤트의 일부가 지연되더라도 발생했던 일을 제대로 반영
- 그림 2-13. 이벤트 시간은 윈도우 안에 이벤트를 정확히 넣는다. 이 윈도우는 발생한 이벤트를 제대로 반영
-
- 처리 결과를 처리 속도와 완전히 분리. 연산의 예측이 가능하고 결과는 결정적
- 스트림을 얼마나 빨리 처리하고 이벤트가 연산자에 언제 도착하든 간에 이벤트 시간 윈도우는 항상 동일한 결과 생성
- 데이터의 순서가 바뀌더라도 결과의 정확성 보장
- 재생(replay)가능한 스트림 로그와 결합하면 시간이 항상 동일한 것을 보장하므로(타임스탬프 결정성) 과거 이벤트도 빠르게 되돌려 볼 수 있음
- 과거의 이력 데이터도 마치 실제시간에 발생한 것처럼 분석 가능
- 현재 시간까지 빠르게 따라잡으면 완전히 동일한 프로그램 로직으로 실시간 애플리케이션 계속 이어나갈 수 있음
- 워터마크
- 이벤트가 더 지연되지 않고 도착할 것이라고 확신할 수 있는 시점을 가리키는 메트릭으로, 전체 진행 시간을 나타냄
- 스트리밍 시스템에 현재 이벤트 시간을 알려주는 논리적인 시계 제공
- 순서가 바뀐 이벤트를 처리하는 이벤트 윈도우와 연산자 모두에게 꼭 필요함
- 연산자가 워터마크를 수신하면 일정 기간동안 발생한 모든 이벤트의 시간이 감지됐다는 것을 알게되고 수신한 이벤트를 대상으로 계산을 시작하거나 이벤트 정리
- 결과의 신뢰성과 지연사이의 균형을 맞출 수 있는 설정 제공
- 너무 빠듯하게 발생시키면 짧은 지연 보장 가능, 낮은 신뢰성
- 워터마크를 먼저 도착한 이벤트의 시간에 가깝게 맞춘다는 것을 의미.
- 워터마크 이후 늦게 도착한 이벤트를 처리하는 별도 코드 제공해야 함
- 너무 느슨하게 생성 시 높은 신뢰성, 불필요한 처리 지연
- 첫 번째 이벤트의 시간보다 늦게 워터마크를 발생시켜 늦게 도착하는 이벤트도 가능한 많이 같은 윈도우에 포함시킴
- 워터마크를 사용자가 정의하든 자동으로 생성하든 분산 시스템에서 뒤처진 작업이 존재하는 한 전체 진행 상황을 완전하게 추적하는 것 쉽지 않음
- 워터마크보다 늦게 도착한 이벤트 처리 방법
- 애플리케이션의 요구 사항에 따라 워터마크보다 늦게 도착한 이벤트 무시 가능
- 이전 결과를 보정하는 데이터로 사용 가능
- 처리 시간과 이벤트 시간
- 이벤트 시간으로 모든 문제를 해결할 수 있는데 왜 처리 시간도 고민해야 할까?
- 처리 시간이 쓸만할 때
- 정확성보다 속도가 더 중요한 애플리케이션에서는 처리 시간이 유용
- 늦게 도착한 이벤트나 이벤트 순서 고려하지 않을 때 윈도우는 단순히 이벤트를 버퍼링, 지정 윈도우에 이벤트 도착하는 즉시 계산 동작
- 정확도가 크게 중요하지 않은 실시간 보고서를 주기적으로 생성해야 할 때
- 이벤트가 도착하는 대로 집계해서 보여주는 실시간 모니터링 대시보드 애플리케이션
- 처리 시간 윈도우도 스트림 자체의 특성을 충분히 표현 가능
- 정전 감지하고자 스트림 애플리케이션이 스트림에서 초당 얼마나 많은 이벤트 발생하는지 관찰 가능
- 처리 시간은 짧은 지연을 제공하지만 결과는 처리 속도에 따라 다르고 결정적이지 않음
- 이벤트 시간은 결정적 결과를 보장하고, 늦게 도착하거나 순서가 바뀐 이벤트도 처리할 수 있게 해줌
상태와 일관성 모델
- 상태(state) : 데이터 처리 어디에나 존재. 거의 모든 계산에서 필수.
- 함수는 일정 기간이나 일정 개수의 이벤트를 대상으로 상태(ex. 집계, 패턴 감지)를 누적해 결과 생성
- 상태가 있는 연산자는 수신한 이벤트와 내부 상태를 사용해 연산 결과를 생성
- ex. 롤링 집계 연산
- 이 연산자는 내부 상태로 현재까지 합계 값을 보관하고 새로운 이벤트를 받을 때마다 이 값 갱신
- ex. '높은 온도' 이벤트 감지 후 '연기' 이벤트가 10분 안에 발생할 것을 감지할 때 알람 보내는 연산자 가정
- '연기' 이벤트가 도착하거나 10분이 지날 때까지 '높은 온도' 이벤트를 내부 상태에 보관해야 함
- 상태의 중요성은 배치 처리 시스템으로 무한 데이터셋을 분석하는 것과 비교하면 더 명확해짐
- 배치 처리 시스템
- 작은 배치를 대상으로 잡(job)을 반복 스케줄링. 어떤 잡이 종료되면 결과를 영구적인 저장소로 전송하고 모든 연산자의 상태 사라짐
- 다음 배치 처리에 스케줄돼 있는 잡은 이전 잡의 상태 접근 불가
- 데이터베이스 같은 외부 시스템에 상태 관리를 위임해 문제 해결
- 스트림 잡에서 모든 이벤트는 상태에 접근 가능, 이 상태를 프로그래밍 모델의 일급 시민(first-class citizen)으로 노출 가능
- 추가 지연 시간이 발생할 수는 있지만 스트림 상태를 안전하게 관리하기 위해 외부 시스템 사용 가능
- 스트림 연산자는 무한 데이터를 처리하므로 내부 상태가 무한으로 커지지 않도록 조심해야 함
- 연산자는 지금까지 받은 이벤트들을 요약 정보나 개요(synopsis)같은 형태로 관리해 상태의 크기를 제한하는 것이 일반적
- 카운트, 합계, 현재까지 받은 이벤트들에 대한 샘플, 윈도우 버퍼, 사용자 정의 데이터 구조를 사용
- 상태가 있는 연산자를 사용하기 위한 극복 대상 도전 과제
- 상태 관리(State management)
- 시스템은 상태 관리를 효율적으로 해야 하고 동시에 상태를 갱신할 때 상태를 보호
- 상태 분할(State partitioning)
- 병렬로 처리할 때 처리 결과가 상태와 입력 이벤트 모두에 의존함으로 복잡해짐
- 키(key)로 상태 분할 가능, 각 파티션 별로 상태 관리 가능
- ex. 센서 측정하고 이 측정 값을 이벤트로 저장하는 스트림 처리 시 분할 상태를 사용해 센서별 독립적인 상태 유지 가능
- 상태 복구(State recovery)
- 상태를 복구하고 장애 시에도 정확한 결과를 내보내야 함
태스크 실패
- 스트리밍 잡에서 연산자 상태는 매우 중요하고 장애에 대비해 보호해야 함
- 장애 발생 시 상태 잃어버리면 복구 후 결과 부정확
- 스트리밍 잡은 오랜 시간 동안 실행되므로 상태는 며칠이나 몇달동안 수집한 것일 수 있음
- 장애로 유실한 상태 재생산하려 과거 입력을 모두 재처리하는 것은 매우 비용이 많이 들면서 많은 시간 소모
- 실재 환경에서 병렬 태스크를 수백 개 실행하는 것이 일반적이며 각 태스크는 언제나 실패할 수 있음
- 태스크 실패
- 태스크란 입력 스트림의 각 이벤트를 대상으로 아래의 절차들을 수행하는 하나의 처리 단계
- 이벤트를 수신해 로컬 버퍼에 저장
- 경우에 따라 내부 상태 갱신
- 출력 레코드 생산
- 실패는 위과정 어디에서나 발생 가능하며, 시스템은 장애 시나리오마다 어떻게 동작할지를 명확하게 정의해야 함
- 배치 처리 시나리오에서는 배치 잡을 처음부터 다시 시작할 수 있기 때문에 어떤 이벤트도 유실되지 않고, 상태도 처음부터 다시 완전하게 만들 수 있음
- 스트리밍 시스템은 실패가 발생할 때 결과를 보장할 수 있는 여러 방식 정의
결과 보장
- 스트림 처리기 내부 상태의 일관성을 의미
- 결과 보장의 관심사는 실패 복구 후 애플리케이션 코드가 바라보는 상태 값
- 애플리케이션 상태 일관성 보장은 출력의 일관성 보장과 같지 않음
- 결과를 한번 싱크로 보내면 싱크 시스템이 트랜잭션을 지원하지 않는 이상 결과의 정확성을 보장하기 어려움
- 최대 한번(at-most-once)
- 각 이벤트를 최대 한번 처리하는 것을 보장하는 낮은 수준의 보장 방법
- 이벤트는 날아가고 결과의 정확성 보장을 위해 아무것도 하는 것이 없음
- 무보장(no guarantee)이라고도 함
- 대략적인 결과와 지연 시간 최소화에만 관심이 있다면 좋은 선택
- 최소 한 번(at-least-once)
- 모든 이벤트를 처리하지만 그중 일부는 한 번 이상 처리할 수 있음
- 애플리케이션이 결과의 완결만 중요시한다면 중복 처리는 수용 가능한 조건
- 결과의 정확성 보장하려면 소스나 어떤 버퍼에서 이벤트를 재생할 방법을 갖고 있어야 함
- 지속성 이벤트 로그는 모든 이벤트를 신뢰할 수 있는 저장소에 저장하므로 태스크가 실패하더라도 이벤트 재생 가능
- 파이프라인에 있는 태스크들이 이벤트를 모두 처리했다는 것을 이벤트 로그가 인식할 때 까지 모든 이벤트를 버퍼에 저장
- 이벤트 로그는 이벤트가 모두 처리됐다고 인식하는 시점이 되면 해당 이벤트를 이벤트 로그에서 삭제 가능
- 정확히 한번(exactly-once)
- 단지 이벤트 유실이 없다는 것뿐만 아니라 이벤트마다 정확히 한 번씩만 내부 상태를 갱신
- 마치 실패가 한 번도 없었던 것처럼 애플리케이션이 정확한 결과를 제공하리란 것 의미
- 가장 엄격한 보장 방식이고 구현이 쉽지 않음
- 최소 한 번 보장이 필요하고, 따라서 데이터 재생 기능 필수
- 스트림 처리기는 내부 상태 일관성 보장해야 함
- 장애 복구 후 어떤 이벤트가 상태에 이미 반영됐는지 알아야 한다는 것 의미
- 트랜잭션 업데이트 방식 사용 가능하나 부가적인 성능 저하 발생 가능
- 플링크는 경량 스냅샷(lightweight snapshot) 메커니즘을 사용해 정확히 한 번 결과 보장
- 단대단 정확히 한 번
- 단대단(end-to-end) 보장은 전체 데이터 처리 파이프라인에 걸친 결과의 정확성 의미
- 각 컴포넌트가 각자 상태를 보장하는 방법을 완벽히 제공하더라도 전체 파이프라인의 단대장 보장 제공 쉽지 않음
- 가끔 약한 상태 보장 방식으로 강한 상태 보장 얻을 수 있음
- ex. 스트림 처리가 최댓값이나 최솟값 같은 멱등적(idempotent)연산을 수행할 때
- 최소 한 번(at-least-once) 보장으로 정확히 한 번(exactly-once)보장 얻을 수있음
- 멱등적 - 어떤 값을 여러 번 반복 연산해도 결과 같음