How we built it: Real-time analytics for Stripe Billing
Quick Summary
Stripe는 Flink 기반 상태형 스트리밍, Spark 기반 과거 데이터 초기화, Pinot v2의 실시간 집계를 결합해 Billing 구독 분석의 평균 24시간 지연을 거의 모든 업데이트가 15분 이내 제공되는 수준으로 단축하면서 맞춤형 지표 정의와 역사적 일관성을 유지했다.
🖼️ 인포그래픽
🖼️ 4컷 인포그래픽
💡 한 줄 요약
Stripe는 Flink 기반 상태형 스트리밍, Spark 기반 과거 데이터 초기화, Pinot v2의 실시간 집계를 결합해 Billing 구독 분석의 평균 24시간 지연을 거의 모든 업데이트가 15분 이내 제공되는 수준으로 단축하면서 맞춤형 지표 정의와 역사적 일관성을 유지했다.
📌 핵심 요약
- Stripe는 MRR 성장률, 이탈률, 체험 전환율 등의 구독 지표에 새로운 활동을 거의 실시간으로 반영하기 위해 기존의 평균 24시간 지연 배치 분석 시스템을 교체했다.
- 새 시스템은 구독·청구서 객체의 변경을 분석 이벤트로 변환하고, Apache Flink가 압축된 구독 이력을 상태로 보관하면서 새 이벤트에 따라 이를 증분 갱신한다.
- 장기 고객의 초기 Flink 상태는 동일한 스트리밍 변환 로직을 Apache Spark 작업으로 실행해 대규모 과거 이벤트를 병렬 처리하고 검증 가능한 플랫 파일로 생성했다.
- Apache Pinot v2의 윈도우 집계와 복잡한 조인 기능을 활용해 오프라인 사전 집계를 제거했으며, 운영 환경에서 300밀리초 미만의 쿼리 지연을 달성했다.
- 고객이 지표 정의를 변경하면 과거 데이터는 배치로 재계산하고 신규 이벤트는 기존 정의로 계속 처리·버퍼링한 뒤, Flink 상태를 교체하고 버퍼 이벤트를 재처리해 전체 기간의 일관성을 보장한다.
🧩 주요 포인트
- 전체 이력 재분석 방식에서 상태 기반 증분 처리로 전환해 구독 업데이트마다 과거 거래를 다시 계산할 필요를 없애고 데이터 반영 시간을 크게 줄였다.
- 오프라인 사전 집계를 Pinot v2의 쿼리 시점 윈도우 집계로 대체해 실시간 데이터까지 포함하면서도 대시보드의 빠른 탐색성과 다양한 분석 차원을 유지했다.
- 과거 재계산과 신규 이벤트 스트리밍을 병행하고 마지막에 상태를 원자적으로 전환함으로써 지표 정의가 바뀌는 동안에도 사용자에게 일관된 분석 결과를 제공했다.
🧠 상세 정리
1. 실시간 Billing 분석이 필요해진 배경
Stripe가 인용한 최근 설문에서는 전 세계 비즈니스 리더의 84%가 향후 1~2년 동안 가격을 신속하게 조정하는 역량이 핵심 경쟁 우위가 될 것이라고 답했다. Stripe 고객들도 빠르게 움직이려면 새로운 고객 행동 패턴이 나타나는 즉시 이를 파악해야 하며, 그러려면 품질 높은 청구 데이터가 실시간에 가깝게 제공되어야 한다고 요구했다. 이에 Stripe는 대시보드에서 MRR 성장률, 이탈률, 체험 전환율 등 구독 지표를 탐색하고 시각화할 때 새로운 구독 활동을 빠르게 반영하는 스트리밍 분석 시스템을 개발했다. 목표는 최신 동향을 조기에 파악할 수 있는 가시성을 제공하는 동시에, 사업과 지표 정의가 바뀌더라도 과거 데이터의 정확성과 일관성을 유지하는 것이었다.
2. 기존 배치 처리의 한계와 세 가지 과제
이전 시스템은 구독 업데이트가 분석에 반영되기까지 평균 24시간이 걸리는 전통적인 배치 처리 방식에 의존했다. Stripe는 이를 교체하기 위해 구독 상태를 실시간으로 갱신하는 데이터 구조, 같은 시간 범위 안에서 대시보드 집계를 갱신하는 질의 체계, 고객이 지표 정의를 바꾸더라도 실시간·과거 분석이 흔들리지 않는 처리 절차라는 세 가지 문제를 함께 해결해야 했다. 구독은 단일 결제만으로 의미를 설명하기 어렵고, 가입 이후의 납부 이력과 현재 상태를 결합해야 정확히 이해할 수 있다는 특성이 있었다. 따라서 단순히 이벤트 전달 속도만 높이는 것으로는 부족했고, 전체 이력에 의존하는 계산 방식부터 사용자 질의와 설정 변경 절차까지 분석 경로 전반을 다시 설계해야 했다.
3. 구독 이력을 상태로 관리하는 Flink 파이프라인
기존 방식은 각 구독의 현재 상태를 계산할 때 처음부터 관련 데이터를 모두 다시 분석했기 때문에 정해진 주기의 배치 실행이 필요했고, 아키텍처 한계상 이를 실시간에 가까운 빈도로 수행하기 어려웠다. 새 파이프라인은 구독 및 청구서 객체의 업데이트를 분석 이벤트로 변환하고, Apache Flink가 각 구독의 이력을 고도로 압축한 상태로 저장하도록 구성됐다. 새 분석 이벤트가 도착하면 Flink는 전체 이력을 재실행하지 않고 기존 상태에 해당 변경분만 증분 반영한다. 예를 들어 6월의 20달러 구독 결제는 1월 가입 이후의 모든 결제와 함께 다시 평가되는 대신, Flink 상태가 보관하는 지속적인 원장에 추가된다. 이 구조를 통해 Stripe는 거의 모든 업데이트를 15분 이내에 제공할 수 있는 낮은 지연 시간의 구독 분석 기반을 마련했다.
4. Spark를 이용한 초기 상태 생성과 오프라인 검증
상태형 스트리밍 구조를 도입하더라도 오랫동안 Stripe를 사용해 온 고객의 초기 Flink 상태를 만드는 일은 별도의 난제로 남았다. 초기 상태를 스트리밍 방식만으로 구축하려면 수십억 건의 과거 이벤트를 정확한 순서대로 다시 재생해야 했기 때문이다. Stripe는 스트리밍 변환 로직을 Apache Spark 데이터 작업으로도 실행할 수 있는 맞춤형 도구를 개발해, 대규모 과거 이벤트를 병렬로 처리하고 결과를 검증 가능한 플랫 파일로 출력했다. 이 작업의 결과는 Flink가 사용할 초기 상태를 효율적으로 생성하는 데 쓰였으며, 동시에 데이터 검증과 내보내기에 활용되는 중복 오프라인 파이프라인에도 공급됐다. 같은 변환 논리를 스트리밍 처리와 대규모 과거 처리에 적용함으로써 초기 적재 문제와 지속적인 검증 요구를 함께 해결한 것이다.
5. 실시간 집계를 가로막은 대시보드 질의 문제
Stripe 대시보드는 사용자가 데이터를 필터링하고 그룹화하며 세부 항목으로 내려갈 때 처리 시간을 의식하지 않을 정도로 유연하고 빠르게 응답하도록 설계됐다. 그러나 MRR의 시간별 변화를 표시하려면 사용자가 지정한 기간의 모든 시점에서 각 구독이 어떤 상태였는지 분석해야 하므로 내부 계산량은 매우 크다. 초기 Billing 분석 시스템은 온라인 분석 처리 데이터베이스로 Apache Pinot을 사용했지만, 당시에는 예약된 배치 작업에서 구독 데이터를 미리 집계하는 것이 현실적인 해법이었다. 실시간 분석을 위해서는 방금 원장에 새 데이터가 추가된 구독까지 포함하도록 쿼리 시점에 현재와 과거 상태를 분석해야 했고, 동시에 기존 대시보드가 제공하던 빠른 응답성도 유지해야 했다. 따라서 데이터 수집 지연을 줄인 뒤에도 오프라인 사전 집계를 제거할 수 있는 새로운 질의 엔진이 필요했다.
6. Pinot v2의 윈도우 집계와 운영 성능
해결책은 오픈소스 Pinot 유지관리자들이 여러 날짜 구간을 대상으로 윈도우 집계 쿼리를 수행할 수 있는 새로운 v2 엔진을 공개하면서 마련됐다. 이 엔진은 데이터를 여러 시간 창으로 나누고 각 구간에서 합계나 평균 등의 집계를 수행하므로, 오프라인 사전 집계 없이도 시간에 따른 MRR을 한 번에 계산할 수 있었다. 또한 더 복잡한 데이터 조인을 지원해 데이터 공백 채우기, 통화 변환, 사용자 정의 질의 차원과 같은 대시보드 기능도 실시간 경로에 포함할 수 있게 됐다. Stripe는 Pinot 유지관리자들과 협력해 Stripe 규모의 사용자 대상 환경에 처음 적용되는 이 엔진을 시험하고 운영에 도입했다. 그 결과 대부분의 업데이트는 1분보다 훨씬 짧은 시간 안에 처리되고 거의 모두 15분 이내 사용자에게 제공되며, 운영 쿼리 지연은 300밀리초 미만으로 유지됐다.
7. 맞춤형 지표 정의 변경과 데이터 일관성
MRR처럼 단순해 보이는 지표도 기업마다 정의가 다르기 때문에 Stripe Billing은 고객이 MRR과 기타 지표의 계산 공식을 조정할 수 있도록 지원해 왔다. 예를 들어 일회성 쿠폰을 MRR에서 제외하도록 정의를 바꾸면 2017년부터 Stripe를 이용한 고객의 수년치 데이터 전체를 새 공식으로 재처리해야 하며, 이 작업에는 수 시간이 걸릴 수 있다. Stripe는 정의 변경 시 과거 데이터를 새 정의에 맞추는 배치 작업을 시작하는 한편, 신규 이벤트는 기존 정의로 계속 실시간 처리하고 Flink 애플리케이션 메모리에 임시 버퍼링한다. 과거 재처리가 끝나면 재계산된 데이터로 Flink 상태를 교체하고, 저장해 둔 신규 이벤트를 갱신된 이력 위에서 다시 처리한다. 이후 대시보드를 완전히 갱신된 데이터로 전환하고 기존 정의를 사용하는 처리를 종료해, 변경 과정에서도 과거부터 현재까지 하나의 일관된 결과만 표시한다.
8. 달성한 결과와 후속 개선 방향
새 시스템은 고객에게 구독 데이터 업데이트를 실시간에 가깝게 제공하는 것과, 그 데이터를 실제 업무에 맞는 방식으로 질의하고 정렬할 수 있게 하는 것이라는 두 가지 목표를 중심으로 구축됐다. Flink의 상태형 이벤트 처리, Spark의 과거 데이터 병렬 처리, Pinot v2의 쿼리 시점 집계를 결합해 수집부터 집계와 사용자 표시까지 이어지는 전체 분석 경로를 개편했다. 대시보드는 지표 정의가 변경되거나 과거 재처리가 진행되는 동안에도 비활성화되거나 서로 충돌하는 값을 보여주지 않고 계속 사용할 수 있다. Stripe는 앞으로도 신뢰성과 정확성을 유지하면서 데이터 지연을 더 낮추고, 사용량 기반 지표와 고객 지역·코호트 필터를 포함한 데이터 및 질의 차원을 추가할 계획이다. 후속 작업 역시 데이터 최신성과 사용자가 선택할 수 있는 분석 방식의 범위를 함께 확장하는 데 초점을 둔다.
🧾 핵심 주장 / 시사점
- 실시간 분석은 이벤트 수집만 빠르게 만드는 문제가 아니라 상태 갱신, 과거 데이터 초기화, 쿼리 시점 집계, 사용자 화면 전환까지 전체 경로의 지연을 함께 줄여야 달성된다.
- 과거 전체를 새 정의로 재계산하는 배치 처리와 신규 이벤트를 다루는 스트리밍 처리를 병행하면, 장기간의 이력 수정 중에도 최신 데이터 유입과 일관된 사용자 경험을 유지할 수 있다.
- 동일한 변환 로직을 Flink의 지속적 처리와 Spark의 병렬 과거 처리에 적용하고 검증 가능한 결과물을 생성한 구조는 실시간 경로와 오프라인 검증 경로를 연결하는 핵심 장치다.
✅ 액션 아이템
- 구독 분석 파이프라인을 전체 이력 재계산 대신 Flink 상태 기반 증분 갱신 구조로 재설계할지 범위를 정한다.
- 장기 고객 초기 상태는 Spark로 동일 변환 로직을 병렬 처리해 검증 가능한 플랫 파일로 생성하는 절차를 점검한다.
- 지표 정의 변경 시 과거 배치 재계산·신규 이벤트 버퍼링·Flink 상태 원자 교체 순서로 일관성 보장 기준을 정의한다.
❓ 열린 질문
- 평균 24시간 지연을 거의 모든 업데이트 15분 이내로 줄인 구조에서 허용 가능한 잔여 지연 기준은 무엇인가?
- Pinot v2 쿼리 시점 윈도우 집계가 오프라인 사전 집계 없이도 300ms 미만 지연을 유지하는 조건은 무엇인가?
- 맞춤형 지표 정의 변경 중 버퍼 재처리 구간에서 역사적 일관성을 깨는 경계 사례는 어디인가?