Search

9주차 - Kafka를 통한 분산 트랜잭션 구현 방안

생성일
2025/04/22 14:35
태그

이벤트 브로커 방식으로 분산 트랜잭션을 처리하기

MSA에서는 API를 호출하여 처리하는 것도 가능하지만, 이벤트 브로커를 통한 메세지(이벤트)를 넘기는 방식으로도 처리가 가능하다. 그렇다면 언제는 API를 호출하는 것이 좋고, 이벤트 브로커 방식은 언제 적합할까?
각 요청의 특징을 비교해보면 알 수 있을 것 같다.
API 직접 호출
동기 방식(응답을 받을 때까지 대기)
요청에 대한 결과를 즉시 확인 가능
이벤트 전달
비동기 방식(Pub/Sub)
느슨한 결합
이벤트 기반의 아키텍처를 통해 여러 서비스가 동일한 이벤트에 반응하는 것이 가능
최종적 결과 보장
이러한 특성을 생각해보면, 각 방식이 적합한 경우는 다음과 같다.
API 직접 호출
결제 처리와 같이 결과를 즉시 확인하여 사용자에게 응답으로 내보내야하는 경우
데이터 조회와 같이 결과를 통해 이후 로직을 수행하는 경우
서비스 예시
예약 로직의 좌석 선점 로직(좌석 선점을 성공했는지 결과로 사용자에게 응답하기)
결제 로직의 포인트 차감, 결제 생성(포인트 차감 및 결제 생성 성공 여부에 따라 사용자에게 응답하기)
이벤트 전달
핵심 비즈니스 로직이 아니여서, 약간의 지연이 허용되고 결과적 일관성만 보장되면 되는 경우
서비스 예시
데이터플랫폼에 예약/결제 정보 전송
결제 로직의 포인트 차감 및 결제 생성 이후 로직(포인트 차감과 결제 생성에 성공한다면, 이후 좌석 및 예약의 만료 기한을 삭제하는 로직과 데이터 플랫폼에 전달하는 로직은 결과적 일관성만 유지하기)

서비스 로직에 적용 고민하기

예약 요청

1.
콘서트 서비스에 좌석 선점 요청하기
사용자가 해당 좌석을 확보했는지 결과를 즉시 확인할 필요가 있음(성공 시 결제 / 실패 시 다른 좌석의 예약)
동시성 제어가 중요함(분산 락 적용)
API 호출로 처리
2.
유저 정보 조회하기
데이터 조회 후 해당 데이터를 통해 예약 진행하기 때문에 결과를 즉시 확인할 필요가 있음
API 호출로 처리
3.
예약 서비스에 예약 생성 요청하기
코레오그래피 요청 진입 지점
4.
데이터플랫폼에 예약 내역 전송
데이터 수집이 목적이기 때문에 지연 허용
이벤트로 처리
고려사항
데이터플랫폼에 예약 내역 전송이 장애로 인해 지연 혹은 실패하는 경우
전송 재시도 정책
재시도 횟수 초과 시까지 실패하면 데드레터 큐를 통한 알럿

결제 요청

1.
유저 서비스에 포인트 차감 요청하기
사용자 응답에 결제 가능 여부를 반환하기 위해서는 즉시 확인할 필요가 있음(포인트가 적으면 결제 실패)
API 호출로 처리
2.
결제 서비스에 결제 생성 요청하기
결제 중복 생성 등의 상황 시 에러 발생 방지를 위해 즉시 확인 후 사용자에게 응답할 필요가 있음
API 호출로 처리
3.
예약 서비스에 예약 만료 기한 해제 요청하기
결제 프로세스 진행 중이므로 약간의 지연 허용
이벤트로 처리
4.
좌석 서비스에 선점 만료 기한 해제 요청하기
결제 프로세스 진행 중이므로 약간의 지연 허용
이벤트로 처리
5.
데이터플랫폼에 결제 내역 전송
데이터 수집이 목적이기 때문에 지연 허용
이벤트로 처리 (최후순위)
6.
보상 트랜잭션
포인트 차감 롤백
결제 생성 롤백
예약 만료 기한 해제 롤백
좌석 선점 만료 기한 해제 롤백
고려사항
콘서트/예약 서비스 장애로 만료 기한 해제에 실패하는 경우
재시도 정책
재시도 횟수 초과 시까지 실패 시 보상 트랜잭션 & 로그
보상 트랜잭션의 실패
마찬가지로 n회 재시도, 그래도 안되면 데드레터 큐를 통한 알럿

보상 트랜잭션이 기존 트랜잭션 성공 전에 발생하면?

예약 서비스에서 예약 만료기한 해제 진행 중 보상 트랜잭션이 들어오는 상황이 발생하는 경우
조치
좌석과 예약 update 로직에 분산 락 걸어두었기 때문에 문제 X
빛석범님의 도메인 관점에서 상태 변경 중에 다른 요청으로 동시성 문제가 발생하는 것을 방지해야한다는 피드백이 이미 적용되어 있음으로 인해, 문제 발생도 전에 해결
동시성 제어를 통해 예약 만료기한 해제 성공 이후 보상 트랜잭션으로 다시 만료기한 설정될 것으로 예상
동시성 제어가 없다면…?
outbox 상태 관리를 통해, 상태가 맞지 않으면 메세지 재소비하도록…

서비스의 장애 시 Kafka 설정을 통한 메세지 재소비(Retry)의 의미가 있을까

각 장애 상황에 따른 재소비의 의미 찾기
일시적 시스템 장애 → 복구 가능한 상황이며 다시 시도했을 때 성공할 수 있기 때문에 Retry가 의미를 가진다
네트워크 일시 단절 / readTimeout
DB 커넥션 풀 부족
시스템 자원(CPU, 메모리)의 일시적 부족
비즈니스 예외 → 비즈니스 상황과 정책에 따라 다르지만, 일반적으로 Retry의 의미가 없을 가능성이 크다
낙관적 락 : 중복 로직을 방지하기 위해 일부러 Exception이 발생하도록 만들수도
서비스 로직 내 exception 던지기 : 비즈니스에 따라 다르지만, 다시 시도해도 동일할 가능성이 높다
비즈니스 로직 오류(버그) / 잘못된 데이터(검증)
데이터 정합성 문제 → 다시 시도하면 성공할 가능성이 높아 Retry가 의미를 가진다
선행 트랜잭션의 미완료(일시적 데이터 불일치)
동시성 문제로 인한 락 충돌
인프라 장애 → 복구 불가능하기 때문에 Retry가 의미 없다
DB 커넥션 에러
DB read timeout → 경우에 따라 복구 가능
스토리지(DB) 용량 부족
특히 분산 트랜잭션에서는 일시적 데이터 불일치가 발생할 가능성이 있기 때문에, Retry를 시도해보는 것이 좋을 것 같다.

최종 플로우차트

예약 요청

결제 요청

번외(결제 요청을 코레오그래피 방식으로 처리한다면?)