PostgreSQL 19 WAIT FOR: 비동기 복제본에서 Read-Your-Writes 일관성 확보
요약
PostgreSQL은 읽기 부하를 비동기 복제본(async replica)으로 분산하는 아키텍처를 오랫동안 지원해 왔다. 그러나 비동기 복제본은 주 서버보다 약간 뒤처지기 때문에, 사용자가 방금 쓴 데이터를 즉시 복제본에서 읽으면 보이지 않는 경우가 생긴다. 이 Read-Your-Writes(RYW) 불일치 문제를 해결하기 위해 애플리케이션은 지금까지 임시 해결책(세션별 타임아웃, 쓰기 후 primary로 재라우팅)에 의존해 왔다. PostgreSQL 19 Beta 1(2026년 6월 4일 출시)이 도입한 WAIT FOR 명령은 이 문제를 데이터베이스 레이어에서 직접 해결하는 첫 번째 공식 메커니즘이다.
1. 문제: 비동기 복제의 일관성 갭
비동기 복제의 동작 방식
PostgreSQL의 스트리밍 복제는 기본적으로 비동기 모드다. 주 서버가 트랜잭션을 커밋하면 즉시 클라이언트에게 성공을 응답하고, WAL(Write-Ahead Log) 레코드는 별도 스트림으로 복제본에 전달된다. 복제본의 WAL 리시버가 WAL을 받아 적용(replay)하는 데는 수 밀리초에서 수백 밀리초의 지연이 있다.
→ "내 데이터가 없어요!"
기존 해결책의 한계
| 방법 | 동작 | 문제점 |
|---|---|---|
| 쓰기 후 primary로 재라우팅 | 일정 시간 또는 세션 전체를 primary에서 읽기 | primary 읽기 부하 증가, 복제본 효과 반감 |
| 동기 복제 | synchronous_commit = on | 쓰기 레이턴시 급증, 복제본 장애 시 쓰기 블로킹 |
pg_sleep + 재시도 | 쓰기 후 일정 시간 대기 | 레이턴시 낭비, 복제 지연이 가변적이라 신뢰 불가 |
| PgPool / Pgbouncer 라우팅 미들웨어 | LSN 기반 라우팅 직접 구현 | 복잡성 높고 각 미들웨어마다 구현 방식이 달라 표준화 어려움 |
2. WAIT FOR 명령: 문법과 모드
기본 문법
WAIT FOR LSN 'lsn' [ WITH ( option [, ...] ) ]옵션:
WAIT FOR LSN '0/1ABC000' WITH (
MODE 'standby_replay', -- 적용 모드 (기본값)
TIMEOUT '5000', -- 최대 대기 밀리초 (0 = 무제한)
NO_THROW -- 타임아웃 시 오류 대신 false 반환
)네 가지 모드
standby_replay (기본값)가 RYW 일관성에 직접 해당하는 모드다. 복제본이 해당 LSN까지 WAL을 적용(replay)한 후에야 쿼리를 실행한다. 이 모드는 standby 서버에서만 사용 가능하다.
primary_flush 는 primary 서버에서만 사용하며, 특정 LSN까지 WAL이 durable하게 저장됐는지 확인하는 용도다.
3. 실전 사용 패턴
패턴 1: 쓰기 후 복제본 읽기 보장
-- Primary에서 쓰기 수행
INSERT INTO orders (user_id, amount) VALUES (42, 9900);
-- 쓰기 시점의 LSN 수집
SELECT pg_current_wal_lsn() AS write_lsn;
-- 예: 0/1ABC000
-- ─────────────────────────────────────────────
-- Replica에 연결 후:
-- 해당 LSN이 replay될 때까지 대기 (최대 3초)
WAIT FOR LSN '0/1ABC000' WITH (
MODE 'standby_replay',
TIMEOUT '3000',
NO_THROW
);
-- 이제 방금 쓴 데이터를 안전하게 읽을 수 있음
SELECT * FROM orders WHERE user_id = 42;NO_THROW 옵션을 쓰면 타임아웃 시 오류 대신 false를 반환하므로, 애플리케이션이 직접 처리 흐름을 제어할 수 있다.
패턴 2: 미들웨어(Pgpool, HAProxy) 통합
-- 미들웨어가 LSN을 쿠키/헤더에 담아 복제본 연결 시 자동 실행
-- (PgBouncer init_query 또는 애플리케이션 커넥션 훅)
WAIT FOR LSN :'client_lsn' WITH (MODE 'standby_replay', TIMEOUT '2000', NO_THROW);세션 변수나 애플리케이션 파라미터로 LSN을 전달하면, 미들웨어 레이어에서 일관성 보장을 투명하게 처리할 수 있다.
패턴 3: 함수로 추상화
CREATE OR REPLACE FUNCTION wait_for_replica(lsn pg_lsn, timeout_ms int DEFAULT 2000)
RETURNS bool AS $
BEGIN
WAIT FOR LSN lsn WITH (MODE 'standby_replay', TIMEOUT timeout_ms::text, NO_THROW);
RETURN true;
EXCEPTION WHEN OTHERS THEN
RETURN false;
END;
$ LANGUAGE plpgsql;4. 구현 내부: WAL replay 알림 메커니즘
복제본의 WAL replay 프로세스(startup process)는 WAL 레코드를 하나씩 적용할 때마다 내부 공유 메모리의 walRcvWrittenLSN과 replayLSN을 갱신한다. WAIT FOR를 실행한 세션은 이 값을 폴링하거나 조건 변수(condition variable)를 통해 알림을 받는다. CPU 스핀 없이 latch 기반으로 대기하므로, 다수의 세션이 동시에 WAIT FOR를 실행해도 replay 프로세스에 영향을 주지 않는다.
WAIT FOR LSN 실행
replayLSN 상태
(CPU 스핀 없음)
레코드 적용 시마다
replayLSN 업데이트
5. 제한 사항과 트레이드오프
복제 지연이 크면 대기 시간도 길어진다
WAIT FOR는 마법이 아니다. 복제본이 실제로 지연된 만큼 기다린다. 복제 지연(replication lag)이 수 초에 달하는 상황에서 WAIT FOR를 남용하면 읽기 레이턴시가 쓰기 레이턴시보다 길어진다.
적합한 환경: 복제 지연이 < 100 ms 인 안정적 클러스터
부적합한 환경: 복제 지연이 크거나 가변적인 환경 (네트워크 불안정, 복제본 고부하)
TIMEOUT 설정 지침
TIMEOUT = max(예상 복제 지연 × 3, 500ms)타임아웃이 너무 짧으면 NO_THROW 없이는 오류가 발생하고, 너무 길면 사용자 경험에 악영향을 준다. NO_THROW와 함께 사용하고, 타임아웃 발생 시 primary로 재라우팅하는 폴백을 두는 것이 권장 패턴이다.
standby 전용 모드의 제약
standby_replay, standby_write, standby_flush는 복제본(recovery 모드)에서만 동작한다. Primary 서버에서 실행하면 오류가 발생한다. 애플리케이션이 연결을 primary/replica로 구분하지 않는 경우 주의가 필요하다.
6. 기존 솔루션과의 비교
| 특성 | WAIT FOR (PG 19) | pg_last_xact_replay_timestamp 폴링 | 동기 복제 (synchronous_commit) | Citus / 분산 DB |
|---|---|---|---|---|
| 구현 위치 | DB 내부 | 애플리케이션 | DB 내부 | 플랫폼 |
| 쓰기 레이턴시 영향 | 없음 | 없음 | 있음 (복제본 응답 대기) | 있음 |
| 읽기 레이턴시 | 복제 지연만큼 추가 | 폴링 간격만큼 추가 | 추가 없음 | 다양 |
| 복제본 장애 시 | 타임아웃 후 폴백 | 애플리케이션 처리 | 쓰기 블로킹 | 다양 |
| 표준화 | SQL 표준 (PG 19+) | 비표준 | SQL 표준 | 플랫폼 의존 |
7. 운영 체크리스트
- [ ] PostgreSQL 버전 확인: 19 Beta 1+ 필요 (2026년 6월 4일 이후); GA 릴리스는 2026년 말 예상
- [ ] 복제 지연 모니터링:
pg_stat_replication.replay_lag을 Prometheus exporter로 수집 - [ ] TIMEOUT 값 결정:
replay_lag p99 × 2를 기본값으로, 애플리케이션 SLO에 맞게 조정 - [ ] NO_THROW + 폴백 구현: 타임아웃 발생 시 primary로 재라우팅하는 코드 경로 확보
- [ ] standby 전용 코드 경로: primary 연결에
WAIT FOR standby_*모드가 실행되지 않도록 Guard 추가 - [ ] 커넥션 풀 통합: pgBouncer
server_reset_query나 Pgpool session_init_query로 LSN 자동 설정 여부 검토 - [ ] 부하 테스트: WAIT FOR 대기 중 복제본 부하 측정 (latch 기반이므로 CPU 부담은 낮음)
References
- PostgreSQL: Documentation: 19: WAIT FOR
- PostgreSQL 19 Beta 1 Released! — PostgreSQL News
- Read your writes: WAIT FOR in PostgreSQL 19 — ClickHouse Blog
- Reading your own writes with WAIT FOR LSN in Postgres 19 — Redowan's Reflections
- Read-your-writes on replicas: PostgreSQL WAIT FOR LSN and MongoDB Causal Consistency — DEV Community
- PostgreSQL 19 Monitoring and Operations Improvements — Neon