본문으로 건너뛰기
5.4 replication slot 관리

5.4 replication slot 관리

replication slot은 소비자가 아직 받지 못한 WAL을 서버가 지우지 않도록 잡아 두는 PostgreSQL 장치다. HA 클러스터에서는 slot이 멤버의 생애와 함께 생기고 사라져야 하고, failover가 나면 새 primary에도 준비되어 있어야 한다. Patroni는 postgresql.use_slots(기본 true)일 때 이 생명주기를 대신 관리한다.

member slot 자동 관리

use_slots가 켜져 있으면 Patroni는 각 멤버의 streaming replication을 위해 멤버 이름에서 유도한 physical slot을 만들어 관리하고, 멤버가 사라지면 slot도 정리한다.

문제는 잠깐 내려간 멤버다. slot이 즉시 제거되면 그 사이 WAL이 지워져, 멤버가 돌아와도 따라붙지 못하고 reinit이 필요해지는 상황이 생긴다. member_slots_ttl(기본 30min, PostgreSQL 11 이상)이 이 유예 기간을 정한다. 멤버의 DCS member key가 만료된 뒤에도 해당 slot을 이 시간 동안 보존한다. 0으로 두면 member key 만료 즉시 slot을 제거하는 이전 버전의 동작으로 돌아간다.

# patronictl edit-config
member_slots_ttl: 30min

permanent slot 선언

멤버가 아닌 소비자(백업 도구, CDC, standby cluster)의 slot은 dynamic configuration의 slots hashmap으로 선언한다. permanent slot은 switchover와 failover를 거쳐도 보존되고, 없으면 Patroni가 만들어 준다.

slots:
  backup_slot:
    type: physical
    cluster_type: primary
  cdc_slot:
    type: logical
    database: orders
    plugin: test_decoding
  • typephysical 또는 logical이다.
  • logical slot은 databaseplugin이 필수다.
  • physical slot에는 cluster_type(primary 또는 standby)을 선택적으로 지정한다. 지정하면 해당 유형의 클러스터에서만 slot을 만들고, 유형이 다르면 만들지 않거나 기존 slot을 drop한다.
  • permanent slot을 쓰려면 postgresql.use_slots가 true여야 한다.

PostgreSQL 11 이상에서 permanent physical slot은 primary만이 아니라 모든 노드에 만들어지고 loop_wait초마다 위치가 전진한다. PostgreSQL 11 미만에서는 primary에만 만들어진다.

failover를 견디는 logical slot

Patroni는 permanent logical slot을 failover 후에도 이어 쓸 수 있도록 직접 동기화한다(PostgreSQL 11 이상). 동작은 이렇다.

  • primary에서 만든 logical slot을 각 standby로 복사한다. 복사에는 해당 노드의 restart가 동반된다.
  • 복사에는 rewind 또는 superuser credential을 쓰는 libpq 접속이 사용된다.
  • 이후 loop_wait초마다 standby 쪽 slot 위치를 primary를 따라 전진시킨다.
  • permanent logical slot이 있으면 Patroni가 hot_standby_feedback을 자동으로 활성화한다.
    flowchart TD
  P["primary의<br/>permanent logical slot"] -->|restart 동반 복사| S["standby의 slot 사본"]
  P -->|loop_wait마다 전진| S
  P -.->|failover| NEW["standby 승격"]
  NEW --> C["같은 slot으로 소비 재개"]
  
standby 쪽 slot 위치는 loop_wait 간격으로만 따라가므로, failover 직후 소비자는 이미 받았던 변경 일부를 다시 받는 경우가 있다. 공식 문서는 소비자 애플리케이션이 confirmed_flush_lsn을 추적해 중복을 걸러내기를 권장한다.

ignore_slots

Patroni는 자신이 모르는 slot을 drop하므로(5.2에서 본 함정), 외부 도구가 만들고 관리하는 slot은 ignore_slots에 등록해 손대지 않게 한다. 각 항목은 name, type, database, plugin 속성의 부분집합이고, 적은 속성이 모두 일치하는 slot이 무시 대상이 된다.

ignore_slots:
  - name: external_cdc
    type: logical
    database: orders
    plugin: wal2json

nostream tag의 영향

nostream: true인 노드는 streaming replication 대신 restore_command 기반 archive recovery와 pg_wal 폴링으로 WAL을 받는다. 이 tag는 slot 동기화에도 영향을 준다. 해당 노드와 그 아래에 붙은 cascading replica 전체에서 permanent logical slot의 복사와 동기화가 비활성화된다. primary에 설정하면 효과가 없다.

주의해야 할 두 패턴

replica에서 소비하는 permanent slot을 만들면 안 된다. permanent slot의 위치는 primary(또는 standby leader)에서 replica 방향으로만 동기화되므로, replica 쪽 slot에서 직접 소비하면 그 진행 상황이 다른 노드로 전파되지 않아 다른 노드들의 pg_wal이 무한히 쌓인다. 예외는 멤버 이름과 일치하는 physical slot뿐이다.

반대로 유용한 패턴도 있다. 노드 구성이 고정된 클러스터에서 멤버 이름과 같은 physical slot을 permanent로 선언해 두는 방식이다. 이렇게 하면 그 멤버가 오래 응답이 없어도 slot이 제거되지 않아, member_slots_ttl을 넘긴 장기 다운 후에도 WAL이 보존된다. slot 이름이 자기 자신의 노드 이름과 같으면 그 노드에는 만들어지지 않으므로 자기 참조 문제는 없다. 대신 slot이 WAL을 계속 잡아 두므로 primary의 디스크 사용량 감시가 전제다.

정리

  • Patroni는 use_slots(기본 true)로 멤버별 physical slot을 자동 관리하고, 멤버가 내려가도 member_slots_ttl(기본 30min) 동안 slot을 보존한다.
  • failover를 넘어 살아남아야 하는 slot은 permanent로 선언한다. logical은 databaseplugin이 필수이며, PostgreSQL 11 이상에서 standby로 복사되고 loop_wait마다 전진한다.
  • logical 소비자는 confirmed_flush_lsn 추적으로 failover 직후의 중복 수신에 대비한다.
  • 외부에서 관리하는 slot은 ignore_slots로 보호하고, replica에서 소비하는 permanent slot은 pg_wal 잠식 때문에 피한다.