PostgreSQL 복제 슬롯 방치로 인한 WAL 디스크 급증 원인과 안전한 복구 절차

PostgreSQL 서버의 디스크 사용량이 갑자기 증가하면 대형 테이블, 인덱스 팽창 또는 임시 파일부터 의심하는 경우가 많다. 하지만 pg_wal 영역이 지속적으로 증가하고 있다면 가장 먼저 확인해야 할 대상 중 하나가 Replication Slot이다.
Replication Slot은 복제 대상이 아직 받지 못한 WAL을 원본 서버가 삭제하지 않도록 보존하는 장치다. 안정적인 복제를 위해 반드시 필요한 기능이지만, 복제 소비자가 중단되거나 더 이상 사용하지 않는 슬롯을 방치하면 WAL이 제한 없이 누적될 수 있다. 논리 복제 슬롯은 WAL뿐 아니라 오래된 시스템 카탈로그 행의 정리까지 제한해 Autovacuum과 Transaction ID 관리에도 영향을 줄 수 있다.
이 문제를 해결할 때 가장 위험한 행동은 디스크가 부족하다는 이유로 슬롯을 즉시 삭제하거나 pg_wal 파일을 운영체제에서 직접 제거하는 것이다. 슬롯의 실제 사용자를 확인하지 않고 삭제하면 복제가 영구적으로 끊기거나 대상 시스템을 처음부터 다시 구축해야 할 수 있다. 따라서 WAL 증가 원인을 수치로 확인하고 복제 소비자의 상태를 파악한 뒤 복구 방법을 선택해야 한다.
Replication Slot이 WAL을 계속 보존하는 원리
PostgreSQL은 데이터 변경 내용을 WAL에 먼저 기록한다. 물리 복제에서는 Standby 서버가 이 WAL을 받아 동일한 데이터 블록 변경을 재현하고, 논리 복제에서는 WAL을 해석해 INSERT, UPDATE, DELETE와 같은 행 단위 변경을 Subscriber나 CDC 시스템으로 전달한다.
Primary 서버는 일반적으로 체크포인트와 보존 정책에 따라 오래된 WAL 세그먼트를 재활용하거나 제거한다. Replication Slot이 생성되면 동작이 달라진다. PostgreSQL은 해당 슬롯의 소비자가 아직 필요로 하는 WAL 위치를 restart_lsn으로 관리하고, 이 지점 이후의 WAL을 계속 보존한다.
복제 소비자가 정상적으로 WAL을 처리하면 restart_lsn도 앞으로 이동한다. 반대로 Subscriber, AWS DMS, Debezium, Kafka Connect 또는 자체 CDC 프로그램이 중단되면 슬롯의 위치는 멈추지만 Primary의 현재 WAL 위치는 계속 증가한다. 두 위치의 차이가 누적된 WAL 보존량이 된다.
슬롯은 소비자의 실제 상태를 스스로 판단하지 않는다. 대상 시스템이 삭제됐거나 복제 작업이 영구적으로 종료됐더라도 슬롯은 명시적으로 제거하기 전까지 남을 수 있다. 특히 max_slot_wal_keep_size가 기본값인 -1이면 슬롯이 보존할 수 있는 WAL 용량에 제한이 없어 디스크를 가득 채울 가능성이 있다. PostgreSQL 복제 설정 문서
물리 복제 슬롯은 Standby 복구에 필요한 WAL을 보존한다. 논리 복제 슬롯은 WAL과 함께 논리 디코딩에 필요한 카탈로그 정보를 유지한다. 논리 슬롯의 catalog_xmin이 오래된 상태로 고정되면 Vacuum이 시스템 카탈로그의 오래된 행을 정리하지 못해 테이블 팽창이나 Transaction ID Wraparound 위험까지 커질 수 있다.
WAL 급증 원인과 위험 슬롯 진단하기
가장 먼저 전체 Replication Slot 상태를 조회한다.
SELECT slot_name,
slot_type,
plugin,
database,
active,
active_pid,
restart_lsn,
confirmed_flush_lsn,
wal_status,
safe_wal_size,
xmin,
catalog_xmin
FROM pg_replication_slots
ORDER BY slot_name;
PostgreSQL 버전에 따라 wal_status와 safe_wal_size 열이 제공되지 않을 수 있다. 이 경우 해당 열을 제외하고 조회하면 된다.
active가 false라고 해서 무조건 불필요한 슬롯은 아니다. 야간에만 실행되는 CDC 작업, 일시적으로 중단된 DMS Task 또는 점검 중인 Subscriber가 사용하는 슬롯일 수 있다. 반대로 active가 true여도 소비 속도가 WAL 생성 속도보다 느리면 보존량은 계속 증가한다.
슬롯별 추정 WAL 보존량은 현재 LSN과 restart_lsn의 차이로 계산할 수 있다.
SELECT slot_name,
slot_type,
database,
active,
restart_lsn,
pg_current_wal_lsn() AS current_lsn,
pg_size_pretty(
pg_wal_lsn_diff(
pg_current_wal_lsn(),
restart_lsn
)
) AS retained_wal,
confirmed_flush_lsn,
wal_status,
safe_wal_size
FROM pg_replication_slots
WHERE restart_lsn IS NOT NULL
ORDER BY pg_wal_lsn_diff(
pg_current_wal_lsn(),
restart_lsn
) DESC;
여기서 확인해야 할 핵심 항목은 다음과 같다.
restart_lsn: PostgreSQL이 해당 슬롯을 위해 보존해야 하는 가장 오래된 WAL 위치confirmed_flush_lsn: 논리 복제 소비자가 수신을 확인한 WAL 위치active: 현재 슬롯을 사용하는 복제 연결의 존재 여부active_pid: 슬롯을 사용 중인 WAL Sender 프로세스 번호wal_status: 필요한 WAL이 현재 보존되고 있는지 보여주는 상태safe_wal_size: 슬롯이 WAL 손실 위험에 도달하기 전까지 추가로 기록할 수 있는 추정 용량xmin,catalog_xmin: 슬롯이 Vacuum으로부터 보호하는 오래된 트랜잭션 기준점
현재 연결된 복제 프로세스도 함께 확인한다.
SELECT pid,
usename,
application_name,
client_addr,
state,
sent_lsn,
write_lsn,
flush_lsn,
replay_lsn,
pg_size_pretty(
pg_wal_lsn_diff(
pg_current_wal_lsn(),
replay_lsn
)
) AS replay_lag_bytes,
write_lag,
flush_lag,
replay_lag
FROM pg_stat_replication
ORDER BY replay_lsn NULLS FIRST;
pg_stat_replication에 세션이 없고 슬롯만 남아 있다면 소비자 연결이 완전히 끊긴 상태일 가능성이 크다. 연결은 있지만 LSN이 장시간 이동하지 않으면 네트워크 문제, Subscriber 오류, Apply 지연 또는 대상 DB의 잠금과 성능 문제를 점검해야 한다.
자체 구축 PostgreSQL이라면 실제 pg_wal 디렉터리 사용량도 운영체제에서 확인할 수 있다. 다만 Amazon RDS와 Aurora PostgreSQL에서는 관리형 스토리지에 직접 접근할 수 없으므로 CloudWatch 지표와 SQL 결과를 이용해야 한다. Aurora PostgreSQL의 논리 복제에서는 ReplicationSlotDiskUsage, OldestReplicationSlotLag, VolumeBytesUsed, FreeLocalStorage 등의 관련 지표를 환경에 맞게 함께 관찰하는 것이 좋다.
슬롯 삭제 전 확인해야 할 복제 소비자
장애 대응 과정에서 가장 중요한 단계는 각 슬롯의 소유자와 용도를 확인하는 것이다. 슬롯 이름만으로 사용 여부를 판단해서는 안 된다. 다음 항목을 함께 조사해야 한다.
- 물리 Standby 또는 Read Replica가 사용하는 슬롯인지
- PostgreSQL Subscription이 자동 생성한 논리 슬롯인지
- AWS DMS의 Full Load 및 CDC Task가 사용하는 슬롯인지
- Debezium이나 Kafka Connect 커넥터가 사용하는 슬롯인지
pglogical확장 기능이 만든 슬롯인지- Blue/Green Deployment 또는 데이터 마이그레이션 작업과 관련된 슬롯인지
- 백업, 감사 또는 외부 데이터 파이프라인이 사용하는 슬롯인지
Subscriber에서는 다음 쿼리로 Subscription과 슬롯 이름을 확인할 수 있다.
SELECT subname,
subenabled,
subslotname,
subconninfo,
subpublications
FROM pg_subscription;
논리 복제 Worker의 상태도 확인한다.
SELECT subid,
subname,
pid,
relid::regclass AS relation_name,
received_lsn,
latest_end_lsn,
latest_end_time
FROM pg_stat_subscription
ORDER BY subname;
AWS DMS를 사용한다면 DMS 콘솔에서 Task 상태가 Running, Stopped, Failed 중 무엇인지 확인하고, 해당 슬롯을 다시 사용할 계획이 있는지 검토해야 한다. Debezium이나 Kafka Connect는 커넥터 설정의 slot.name, 오류 로그, 마지막 처리 LSN을 확인한다.
개인적으로 복제 슬롯 장애를 처리할 때는 active = false라는 값만 보고 삭제하지 않는다. 슬롯명, 생성 목적, 마지막 연결 시간, 대상 시스템의 존재 여부, 재동기화 가능 여부를 표로 정리한 다음 삭제 대상을 결정하는 방식이 가장 안전했다. 운영 중단으로 잠시 비활성화된 슬롯과 이미 폐기된 슬롯은 SQL 결과만으로 완벽히 구분할 수 없기 때문이다.
상황별 안전한 WAL 복구 절차
복구 방법은 슬롯의 소비자가 다시 사용할 수 있는지에 따라 달라진다.
소비자가 정상적으로 존재하고 단순히 일시 중단된 상태라면 Subscriber나 CDC 작업을 먼저 복구한다. 네트워크, 인증서, 비밀번호, 방화벽, Publication, Subscription과 대상 DB 오류를 해결한 뒤 슬롯의 restart_lsn과 confirmed_flush_lsn이 이동하는지 확인한다. 복제 처리가 WAL 생성 속도보다 빨라야 누적량이 감소한다.
소비자가 느려서 지연이 증가하는 상황이라면 대상 DB의 잠금, 인덱스, Apply Worker, CPU, I/O와 대량 트랜잭션을 점검한다. Primary의 쓰기량을 일시적으로 줄일 수 있다면 복제 회복 시간을 단축할 수 있다.
사용자가 명확히 확인한 폐기 슬롯은 다음과 같이 제거할 수 있다.
SELECT pg_drop_replication_slot('unused_slot_name');
활성 슬롯은 바로 삭제할 수 없다. 먼저 해당 슬롯을 사용하는 서비스나 복제 연결을 정상적으로 중지해야 한다. 불가피하게 연결 종료가 필요하다면 active_pid의 용도를 확인한 후 세션을 종료한다.
SELECT pg_terminate_backend(active_pid)
FROM pg_replication_slots
WHERE slot_name = 'unused_slot_name'
AND active_pid IS NOT NULL;
세션 종료 후 슬롯이 비활성 상태인지 다시 확인하고 삭제한다. 슬롯이 삭제되면 해당 슬롯 때문에 보존되던 WAL은 이후 체크포인트와 WAL 정리 과정에서 재활용 또는 제거 대상이 된다. 디스크 사용량이 즉시 줄어들지 않을 수 있으므로 체크포인트 상태와 사용량 추이를 관찰해야 한다.
슬롯을 유지한 채 위치를 강제로 앞으로 이동시키는 pg_replication_slot_advance()도 있지만, 건너뛴 WAL 변경은 소비자에게 다시 전달할 수 없다.
SELECT pg_replication_slot_advance(
'logical_slot_name',
pg_current_wal_lsn()
);
이 명령은 데이터 누락을 허용하고 별도의 재동기화 계획이 있을 때만 사용해야 한다. 디스크를 빠르게 줄이기 위한 일반적인 해결책으로 사용하면 Primary와 Subscriber의 데이터가 달라질 수 있다.
필요한 WAL이 이미 삭제되어 슬롯의 wal_status가 손실 또는 무효 상태를 나타낸다면 단순 재시작으로 복제가 회복되지 않을 수 있다. 물리 Standby는 새로운 Base Backup으로 다시 구축해야 할 수 있으며, 논리 Subscriber는 새로운 스냅샷이나 초기 데이터 복사를 수행한 뒤 Slot과 Subscription을 재생성해야 한다.
어떤 상황에서도 운영체제에서 pg_wal 파일을 직접 삭제하면 안 된다. PostgreSQL이 필요로 하는 WAL을 임의로 제거하면 서버 기동 실패, 복구 불가능 또는 데이터 손상으로 이어질 수 있다. WAL 정리는 반드시 슬롯, 복제 소비자 및 PostgreSQL 설정을 통해 처리해야 한다. PostgreSQL Replication Slot 주의 사항
WAL 재발 방지를 위한 운영 기준
복구가 끝났다면 동일한 문제가 반복되지 않도록 슬롯별 모니터링을 구성해야 한다. 최소한 다음 항목을 일정 주기로 수집하는 것이 좋다.
- 슬롯별
active상태 - 현재 LSN과
restart_lsn의 차이 - 논리 슬롯의
confirmed_flush_lsn wal_status와safe_wal_sizexmin과catalog_xmin의 나이- 전체 디스크 사용률과 시간당 WAL 증가량
- Subscriber 및 CDC 작업 상태
경보 기준은 고정된 용량만으로 정하기보다 디스크 여유 공간과 WAL 생성 속도를 함께 고려해야 한다. 예를 들어 남은 공간이 500GB라도 시간당 WAL이 100GB씩 증가한다면 대응 시간은 길지 않다. 다음과 같은 방식으로 예상 잔여 시간을 계산할 수 있다.
예상 잔여 시간 = 현재 디스크 여유 공간 ÷ 시간당 WAL 증가량
max_slot_wal_keep_size를 설정하면 슬롯 하나가 무제한으로 WAL을 보존하는 것을 제한할 수 있다. 그러나 제한을 넘으면 소비자가 필요한 WAL을 잃어 복제를 다시 구축해야 할 수 있다. 이 설정은 장애를 자동으로 해결하는 기능이라기보다 Primary 디스크 고갈과 복제 재구축 중 어느 위험을 우선 통제할 것인지 결정하는 보호 장치다.
최신 PostgreSQL 버전에서는 장시간 비활성 슬롯을 무효화하는 idle_replication_slot_timeout도 검토할 수 있다. 다만 정기적으로만 접속하는 정상 슬롯까지 무효화될 수 있으므로 복제 운영 주기보다 충분히 긴 값과 모니터링 체계가 필요하다.
Aurora PostgreSQL에서 논리 복제를 사용한다면 rds.logical_replication 활성화만으로 끝내지 말고 슬롯 수, 소비 속도, ReplicationSlotDiskUsage와 논리 WAL 캐시 상태를 함께 관리해야 한다. AWS도 비활성 논리 슬롯을 방치하면 WAL 보존과 Vacuum 지연이 발생할 수 있으므로 지속적인 모니터링과 불필요한 슬롯 정리를 권장한다. Aurora PostgreSQL 논리 복제 구성
마치며
PostgreSQL Replication Slot은 데이터 손실 없이 복제를 이어가기 위한 중요한 장치지만, 소비자 상태와 무관하게 필요한 WAL을 계속 보존한다. 따라서 복제 작업이 중단되거나 폐기된 뒤 슬롯만 남으면 restart_lsn이 멈추고 WAL 디스크 사용량이 빠르게 증가할 수 있다.
안전한 복구의 핵심은 슬롯을 먼저 삭제하는 것이 아니라 해당 슬롯의 실제 소비자를 확인하는 것이다. 복구 가능한 소비자는 다시 연결해 누적 WAL을 처리하고, 폐기가 확정된 슬롯만 삭제해야 한다. 필요한 WAL이 이미 손실됐거나 슬롯 위치를 강제로 이동했다면 Subscriber를 재동기화하거나 복제를 처음부터 재구성해야 한다.
장애가 해결된 뒤에는 active 상태만 확인하지 말고 슬롯별 보존 WAL 용량, LSN 이동 속도, wal_status, 디스크 여유 공간과 시간당 WAL 증가량을 함께 감시하는 것이 중요하다. Replication Slot은 생성보다 폐기와 모니터링 절차가 더 중요한 운영 객체다. 소유자, 사용 목적, 최대 허용 지연과 정리 기준을 사전에 정해두면 디스크 고갈 장애와 불필요한 복제 재구축을 예방할 수 있다.




댓글 0
첫 댓글을 남겨보세요.