서론
이전 글에서는 WAL Timeline과 pg_rewind, WAL Replay를 이용해 Old Primary를 New Primary의 Standby로 다시 구성하는 과정을 살펴보았습니다.
Primary 장애가 발생하면 Secondary 하나를 New Primary로 Promotion하여 쓰기 서비스를 복구할 수 있습니다.
이후 Old Primary의 WAL 이력을 확인하고, 필요한 경우 pg_rewind 또는 새로운 Base Backup을 이용해 Old Primary를 다시 Standby로 구성합니다.
Primary 장애
↓
Old Primary Fencing
↓
Secondary Promotion
↓
애플리케이션 연결 전환
↓
Old Primary 재동기화
↓
Replication 구조 복구
↓
업무 데이터 검증
하지만 이러한 절차가 문서에 작성되어 있다는 사실만으로 실제 장애 상황에서도 정상적으로 동작한다고 볼 수는 없습니다.
평상시에는 문제가 없어 보이던 구조가 실제 Failover 과정에서는 다음과 같은 문제를 일으킬 수 있습니다.
- Primary 장애를 늦게 감지합니다.
- 정상적인 Primary를 장애 상태로 잘못 판단합니다.
- 데이터 반영 수준이 낮은 Secondary를 Promotion합니다.
- Old Primary의 Fencing에 실패합니다.
- 애플리케이션의 기존 Connection이 Old Primary에 남습니다.
- 나머지 Secondary가 New Primary를 따라가지 못합니다.
- Old Primary 재동기화에 필요한 WAL이 존재하지 않습니다.
- 장애 과정에서 일부 요청이 누락되거나 중복됩니다.
따라서 Primary-Secondary Replication을 운영하려면 Replication 상태와 Failover 진행 상황을 지속적으로 관찰해야 합니다.
또한 실제 장애와 유사한 조건에서 Failover 절차를 반복적으로 테스트해야 합니다.
이번 글에서는 운영 환경에서 관찰해야 하는 지표와 반드시 수행해야 하는 Failover 테스트를 살펴보겠습니다.
그리고 마지막으로 Primary-Secondary Replication이 해결할 수 있는 문제와 해결하지 못하는 문제를 구분하고, Multi-Master Replication을 검토해야 하는 조건까지 정리하겠습니다.
1. 운영 환경에서 Replication 상태를 관찰해야 한다
Primary-Secondary 구조는 Secondary가 존재하고 Replication 연결이 유지되고 있다는 사실만으로 정상이라고 판단할 수 없습니다.
Secondary가 Primary의 WAL을 수신하고 있더라도 저장이나 Replay가 지연되고 있을 수 있습니다.
Primary
│ WAL 전송
▼
Secondary
│
├─ WAL 수신 정상
├─ WAL 저장 지연
└─ WAL Replay 지연
이 상태에서는 Replication 연결이 살아 있지만 Secondary의 데이터 반영 수준은 계속 뒤처질 수 있습니다.
따라서 일반적인 CPU, 메모리, 디스크 사용량뿐 아니라 다음과 같은 Replication 상태를 함께 관찰해야 합니다.
- Primary와 Secondary의 연결 상태
- Primary가 마지막으로 전송한 WAL 위치
- Secondary가 마지막으로 수신한 WAL 위치
- Secondary가 마지막으로 영구 저장한 WAL 위치
- Secondary가 마지막으로 Replay한 WAL 위치
- Replay를 기다리고 있는 WAL의 양
- WAL 수신 오류
- WAL Replay 오류
- Secondary별 Replication 상태 차이
- 현재 동기 Standby로 선택된 노드
- Secondary별 Replication Lag
각 Secondary는 서로 다른 데이터 반영 수준을 가질 수 있습니다.
Primary : W1, W2, W3, W4, W5
Secondary A : W1, W2, W3, W4
Secondary B : W1, W2, W3
Secondary C : W1, W2
Secondary A, B, C가 모두 실행 중이더라도 장애 발생 시 Promotion했을 때의 데이터 손실 가능성은 서로 다릅니다.
따라서 Secondary의 상태를 단순히 정상과 비정상으로만 구분해서는 안 됩니다.
각 Secondary가 Primary의 변경을 어디까지 수신하고 저장하고 Replay했는지를 함께 확인해야 합니다.
2. Replication Lag을 단계별로 확인한다
Replication Lag을 하나의 시간 값으로만 확인하면 어느 구간에서 지연이 발생했는지 알기 어렵습니다.
Primary의 변경이 Secondary의 데이터에 반영되기까지는 다음 단계를 거칩니다.
Primary WAL 생성
↓
Primary WAL 전송
↓
Secondary WAL 수신
↓
Secondary WAL 저장
↓
Secondary WAL Replay
↓
Secondary 데이터에 변경 반영
가능하다면 각 단계의 진행 위치와 지연 시간을 나누어 관찰해야 합니다.
WAL 전송이 지연되는 경우
Primary가 생성한 WAL과 Secondary가 수신한 WAL 사이의 차이가 계속 증가한다면 네트워크나 WAL 전송 과정에 문제가 있을 수 있습니다.
확인해야 할 항목은 다음과 같습니다.
- Primary와 Secondary 사이의 네트워크 지연
- 네트워크 대역폭 부족
- Primary의 WAL Sender 상태
- Secondary의 WAL Receiver 상태
- 연결 재시도 및 Timeout
- 방화벽 또는 네트워크 정책 변경
Primary WAL 생성 위치 : W100
Secondary 수신 위치 : W80
이 경우 Secondary는 W81부터 W100까지의 WAL을 아직 전달받지 못한 상태입니다.
WAL 저장이 지연되는 경우
Secondary가 WAL을 수신했지만 영구 저장 속도가 느릴 수 있습니다.
Secondary WAL 수신 위치 : W100
Secondary WAL 저장 위치 : W85
이 경우 다음 항목을 확인해야 합니다.
- Secondary의 디스크 I/O
- 저장 장치 응답 시간
- 파일 시스템 상태
- 디스크 공간 부족
- 다른 작업과의 저장 장치 경쟁
- 가상화 또는 클라우드 저장 장치의 성능 제한
WAL을 네트워크로 수신했다고 해서 해당 변경이 안전하게 저장되었다는 의미는 아닙니다.
수신 위치와 저장 위치를 구분해서 관찰해야 합니다.
WAL Replay가 지연되는 경우
Secondary가 WAL을 저장했지만 실제 데이터에 적용하는 과정이 뒤처질 수 있습니다.
Secondary WAL 저장 위치 : W100
Secondary WAL Replay 위치: W70
이 경우 다음 항목을 확인해야 합니다.
- Secondary CPU 사용량
- 디스크 I/O 사용량
- Secondary에서 실행 중인 무거운 읽기
- 긴 트랜잭션
- WAL Replay와 조회 사이의 충돌
- Recovery 충돌
- Secondary의 처리 성능 부족
Secondary에서 리포트나 분석용 조회를 실행하면 Primary의 읽기 부하는 줄일 수 있습니다.
하지만 해당 조회가 Secondary의 CPU, 메모리, 디스크 I/O를 과도하게 사용하면 WAL Replay가 느려질 수 있습니다.
무거운 Secondary 조회
↓
Secondary 자원 사용 증가
↓
WAL Replay 지연
↓
Replication Lag 증가
↓
이전 데이터 조회 가능성 증가
따라서 Secondary의 읽기 부하와 WAL Replay 상태를 함께 관찰해야 합니다.
3. Secondary 별 상태 차이를 관찰한다
Secondary가 여러 대라면 전체 Replication Lag의 평균만 확인해서는 안 됩니다.
평균값만 보면 특정 Secondary가 크게 뒤처진 상황을 놓칠 수 있습니다.
Secondary A Lag: 100ms
Secondary B Lag: 200ms
Secondary C Lag: 20초
평균값은 약 6.7초이지만 실제로는 Secondary C만 크게 뒤처져 있습니다.
이 경우 Secondary C를 일반 읽기 대상이나 Promotion 후보에서 제외해야 할 수 있습니다.
Secondary별로 다음 상태를 확인해야 합니다.
- WAL 수신 위치
- WAL 저장 위치
- WAL Replay 위치
- 현재 Replication Lag
- Replication 연결 상태
- 최근 오류 발생 여부
- 동기 또는 비동기 Standby 여부
- 현재 동기 Standby로 선택되었는지 여부
- 읽기 트래픽 처리량
- Promotion 후보로 사용할 수 있는지 여부
읽기 라우팅에서는 해당 요청에 필요한 데이터 반영 수준과 Secondary의 현재 상태를 함께 고려해야 합니다.
Failover에서는 Primary의 변경을 가장 많이 반영한 Secondary가 무엇인지 확인할 수 있어야 합니다.
4. Failover 진행 시간을 단계별로 기록한다
RTO는 장애 발생 이후 서비스를 다시 제공하기까지 허용되는 시간을 의미합니다.
하지만 전체 Failover 시간만 기록하면 어느 단계에서 지연이 발생했는지 알 수 없습니다.
Failover는 여러 단계로 이루어집니다.
Primary 이상 발생
↓
장애 감지
↓
실제 장애 판정
↓
Old Primary Fencing
↓
Secondary 상태 비교
↓
Promotion 대상 선택
↓
New Primary Promotion
↓
애플리케이션 연결 전환
↓
쓰기 서비스 재개
따라서 다음 시간을 각각 기록해야 합니다.
- Primary 이상 발생 시각
- 장애 감지 시각
- 실제 장애로 판정한 시각
- Fencing 시작 및 완료 시각
- Secondary 상태 비교 완료 시각
- Promotion 대상 선택 시각
- Promotion 시작 및 완료 시각
- 애플리케이션 연결 전환 시각
- 쓰기 서비스 재개 시각
- Old Primary 재동기화 시작 및 완료 시각
- 전체 Replication 구조 복구 시각
- 업무 데이터 검증 완료 시각
이를 통해 전체 RTO 중 어느 단계에서 시간이 많이 소요되었는지 확인할 수 있습니다.
장애 감지 : 10초
장애 판정 : 20초
Fencing : 15초
Promotion : 5초
연결 전환 : 30초
쓰기 서비스 복구: 총 80초
이 예시에서는 Promotion 자체보다 애플리케이션 연결 전환에 더 많은 시간이 걸렸습니다.
따라서 Promotion 속도만 개선해도 전체 RTO는 크게 줄어들지 않을 수 있습니다.
5. 서비스 복구와 전체 Recovery 시간을 구분한다
Failover 이후 New Primary가 쓰기를 처리하기 시작했다고 해서 모든 Recovery가 완료된 것은 아닙니다.
다음 세 단계를 구분해서 추적해야 합니다.
쓰기 서비스 복구
→ New Primary가 쓰기를 처리한다.
→ 애플리케이션이 New Primary에 연결된다.
Replication 구조 복구
→ 나머지 Secondary가 New Primary를 따라간다.
→ Old Primary가 Standby로 재구성된다.
업무 Recovery
→ 누락되거나 중복된 요청을 확인한다.
→ 주문·결제·재고 상태를 검증한다.
쓰기 서비스는 빠르게 복구되었지만 Old Primary 재동기화에는 몇 시간이 걸릴 수 있습니다.
또한 데이터베이스 구조가 정상화된 뒤에도 장애 시간대에 처리된 주문이나 결제를 별도로 검증해야 할 수 있습니다.
따라서 다음 시간을 각각 관리해야 합니다.
- 쓰기 서비스 복구 시간
- 읽기 서비스 정상화 시간
- 전체 Replication 구조 복구 시간
- Old Primary 재동기화 시간
- 업무 데이터 검증 시간
- 전체 Recovery 완료 시간
이러한 구분이 없으면 서비스가 다시 응답하는 순간 전체 장애 처리가 끝난 것으로 잘못 판단할 수 있습니다.
6. 애플리케이션 수준의 지표도 함께 확인해야 한다
데이터베이스의 Replication 상태만으로 사용자가 경험한 문제를 모두 파악할 수는 없습니다.
Replication Lag이 발생해도 사용자가 해당 Secondary를 조회하지 않았다면 실제 서비스 문제로 이어지지 않을 수 있습니다.
반대로 Replication Lag이 짧더라도 결제 상태나 사용자 권한처럼 중요한 데이터에서 이전 값이 반환되면 심각한 업무 오류가 발생할 수 있습니다.
따라서 애플리케이션에서는 다음 지표를 함께 관찰해야 합니다.
- Primary 읽기와 Secondary 읽기의 비율
- Secondary 읽기 실패 횟수
- Secondary 읽기 실패 후 Primary로 전환된 요청 수
- 쓰기 직후 이전 데이터가 반환된 요청 수
- Replication Lag 기준으로 Secondary가 제외된 횟수
- Failover 중 발생한 요청 오류
- Failover 중 발생한 재시도 횟수
- 멱등성 키 충돌 횟수
- 중복 요청이 차단된 횟수
- 결과를 확인하지 못한 요청 수
- 주문·결제·재고 상태 불일치 건수
- 외부 시스템과의 상태 대조 실패 건수
예를 들어 데이터베이스의 WAL Replay는 정상적으로 진행되고 있어도 애플리케이션의 Connection Pool이 Old Primary 연결을 유지하고 있다면 일부 요청은 계속 실패할 수 있습니다.
데이터베이스 역할 전환 완료
↓
Connection Pool에는 Old Primary 연결이 남아 있음
↓
일부 요청이 Old Primary로 전달됨
↓
서비스 오류 지속
따라서 데이터베이스 지표와 애플리케이션 지표를 함께 확인해야 실제 서비스 복구 여부를 판단할 수 있습니다.
7. Failover 테스트가 필요한 이유
Failover 절차가 문서에 존재한다는 사실과 실제 장애 상황에서 해당 절차가 정상적으로 동작한다는 사실은 다릅니다.
장애 유형에 따라 다음 요소가 달라질 수 있습니다.
- 장애 감지 방식
- 실제 장애 판정 조건
- Fencing 가능 여부
- Promotion 대상
- 데이터 손실 범위
- 애플리케이션 연결 전환
- Old Primary Recovery 절차
예를 들어 데이터베이스 프로세스만 종료된 경우와 서버 전체가 종료된 경우는 외부에서 관찰되는 상태가 다릅니다.
네트워크만 분리된 경우에는 Old Primary가 계속 쓰기를 처리할 수 있으므로 Split-Brain 위험이 훨씬 커집니다.
따라서 운영 환경과 유사한 조건에서 다양한 장애 상황을 반복적으로 테스트해야 합니다.
8. Primary 프로세스만 종료되는 경우
데이터베이스 프로세스만 종료하고 Failover 과정을 확인합니다.
서버와 네트워크는 정상적으로 동작하지만 데이터베이스 프로세스만 사용할 수 없는 상황입니다.
다음 항목을 확인해야 합니다.
- 장애 관리 계층이 Primary 프로세스의 종료를 감지하는가?
- 실제 Failover가 필요한 장애로 판단하는가?
- 데이터베이스 프로세스의 자동 재시작과 Failover가 충돌하지 않는가?
- 적절한 Secondary가 Promotion되는가?
- 애플리케이션 연결이 New Primary로 전환되는가?
- Old Primary의 기존 Connection이 제거되는가?
- 종료된 Primary가 다시 시작되었을 때 쓰기를 받지 않는가?
프로세스 자동 재시작과 Failover가 동시에 수행되면 다음과 같은 상황이 발생할 수 있습니다.
장애 관리 시스템
→ Secondary Promotion
프로세스 관리 시스템
→ Old Primary 재시작
따라서 데이터베이스 프로세스 재시작 정책과 Failover 정책이 서로 충돌하지 않도록 해야 합니다.
9. Primary 서버 전체가 종료되는 경우
Primary 서버 자체를 종료하여 데이터베이스 프로세스, 운영체제, 네트워크 연결이 모두 사라진 상황을 테스트합니다.
다음 항목을 확인해야 합니다.
- Primary 서버에 접근할 수 없어도 장애를 판정할 수 있는가?
- Old Primary의 쓰기 권한이 제거되었다고 판단할 수 있는가?
- Secondary를 안전하게 Promotion할 수 있는가?
- 애플리케이션 연결이 New Primary로 전환되는가?
- 나머지 Secondary가 New Primary를 따라가는가?
- 서버가 다시 시작되었을 때 Old Primary가 쓰기를 받지 않는가?
- Old Primary를 Standby로 재구성하는 절차가 동작하는가?
서버가 완전히 종료된 동안에는 Old Primary가 쓰기를 처리할 수 없습니다.
하지만 서버가 다시 시작되었을 때 기존 설정으로 Primary 역할을 수행하려고 할 수 있습니다.
따라서 재시작 이후에도 Old Primary가 자동으로 쓰기 대상에 포함되지 않도록 해야 합니다.
10. Primary와 관리 시스템 사이의 네트워크만 끊기는 경우
Primary는 정상적으로 실행되고 있지만 장애 관리 시스템이나 다른 데이터베이스 노드에서 Primary에 접근할 수 없는 상황입니다.
네트워크 단절
[관리 시스템] X──────────── [Old Primary]
↑
일부 Client
Old Primary는 일부 클라이언트의 쓰기를 계속 처리할 수 있습니다.
이 상태에서 Secondary를 Promotion하면 Old Primary와 New Primary가 동시에 쓰기를 처리하는 Split-Brain이 발생할 수 있습니다.
따라서 다음 항목을 반드시 확인해야 합니다.
- 네트워크 단절을 Primary 프로세스 장애로 잘못 판단하지 않는가?
- 일부 애플리케이션이 Old Primary에 계속 접근하고 있는지 확인할 수 있는가?
- Failover가 필요하다면 Old Primary를 확실하게 Fencing할 수 있는가?
- Fencing 완료 전에 Secondary를 Promotion하지 않는가?
- Old Primary와 New Primary의 동시 쓰기를 방지하는가?
- 네트워크 복구 후 Old Primary가 자동으로 쓰기 대상에 포함되지 않는가?
이 테스트는 Primary 프로세스 종료보다 더 중요한 Failover 테스트일 수 있습니다.
Primary가 실제로 살아 있기 때문에 잘못된 Promotion이 두 개의 유효한 쓰기 경로를 만들 수 있기 때문입니다.
11. Replication Lag이 큰 상태에서 장애가 발생하는 경우
Secondary의 WAL Replay를 의도적으로 지연시킨 뒤 Primary 장애를 발생시킵니다.
Primary : W1, W2, W3, W4, W5
Secondary A : W1, W2, W3, W4
Secondary B : W1, W2
다음 항목을 확인해야 합니다.
- Secondary별 WAL 수신 위치를 비교하는가?
- Secondary별 WAL 저장 위치를 비교하는가?
- Secondary별 WAL Replay 위치를 비교하는가?
- Primary의 변경을 가장 많이 반영한 Secondary가 선택되는가?
- 데이터 손실 가능 범위를 계산할 수 있는가?
- 허용 가능한 RPO를 초과했는지 판단하는가?
- 기준을 초과하면 자동 Promotion을 중단하는가?
- 운영자의 판단이 필요한 상태를 알리는가?
- 누락 가능성이 있는 트랜잭션을 식별할 수 있는가?
데이터 반영 수준이 낮은 Secondary를 Promotion하면 더 많은 쓰기가 New Primary에서 사라질 수 있습니다.
따라서 단순히 먼저 응답한 Secondary를 Promotion 대상으로 선택해서는 안 됩니다.
12. 동기 Secondary가 응답하지 않는 경우
동기 Replication을 사용한다면 동기 Secondary의 장애가 Primary 쓰기에 어떤 영향을 미치는지 반드시 확인해야 합니다.
다음 질문에 실제 운영 설정을 기준으로 답할 수 있어야 합니다.
- Primary의 Commit이 계속 대기하는가?
- 다른 동기 후보가 자동으로 선택되는가?
- 원격 확인을 받을 수 없으면 쓰기가 실패하는가?
- Timeout 이후 애플리케이션에 어떤 오류가 반환되는가?
- 일정 시간이 지난 뒤 비동기 방식으로 전환되는가?
- 대기 중인 트랜잭션이 보유한 잠금은 어떻게 되는가?
- 이미 처리 중인 요청은 어떻게 종료되는가?
- 동기 Secondary가 복구되면 자동으로 다시 선택되는가?
다음 상황을 가정할 수 있습니다.
Primary
│
X Sync Secondary 응답 없음
데이터 보호를 우선하는 설정이라면 Primary의 쓰기가 중단될 수 있습니다.
반대로 자동으로 동기 조건을 낮추도록 구성했다면 쓰기는 계속될 수 있지만 장애 시 데이터 보존 범위가 달라질 수 있습니다.
정확한 동작은 데이터베이스 제품과 운영 설정에 따라 달라지므로 실제 환경에서 테스트해야 합니다.
13. Promotion 직후 Old Primary가 다시 실행되는 경우
Secondary가 New Primary로 Promotion된 직후 Old Primary가 다시 실행되는 상황을 테스트합니다.
다음 항목을 확인해야 합니다.
- Old Primary가 자동으로 쓰기 요청을 받지 않는가?
- 애플리케이션이나 Proxy가 Old Primary에 다시 연결하지 않는가?
- Old Primary가 기존 Primary Endpoint에 다시 등록되지 않는가?
- WAL 이력을 확인하기 전에 Replication 구조에 합류하지 않는가?
- WAL Timeline이 갈라졌는지 확인하는가?
- 필요한 경우 pg_rewind를 수행할 수 있는가?
- pg_rewind가 불가능하면 Base Backup으로 재구성할 수 있는가?
- 재동기화 이후 New Primary의 Standby로 동작하는가?
Old Primary가 다시 실행되었다는 사실만으로 정상 노드로 간주해서는 안 됩니다.
New Primary와의 WAL 이력을 확인하고 데이터 상태를 맞춘 뒤 Standby로 합류시켜야 합니다.
14. 애플리케이션 Connection Pool이 남아 있는 경우
애플리케이션이 Old Primary에 연결된 기존 Connection을 유지한 상태에서 Failover를 수행합니다.
Connection Pool
Connection 1 → Old Primary
Connection 2 → Old Primary
Connection 3 → New Primary
다음 항목을 확인해야 합니다.
- Old Primary에 연결된 기존 Connection이 폐기되는가?
- New Primary에 대한 새로운 Connection이 생성되는가?
- 재연결 과정에서 요청이 무한히 대기하지 않는가?
- Connection Timeout이 적절하게 동작하는가?
- 실패한 요청이 New Primary로 재시도되는가?
- 재시도 과정에서 동일한 요청이 중복 처리되지 않는가?
- 읽기와 쓰기 연결이 각각 올바른 대상으로 전환되는가?
데이터베이스 역할이 정상적으로 전환되었더라도 애플리케이션 연결이 전환되지 않으면 서비스는 복구되지 않습니다.
또한 결과를 확인하지 못한 쓰기 요청이 New Primary로 재시도될 수 있으므로 멱등성 키가 정상적으로 동작하는지도 확인해야 합니다.
15. Failover 자동화가 중간에 중단되는 경우
Failover 자동화가 모든 단계를 완료하지 못하고 중간에 중단되는 상황을 테스트해야 합니다.
예를 들어 다음과 같은 상태에서 자동화가 멈출 수 있습니다.
- Fencing은 완료되었지만 Promotion은 수행되지 않았습니다.
- Promotion은 완료되었지만 애플리케이션 연결이 전환되지 않았습니다.
- 애플리케이션 연결은 전환되었지만 기존 Connection이 남아 있습니다.
- 일부 Secondary만 New Primary를 따라가도록 변경되었습니다.
- Old Primary의 pg_rewind 수행 중 작업이 중단되었습니다.
- Base Backup 전송 중 장애가 발생했습니다.
- 업무 데이터 검증 중 자동화가 종료되었습니다.
자동화를 다시 실행했을 때 이미 완료된 단계가 위험하게 반복되어서는 안 됩니다.
예를 들어 New Primary가 이미 Promotion된 상태에서 다른 Secondary를 추가로 Promotion하면 안 됩니다.
첫 번째 실행
→ Secondary A Promotion 완료
→ 이후 단계에서 중단
두 번째 실행
→ Secondary B를 다시 Promotion하면 안 됨
따라서 Failover 자동화도 부분 실패와 중복 실행을 고려하여 멱등적으로 설계해야 합니다.
각 단계는 다음 내용을 확인한 뒤 실행되어야 합니다.
- 해당 단계가 이미 완료되었는가?
- 현재 클러스터 상태에서 다시 실행해도 안전한가?
- 이전 실행이 남긴 중간 상태가 있는가?
- Promotion 대상이 이미 결정되었는가?
- Old Primary Fencing이 유지되고 있는가?
- 자동으로 진행할 수 없는 상태인지 여부
16. 테스트 결과로 확인해야 할 사항
각 테스트에서는 서비스가 다시 응답하는지만 확인해서는 안 됩니다.
다음 항목을 함께 기록해야 합니다.
- 장애 발생 시각
- 장애 감지 시간
- 장애 판정 시간
- Fencing 완료 시간
- Promotion 완료 시간
- 애플리케이션 연결 전환 시간
- 전체 쓰기 중단 시간
- Promotion된 Secondary의 WAL 수신 위치
- Promotion된 Secondary의 WAL Replay 위치
- 데이터 손실 가능 범위
- 결과가 불확실한 요청
- 중복 처리된 요청
- 멱등성 키를 통해 차단된 요청
- Old Primary 재동기화 시간
- 전체 Replication 구조 복구 시간
- 업무 데이터 검증 결과
- 운영자의 수동 작업 내용
테스트 결과를 반복적으로 기록하면 다음 내용을 확인할 수 있습니다.
- 실제 RTO가 목표를 만족하는가?
- 실제 데이터 손실 가능 범위가 RPO를 만족하는가?
- Failover 과정에서 가장 오래 걸리는 단계는 무엇인가?
- 자동화가 처리하지 못하는 상태는 무엇인가?
- 수동으로 판단해야 하는 상황은 무엇인가?
- 업무 Recovery에 필요한 기록이 충분한가?
17. Primary-Secondary가 해결하는 문제
Primary-Secondary Replication은 읽기 부하를 분산하고 Primary 장애에 대응하는 데 효과적인 구조입니다.
읽기 확장
일정 수준의 Replication Lag을 허용할 수 있는 읽기 요청을 여러 Secondary로 분산할 수 있습니다.
┌─→ Secondary A
[Application] ────────────┼─→ Secondary B
└─→ Secondary C
다음과 같은 읽기 작업을 Secondary로 분리할 수 있습니다.
- 게시글 목록
- 상품 설명
- 과거 주문 내역
- 검색
- 통계
- 리포트
- 분석용 조회
- 실시간성이 낮은 대시보드
Primary가 쓰기 트랜잭션을 처리하는 데 더 많은 자원을 사용할 수 있습니다.
Primary 장애 대응
Primary를 사용할 수 없게 되었을 때 준비된 Secondary 하나를 New Primary로 Promotion하여 쓰기 서비스를 복구할 수 있습니다.
Old Primary 장애
↓
Secondary Promotion
↓
New Primary
장애 감지, Fencing, Promotion, 애플리케이션 연결 전환이 자동화되어 있다면 수동 복구보다 RTO를 줄일 수 있습니다.
운영 목적의 조회 분리
리포트나 분석처럼 무거운 읽기 작업을 Secondary에서 처리하여 Primary의 업무 트랜잭션과 분리할 수 있습니다.
다만 Secondary의 읽기 작업과 WAL Replay가 같은 CPU, 메모리, 디스크 I/O를 사용할 수 있다는 점을 고려해야 합니다.
무거운 조회가 WAL Replay를 지연시키면 Secondary의 데이터 반영 수준이 Primary보다 크게 뒤처질 수 있습니다.
18. Primary-Secondary가 자동으로 해결하지 못하는 문제
데이터베이스 노드를 여러 대 구성했다고 해서 모든 확장성과 가용성 문제가 해결되는 것은 아닙니다.
쓰기 처리량 확장
정상 상태의 쓰기 요청은 여전히 Primary 하나로 전달됩니다.
Application
│
├─ Write 1 ─┐
├─ Write 2 ─┼─→ Primary
└─ Write 3 ─┘
Secondary 수를 늘려도 동일한 데이터에 대한 쓰기 처리량이 자동으로 증가하지는 않습니다.
Primary의 쓰기 처리량이 한계에 도달했다면 다음과 같은 별도의 접근이 필요할 수 있습니다.
- 쿼리 최적화
- 인덱스 조정
- 파티셔닝
- Sharding
- 업무별 데이터베이스 분리
- 데이터 모델 변경
- 비동기 처리
여러 지역에서의 로컬 쓰기
사용자가 여러 지역에 분산되어 있더라도 모든 쓰기는 Primary 하나로 전달됩니다.
서울 사용자 ─────┐
도쿄 사용자 ─────┼─→ 원격 Primary
미국 사용자 ─────┘
사용자와 Primary 사이의 거리가 멀수록 네트워크 왕복 시간이 증가합니다.
각 지역에서 가까운 데이터베이스에 쓰기를 완료해야 한다면 하나의 Primary만 사용하는 구조가 요구사항을 충족하지 못할 수 있습니다.
완전한 무중단 Failover
Primary 장애를 감지하고 New Primary로 전환하려면 다음 과정이 필요합니다.
장애 감지
→ 장애 판정
→ Fencing
→ Promotion
→ 연결 전환
이 과정에는 일정한 시간이 필요합니다.
자동 Failover가 구성되어 있어도 전환 과정에서 일부 요청이 실패하거나 지연될 수 있습니다.
따라서 자동 Failover가 존재한다는 사실이 서비스 중단 시간이 항상 0이라는 의미는 아닙니다.
모든 장애에서 데이터 손실 0
비동기 Replication에서는 Primary의 변경이 Secondary에 전달되기 전에 장애가 발생할 수 있습니다.
동기 Replication에서도 Secondary의 어느 처리 단계까지 기다리는지와 어떤 장애 범위까지 고려하는지에 따라 보장 수준이 달라집니다.
다음과 같은 장애는 Replication만으로 완전히 대응하기 어려울 수 있습니다.
- 여러 저장 장치의 동시 손상
- 데이터센터 전체 장애
- 잘못된 운영 작업
- 애플리케이션 버그
- 데이터 손상
- 잘못된 Failover
- 여러 노드의 동시 삭제
따라서 Replication 방식만으로 모든 장애 상황에서 데이터 손실이 발생하지 않는다고 단정할 수 없습니다.
백업 대체
Secondary는 Primary에서 발생한 정상적인 변경뿐 아니라 잘못된 수정과 삭제도 따라갑니다.
Primary에서 잘못된 DELETE
↓
WAL 전송
↓
Secondary에서도 DELETE 적용
애플리케이션 버그나 운영자의 실수로 데이터가 삭제되면 해당 변경이 Secondary에도 전달될 수 있습니다.
따라서 특정 시점으로 데이터를 되돌리기 위한 별도의 전략이 필요합니다.
- Base Backup
- 증분 백업
- WAL Archive
- Point-In-Time Recovery
- 백업 복원 테스트
- 백업 보존 정책
Replication은 고가용성을 위한 데이터 사본이며, 백업을 대체하지 않습니다.
업무 데이터 자동 Recovery
WAL은 데이터베이스의 물리적 변경을 재현하고 데이터 파일을 일관된 상태로 Recovery하는 데 사용됩니다.
하지만 주문, 결제, 재고와 같은 업무 상태의 모순을 항상 자동으로 해결하지는 않습니다.
장애 과정에서 누락되거나 중복된 요청은 다음 정보를 기준으로 별도로 확인해야 합니다.
- 클라이언트 요청 기록
- 애플리케이션 로그
- 데이터베이스 기록
- 멱등성 키
- 메시지 처리 기록
- 외부 결제 시스템의 상태
- 업무 규칙
따라서 기술적 Recovery와 업무 Recovery를 구분해야 합니다.
19. Primary-Secondary 설계 체크리스트
Primary-Secondary Replication을 도입하기 전에는 다음 질문에 답할 수 있어야 합니다.
읽기 정책
- 어떤 읽기 요청이 Replication Lag을 허용할 수 있는가?
- 쓰기 직후 동일한 데이터를 조회하는 요청은 어디로 전달할 것인가?
- 현재 재고, 결제 상태, 사용자 권한 조회를 Secondary에서 처리해도 되는가?
- Secondary의 Replication Lag이 기준을 초과하면 읽기 대상에서 제외할 것인가?
- 읽기 가능한 Secondary가 없으면 Primary로 전환할 것인가?
- 동기 Standby와 비동기 Standby를 읽기 라우팅에서 구분할 것인가?
- 해당 읽기에 필요한 데이터 반영 수준을 어떻게 정의할 것인가?
- Secondary에서 이전 데이터가 반환되었을 때 애플리케이션은 어떻게 대응할 것인가?
쓰기 완료 조건
- Primary의 Local Commit만으로 성공을 반환할 것인가?
- 몇 개의 Secondary로부터 확인을 받아야 성공으로 처리할 것인가?
- WAL 수신, 저장, Replay 중 어느 단계까지 기다릴 것인가?
- 원격 확인을 받을 수 없을 때 쓰기를 계속할 것인가?
- 동기 Secondary가 느려졌을 때 어떻게 대응할 것인가?
- 데이터 종류에 따라 서로 다른 Commit 조건을 적용할 것인가?
- 쓰기 지연 증가와 데이터 보존 수준 사이에서 어떤 기준을 선택할 것인가?
Failover
- Primary 장애를 어떤 시스템이 판단하는가?
- 장애 판단에 필요한 조건과 확인 횟수는 무엇인가?
- 네트워크 장애와 데이터베이스 장애를 어떻게 구분할 것인가?
- 여러 Secondary 중 어떤 노드를 Promotion할 것인가?
- Promotion 전에 Secondary별 WAL 위치를 비교하는가?
- 여러 Secondary가 동시에 Promotion되는 것을 어떻게 방지할 것인가?
- Old Primary를 어떤 방식으로 Fencing할 것인가?
- Fencing 성공 여부를 어떻게 확인할 것인가?
- 애플리케이션 연결을 어떻게 New Primary로 전환할 것인가?
- 기존 Connection Pool은 어떻게 폐기할 것인가?
- 실패한 요청은 어떻게 재시도할 것인가?
WAL과 Recovery
- WAL Archive를 운영하고 있는가?
- Failover와 재동기화에 필요한 WAL 보존 기간은 얼마인가?
- Old Primary와 New Primary의 Timeline 분기를 확인할 수 있는가?
- pg_rewind 사용 조건을 충족하도록 설정되어 있는가?
- pg_rewind를 실행할 주체와 절차가 정의되어 있는가?
- pg_rewind가 불가능할 때 Base Backup으로 재구축할 수 있는가?
- Old Primary를 Standby로 합류시키기 전에 데이터 상태를 검증하는가?
- 필요한 WAL이 제거되었을 때 어떻게 대응할 것인가?
- Old Primary에서만 처리된 쓰기를 별도로 확인할 수 있는가?
데이터 Recovery
- 허용 가능한 RPO와 RTO가 정의되어 있는가?
- 성공 응답을 받았지만 New Primary에 없는 트랜잭션을 어떻게 확인할 것인가?
- 결과를 확인하지 못한 요청을 안전하게 재시도할 수 있는가?
- 요청마다 멱등성 키를 사용하는가?
- 동일한 멱등성 키에 다른 요청 내용이 전달되면 어떻게 처리할 것인가?
- 주문과 결제 상태를 외부 시스템 기록과 대조할 수 있는가?
- 누락된 트랜잭션을 재처리하는 절차가 있는가?
- 중복된 트랜잭션을 취소하거나 보상하는 절차가 있는가?
- 데이터 검증과 수동 Recovery의 책임자가 정해져 있는가?
운영
- Primary와 Secondary의 연결 상태를 관찰하고 있는가?
- WAL 전송, 수신, 저장 및 Replay 위치를 확인할 수 있는가?
- Replication Lag을 Secondary별로 비교할 수 있는가?
- 현재 동기 Standby가 어떤 노드인지 확인할 수 있는가?
- Failover 단계별 소요 시간을 기록하고 있는가?
- 정기적으로 Failover 테스트를 수행하는가?
- Failover 자동화가 부분 실패했을 때 안전하게 다시 실행될 수 있는가?
- 쓰기 서비스 복구와 Replication 구조 복구를 별도로 추적하는가?
- 업무 Recovery 완료 여부를 별도로 추적하는가?
- Failover 이후 누락되거나 중복된 요청을 확인할 수 있는가?
이러한 질문에 답하지 못한 상태에서 Secondary 수만 늘리면 확인해야 할 WAL 위치, 데이터 상태, 연결 경로와 장애 상황만 증가할 수 있습니다.
20. Multi-Master를 검토해야 하는 순간
Primary-Secondary Replication은 정상 상태의 쓰기 주체를 Primary 하나로 유지합니다.
읽기 요청은 여러 Secondary로 분산할 수 있지만 쓰기 요청은 Primary 하나가 처리합니다.
Primary 장애 시에도 Secondary 하나를 New Primary로 Promotion하고, Old Primary를 Fencing한 뒤 New Primary의 WAL 이력을 기준으로 전체 구조를 재구성합니다.
운영 과정은 복잡하지만 최종 목표는 명확합니다.
유효한 쓰기 주체를 다시 하나로 만든다.
읽기 부하가 증가하거나 고가용성이 필요하다는 이유만으로 Multi-Master Replication이 필요한 것은 아닙니다.
읽기 확장과 Primary Failover는 Primary-Secondary 구조로 해결할 수 있습니다.
그렇다면 Primary-Secondary 구조를 벗어나 Multi-Master를 검토해야 하는 이유는 무엇일까요?
그 이유는, 하나의 Primary만으로 충족하기 어려운 쓰기 요구사항이 존재하기 때문입니다.
예를 들어 다음과 같은 상황을 생각할 수 있습니다.
- 사용자가 서로 멀리 떨어진 여러 지역에 분산되어 있습니다.
- 모든 쓰기를 하나의 원격 Primary로 보내면 네트워크 왕복 시간이 지나치게 증가합니다.
- 특정 지역에 장애가 발생해도 다른 지역에서 쓰기를 계속 처리해야 합니다.
- 지역 사이의 네트워크가 단절되어도 각 지역에서 쓰기 서비스를 제공해야 합니다.
- 여러 지역에서 동일하거나 서로 연관된 데이터에 쓰기를 수행해야 합니다.
이때부터 해결해야 하는 문제의 성격이 달라집니다.
21. Primary-Secondary와 Multi-Master의 질문은 다르다
Primary-Secondary 구조에서 중심이 되는 질문은 다음과 같습니다.
Primary에 장애가 발생했을 때
어떤 Secondary를 New Primary로 Promotion할 것인가?
정상 상태의 쓰기 주체는 하나입니다.
장애가 발생하더라도 최종적으로 새로운 쓰기 주체 하나를 선택합니다.
반면 여러 데이터베이스가 동시에 쓰기를 처리하는 구조에서는 다음 질문이 중심이 됩니다.
여러 데이터베이스가 각각 성공시킨 변경을
어떻게 하나의 유효한 데이터 상태로 합칠 것인가?
여러 데이터베이스가 로컬에서는 정상적으로 트랜잭션을 성공시켰더라도 변경을 서로 교환하는 과정에서 충돌이 발생할 수 있습니다.
동일한 Row에 대한 동시 쓰기
Database A
Order O-100 → PAID
Database B
Order O-100 → CANCELLED
어느 변경을 유지할지 결정해야 합니다.
Unique Constraint 충돌
Database A
User ID 100 생성
Database B
User ID 100 생성
각 데이터베이스에서는 정상적인 쓰기였지만 변경을 합치면 Unique Constraint가 충돌합니다.
업무 불변식 위반
각각의 변경은 정상이어도 결과를 합치면 재고, 잔액과 같은 업무 규칙이 깨질 수 있습니다.
현재 재고: 1
Database A
1개 주문 성공
Database B
1개 주문 성공
두 주문을 합치면 재고가 부족해집니다.
Network Partition 중 쓰기
지역 사이의 네트워크가 끊긴 상태에서도 각 지역이 쓰기를 계속 처리하면 변경이 서로 독립적으로 누적됩니다.
Region A X──────── Region B
A에서 쓰기 계속
B에서도 쓰기 계속
네트워크가 복구된 뒤 어떤 변경을 유지하고 어떻게 합칠지 결정해야 합니다.
따라서 Multi-Master Replication에서는 다음과 같은 새로운 문제를 다뤄야 합니다.
- Local Commit과 Remote Apply의 분리
- 동일한 Row에 대한 동시 쓰기
- Unique Constraint 충돌
- Cross-Row Business Invariant 위반
- Network Partition 중 쓰기 허용 정책
- 충돌 감지
- 충돌 해결
- 네트워크 복구 이후 데이터 재동기화
- 자동으로 해결할 수 없는 충돌의 수동 처리
마무리
Primary-Secondary Replication은 하나의 Primary가 쓰기를 담당하고, 하나 이상의 Secondary가 Primary의 변경을 따라가는 구조입니다.
시리즈에서 살펴본 전체 흐름은 다음과 같습니다.
기본 구조
→ Primary가 쓰기를 처리하고 Secondary가 변경을 따라간다.
읽기 확장
→ 데이터 반영 지연을 허용할 수 있는 읽기를 Secondary로 분산한다.
쓰기 완료 조건
→ 동기·비동기 Replication에 따라 성공의 보장 범위를 결정한다.
Primary 장애
→ Secondary 하나를 New Primary로 Promotion한다.
Split-Brain 방지
→ Old Primary를 Fencing하여 쓰기 권한을 제거한다.
Failover Recovery
→ New Primary의 WAL Timeline을 기준으로 Old Primary를 재동기화한다.
업무 Recovery
→ 누락되거나 중복된 주문·결제 등의 업무 상태를 확인한다.
운영
→ Replication 상태를 관찰하고 Failover 테스트를 반복한다.
Primary-Secondary Replication은 다음 문제를 효과적으로 해결할 수 있습니다.
- 읽기 부하 분산
- Primary 장애 대응
- 리포트와 분석 조회 분리
- Secondary를 이용한 서비스 복구
하지만 다음 문제까지 자동으로 해결하지는 않습니다.
- 쓰기 처리량 확장
- 여러 지역에서의 로컬 쓰기
- 모든 장애에서 데이터 손실 0
- 완전한 무중단 Failover
- 백업
- 업무 데이터 자동 Recovery
Primary-Secondary 구조의 핵심은 데이터베이스 수를 늘리는 것이 아니며, 적용하기 위해서는 다음 질문에 명확하게 답할 수 있어야 합니다.
- 쓰기 성공을 어느 시점에 확정할 것인가?
- 각 읽기에 필요한 데이터 반영 수준은 어느 정도인가?
- 장애 발생 시 어떤 Secondary를 Promotion할 것인가?
- Old Primary를 어떻게 Fencing할 것인가?
- 갈라진 WAL 이력을 어떻게 New Primary 기준으로 정리할 것인가?
- 누락되거나 중복된 업무 요청을 어떻게 확인할 것인가?
- Failover 절차가 실제로 동작하는지 어떻게 검증할 것인가?
이 구조가 유지해야 하는 가장 중요한 조건은 다음 한 문장으로 정리할 수 있습니다.
어떤 장애 상황에서도 유효한 쓰기 주체는 하나여야 한다.
하지만 모든 쓰기를 하나의 Primary로 전달하는 방식으로 지연 시간이나 지역별 가용성 요구사항을 충족할 수 없다면 문제는 달라집니다.
Primary-Secondary
어떤 Secondary를 New Primary로 Promotion할 것인가?
↓
Multi-Master
여러 데이터베이스가 각각 성공시킨 쓰기를
어떻게 하나의 유효한 상태로 합칠 것인가?
다음 시리즈에서는 이 질문을 출발점으로 Multi-Master Replication의 Local Commit, Remote Apply, 쓰기 충돌, Network Partition과 Recovery를 살펴보겠습니다.