26장 - 전자 지갑
- 생성일
- 2024년 8월 31일 PM 11:11
1. 요구사항 정리#
- 결제 플랫폼은 전자 지갑 서비스를 제공하여 고객이 예치금을 넣어두고 이를 통해 사용할 수 있도록 한다.
mermaid
flowchart LR
은행 --> 결제시스템
결제시스템 --> 이커머스mermaid
flowchart LR
전자지갑A--전송-->전자지갑B- 전자 지갑 간 이체가 가능해야 함
- 1,000,000 TPS
- 99.99%의 안정성
- 트랙잭션이 가능해야 함
- 재현성이 있어야 함
1.1. 추정치 계산#
- 이번 설계의 목표는 단일 노드가 처리할 수 있는 트랜잭션의 수를 늘리는 것
| 노드당 TPS | 노드 수 |
|---|---|
| 100 | 20,000 |
| 1,000 | 2,000 |
| 10,000 | 200 |
2. 청사진 그리기#
2.1. 인메모리 샤드#
- 주키퍼와 레디스를 사용해서 높은 가용성과 안정성을 확보하자
- 레디스의 파티셔닝은 해쉬를 통해서 진행하자
- 주키퍼는 이런 파티션 구성 정보를 가지고 있도록 하자
2.2. 분산 트랜잭션#

- 샤딩된 데이터에서 원자적으로 연산을 수행하는 방법
- 우선 레디스를 RDB로 변경해야 한다.
2.2.1. 2단계 커밋#

- 저수준 방안으로 데이터베이스 자체에 의존하는 방안이다.
- 가장 일반적으로 사용되는 방법은 2PC다.
- 문제는 조정자가 SPOF가 될 수 있다.
- 또 다른 문제로는 2개의 데이터베이스가 락을 사용하기 때문에 성능이 별로다.
2.2.2. TC/C#
- TC/C(시도-확정/취소, Try-Confirm/Cancel)는 두 단계로 구성된 보상 트랙잰션
- 조정자는 모든 데이터베이스에 트랜잭션에 필요한 자원을 예약
- 모두 ‘예’ 응답시 조정자는 작업 확인을 요청
- 어느 하나라도 ‘아니요’응답 시 조정자는 모든 데이터베이스에 작업 취소를 요청함


- 2PC와 유사점이 많지만 다음의 차이점이 있다.
| 유형 | 첫 번째 단계 | 두 번째 단계 : 성공 | 두 번째 단계 : 실패 |
| 2PC | 로컬 내 트랜잭션이 완료되기 전 상태 | 모든 로컬 트랜잭션 커밋 | 모든 로컬 트랜잭션 롤백 |
| TC/C | 모든 로컬 트랜잭션이 커밋되거나 취소된 상태 | 필요한 경우 새 로컬 트랜잭션 생성 | 이미 커밋된 트랜잭션의 되돌림 (undo) |
- 즉 트랜잭션이 2번 생성되냐 한번 생성되느냐가 가장 큰 차이다.
2.2.3. 단계별 상태 테이블#
- TC/C 실행 중 서버가 다운되는 상황 방지를 위해 각 단계별 상태 정보를 트랜잭션 데이터베이스에 저장하자
- 같은 데이터베이스 내에 별도의 테이블을 만들어 위치시키는게 좋다.
2.3. 사가 (Saga)#
- 개념 소개
- 모든 연산은 순서대로 정렬된다.
- 각 연산은 자기 데이터베이스에 독립 트랜잭션으로 실행됨
- 연산은 첫 번째부터 마지막까지 순서대로 실행된다.
- 한 연산이 완료되면 다음 연산이 개시
- 도중에 실패하면 실패한 연산부터 맨 처음 연산까지 역순으로 보상 트랜잭션을 통해 롤백됨
- 연산 실행 순서 조율 방법
- 분산 조율 = 사가 분산 트랜잭션에 관련된 모든 서비스가 다른 서비스의 이벤트를 구독해 작업하는 방식
- 중앙 집중형 조율 = 하나의 조정자가 모든 서비스가 올바른 순서로 작업을 실행하도록 조율

- TC/C와 사가는 병렬이냐 순서대로 실행이냐에 따라서 실무에서 레이턴시 결정에 따라 선택 가능하다.
2.4. 이벤트소싱#
- 명령
- 명령은 외부에서 전달된, 의도가 명확한 요청
- A에서 C로 이체하라는 요청은 명령이다.
- 명령은 일반적으로 FIFO큐에 저장됨
- 이벤트
- 명령은 검증되기 전 팩트가 아님 = 유효하지 않을 수 있음
- 이벤트는 검증된 팩트임 = 송금 명령하더라도 돈이 없으면 이벤트가 발생하지 않음
- 하나의 명령에 여러 이벤트가 만들어질 수 있음
- 이벤트도 일반적으로 FIFO큐에 저장됨
- 상태
- 이벤트가 적용될 때 변경되는 내용
- 키-값 저장소로 저장됨
- 상태기게
- 상태기계는 이벤트 소싱 프로세스를 구동함
- 명령의 휴효성을 검증하고 이벤트를 생성
- 이벤트를 적용하여 상태를 갱신

- 재현성
- 시작부터 계정 잔액을 알고 싶은 시점까지 이벤트를 재생하면 알 수 있다.
- 이벤트 이력에서 계정 잔액을 다시 계산해 보면 잔액이 정확한지 확인 수 있다.
- 새로운 코드에 동일한 이벤트 이력을 입력으로 주고 같은 결과가 나오는지 보면 된다.
2.6. 명령-질의 책임 분리 (CQRS)#

- 이벤트 소싱 프레임워크의 클라이언트가 상태를 알도록 할 방법이 필요하다.
- 명령-질의 책임 분리
- 이벤트를 수신하는 외부 주체가 직접 상태를 재구축 할 수 있다.
- CQRS에서는 상태 기록을 담당하는 상태 기계는 하나고, 읽기 전용 상태기계는 여러개 있을 수 있다.
3. 실제 설계#
3.1. 고성능 이벤트 소싱#
- 파일 기반의 명령/이벤트 기록
- 카프카 대신 로컬 디스크 저장을 고민해볼 수 있다.
- 추가는 순차적으로 매우 빠르다.
- OS는 보통 순차적 읽기 및 쓰기에 연산이 엄청나게 최적화되어 있어 HDD에서도 잘 작동한다.
- 최근 명령을 메모리에 캐싱하는 방법도 고려해볼 수 있다.
- 구체적으로 mmap을 사용하면 로컬 디스크에 쓰는 동시에 최근 데이터는 메모리에 자동으로 캐시할 수 있다.
- 파일 기반 상태
- RocksDB를 사용하여 파일 기반의 데이터베이스를 구축한다.
- 이는 LSM 자료구조를 사용하기 때문에 이는 쓰기 작업에 최적화 되어있다.
- 스냅숏
- 주기적으로 상태 기계를 멈추고 현재 상태를 파일에 저장한다면 시간을 절약할 수 있다.
- 스냅숏은 과거 특정 시점을 상태로 변경이 불가능하다.
- 과거 스냅숏의 날짜를 보고 이후 이벤트부터 처리를 시작하면 된다.
- 스냅숏은 거대한 이진파일이며 일반적으로 HDFS와 같은 저장소에 보관한다.
