2.3 health check와 unhealthy 판정
primary를 kill하면 pg_autoctl show events에 "marked as unhealthy"가 먼저 기록됩니다. 실제 failover는 10초 이상 지난 뒤 시작합니다. 두 사건의 조건이 다르기 때문입니다. health check 실패는 판정에 사용하는 여러 정보 중 하나입니다. failover 시작에 필요한 조건은 IsUnhealthy() 함수가 별도로 정합니다. 이 조건도 필요조건일 뿐입니다. 2노드에서는 standby가 secondary이고 healthy여야 합니다. candidate priority는 양수여야 합니다. WAL 차이는 promote_wal_log_threshold 이내여야 합니다. multi-standby에서는 quorum 후보 수와 LSN 보고 조건도 충족해야 합니다. 이 장에서는 두 조건을 소스로 확인하고 실측 타임라인과 비교합니다.
monitor GUC
health check와 판정에 관련된 설정은 monitor PostgreSQL의 GUC입니다. 다음은 실습 monitor에서 조회한 기본값입니다.
SELECT name, setting FROM pg_settings WHERE name LIKE 'pgautofailover.%' ORDER BY 1;
pgautofailover.enable_sync_wal_log_threshold|16777216
pgautofailover.health_check_max_retries|2
pgautofailover.health_check_period|5000
pgautofailover.health_check_retry_delay|2000
pgautofailover.health_check_timeout|5000
pgautofailover.node_considered_unhealthy_timeout|20000
pgautofailover.primary_demote_timeout|30000
pgautofailover.promote_wal_log_threshold|16777216
pgautofailover.startup_grace_period|10000
| GUC | 기본값 | 의미 |
|---|---|---|
health_check_period | 5000 ms | 노드별 health check 주기 |
health_check_timeout | 5000 ms | health check 연결 timeout |
health_check_max_retries | 2 | 실패 후 재시도 횟수 |
health_check_retry_delay | 2000 ms | 재시도 간격 |
node_considered_unhealthy_timeout | 20000 ms | 마지막 node_active() 보고 이후 이 시간이 지나야 unhealthy 후보 |
startup_grace_period | 10000 ms | monitor PostgreSQL 기동 후 이 시간 동안 unhealthy 판정 보류 |
health_check_max_retries = 2는 최초 시도를 제외한 재시도 횟수입니다. health_check_worker.c는 시도 횟수가 HealthCheckMaxRetries + 1에 이르면 노드의 health를 BAD로 기록합니다. 기본값에서는 세 번 연속 실패해야 BAD가 됩니다.
두 단계 판정
v2.2 node_metadata.c의 IsUnhealthy()는 다음 두 경로 중 하나라도 참이면 노드를 unhealthy로 판단합니다.
- keeper의 마지막
node_active()보고가node_considered_unhealthy_timeout보다 오래되었고, 마지막 health check 결과가 BAD이며, monitor PostgreSQL이 시작된 지startup_grace_period가 지났을 때 - keeper가 "PostgreSQL이 실행 중이 아니다(
pgIsRunning = false)"라고 보고했을 때
첫 번째 경로는 서버 전체가 사라진 경우입니다. keeper와 PostgreSQL이 모두 응답하지 않으므로 두 신호가 모두 필요합니다. 문서에 따르면 이 설계는 PostgreSQL 재시작을 health check 실패로 오판하지 않게 합니다. keeper가 계속 보고하는 동안에는 첫 번째 경로가 성립하지 않습니다.
두 번째 경로는 서버와 keeper는 살아 있고 PostgreSQL만 종료된 경우입니다. keeper는 먼저 로컬에서 재시작을 시도합니다. 설정 timeout.postgresql_restart_failure_max_retries(기본 3회)와 timeout.postgresql_restart_failure_timeout(기본 20초) 중 한도에 먼저 이를 때까지 재시도합니다. 그런 다음 monitor에 알립니다. 이 노트의 실습에서는 이 경로를 측정하지 않았습니다.
startup_grace_period는 데이터 노드가 아닌 monitor 자신의 기동 시각을 기준으로 합니다. 소스는 PgStartTime, 즉 monitor PostgreSQL의 시작 시각과 비교합니다. node.health와 healthchecktime은 재시작 후에도 테이블에 남습니다. 하지만 소스는 healthCheckTime이 PgStartTime 이후인지 검사합니다. 이 검사로 재시작 전 결과를 무시합니다. 새 health check가 쌓이기 전에 성급한 failover가 발생하지 않도록 하는 장치입니다.
실측 타임라인: primary kill
2노드 구성에서 primary(node2)를 docker kill로 멈췄을 때의 monitor 이벤트입니다.
kill at 05:27:40.263 UTC
2026-09-28 05:27:48.528422+00|node2|primary|primary|Node node 2 "node2" (node2:5432) is marked as unhealthy by the monitor
2026-09-28 05:28:00.46183+00|node1|secondary|prepare_promotion|Setting goal state of node 2 "node2" (node2:5432) to draining and node 1 "node1" (node1:54
- +8.3초: health check 세 번 실패, health BAD 기록. 아직 failover 조건 아님
- +20.2초: 마지막 보고 후 20초가 지나
IsUnhealthy()가 참이 됨. failover 시작
"marked as unhealthy" 이벤트는 health 컬럼이 BAD로 바뀐 기록입니다. failover 시작 시점과 일치하지 않습니다. 컨테이너가 사라지면 이름 해석과 연결이 즉시 실패합니다. 따라서 세 번의 시도가 빠르게 끝났습니다. 서버가 응답 없이 멈춰 연결이 health_check_timeout(5초)까지 대기하는 경우에는 BAD 기록이 더 늦어집니다.
실측 타임라인: monitor 재시작
monitor가 멈춰 있는 동안 primary를 kill했습니다. 45초 뒤 monitor를 다시 시작했습니다.
monitor start at 05:35:04.079 UTC
05:35:10.608|node1|primary|primary|Node node 1 "node1" (node1:5432) is marked as unhealthy by the monitor
05:35:15.021|node1|primary|draining|Setting goal state of node 1 "node1" (node1:5432) to draining after it became unhealthy.
node1의 마지막 보고는 45초 전이었고 health도 +6.5초에 BAD가 되었습니다. 그런데도 failover는 monitor 시작 후 10.9초에 시작했습니다. startup_grace_period 10초를 기다린 결과입니다.
값을 줄이면
node_considered_unhealthy_timeout을 10초로 줄인 실험에서는 health BAD 기록과 failover가 거의 동시에 시작되었습니다.
05:43:10.366|node1|primary|Node node 1 "node1" (node1:5432) is marked as unhealthy by the monitor
05:43:10.391|node2|prepare_promotion|Setting goal state of node 1 "node1" (node1:5432) to draining and node 2 "node2"
판정이 빨라질수록 일시적인 네트워크 지연을 장애로 판단할 가능성도 커집니다. 이 값만 따로 줄여서는 안 됩니다. 2.5의 keeper 쪽 timeout과 함께 조정해야 합니다. 전체 조정 실험은 7.6에 있습니다.
이 GUC들은 PGC_SIGHUP입니다. monitor에서 ALTER SYSTEM SET이나 설정 파일을 수정한 뒤 reload하면 적용됩니다. 실습에서도 ALTER SYSTEM을 사용했습니다. v2.2 설정 문서는 ALTER DATABASE ... SET을 안내합니다. 이 방법은 세션 기본값을 변경하므로 health check worker에 적용되는지는 확인하지 못했습니다. monitor를 새로 만들면 이 값은 기본값으로 돌아갑니다. monitor 교체 절차(6.6)에 GUC 재적용 단계를 포함합니다.
정리
health check 실패는 노드를 BAD로 표시할 뿐입니다. failover가 시작되려면 keeper의 보고가 20초 넘게 끊겨야 합니다. 따라서 primary kill 후 실제 failover가 시작되기까지 약 20초가 걸렸습니다. 이 20초에 2.5의 demote 대기 30초가 더해집니다. 그 결과 Part VII에서 측정한 50초대 쓰기 공백이 발생합니다.