스트림 처리 애플리케이션이 반복해서 취하는 모양들. 상태가 필요한지, 전체 정보가 필요한지에 따라 갈린다.
단일 이벤트 처리
가장 단순한 패턴이다. 각 이벤트를 개별적으로 처리해서 불필요한 이벤트를 걸러내거나 각 이벤트를 변환한다. 맵과 필터라고도 부른다.
상태가 필요 없다는 것이 이 패턴의 전부다. 각 이벤트가 독립적이므로 애플리케이션이 상태를 유지할 필요가 없고 따라서 복구할 것도 없다. 장애 복구와 부하 분산이 매우 쉬워서 다른 인스턴스가 그냥 넘겨받으면 된다. 간단한 프로듀서와 컨슈머만으로도 되므로 스트림 처리 프레임워크가 필요 없는 유일한 패턴이다.
로컬 상태를 쓰는 집계
윈도우 집계처럼 상태가 필요한 경우다. 그룹별 집계는 공유 상태가 아닌 로컬 상태로 해야 하고, 애플리케이션이 로컬 상태를 보유하는 순간 메모리와 영속성과 리밸런싱을 전부 고려해야 한다. 그 셋을 어떻게 다루는지는 상태를 가진 스트림 처리에 적었다.
다단계 처리와 리파티셔닝
그룹별 집계는 로컬 상태로 되는데 전체 정보가 필요한 결과는 안 된다. 가장 많이 팔린 상품 10개가 그런 경우다.
해법은 여러 단계로 나누는 것이다.
- 각 인스턴스가 로컬 상태만으로 할 수 있는 만큼 한다
- 결과를 파티션 하나짜리 새 토픽에 쓴다
- 하나의 인스턴스가 그것을 읽어 최종 결과를 낸다
2단계 토픽은 전체 정보를 담은 토픽보다 훨씬 작고 트래픽도 적으므로 단일 인스턴스로 충분히 처리된다. 맵리듀스의 리듀스 단계를 여러 번 거치는 것과 같은 구조다. 대부분의 프레임워크는 이 모든 단계를 하나의 애플리케이션에 담을 수 있게 해주고, 어느 인스턴스가 어느 단계를 수행할지는 프레임워크가 알아서 정한다.
재처리
두 가지 상황이 있고 대응이 다르다.
개선된 새 버전을 만든 경우에는 구버전이 쓰던 이벤트 스트림을 신버전이 읽어서 새로운 결과 스트림을 쓴다. 기존 결과를 교체하지 않고 한동안 두 결과를 비교한 뒤 시점을 정해 전환한다. 이때 신버전을 새 컨슈머 그룹으로 실행하고, 입력 토픽의 첫 오프셋부터 처리하게 설정하고, 신버전이 따라잡았을 때 클라이언트를 새 결과 스트림으로 전환한다.
버그를 고치고 다시 계산하는 경우에는 기존 애플리케이션을 초기화한다. 입력 스트림의 맨 처음부터 다시 처리하도록 되돌리고, 로컬 상태를 초기화하고, 기존 출력 스트림의 내용물도 지운다.
용량이 된다면 첫 번째 방식이 안전하다. 두 버전을 왔다갔다 할 수 있고, 결과를 비교할 수 있고, 정리 과정에서 중요 데이터를 유실할 위험이 없다.