Ⅴ. 데이터 처리
5.1 독립적인 배포와 데이터 공유
ch4에서 샘뉴먼이 말한 일반적인 마이크로서비스의 제안항목
- 마이크로서비스는 서로 느슨하게 결합되어 독립적으로 배포 가능해야 함
- 마이크로서비스 내부의 기능 관련해서 높은 응집력을 가져야 한다
느슨한 결합의 또다른 중요한 측면
- 느슨한 결합
- 서비스가 느슨하게 결합 되면 서비스를 변경해도 다른 서비스에 영향을 주지 않음
- 마이크로서비스 주요 이점 : 규머가 커지더라도 안정성/품질의 조화와 함께 속도↑
- 마이크로서비스간의 조정을 제거하거나 최소한으로 줄임으로써 달성 가능
- 또다른 측면 : 독립적인 배포 가능성
- 시스템의 다른 부분이나 다른 마이크로서비스의 변경/배포 필요 없이 독립적으로 서비스를 변경/배포 가능해야한다
- 마이크로서비스 배포가 의존성을 가지면? 릴리즈가 복잡하고 취약해짐, 전체 시스템의 속도와 안전성 모두 손상 위험
마이크로서비스 독립적 배포 불가한 이유와 데이터 ?
- 데이터 맥락에서 가장 일반적인 문제는?
- 여러 마이크로서비스가 데이터 공간을 공동으로 소유하는 것
- 데이터를 공동으로 소유하면 느슨한 결합, 독립적인 코드 배포가 어려움
5.2 데이터를 포함하는 마이크로서비스
opening
모놀리스의 문제
- 데이터 공유
- 대규모 시스템의 다양한 모듈화된 부분을 통합하닌 기본 패턴
- 모놀리스 아키텍처에서 주로 사용
- 기존 레거시보다 더 모듈화된 SOA 또한 데이터 공동 소유가 관행
- 모놀리스는 모듈화가 아니다? 틀린 사실
- 사람들은 모놀리스가 모듈화 되지 않고 구성요소로 나눈 큰 덩어리라고 생각
- 개발자들은 작은 코드 베이스로 나눈 것이 관리와 효율이 좋다는 것을 알고 있음
- 모놀리스의 문제 : 분리된 모듈을 독립적 배포가 불가능하다는 문제
- ex)SOA설계도 데이터 결합 때문에 독립적 배포 불가 → 더 큰 규모로 발전하지 못함
문제예시
여러 마이크로서비스가 DB에서 고객 테이블 소유권을 공유하고 있음
(소유권 : 공유된 테이블에서 서로 다른 마이크로서비스가 데이터를 읽고 수정할 수 있음을 의미)
- 가정:
항공편 검색
마이크로서비스가 공유 테이블의 열중 하나의 필드 유형을 변경해야 함- 정수→ 부동소수점등으로 변경한다면?
- 동일 테이블에 엑세스하고 특정타입에 의존하는
예약
이나항공편 추적
마이크로서비스에 문제 생길 수 있음 - 문제해결?
항공편 검색
마이크로서비스의 필요로 데이터 모델 변경할 때마다예약
,항공편 추적
마이크로서비스 관련 코드 변경 필요- 또한 하나의 변경으로 연관된 모든 마이크로서비스를 다시 배포해야 한다
- 데이터 계증 변경의 파급 효과
- 여러 구성요소가 공통으로 데이터 소유시 매우 일반적
- 다양한 서비스간 결합 유발 → 독립적 배포 가능성 문제 유발
마이크로서비스에서는 독립적 배포 가능성이 핵심 가치
- 결과적으로 데이터 공유가 금지된다
- db의 데이터 공간에 대한 공통 책임 허용 X
- 마이크로서비스는 자체 데이터를 소유(포함)해야 한다
- db의 데이터셋을 어떤 마이크로서비스가 소유하는지 명확히 알 수 있어야 한다
자체 데이터 포함에 대한 중요한 고려사항
5.2.1 데이터 포함으로 DB 클러스터 수가 급증해서는 안된다
- 복잡한 어플리케이션 구축 : 다양한 종류의 DB 사용
- DB의 데이터셋(RDBMS의 TABLE등의)은 여러 마이크로서비스가 공동 소유해선 안됨
- 데이터 독립성은 복잡한 DB를 별도로 구축해야 함을 의미하지는 않는다
- ‘마이크로서비스는 데이터를 소유한다’의 개념을 명확하게 이해가 필요
- 마이크로서비스는 물리적 db 클러스터를 공유할 수 있다
- 여러 마이크로 서비스가 동일한 논리적인 테이블 공간과 동일 데이터를 수정하지 않는다면?
물리적 클러스터를 공유하는 것은 실제로 문제가 되지 않는다
- 데이터관리의 독립성은 스트림을 넘지 않는 것
- 필요한 경우 마이크로서비스를 가져와 다른 DB 설치와 함께 배포 가능해야 한다
- 비용을 들여 다른 DB를 사용해서 각 서비스를 배포할 필요는 없음
- 비용은 중요 고려 대상이다
- 단순성 또한 중요 고려대상
- 여러 마이크로서비스가 동일한 데이터 공간에 접근하고 수정하지 않으면 데이터 독립성 조건 만족
5.2.2 데이터 포함 및 데이터 위임 패턴
모놀리식을 작은 모듈화를 하면 마이크로 서비스인가? 절대 아님
- 모놀리식의 N계층 아키텍처도 ‘마이크로’수준의 작은 모듈로 나눌 수 있으며 각 모듈은 네트워크 서비스로 배포될 수 있음
- 마이크로서비스는 구성요소가 조정제거를 목표로 모듈화 되고 느슨하게 결합되며 느슨하게 배포가 가능해야 한다
- 임의로 분할되고 느슨하게 결합되지 않은 시스템은 마이크로서비스 아키텍처가 될 수 없음
- 앞에서 봤던 이미지를 다시 살펴보자
- 문제점
- 마이크로서비스의 데이터 포함 원칙을 떠올려보자
- 이러한 데이터 설계는 3개의 서비스가 동일 데이터 공간을 공유
- 이는 독립적인 배포 가능성을 손상 시킴 → MSA의 큰 문제
- 해결책 : 위임 서비스 뒤에 공유 데이터를 숨기는 간단한 기술
항공편 인벤토리
서비스 : 항공편 정보와 관련된 모든 것을 처리할 권한을 가진 서비스 선언- 항공편에 대한 정보가 필요하거나 업데이트해야하는 모든 서비스는
항공편 인벤토리
서비스에서 적절한 엔드포인트를 호출해야 한다- 유연한 조회 API를 구현했다면 이전의
항공편 검색
서비스는항공편 인벤토리
의 기능중 일부가 된다 - 중요 포인트 :
예약
과항공편 추적
서비스가 직접 항공편 테이블 접근하는 문제를 해결 - 앞으로 항공편에 대한 모든 정보는
항공편 인벤토리
서비스를 통해서 얻어야 한다 - 예시1:
예약 서비스
가 항공편에 충분한 좌석이 남아있는지 알아야 할 경우- DB의 항공편 테이블을 직접 쿼리하는 대신 해당 쿼리를 항공편 인벤토리 서비스에 전달하여 조회 가능
- 예시2:
항공편 추적
서비스가 비행중인 비행기의 위치를 알고 업데이트 해야할 경우- DB 테이블에 직접 접근 하는 대신 항공 인벤토리 서비스를 통해 작업 수행
항공편 인벤토리
서비스가 데이터를 숨기고 캡슐화하여 데이터를 감싸는 대리인 역할 가능- 여러 서비스가 동일한 데이터 테이블을 공유하지 않음
- 반드시 하나의 서비스를 대리인으로 변환할 필요는 없다
- 이러한 패턴에서 꼭 그럴 필요는 없으며 다른 방법도 가능
- 위에서는
항공편 검색
서비스를항공편 인벤토리
서비스로 전환 하고 항공편 테이블을 캡슐화 항공편 인벤토리
서비스를 새로 개발 후항공편 검색
서비스가 이를 참조하게 할 수도 있음
이제 모든 문제가 끝?
- 위임을 활용한 방식은 우아하며 다양한 경우에 활용 가능
- 불행히도 모든 데이터 공유를 이러한 방법으로 해결할 수 없음
- 위임 패턴이 모든 시나리오에 적용된다고 믿는 것은 매우 순진한 생각
- 분석, 데이터 감사, 머신러닝과 같은 시스템의 경우 마이크로서비스 경계를 넘어 데이터 접근/수정 필요
- 전통적인 데이터 트랜잭션 또한 공유 데이터 Lock 필요
데이터 중복을 사용하여 독립 문제 해결
분석, 데이터 감사, 머신러닝과 같은 시스템
문제점
- 데이터 수정없이 분산된 데이터에 대한 읽기 전용 접근이 필요
- 마이크로서비스 경계를 넘어선 접근에 해당
일반적인 해결책
- 모든 마이크로서비스의 데이터 세트를 공유된 공간으로 복사
- Data Lake : 공유된 공간을 일반적으로 일컽는 말
- 데이터는 이동이 아니라 복사된다는 점에 유의
- DataLake는 데이터 쿼리가 가능한 읽기 전용 공간
- 마이크로서비스는 여전히 해당 데이터셋 권한 가지며 기본 소유자 역할
- 마이크로서비스가 관련된 데이터를 데이터 스트리밍하면 데이터가 축적되어 쿼리 준비
- DataLake와 같은 집계 인덱스에는 이러한 데이터를 운영적으로 업데이트하지 않는 것이 중요
(→ 데이터의 무결성, 명확성을 위해서) - DataLake는 기록용 DB로 취급되면 안되며, 단순히 참조된 데이터 스토어다
SOR(System of Record)
- 마이크로서비스 같은 SOR 데이터 저장소에서 데이터가 스트리밍되면?
→ 집계 데이터는 쿼리에 최적화된 방식으로 인덱싱 된다
신뢰할 수 있는 메시징 인프라
SOR에서 DataLake로 스트리밍→ 일반적으로 신뢰할 수 있는 메시징 인프라 사용
- IBM MQ, RabbitMQ 등
- Kafka : 가장 인기 있는 솔루션
- Apache Pulsar : 빠르게 성장하고 있는 신규 솔루션
DataLake와 공유 데이터 인덱스 → 다양한 읽기 전용 사용 사례 해결
- 하지만 분산 데이터가 읽기 전용이 아니라면?
5.2.4 분산 트랜잭션과 실패 처리
가정 케이스(항공편 예약)
- 지불 프로세스 시작시에 좌석 예약이 가능했으나 중간에 다른 사람이 예약 완료 한 경우
- 현업의 경우 언제든 이러한 문제 발생 가능 - 전체 프로세스 롤백 처리
- 신용카드 결제는 취소까지 시간이 걸리더라도 최소 마일리지 포인트를 사용한 경우 마일리지 포인트도 환불되어야 함
모놀로식 어플리케이션 : ACID특성의 DB 트랜잭션으로 안전하게 수행
- 위의 케이스 경우 DB 트랜잭션으로 안전하게 관리됨
- 장애가 생겨도 ACID 특성의 DB 트랜잭션으로 안전하게 수행
- ACID
- Atomicity(원자성)
- All or nothing
- 트랜잭션 단계는 모두 실행되거나, 모두 실행되지 않아야 한다
- Consistency(일관성)
- 트랜잭션은 시스템을 하나의 유효한 상태에서 다른 유효한 상태로 전환해야 한다
- Isolation(격리성)
- 트랜잭션의 병렬 실행은 각 트랜잭션을 순차적으로 실행한 것과 동일한 결과야 함
- Durability(지속성)
- 트랜잭션이 커밋(완전히 실행)되면 장애가 발생해도 데이터 손실이 없어야 한다
- Atomicity(원자성)
ACID 트랜잭션은 기존 데이터 관리의 대표적 방법
- 복잡한 시스템에서 실패는 항상 존재한다
- 오류를 완전히 피하는 것은 불가능한 일
- 실패를 고려해서 자동으로 복구할 수 있는 방법이 최선
- ACID 구현 시스템→ 실패 항상 발생한다고 가정 → 장배 복구 가능 방식으로 설계
ACID트랜잭션의 한계 : 분산시스템에 적합한 솔루션이 아님
- ACID 트랜잭션은 배타적 잠금(exclusive lock)에 의존
- 마이크로서비스는 데이터를 소유, 다른 마이크로서비스의 데이터 수정을 허용X
- 위 사항을 고려하면 ACID 잠금은 마이크로서비스 시스템에서 구현이 어려움(혹은 높은 비용)
- 분산 시스템에는 더 적합한 패턴이 필요
Saga를 사용한 분산 트랜잭션
- 1987년 헥터 가르시아 몰리나Héctor García-Molina에 의해 처음 소개됨
- 2012년 클레멘트 베스터스의 블로그 게시물 분산 시스템을 위한 효과적인 솔루션을 통해 대중화됨
사가를 사용하면?
- ACID 트랜잭션과 직접적으로 동일하지 않음
- 사가는 롤백시 시스템이 초기 상태로 반드시 돌아감을 보장하지 않음
- 오히려 시스템이 부분 완료된 트랜잭션의 실행취소를 수용할수 있는 수준을 반영하는 ‘합리적인 상태’에 도달해야함
- 트랜잭션의 모든 단계: 각 단계에 요청된 작업 수행 + 롤백시 실행해야 하는 보상 작업 정의
- 보상 작업 : routing slip에 등록되며 다음 단계로 넘어감
- 만약 이후 단계 실패시?
- routing sliop에 정의된 모든 보상 작업 실행 → 수정사항 되돌림, 시스템을 ‘합리적으로 보상된 상태’로 만듬
합리적인 상태
케이스) 좌석예약
예약
서비스 예약 실패시?- 이전의
알림
과결제
의 보상 작업도 같이 호출된다 결제
의 보상 조치 : 고객에게 돈을 환불 하는 작업 → 결제 유형에 따라 즉시 환불이 아닐 수도 있음- 따라서 결제 시스템은 즉시 초기 단계로 돌아가지 않을 수 있다
- 하지만 결국에 고객은 돈을 돌려받을 수 있다
- ACID에선 고객이 트랜잭션 롤백 여부를 알 수 없지만 사가에서는 결제가 진행되고 취소됨을 인지 가능하다
- 이전의
알림
서비스 실패시?- 더 복잡해질 수 있음
- email, sms
- 회수가 불가능 → 보상 트랜잭션으로 회수 설정 불가
- 보상 트랜잭션 : 고객에게 예약 실패를 알리고 이전 메시지를 무시해야함을 전달하는 새로운 메시지 보내야함
- 역시 상황에 따라 합리적이지만 시스템을 초기상태로 되돌리지 않음
- 고객은 아무 메시지도 받지 않는 상태로 돌아가는게 아니라 2개의 메시지를 더 보게 됨
사가에서 이벤트의 순서는 유의미
- 사가의 이벤트 순서는 중요하며 신중히 구성되어야 한다
- 일반적으로 보상하기 어려운 단계를 트랜잭션 마지막 단계로 이동하는 것이 좋다
- 위 예시에서 (비지니스 규칙이 허용하는 경우)알림을 맨 끝으로 이동하면 수정 메시지를 크게 줄일 수 있음
- 이렇게 하면 트랜잭션이 알림을 전달하는 경우 이전 단계들이 성공했음을 알 수 있음
또?
위임 서비스, 데이터 레이크 사가
- 모두 강력한 패턴
- MSA의 많은 데이터 격리 문제 해결 가능
- 이 3가지 패턴이 해결할수 없는 문제들은 어떻게??
5.3 이벤트 소싱과 CQRS
관계형 데이터 모델링과 데이터 공유의 관계
- 여러 해결 방법을 써도 복잡한 시나리오에선 결국 원하는 수준의 데이터 격리와 느슨한 결합 지원이 불가능
- 예) 마이크로서비스가 소유한 데이터셋에서 ‘조인’을 생성하는 경우
- 관계형 데이터 모델링은 데이터의 정규화, 재사용, 참조와 같은 기본 원칙에 뿌리
- 즉 관계형 데이터 모델은 기본적으로 데이터 공유를 선호하는 경향
5.3.1 이벤트 소싱
이벤트 소싱
- 2005년 마틴 파울러에 의해 처음 언급
- 디자인 패턴 분야의 유명한 Greg Young의 2014년 세미나 발표를 통해 인기시작
- Greg Young이 말하는 이벤트 소싱
- 시스템 도메인 객체가 아닌 이벤트를 저장하는 것에 대한 데이터 모델링 접근 방식
- 이벤트 소싱 : ‘사실’과 ‘상태’(구조적 모델)을 저장하는 것
- ‘사실’ : 이벤트 발생의 대표적인 값을 의미
- ex) LA→NewYork의 이코노미 좌석이 20만원 이상되었다
- ‘상태’ : ‘사실’에서 파생된 값, 일시적
회계외 체스의 이벤트 소싱
회계와 체스를 통해 이벤트 소싱의 개념을 파악해보자
- 회계일지 (accounting journal)
- 고전적 이벤트 저장 방식 사례중 하나
- 회계사는 개별 거래를 기록하고 잔액은 모든 거래를 합산한 결과
- 회계사는 ‘상태’를 기록하지 않으며 각 거래 후 결과 잔액을 기록
- 체스
- 경기 기록시 보드에 있는 모든 말의 위치를 기록하지 않는다
- 개별적인 이동을 저장
- 보드의 상태는 현재까지 일어난 모든 이동의 합이다
- 특정 경기에 대한 모든 이동에 대한 로그가 있다면 게임 상태를 완전히 재현 가능
→ 실생활에서의 이벤트 소싱과 동일
이벤트 소싱 VS 관계형 모델링
- 기존 시스템(RDBMS, NoSQL, Document DB)의 접근 방식
- 일반적으로 어떠한 상태(state)를 저장
- 예) 항공편 이코노미 좌석의 현재 가격 → 어떤 상태를 저장
- 이벤트 소싱의 접근 방식
- 상태를 저장하지 않음
- 데이터 변경에 대한 사실을 저장
- 시스템의 현재 상태는 일련의 변경에서 계산된 파생값
예약시스템의 예시
- 기존 시스템의 관계형 데이터 모델
- 고객 계정 및 결재 방법과 1:N 관계를 맺는 고객 연락처 정보 테이블로 구성
- 각 고객 계정 레코드는 완료된 여행, 진행 중인 예약, 계정 관련 선호도를 가르킬 수 있음
- 세부 사항은 다를 수 있으나 대부분의 전통적인 유형과 유사
- 이벤트 소싱 데이터 모델
- 이벤트 소싱을 이용하면 위 그림과 같은 이벤트 시퀀스로 동일한 모델 설계 가능
- 시스템을 이끄는 이벤트를 확인 가능
- 고객 연락처 정보를 수집 → 개인 계정을 열고 지불 방법을 입력
- 여러번의 예약과 여행을 마친 고객은 비지니스 계정을 만들기로 결정
- 고객은 지불정보 입력후 새로운 계정으로 여행을 예약
- 몇가지 선호도 추가 하면 윗 그림과 동일한 상태가 됨
- 단순 상태 기반의 표현과 다름
- 현재 상태로 이어진 ‘사실’의 정확한 순서를 볼 수 있다
- 이 일련의 이벤트는 관계형 DB의 모델과 동일한 상태를 결과적으로 제공한다
- 이전과 다르게 보이지만 사실은 동일하며 오히려 더 균일해 보이기 까지 한다
- 다양한 엔티티 유형과 서로간의 관계에 대해 내릴 수 있는 의사결정이 훨씬 적다
- 어떤 면에서는 더 간단하다
- 다양한 비지니스 이벤트가 발생한다
- 그리고 이 이벤트의 파생물로 현재 상태를 계산할 수 있다
- 더 간단하고 예측 가능하며 다양한 엔티티 간 참조 관계가 없다
- 또한 각 유형의 이벤트는 서로 다른 마이크로서비스에서 소유 → 데이터 공유 회피 가능
- 예) 고객 인구 통계 마이크로서비스 :
입력된 고객정보
는 해당 시스템에 속하는 자연스러운 이벤트
- 예) 고객 인구 통계 마이크로서비스 :
이벤트는 어떻게 생겼?
이벤트 데이터 구조의 ‘모양’
- 고유한 식별자가 필요 : 예로 분산 시스템에서 전역적 고유함을 보장하는 UUID를 사용 가능
- 이벤트 유형 : 다른 이벤트와 착각하지 않도록
- 데이터 : 이벤트 유형과 관련된
1 | { |
기술적 특성에 대한 설계 결정
- 이벤트와 함께 작업할 경우 상당히 직관적
- 대부분의 작업: 비지니스 로직을 기반으로 이벤트의 도메인 관련 필드 올바르게 설명하는데 사용
- 관계형 접근방식의 ‘주관적이고 기술적인 테이블 형태와 관계 형성의 종류’가 훨씬 적다
projection
프로젝션
- 이벤트 소싱에서 현재와 같은 특정 시점의 상태를 계산하는 작업
- 이벤트 기반의 상태 제공, 매우 간단
- 프로젝션을 실행하려면 프로젝션 함수가 필요
- 프로젝션 함수 : 현재 상태와 새로운 이벤트를 기반으로 새로운 상태를 계산
- 예시
1
2
3
4
5
6
7
8
9
10
11
12// 항공권 가격에 대한 프로젝션 함수
function priceUp(state, event){
state.increasePrice(event.amount)
}
function priceDown(state, event){
state.decreasePrice(event.amount)
}
//특정 시점의 가격 : 관계된 모든 이벤트에 대한 프로젝션 함수 실행
let price = priceUp(priceUp(priceDown(s,e),e),e); - 관계형 모델의
UPDATE prices SET price=..
SQL 쿼리와 동일 - 현재 상태 : 현시점 까지 발생한 이벤트의
left fold
임을 알수 있음[함수형 프로그래밍]
이벤트 소싱을 사용하면?
- 현재 상태뿐 아니라 특정 시점의 상태를 계산 가능
- 과거 시점의 상태 값을 확인 가능한 정교한 분석이 가능
5.3.2 롤링 스냅샷으로 성능 향상
프로젝션은 비싸다
- 예) 은행잔고 같이 상태변화가 빈번히 발생
- 현재 잔고 확인할 때마다 처음부터 계산?
- 느리며 리소스 낭비, 또한 현재 상태 조회는 빠른 응답속도 필요
- 중간 값들을 저장하고 마지막 스냅샷부터 계산하면 ? 빠른 계산 가능
중간값 스냅샷은 일반적
- 적절한 스냅샷 시점은 어플리케이션 도메인 마다 다름
- 예) 은행 매월 말일 계정 잔액 스냅샷 → 잔고 확인시 전달 스냅샷부터 프로젝션 계산
롤링 스냅샷
- 이벤트 소싱에서 저장된 프로젝션
- 롤링 스냅샷, 프로젝션 구현 세부사항은 어플리케이션 마다 달라질 수 있음
- 예) 은행은 월,분기, 연말에 다양한 잔액 계산 → 월단위 롤링 스냅샷을 생성하는 것은 매우 적절
- 도메인 마다 자연스러운 시점을 찾아서 스냅샷을 정렬해야 한다
- 해당 챕터 뒤에서 CQRS 패턴을 사용해 롤링 스냅샷에서 캐시 상태 이상의 작업 수행 가능
5.3.3 이벤트 스토어
이벤트 스토어
- 비교적 단순한 시스템으로 다양한 데이터 스토리지 시스템을 사용해서 구현 가능
- 다양한 스토리지 예시
- 파일 시스템의 파일
- 아마존의 S3 버킷
- DB 스토리지
이벤트 스토어의 인터페이스 : 다음 3가지 기본 기능
- 새로운 이벤트를 저장하고 올바른 시퀀스를 할당하여 저장된 순서대로 이벤트 검색 가능
- 관심있는 이벤트에 대한 프로젝션을 생성한 구독자에게
알림을 생성하고 경쟁 소비자 패턴(competing-consumers)을 활성화하는 기능 - 조정 흐름을 위해 특정 유형의 이벤트 X 이후에 N개의 이벤트를 가져올 수 있는 기능
(예: 프로젝션이 손실/손상/의심되는 경우 다시 계산)
→ 따라서 본질적으로 이벤트 저장소의 기본 인터페이스는 다음 두가지 기능으로 구성된다1
2save(x)
getNAfterX()
소비자가 이벤트 구독 가능하게 하는 강력한 알림 시스템
강력한
의 의미 : 경쟁 소비자 패턴에 대한 적합성을 의미- 경쟁 소비자 패턴
- 어떤 시스템에서 이벤트 프로젝션을 구축하든
- 중복성/확장성을 위해 이벤트 수신하는 클라이언트 인스턴스가 여러개 필요할 수 있음
- 따라서 중요한 패턴
- 알림
- 데이터 손상으로 이어지는 이벤트 중복 방지 필요
→ 수신자의 단일 인스턴스에 한 번만 전달되어야 한다
- 데이터 손상으로 이어지는 이벤트 중복 방지 필요
- 두 가지 접근 방법
- 카프카와 같은 소비자에게 신뢰성 보장하는 메시지 큐 구현 보장
- 소비자가 HTTP endpoint를 콜백으로 등록 가능토록 허용
→ 새로운 이벤트에 대해 콜백 엔드포인트 호출하고 소비자 측의 LB가 작업 분배 처리
- 어느 쪽도 본질적으로 더 나은 접근 방식이 아님
- 하나는 푸시, 하나는 풀 기반. 수행하는 작업에 따라 적합한 방식을 선택하여 사용
- 샘플 구현
- 스켈레톤 이벤트 스투어에 대한 독자적인 참조 구현
- https://github.com/implementing-microservices/gevent-store
- 테스트 해보자 나중에..
강력한 프로젝션 위해선? CQRS 보안 패턴이 필요
5.3.4 명령과 쿼리의 분리
고급 이벤트 소싱 시스템에 대한 프로젝션 : 일반적으로 CQRS 패턴 사용해서 빌드
CQRS Pattern
- 시작 : 쿼리 시스템과 데이터 저장 시스템이 동일할 필요가 없다는 것에서 시작
숨어있는 진실
- 앞에서 말한 내용 : 이벤트 스토어는 구현이 쉽다
- 진실
save(x)
와getNAfterX()
함수가 해당 데이터에 대한 정교한 쿼리를 수행 불가능하다
- 예시: 지난 24시간 동안 승객이 업데이트한 모든 좌석의 예약을 조회하는 쿼리 → 실행 불가능
- 이벤트 저장소를 단순/집중적으로 유지하기 위해 위와 같은 유형의 쿼리는 이벤트 저장소에 구현되지 않음
- 이벤트 소싱 : 이벤트 로그를 신뢰할 수 있고 안정적으로 저장하는 문제만 해결해야 한다
- 고급 쿼리
- 이벤트 발생할 때마다 이벤트 저장소를 구독하는 다른 시스템에게 알리고
- 시스템은 필요한 방식으로 데이터를 쿼리하는 데 최적화된 인덱스 작성을 시작
CQRS의 기본 개념
→ 데이터 저장소, 데이터 소유권, 데이터 쿼리 가능성의 문제를 동일 시스템에서 해결 시도 않는 것
→ 이런 문제는 독립적으로 해결해야 함
이벤트 소싱 & CQRS 사용의 장점
- 가장 큰 장점 : 매우 세분화되고 느슨하게 결합된 구성 요소를 설계 가능
- 이벤트 소싱 : 한가지 유형의 이벤트 관리/ 단일리포트 실행가능한 아주 작은 마이크로서비스 생성 가능
- 목표에 잘 맞춰 사용시 MSA에서 한단계 높은 자체 세분화 달성 가능
- 서비스 경계에 걸쳐 데이터 조인등의 복잡한 경우에 마이크로서비스간 데이터 공유 방지 가능
이벤트 소싱 & CQRS 사용의 단점
- 가장 큰 단점 : 높은 복잡성
- 이벤트 소싱과 CQRS는 만병 통치약이 아니다
- 이벤트 소싱과 CQRS를 과도하게 사용하지 않도록 주의
- 이벤트 소싱과 CQRS는 구현하기 위한 복잡성이 있음 → 필요한 경우에만 사용해야 한다
- 전체 시스템에 대한 유일한 데이터 모델링 접근 방식으로 사용해서는 안됨
- 기존의 관계형 모델이 훨씬 간단/활용되는 사례가 여전히 많음
5.4 마이크로서비스 외의 이벤트 소싱과 CQRS 활용
이벤트 소싱과 CQRS
- 데이터 공유를 방지하고 MSA의 느슨한 결합 구현시 매우 유용하다
- 하지만 MSA 외에도 다양한 시스템에 활용될 수 있는 강력한 데이터 모델링 도구임
1] CAP 정리 시점으로 바라보기
CAP 정리
- 에릭 브루어(Eric Brewer)가 2000년 심포지엄에서 발표한 내용
- https://oreil.ly/hiQMB
- 내용 요약 : 분산 공유 데이터 시스템은 아래 3가지 특성중 2가지 특성만 가질 수 있다
- 일관성(Consistency) : 데이터의 최신 상태에 대한 단일성 보장
- 가용성(Availability) : 항상 데이터를 읽거나 업데이트 가능
- 분할 내성(partition tolerance) : 네트워크 파티션에도 정확한 데이터를 얻을 수 있음
- CAP의 모든 조합이 유효하지 않다
- 시간이 지남에 따라 CAP 모든 조합이 유효하지 않다는 것이 점점 명확해짐(https://oreil.ly/nHBoN)
- 분산 시스템의 경우
→ 네트워크 파티션 피할 수 없음
→ 분할 내성 고려
→ 일관성/가용성 모두 만족시킬 수 없음
→ 하지만 일관성/가용성이 꼭 필요하다면?!
CQRS가 출동한다면 어떨까?!
- CAP 정리: 데이터 공유를 사용하는 단일 시스템이 일관성/가용성/분할 내성의 모든 조건 만족이 불가능
- 하지만 CQRS 활용 → 여러 시스템 사용, 데이터 공유 최소화 → 이러면 이야기가 달라짐
- 이 경우 이벤트 스토어에서 일관성 우선순위 정하고, 쿼리 인덱스에서 가용성의 우선순위 정할 수 있음
- 쿼리 인덱스에 사용하는 시스템은 일관성 깨질 수 있음?
- 신뢰할 수 있는 소스가 아님
- 필요한 경우 이벤트 스토어에서 다시 인덱싱 가능
2] 감사 가능성(auditability)과 관련하여 바라보기
관계형 데이터 모델을 사용할 경우 in-place 업데이트
- 예) 고객의 주소/전화번호가 올바르지 않으면? 해당 테이블에 바로 업데이트
- 나중에 고객이 이러한 기록에 이의를 제기하면?
- 관계형 모델 → 히스토리가 없음 → 복구할 방법이 없음
- 이벤트 소싱 → 모든 변경에 대한 기록이 안전하게 보존 → 과거의 값과 업데이트 이력 확인 가능
- 다른 목소리
- “Splunk나 ELK같은 시스템을 사용하면 모든 변경 사항을 로깅할 수 있어요~”
- 로깅와 이벤트 소싱은 다르다. 절대 다르다
- 아키텍처의 the source of truth(진실의 근원)이 되는 시스템은 무엇인가?
- 로그와 현재 상태가 일치하지 않으면 어떤 것을 ‘신뢰’할 것인가
- 이벤트 소싱
- 상태 : 계산된(computed)값 → 더 정확하다
-Splunk 로그 - 일부 버그를 찾기 위해 두번씩 확인해도 진실의 근원이 관계형 모델일 가능성이 높음
- 상태 : 계산된(computed)값 → 더 정확하다
- 이벤트 소싱
- 신뢰할 수 있는 이벤트 로그가 진실의 근원이라면?
- 데이터 모델링 접근 방식으로 이벤트 소싱을 사용하는 것
- 그렇지 않으면 아무리 많은 로그를 생성해도 이벤트 소싱이 아니다