현장 사례와 재현 실험에 따르면 Postgres의 recovery_prefetch는 복제본의 디스크 읽기 IOPS를 크게 늘려 동기식 복제 환경에서 쓰기 중단을 일으킬 수 있다. 특히 쓰기 작업이 몰린 때 복제 연결까지 끊기면 복제본이 따라잡을 때까지 기본 서버의 쓰기가 오래 멈출 수 있다는 주장이다.
이 문제와 관련해 2021년 2월에는 기능 개발 중 스티븐 프로스트(Stephen Frost)가 불필요한 프리페치와 FPW가 활성화된 경우의 영향을 우려했다. 2025년 3월에는 Postgres Pro의 미하일 질린(Michael Zhilin)이 한 고객의 물리적 대기 서버에서 몇 시간에 이르는 재생 지연이 발생했고, recovery_prefetch를 끄자 문제가 해결됐다고 보고했다. 질린은 WAL의 FPI에서 공유 버퍼가 채워진 경우에도 프리페치 코드가 이를 고려하지 않고 fadvise 호출을 한다고 설명했다.
이 글은 두 보고가 문제의 일부를 짚었지만 전체 원인은 아니라고 본다. 첫째, FPW(Full Page Writes)는 운영 환경에서 널리 활성화돼 있다. 성능 비용이 크지만, 운영체제 재시작 때 발생하는 찢긴 쓰기(torn write)를 막는 Postgres의 유일한 방법이라는 설명이다. 둘째, 동기식 복제도 더 보편화되고 있다. 글은 하드웨어 장애로 인한 데이터 손실을 막는 방법으로 DRBD 같은 오픈소스 스토리지 복제나 고가의 독점 스토리지·클러스터링 기술, 또는 Postgres 동기식 복제를 들며, 쿠버네티스와 일반 서버 환경에서는 후자가 일반적인 선택이라고 설명한다.
글에서 새롭게 지적하는 문제는 FPI뿐 아니라 순차 읽기 작업이 대기 서버에서 8KB 단위의 무작위 읽기로 바뀔 수 있다는 점이다. 기본 서버가 대량의 순차 I/O를 수행하면 recovery_prefetch가 대기 서버의 읽기 작업을 늘려, 같은 데이터 용량을 읽더라도 IOPS가 최대 16배까지 증가할 수 있다는 주장이다. 이로 인해 대기 서버의 가용 IOPS가 소진되고 쓰기 작업 뒤 복제 지연이 커질 수 있다.
재현 조건과 복제 지연
글에 제시된 재현 절차는 저사양 서버와 동기식 복제 환경을 사용한다. 예시 서버는 500 IOPS 클라우드 디스크, CPU 2개, 메모리 8GB다. pgbench 데이터 규모는 약 10GB인 스케일 640으로 초기화한다.
pgbench_accounts에서 자동 진공(autovacuum)을 끈다.- 터미널에서 고정 5 TPS로
pgbench를 실행한다. - 체크포인트를 수행하고
aid%20 = 0인pgbench_accounts행을 갱신해 테이블의 5%에 쓰기 작업을 발생시킨다. 작업이 끝날 때까지 기다린다. - 네트워크나 시스템 정지·불안정 상황을 흉내 내기 위해
walreceiver프로세스를 종료한다. - 이 실험에서는 TPS가 0으로 떨어지고 모든 쓰기가 30분 넘게 멈췄다.
pgbench테이블이 더 크면 몇 시간까지 이어질 수 있다고 한다.
글은 VACUUM도 쓰기 작업을 유발할 수 있으므로, 네트워크 장애로 스트리밍 복제가 끊기는 순간 쓰기 작업이 몰리는 것만으로도 장애 조건이 만들어질 수 있다고 설명한다. 동기식 복제에서는 정상 작동하는 대기 서버가 최소 하나 있어야 기본 서버가 쓰기를 받아들일 수 있다. 트랜잭션을 커밋하기 전에 원격 서버에 데이터가 저장되도록 보장하기 위해서다.
대기 서버의 핵심 상태 머신은 Postgres 소스 코드의 src/backend/access/transam/xlogrecovery.c에 정의돼 있다.
WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
bool fetching_ckpt, XLogRecPtr tliRecPtr,
TimeLineID replayTLI, XLogRecPtr replayLSN,
bool nonblocking)
{
static TimestampTz last_fail_time = 0;
TimestampTz now;
bool streaming_reply_sent = false;
/*-------
* Standby mode is implemented by a state machine:
*
* 1. Read from either archive or pg_wal (XLOG_FROM_ARCHIVE), or just
* pg_wal (XLOG_FROM_PG_WAL)
* 2. Check for promotion trigger request
* 3. Read from primary server via walreceiver (XLOG_FROM_STREAM)
* 4. Rescan timelines
* 5. Sleep wal_retrieve_retry_interval milliseconds, and loop back to 1.
*
* Failure to read from the current source advances the state machine to
* the next state.
...
...
글의 설명에 따르면 대기 서버가 WAL 복구 모드로 전환되면 복구를 따라잡을 때까지 스트리밍으로 돌아가지 않는다. 쓰기 급증과 IOPS 증가로 대기 서버의 지연이 커지면 스트리밍 재개가 몇 시간 늦어질 수 있다.
제시된 디스크 통계에서 기본 서버는 약 5분 동안 300 IOPS 안팎을 보였고, 같은 시간대 복제본은 40분 동안 디스크 한도인 500 IOPS에 머물렀다. 다만 두 서버에서 디스크로부터 읽은 바이트 수는 대체로 비슷했다고 글은 설명한다.
recovery_prefetch를 끄고 FPW는 켠 채 실행한 비교에서는 갱신 작업 중의 지연은 남았지만, 작업이 끝난 뒤 이어지는 긴 정지는 나타나지 않았다. 읽기 IOPS 급증도 없었고 대기 서버는 거의 지연 없이 따라잡아 5% 갱신을 10분 안에 적용했다.
recovery_prefetch를 끈 실험 조건과 결과
글은 현재 모든 환경에서 recovery_prefetch를 끄는 쪽으로 기울어 있지만, 관련 논의가 더 진행되는 것을 지켜보고 싶다고 밝혔다. FPW가 기본 활성화된 상황에서 이 설정의 이점은 불분명하다는 판단도 덧붙였다.
부록에서는 동기식 복제에서 WAL sender와 WAL receiver 사이의 연결이 끊기면 기본 서버가 쓰기를 받아들이지 못하고 대기하게 된다고 설명한다. 비용은 늘지만 전체 복제본을 세 대로 구성하고 ANY 1 쿼럼 커밋을 사용하는 방안도 데이터베이스의 견고성을 높이는 선택지로 제시한다.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요