6.4 synchronous mode
asynchronous replication 클러스터에서 failover는 언제나 트랜잭션 유실 가능성을 안고 있다. primary가 커밋을 반환한 뒤 아직 replica로 전송되지 않은 WAL은 primary와 함께 사라진다. synchronous mode는 이 구멍을 막기 위한 기능으로, Patroni가 PostgreSQL의 synchronous replication을 직접 관리하면서 failover 안전성과 연동한다. synchronous_mode는 세 값을 가진다.
| 값 | 동작 |
|---|---|
off (기본) | synchronous replication 미사용 |
on | 지정된 sync standby가 커밋을 확인 (FIRST n 형식) |
quorum | 후보 집합 중 아무나 n개가 확인 (ANY n 형식) |
quorum 모드는 6.5 quorum commit에서 별도로 다루고, 이 절은 on 모드를 중심으로 본다.
Patroni가 직접 관리하는 synchronous_standby_names
synchronous_mode: on에서 DBA는 postgresql.conf의 synchronous_standby_names를 직접 수정하지 않는다. Patroni가 primary에 synchronous_commit: on을 유지하면서 synchronous_standby_names를 스스로 설정하고 갱신한다. 형식은 FIRST n 목록이다.
synchronous_standby_names = 'FIRST 2 (node1,node2)'sync standby 수는 synchronous_node_count(기본 1)로 정한다. Patroni는 매 HA 루프마다 sync standby 선정을 재평가한다. 복제 연결이 유지되고 있고 제거 요청이 없는 노드는 그대로 두고, 빈자리가 생기면 가장 앞서 있는 replica를 새로 선정한다. maximum_lag_on_syncnode(기본 -1, 비활성)를 양수로 설정하면 그 바이트 수보다 lag이 큰 sync standby를 unhealthy로 보고 건강한 asynchronous replica와 교체한다.
# patronictl edit-config (dynamic configuration)
synchronous_mode: on
synchronous_node_count: 1
maximum_lag_on_syncnode: 16777216 # 16MiB, 기본은 -1(비활성)/sync key와 세 가지 불변식
Patroni는 동기화 상태를 DCS의 /sync key에 기록한다. 최신 primary와 현재 synchronous standby 목록이 들어 있으며, 이 key와 PostgreSQL 설정 사이에 세 가지 불변식을 유지한다. 원문 표현 자체가 규칙의 정의라 그대로 옮긴다.
- “A node must be marked as the latest leader whenever it can accept write transactions.”
- “A node must be set as the synchronous standby in PostgreSQL as long as it is published as the synchronous standby in the
/synckey in DCS.” - “A node that is not the leader or current synchronous standby is not allowed to promote itself automatically.”
세 번째 불변식이 failover 안전성의 근거다. synchronous mode에서는 leader race 참여 자격 자체가 “마지막으로 알려진 leader이거나 /sync key에 오른 synchronous replica"로 좁혀지므로, 커밋을 확인하지 않은 노드가 승격되는 경로가 차단된다.
보장과 한계
핵심 보장은 다음 한 문장이다. synchronous mode가 켜져 있으면 Patroni는 “클라이언트에 성공 커밋 상태를 반환했을 수 있는 모든 트랜잭션을 보유했다고 확신할 수 없는 standby는 promote하지 않는다”. 승격 후보의 자격을 데이터 보유 여부로 판정한다는 뜻이다.
다만 문서가 명시하는 한계가 두 가지 있다.
첫째, backend cancel에 의한 유실이다. PostgreSQL synchronous replication의 구현 특성상, replication 확인을 기다리던 backend가 취소되면 그 변경은 로컬에 커밋된 채 다른 세션에 보이지만 아직 복제되지 않은 상태가 된다. 이 직후 failover가 겹치면 synchronous_mode_strict를 쓰더라도 트랜잭션을 잃을 수 있다.
둘째, primary와 현재 sync replica가 동시에 죽는 경우다. 남은 노드에는 모든 확정 트랜잭션이 없을 수 있으므로 이 시나리오에서 zero data loss는 보장되지 않는다.
sync standby가 사라졌을 때
sync standby가 crash하면 primary의 커밋은 다음 Patroni HA 루프가 돌아 primary를 standalone 모드로 전환할 때까지 블로킹된다. 문서 기준 최악 ttl초, 평균 loop_wait/2초 수준이다. 반면 정상 종료나 재시작은 커밋을 멈추지 않는다. standby가 PostgreSQL 종료 전에 primary에 신호를 보내 synchronous standby 의무에서 먼저 빠지기 때문이다.
standalone 전환 덕분에 synchronous mode는 2노드 클러스터에서도 쓸 만하다. sync standby가 사라지면 primary가 홀로 쓰기를 계속하되, 그 구간의 커밋은 미복제 상태다. 이때 primary까지 장애가 나고 적합한 standby가 없으면 자동 승격은 일어나지 않으며, 이전 primary가 복구되면 스스로 다시 primary가 된다.
synchronous_mode_strict가 있다. sync standby 후보가 하나도 없을 때 synchronous replication을 끄지 않고, sync replica가 복귀할 때까지 모든 쓰기 요청을 블로킹한다. 무손실을 앞세우는 대신 가용성을 희생하는 선택이므로, 2노드 클러스터에서는 standby 하나의 장애가 곧 쓰기 중단이 된다는 점을 감안해야 한다.sync 선정을 통제하는 tags
노드별 tag로 sync replica 선정에 개입한다.
nosync: true는 그 노드가 synchronous replica로 선정되는 것을 막는다.nostream: true도 같은 효과를 낸다.sync_priority(Patroni 4.1.0 도입)는synchronous_mode: on에서 sync replica 선정 우선순위를 정한다. 값이 높을수록 우선하고, 0 이하는 선정에서 배제되어nosync: true와 같다.
sync_priority tag는 pg_stat_replication 뷰의 sync_priority 컬럼과 의미가 반대다. 뷰에서는 숫자가 작을수록 우선순위가 높지만, tag는 클수록 우선한다. 모니터링 쿼리와 tag 값을 나란히 볼 때 혼동하기 쉬운 지점이다.정리
synchronous_mode: on이면 Patroni가synchronous_standby_names(FIRST n형식)를 직접 관리하고,/synckey의 불변식으로 “커밋 확인 없는 노드의 자동 승격"을 차단한다.- 보장에는 한계가 있다. backend cancel 직후의 failover, primary와 sync replica 동시 장애에서는 유실이 가능하다.
- sync standby crash 시 커밋은 최악
ttl초 블로킹된 뒤 standalone으로 풀린다. 이 전환까지 막으려면synchronous_mode_strict, 대가는 가용성이다. - 선정 통제는
nosync,sync_prioritytag로 한다.sync_priority는pg_stat_replication과 부호 방향이 반대다.