O0oO0Oo / netty-reservation-service

트랜잭션, 동시성을 공부하기 위한 토이 프로젝트입니다.
0 stars 0 forks source link

docs: refactoring transaction logic for orchestration saga pattern #20

Closed O0oO0Oo closed 1 month ago

O0oO0Oo commented 1 month ago

이슈 개요

기존 스프링 부트 트랜잭션 로직은 직렬적으로 실행되지만, 병렬적이거나 비동기로 실행될 경우 차이가 발생할 수 있다. 사가 패턴 적용을 위해 기존 트랜잭션 로직을 재정렬할 필요가 있다.

image

재현 단계

1. business

register item
[ find business ] --> [ check item condition ] --> [ create item ] 

reservation 의 트랜잭션 순서

Reservation 생성 시 트랜잭션들은 병렬적으로 실행될 수 있으며, 순서 또한 여러 조합이 나올 수 있으므로 이를 고려해야 한다.

기존 트랜잭션에서는 예약 도메인에 대해 다음 두 가지 작업이 같은 트랜잭션 내에서 수행된다.

이전에는 하나의 트랜잭션에서 락이나 격리 수준을 조정하여 동시성 문제를 해결할 수 있었다. 하지만 사가 패턴에서는 이 두 트랜잭션이 분리되므로 추가적인 고려가 필요하다.

4.1 예약 도메인 트랜잭션 통합

image

예약 요청에서 예약 생성은 가장 마지막에 이루어져야 한다.

예약 기록을 조회하기 위해, 이전에는 예약한 아이템의 재고 감소와 유저의 잔액 감소 트랜잭션이 발생했다. 인당 예약 구매 수량 제한을 초과한 유저가 최대 수량으로 요청할 경우, 재고 감소가 이루어지고, 보상 트랜잭션이 발생하기 전까지 다른 유저들은 재고가 남아 있음에도 예약을 하지 못하게 되는 상황이 발생할 수 있습니다.

4.2 예약 도메인 트랜잭션 분리

image Reservation 생성과 예약 기록 조회를 다른 트랜잭션에서 진행할 경우 phantom read가 발생할 가능성이 있다.

4.3 PENDING 상태의 예약상태 도입

image PENDING 이라는 상태를 추가하여 재고의 감소, 유저의 잔액을 감소하기 전에 PENDING, RESERVED, SUCCESS 상태의 예약 기록을 카운트 하고, 구매 제한 수를 초과하지 않았다면, PENDING 상태의 reservation 을 생성한 다음, 재고 감소, 결제가 이뤄진 마지막 단계에서 PENDING 을 RESERVED 상태를 바꾸어 예약이 이뤄진것으로 해야한다.

이렇게 한다면 앞서 말한 문제들이 발생하지 않는다.

5. 예약 요청 읽기 병렬, 쓰기 병렬/직렬 수행

읽기는 모두 병렬적으로 이루어질 수 있다. 하지만 쓰기를 병렬로 수행할 경우, 하나의 트랜잭션 실패가 다른 트랜잭션의 보상 트랜잭션을 유발할 수 있다.

  1. 예약을 위한 유저 잔액 감소 - 유저 잔액 부족
  2. 예약을 위한 아이템 재고 감소 - 아이템 재고 부족
  3. 예약을 위한 pending, reserved 상태의 예약 카운트 후 pending 상태 예약 레코드 추가- 인당 예약 한도 초과

따라서 직렬 혹은 병렬로 수행할지는 구현 후 테스트를 통해서 검증을 해봐야 한다.

6. 트랜잭션의 순서 기준

6.1. 순서에 따라 정당한 사용자들에게 영향이 가는가

아이템의 재고 감소를 가장 마지막에 두어야 하는 이유는 다음과 같다.

  1. 유저의 잔액 부족으로 인한 실패
  2. 인당 아이템의 구매 제한 초과로 인한 실패

이 두 가지는 개인에게 책임이 있으며, 한 유저가 동시에 접근하는 경우는 많지 않다. 또한 트랜잭션 실패는 정당한 다른 사용자에게 영향을 미치지 않는다.

하지만 아이템 재고 감소가 위의 트랜잭션들보다 선행될 경우, 잔액이 부족한 유저나 구매 제한을 넘은 유저들로 인해 재고가 보상 트랜잭션 실행 전까지 감소된 상태로 남게 되며, 클라이언트에서 조회시 재고가 수시로 바뀌는 경우가 발생한다.

이로 인해 정당한 유저들이 구매를 못하게 될 수 있으므로 정당한 사용자에게 영향이 가지 않도록 트랜잭션 순서를 조정해야 한다.

6.2 트랜잭션 부하

순서와 상관없이 병렬적 또는 직렬적으로 진행될 수 있다면 부하를 기준으로 순서를 정해야 한다.

추가 정보