본문으로 건너뛰기

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 설정 사이에 세 가지 불변식을 유지합니다. 원문 표현 자체가 규칙의 정의라 그대로 옮깁니다.

  1. "A node must be marked as the latest leader whenever it can accept write transactions."
  2. "A node must be set as the synchronous standby in PostgreSQL as long as it is published as the synchronous standby in the /sync key in DCS."
  3. "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의 커밋 확인과 승격 자격

보장과 한계​

핵심 보장은 다음 한 문장입니다. 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가 됩니다.

경고

standalone 전환이 싫다면 synchronous_mode_strict가 있습니다. sync standby 후보가 하나도 없을 때 synchronous replication을 끄지 않고, sync replica가 복귀할 때까지 모든 쓰기 요청을 블로킹합니다. 무손실을 앞세우는 대신 가용성을 희생하는 선택이므로, 2노드 클러스터에서는 standby 하나의 장애가 곧 쓰기 중단이 된다는 점을 감안해야 합니다.

sync standby 장애 뒤의 기본 모드와 strict 모드

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와 같습니다.
노트

Patroni의 sync_priority tag는 pg_stat_replication 뷰의 sync_priority 컬럼과 의미가 반대입니다. 뷰에서는 숫자가 작을수록 우선순위가 높지만, tag는 클수록 우선합니다. 모니터링 쿼리와 tag 값을 나란히 볼 때 혼동하기 쉬운 지점입니다.

정리​

  • synchronous_mode: on이면 Patroni가 synchronous_standby_names(FIRST n 형식)를 직접 관리하고, /sync key의 불변식으로 "커밋 확인 없는 노드의 자동 승격"을 차단합니다.
  • 보장에는 한계가 있습니다. backend cancel 직후의 failover, primary와 sync replica 동시 장애에서는 유실이 가능합니다.
  • sync standby crash 시 커밋은 최악 ttl초 블로킹된 뒤 standalone으로 풀립니다. 이 전환까지 막으려면 synchronous_mode_strict, 대가는 가용성입니다.
  • 선정 통제는 nosync, sync_priority tag로 합니다. sync_priority는 pg_stat_replication과 부호 방향이 반대입니다.