Skip to content

Latest commit

 

History

History
81 lines (59 loc) · 5.41 KB

MSA-Distributed-Transaction.md

File metadata and controls

81 lines (59 loc) · 5.41 KB

분산된 서버 환경에서 트랜잭션 처리

들어가기에 앞서

API트랜잭션 을 수행하는데, 트랜잭션 내부 에서 외부 API 를 요청한다. 외부 API 를 요청하는 이유는 외부 API응답 결과에 따라 현재 트랜잭션에서 처리되고 있는 작업을 커밋할 지? 롤백할 지? 판단하기 위해서이다. 즉, 현재 API외부 API 에서 처리하는 데이터의 일관성을 유지 하기 위해 그렇다. 근데 위와 같은 처리할때의 장점은 무엇이고, 단점은 무엇인지를 정리하고, 단점이 치명적이라면, 이를 해결할 방법은 무엇이 있는지 정리하고자 한다.

트랜잭션 내부에서 외부 API 요청 및 응답

1. 클라이언트로 부터 요청
2. 트랜잭션 시작
3. 쿼리 실행 (200ms 소요)
4. 외부 API 요청 및 응답 (500ms 소요)
5. 트랜잭션 종료
   5-1. 외부 API 응답이 성공이면, 트랜잭션 커밋
   5-2. 외부 API 응답이 에러이면, 트랜잭션 롤백
6. 클라이언트로 응답

특징

  • 트랜잭션의 처리 시간700ms 가 소요된다.
  • 클라이언트에 응답하기까지의 Latency700ms 가 소요된다.

장점

  • 외부 API 응답 결과(실패 혹은 장애 발생)에 따라 트랜잭션을 커밋하고, 롤백할 수 있기 때문에 현재 API외부 API 에서 각각의 DB 데이터 일관성 을 보장할 수 있다.

단점

  • 외부 API 응답 시간이 빈번하게 지연되는 상황이면, 현재 API 의 다른 스레드들은 DB 커넥션 을 획득하기 위해 대기하거나, 타임아웃 발생할 가능성이 크며, 병목 현상 이 발생하게 된다.
    • 병목 현상 으로 인해 현재 API 의 처리 성능은 떨어지게 된다.
  • 만약에 4, 5 번 작업 사이에 예외가 발생하면, 현재 API트랜잭션은 롤백 되고, 외부 API트랜잭션은 커밋 된 상태이므로 데이터 일관성을 보장할 수 없다.
    • DB 서버로 커밋을 할 시점 에 DB 서버가 순간 장애로 인해 커넥션을 강제로 종료했거나, DB 서버가 다운된 경우
  • Spring @Transactional 의 전파 레벨로 비유 하자면 다음과 같다. REQUIRED 전파 레벨로 트랜잭션을 시작하고, REQUIRES_NEW 전파 레벨을 통해 물리적으로 다른 트랜잭션을 추가로 실행한 상황에서 REQUIRES_NEW 로 실행된 트랜잭션은 커밋하고, REQUIRED 로 실행된 부모 트랜잭션은 롤백인 된 상황이랑 똑같다. 이에 따라 REQUIRES_NEW 로 실행된 트랜잭션은 이미 커밋 되었기 때문에 롤백을 할 수 없다. 즉, 데이터 일관성을 보장할 수 없는 상황이다.

트랜잭션 종료된 후에 외부 API 요청 및 응답

1. 클라이언트로 부터 요청
2. 트랜잭션 시작
3. 쿼리 실행 (200ms 소요)
4. 트랜잭션 종료 (커밋 or 롤백)
5. 외부 API 요청 및 응답 (500ms 소요)
6. 클라이언트로 응답

특징

  • 트랜잭션의 처리 시간200ms 가 소요된다.
  • 클라이언트에 응답하기까지의 Latency700ms 가 소요된다.

장점

  • 트랜잭션이 위의 상황보다 빠른 200ms 이내에 처리되고, 쿼리 실행만 하기 때문에 다른 스레드에서 DB 커넥션 을 상대적으로 빨리 확보하여 처리한다.
  • 다른 스레드에서는 DB 커넥션 을 빠르게 확보하여, 처리하기 때문에 API전체 성능 이 상대적으로 좋아진다.

단점

  • 현재 API 에서 트랜잭션을 커밋한 상황인데, 외부 API 가 어떠한 에러에 의해서 롤백 되었다면, 데이터 일관성을 보장하지 않는다.
    • Saga Pattern, 2-Phase Commit, Outbox Pattern 기법들을 사용하여, 분산된 서버 환경에서 데이터 일관성을 보장하도록 구현해야 하는데, 구현 복잡도가 높다.

지금까지 내용들을 정리해보자면

  • 첫 번째 작업의 트랜잭션 처리 시간700ms 에 수행되며, 두 번째 작업의 트랜잭션 처리 시간200ms 에 수행된다.
    • 즉, 두 번째 작업의 트랜잭션 처리가 더 빨리 되고, 이에 따라 DB 커넥션 을 더 빨리 반납하게 되므로 API전체 성능 이 상대적으로 더 좋다.
  • 두 개의 작업은 Latency700ms 에 수행되며, 동일한 Latency 를 갖는다.
  • 첫 번째 작업은 분산된 서버 환경에서 데이터 일관성을 확실하게 보장할 수 있는가?
    • 위에서 언급했던 REQUIRED, REQUIRES_NEW 비유를 통해 확인해보자면, 확실하게 보장할 수 없다.
  • 두 번째 작업은 분산된 서버 환경에서 데이터 일관성을 확실하게 보장할 수 있는가?
    • 현재 API 가 커밋되고, 외부 API 처리 과정에서 롤백이 되면, 데이터 일관성을 확실하게 보장할 수 없다.

두 개의 작업중에서 어떠한 기준으로 선택할 것인가

  • 처음에 빠르게 개발해야 되는 상황에서는 첫 번째 작업을 선택해서 비즈니스를 구현하는데 집중하자
  • 시간이 넉넉하다면, 두 번째 작업을 선택하여 현재 API성능을 높이고, 분산 트랜잭션, 보상 트랜잭션 을 통해 데이터 일관성을 유지 하도록 하자
    • Saga Pattern, 2-Phase Commit, OutBox Pattern