Postgres 트랜잭션을 사용하여 분산 시스템 문제 해결하기
Postgres 트랜잭션을 사용하여 분산 시스템 문제 해결하기
워크플로우 상태와 데이터의 공동 배치(Co-locating)
애플리케이션 워크플로우 상태를 Postgres 데이터베이스 내에 공동 배치하면 개발자는 "dual-write" 문제를 제거할 수 있습니다. 이는 시스템이 데이터베이스를 업데이트했지만 메시지 큐에 알림을 보내는 데 실패하거나, 그 반대의 경우에 발생하는 위험을 의미합니다. 비즈니스 데이터와 워크플로우 진행 상황 모두에 대해 데이터베이스를 단일 진실 공급원(single source of truth)으로 취급함으로써, 개발자는 상태 전환과 그에 상응하는 부수 효과(side effect)가 원자적으로 커밋되도록 보장할 수 있습니다.
이 접근 방식은 분산 조정 문제를 로컬 트랜잭션 문제로 변환합니다. 두 개의 별도 시스템(예: 데이터베이스와 메시지 브로커) 간에 트랜잭션을 조정하려고 시도하는 대신, 시스템은 데이터 변경 사항과 의도된 작업을 동일한한 데이터베이스에 단일 ACID 트랜잭션으로 기록합니다.
Transactional Outbox 패턴
Transactional Outbox 패턴은 데이터베이스와 외부 메시지 큐 사이의 원자성을 달성하기 위한 주요 메커니즘입니다. 이 패턴은 데이터베이스의 역할을 기본 상태와 "outbox" 테이블의 두 부분으로 나누어 작동합니다.
- Atomic Write: 애플리케이션은 단일 Postgres 트랜잭션 내에서 비즈니스 상태를 업데이트하고 outbox 테이블에 메시지를 삽입합니다.
- Asynchronous Dispatch: 별도의 프로세스가 outbox 테이블을 폴링하거나 데이터베이스 트리거/UDF를 사용하여 메시지를 외부 큐로 푸시합니다.
- Confirmation: 외부 시스템이 수신을 확인하면, 메시지는 처리된 것으로 표시되거나 outbox에서 삭제됩니다.
커뮤니티 구성원들이 언급했듯이, 이 패턴은 메시지를 보내려는 의도가 데이터 자체만큼 내구적으로 유지되도록 보장함으로써 데이터베이스와 큐 사이의 트랜잭션이 있는 것처럼 "흉내"를 냅니다.
금융 시스템에서의 Dual-Write 버그 제거
자금 이동 시스템과 같이 위험 부담이 큰 환경에서는 "half-committed" 상태를 방지하기 위해 체크포인트를 쓰기 작업과 함께 배치하는 것이 매우 중요합니다. 만약 시스템이 워크플로우 중간에 충돌(crash)한다면, 복구 프로세스는 Postgres에 저장된 워크플로우 상태를 확인하여 프로세스가 정확히 어디서 멈췄는지 판단하고, 트랜잭션을 중복하거나 누락하지 않고 해당 지점부터 재개할 수 있습니다.
"이것은 자금 이동 시스템에서 dual-write 버그를 해결하는 비결입니다: 체크포인트를 쓰기 작업과 함께 배치하여 워크플로우 중간의 충돌이 half-committed 상태를 남기지 않도록 하는 것입니다."
트레이드오프 및 구현 고려 사항
Postgres를 워크플로우 엔진으로 사용하는 것은 강력한 보장을 제공하지만, 특정 아키텍처적 트레이드오프를 도입합니다.
결합도와 복잡성
워크플로우 진행 단위와 데이터베이스 커밋 단위를 일치시키면 데이터베이스 스키마와 애플리케이션 워크플로우 사이에 긴밀한 결합이 발생합니다. 이는 outbox 패턴을 단순화하지만, 향후에 워크플로우 엔진을 데이터베이스에서 분리하는 것을 어렵게 만듭니다. 그러나 많은 서비스에서 데이터베이스는 스택의 가장 안정적인 부분이므로, 이러한 트레이드오프는 수용 가능합니다.
멱등성(Idempotency) 및 부수 효과
데이터베이스 트랜잭션은 물리적 세계에서 이미 발생한 부수 효과(예: 이메일 전송)를를 롤백할 수 없습니다. 따라서 모든 외부 서비스 상호작용에는 멱등성이 필수적입니다.
예를 들어, 이메일 알림 시스템에서는 "at-least-once" 전달 방식이 "exactly-once" 전달 방식보다 우선시되는 일반적인 전략이 있습니다. 시스템은 메시지를 대기 목록에서 제거하는 트랜잭션을 종료하기 전에 이메일이 성공적으로 전송되었음을 확인하며, 드문 실패 사례가 메시지를 유락하는 대신 중복 이메일을 보내는 결과를 초래할 수 있음을 수용합니다.
중앙 집중화 위험
상태와 조정을 위한 단일 데이터베이스를 사용하면 단일 장애점(single point of failure) 단일 장애점(single point of failure)이 됩니다. 이는 운영 오버헤드를 단순화하지만(데이터베이스 장애가 전체 시스템을 일관되게 중단시키기 때문), 데이터 레이어와 독립적으로 조정 레이어를 확장할 수 있는 능력을 제거합니다.
분산 대안과의 비교
일부에서는 이 접근 방식이 중앙 집중식 데이터베이스에 의존하기 때문에 엄밀한 의미의 "분산 시스템"은 아니라고 주장합니다. 하지만, 이는 Temporal이나 복제된 상태 머신(replicated state machines)과 같은 복잡한 distributed coordination 도구의 실용적인 대안이 됩니다. 단계의 양이 관리 가능한 수준이고 워크플로우 진행 상황의 영구적인 기록이 필요한 시스템에서는, Redis나 Valkey와 같은 추가 인프라를 도입하는 것보다 Postgres를 사용하여 내구성이 있는 워크플로우를 직접 구현하는하는 것이 종종 더 효율적입니다입니다.n