이 글에서 다루는 내용
이 글은 MSA 환경에서 로컬 트랜잭션의 커밋이후 메시지 전송을 보장하는 방법에 대해서 다룹니다. 내용을 다루기 위해서 예시와 함께 작성되었습니다.
https://microservices.io/patterns/data/transactional-outbox.html
목차
트랜잭션의 커밋이후 메시지 전송을 보장하는 방법에 대해서 이야기해보기 위해서는
1. 어떤 경우에 문제가 발생할 수 있는지
2. 어떻게 보장해줘야하는지를 생각해봐야한다고 생각합니다.
트랜잭션과 메시지 전송 사이의 경우의 수
1. 모든게 잘 풀렸다.
고민하실 필요가 없습니다.
2. 로컬트랜잭션 스코프안에서 메시지를 발행했는데, 트랜잭션 롤백이 일어난다
먼저 주문서비스(Order)의 로컬 트랜잭션 스코프 안에서 메시지를 전송한다고 해볼까요?
트랜잭션 스코프 안에서 메시지를 발행한 이후 트랜잭션이 실패되면 어떤 사이드 이펙트들이 있을까요?
1. 만약 이벤트 채널을 구독하고 있는 서비스에서 이벤트를 발행한 서비스가 트랜잭션을 실패한 경우에 대해서 고려하고 있지 않다면 주문은 안들어갔는데, 결제는 되어버리는 이슈가 생길 수도 있을 것 같습니다(물론 주문 이벤트 과발행은 결제쪽에서도 신경써야하는 이슈긴 합니다)
2. 나중에 주문을 Retry하게 되면서 이벤트가 과발행될 수 있습니다. 예를 들면 결제가 두번될 수 있는거죠.
3. 이벤트가 발행이 되었을지 안되었을지 아무도 모릅니다.
3. 로컬 트랜잭션 스코프 밖에서 메시지를 발행했는데, 메시지 전송에 실패한다
이번에는 트랜잭션을 마치고나서 이벤트를 발급해봅시다. 이제는 주문 생성이 실패하는 경우는 없을 것 같습니다. 만약 트랜잭션 롤백이 된다면, 다시 그냥 리트라이를 하면 되겠죠? 그런데, 주문생성은 잘 해놓고 이벤트 발급에 실패하면 어떻게 될까요? 카프카나 MQ가 유지보수나 스케일링 이슈로 인해서 몇분, 몇초가 응답이 안될수도 있잖아요. 그러면 무슨 일이 발생할까요?
주문 서비스에서는 결제가 될때까지 계속 주문이 PendingState로 멈춰있을겁니다(물론 이부분도 보상로직을 넣어줘야겠죠)
그러면 어떻게 트랜잭션 커밋이후 메시지 전송을 보장해줄까요?
우선 우리가 원하는걸 정리를 해볼까요? 우리는 로컬트랜잭션이 실패되면 메시지도 아예 안나가고, 트랜잭션이 성공하면 메시지도 꼭 발행되길 바랍니다. 어떻게 해야할까요? 이 문제가 지금 발생하는 이유는 의존성이 두 곳에 분리되어있기 때문인데요. 트랜잭션과 메시지 브로커에 의존성이 분리되어있기 때문에 어느 한쪽만 장애가 나거나 실패하는 경우 문제가 발생할 수 있는 것입니다.
그러면 이 의존성을 한 군데로 모아보면 어떨까요? 가령 메시지 발송과 관련된 부분도 트랜잭션을 통해서 저장해주는거죠. 그러면 발행되어야하는 이벤트를 저장하는 테이블에 데이터가 트랜잭션을 통해서 저장되고, 메시지 브로커에 데이터를 던져주는 워커를 두고, 그 서비스가 outbox 테이블을 쭈욱 읽어가면서 메시지 브로커에 데이터를 던져주면 메시지 전송은 적어도 한번은 될 것입니다.
적어도 한번...?
굉장히 불안한 키워드죠..? 이벤트가 과발행 될 수 있다는거잖아요. 그런데 사실 이벤트 과발행은 구조적으로 컨슈밍을 하는 쪽에서도 반드시 처리를 해줘야하기 때문에 대부분의 경우 과발행되는 경우에 대해서 발생할 수 있다는 것을 인지하고 처리해두므로 너무 걱정은 안해도 됩니다.
Transactional Outbox Pattern 의 정리
트랜잭셔널 아웃박스 패턴을 정리하면 트랜잭션을 통해서 발급되어야하는 메시지의 저장을 데이터베이스에 하고, 이후 메시지 브로커로 넘겨주는 릴레이,혹은 워커등을 두어서 적어도 한번 이상의 메시지 발급을 보장하는 패턴입니다.
이 글을 쓴 이유는 최근에 누가 이 패턴을 들어봤냐는 질문을 해서 공부하고 정리하기로 다짐했기 때문이다. 메시지 발송 로직들을 구현하면서 적용해왔던 패턴이 여기 녹아있음을 보면서 알 수 있었다.
역시 패턴은 발명이 아니라 발견인 것 같다.
'Web' 카테고리의 다른 글
MSA에서 다른 서비스의 트랜잭션이 실패된다면 어떻게 해야할까?, Saga (0) | 2024.04.13 |
---|---|
인텔리제이는 생각보다 더 많은 도움을 준다. (0) | 2023.01.25 |
JWT, Json Web Token (0) | 2020.03.30 |