5.4 장애 조합별 데이터 보장
HA 도구를 평가할 때 "failover가 몇 초 걸리는가"보다 먼저 확인할 것이 있습니다. 어떤 노드들을 잃었을 때 쓰기를 계속 받는지, 자동으로 승격하는지, 그 과정에서 확인 응답을 받은 commit이 사라질 수 있는지입니다. pg_auto_failover는 구성과 장애 순서에 따라 답이 크게 달라집니다. 이 장에서는 2노드와 3노드 구성을 기준으로 답을 한곳에 모읍니다. 근거는 실측 결과, v2.2 문서, 그리고 v2.2 소스(group_state_machine.c, service_keeper.c)입니다. 실측하지 않은 조합은 따로 표시했습니다.
판단에 쓰는 세 가지 규칙
첫째, number_sync_standbys = 0인 구성에서 quorum standby가 모두 건강하지 않으면 monitor는 primary를 wait_primary로 바꾸고 synchronous_standby_names를 비웁니다. 이때부터 commit은 primary에만 남습니다. 값이 1 이상이면 이 전환이 일어나지 않습니다. 필요한 응답 수를 채우지 못하면 commit이 대기합니다.
둘째, 2노드에서 monitor는 standby가 secondary 상태일 때만 승격 후보로 봅니다. standby가 한 번이라도 unhealthy로 판정되면 catchingup으로 내려갑니다. 다시 따라잡기 전까지는 후보가 아닙니다. 3노드 이상의 선출 코드(BuildCandidateList)는 secondary나 catchingup인 노드를 report_lsn으로 보내 후보 수집 대상에 포함합니다. 현재 unhealthy 상태이면서 보고도 하지 않는 노드만 제외합니다. v2.2 소스의 선출 함수는 여기에 두 조건을 더 적용합니다. quorum에 참여하는 standby 중 LSN을 보고하지 못한 노드가 있으면 선출을 멈춥니다. 또한 LSN을 보고한 quorum 후보가 number_sync_standbys + 1대보다 적어도 멈춥니다.
셋째, primary의 keeper는 monitor와 standby 모두와 연락이 끊기면 network_partition_timeout(기본 20초) 뒤 스스로 PostgreSQL을 멈춥니다. 이 검사는 keeper의 현재 상태가 primary일 때만 수행합니다. wait_primary인 primary는 이 검사를 하지 않습니다.
2노드 (number_sync_standbys = 0)
| 잃은 노드 (순서) | 쓰기 | 자동 승격 | 확인된 commit 손실 가능성 | 근거 |
|---|---|---|---|---|
| standby | 약 20초 대기 후 계속 | 해당 없음 | 없음 | 실측 19.9초, 20.4초 |
| primary | 약 51초 중단 후 새 primary | 예 | 없음 (동기 commit 기준) | 실측 51.2초 |
| standby, 그다음 primary | 불가 | 아니오 | primary를 복구하지 못하면 wait_primary 기간의 commit 손실 | 실측(60초 동안 승격 없음, primary 재기동 후 100행 보존) |
| primary, 그다음 새 primary | 불가 | 아니오 | 새 primary가 wait_primary로 받은 commit 손실 가능 | 첫째와 둘째 규칙에 따른 추론, 미측정 |
| monitor | 계속 | 불가 | 없음 | 실측(monitor 소실과 교체 중 150초 동안 실패 0건) |
| monitor, 그다음 standby | primary가 약 20초 뒤 스스로 멈춤 | 불가 | 없음 | 소스 is_network_healthy(), 미측정 |
| standby, 그다음 monitor | 계속 (wait_primary) | 불가 | primary 손실 시 wait_primary 기간의 commit 손실 | 첫째와 셋째 규칙에 따른 추론, 미측정 |
2노드 구성의 약점은 세 번째 줄입니다. standby가 먼저 중단되면 primary는 쓰기를 계속 받지만, 데이터는 한 벌뿐입니다. 이어서 primary까지 잃으면 monitor는 standby를 승격하지 않습니다. 실습에서도 이 순서로 두 노드를 중단한 뒤 standby를 다시 시작했지만 60초 동안 catchingup에 머물렀습니다. primary를 되살리자 standby가 연결되었고, wait_primary 기간에 쓴 100행도 보존되었습니다. 디스크가 남아 있는 primary를 재기동했기 때문에 데이터가 유지된 것입니다. primary의 디스크를 잃었다면 그 100행은 복구할 수 없습니다.
monitor 장애와 standby 장애가 겹치면 순서에 따라 결과가 반대입니다. monitor가 먼저 중단되면 primary는 primary 상태를 유지합니다. 이어서 standby 연결까지 끊기면 keeper는 자신이 네트워크 분리의 소수 쪽에 있다고 판단해 스스로 멈춥니다. 데이터는 보존되지만 서비스는 중단됩니다. 반대로 standby가 먼저 중단되면 primary는 wait_primary가 됩니다. 그 뒤 monitor를 잃어도 스스로 멈추지 않습니다. 두 경우 모두 이 노트에서는 측정하지 않았습니다. 소스 동작에 따른 결론입니다.
3노드 (number_sync_standbys = 1)
| 잃은 노드 | 쓰기 | 자동 승격 | 확인된 commit 손실 가능성 | 근거 |
|---|---|---|---|---|
| standby 1대 | 계속 | 해당 없음 | 없음 | ANY 1을 남은 standby가 충족 |
| standby 2대 | commit 대기 | 해당 없음 | 없음 | v2.2 문서 |
| primary | 약 54초 중단 후 새 primary | 예 (report_lsn 단계 포함) | 없음 (동기 commit 기준) | 실측 54.1초 |
| primary와 standby 1대 동시 | 불가 | 아니오 | 사라진 standby가 응답한 commit은 그 노드와 primary의 저장소를 모두 영구히 잃으면 손실 | 소스: LSN 미보고 quorum 노드 존재, 후보 수 N+1 미만 |
| monitor | 계속 | 불가 | 없음 | 실측(3노드) 40초 동안 실패 0건 |
3노드 구성에서는 standby 한 대를 잃어도 보장이 달라지지 않습니다. standby 두 대를 모두 잃으면 쓰기가 멈추는 쪽을 선택합니다. 2노드처럼 동기 복제를 해제하지 않고 commit을 대기시키기 때문입니다. 확인 응답을 받은 commit이 적어도 두 벌에 있다는 보장을 끝까지 지키는 동작입니다.
primary와 standby 한 대를 동시에 잃는 경우도 같은 원칙을 따릅니다. ANY 1에서는 확인 응답을 보낸 standby가 사라진 노드일 수 있습니다. 남은 standby만 보고 승격하면 그 commit이 사라질 위험이 있습니다. 선출을 멈추는 이유는 이 위험을 피하고 잃은 노드의 복귀를 기다리기 위해서입니다. 응답한 standby와 primary의 저장소를 모두 영구히 잃었다면 그 commit은 남은 standby에 없습니다. v2.2의 선출 함수는 LSN을 보고하지 못한 quorum 노드가 있으면 "Failover still in progress ... waiting for N nodes to report"를 남기고 기다립니다. 후보가 N+1대보다 적을 때도 멈춥니다. 이 조합은 실측하지 않았습니다. main 브랜치에는 같은 계열의 보호 기능을 설정하는 pgautofailover.guard_data_loss와 수동 우회 옵션 perform failover --allow-data-loss가 추가되었습니다(1.3).
"손실 없음"은 synchronous_commit이 on 이상인 트랜잭션에만 해당합니다. 세션이나 역할에 synchronous_commit = local을 지정하면 그 commit은 standby를 기다리지 않습니다. v2.2 문서도 성능이 필요한 역할에만 local을 지정하는 방법을 소개합니다. 다만 데이터 내구성을 낮춰도 될 때만 사용하라고 경고합니다. 실습의 비대칭 네트워크 분리에서 고립된 옛 primary는 local commit 93건을 받았습니다. 재합류 후 해당 행은 0건이었습니다(7.4).
정리
2노드 구성은 standby 장애에도 쓰기를 중단하지 않는 대신, 그 기간에는 데이터가 한 벌만 남습니다. 이어서 primary를 잃으면 자동 복구가 이루어지지 않습니다. 3노드 기본 구성은 standby 한 대에 장애가 발생해도 보장을 유지합니다. 그 이상의 동시 장애에서는 승격이나 쓰기를 멈춥니다. 이 동작은 잃은 노드의 복귀를 기다려 추가 손실을 막습니다. 하지만 이미 영구히 사라진 복사본을 되살리지는 못합니다. 어느 구성이든 monitor는 쓰기 경로에 포함되지 않습니다. 따라서 monitor만 잃으면 쓰기는 계속되며 failover만 멈춥니다. 다만 monitor와 standby를 함께 잃으면 순서에 따라 primary가 스스로 멈출 수 있습니다.