비관락의 한계


두번째 해결방법 : Outbox 패턴 + 비동기 집계

기본전제 요구사항 : 팔로워 수는 굳이 실시간으로 처리되지 않아도 괜찮다. (준실시간성 데이터로 가정)

근본적인 문제 : 팔로잉 요청 API 안의 한 트랜잭션 내부에서 User 테이블과 Following 테이블을 모두 Write하고 있음

⇒ 동기 트랜잭션에서는 Following 테이블에 대해서만 Write 연산을 수행하고, User 테이블에 대한 Write 연산은 Event를 통해 비동기로 처리하자!

1차 설계 흐름

팔로잉 요청 가정

  1. 팔로잉 요청 API의 서비스 코드에서는 Following 테이블의 INSERT 연산만 수행 → User의 S-Lock만 획득
  2. 팔로잉 요청 이벤트 발행
  3. (비동기) 팔로잉 요청 트랜잭션이 커밋되면 팔로잉 요청 이벤트 리스너가 새로운 트랜잭션에서 User 테이블의 followerCount를 업데이트

만약 이벤트를 발행했는데 서버에 문제가 생기면?

⇒ 트랜잭션은 커밋되었는데, 이벤트가 유실되어 DB 데이터 정합성이 맞지 않는 문제 발생!

따라서, Outbox 패턴을 도입한다!

<aside> ☝🏼

Outbox 패턴이란?

MSA(Microservice Architecture) 환경에서 주로 사용되는 데이터 정합성을 맞추기 위한 패턴입니다. 어떤 서비스가 자신의 데이터베이스에 변경을 가한 후, 외부 시스템(다른 마이크로 서비스 등)에 메시지(이벤트)를 안정적으로 발행해야 할 때 사용됩니다.

동작 방식

  1. 서비스는 비즈니스 로직을 처리하고, 관련 데이터를 자신의 데이터베이스에 저장합니다.
  2. 동시에, 발행해야 할 메시지(이벤트)를 같은 데이터베이스 내의 별도 테이블(outbox_events 등)에 저장합니다.
  3. 이 두 작업은 하나의 원자적인 트랜잭션으로 묶입니다.
  4. 별도의 비동기 프로세스(Scheduler, Message Relay 등)가 outbox_events 테이블을 주기적으로 폴링(polling)하여 아직 처리되지 않은 이벤트를 가져옵니다.
  5. 가져온 이벤트를 실제 목적지(메시지 큐, 다른 서비스 등)로 안정적으로 전달하고, 전달이 완료되면 outbox_events 테이블에서 해당 이벤트를 처리 완료 상태로 변경하거나 삭제합니다.

장점

2차 설계 흐름 (Outbox 패턴 도입)