본문으로 건너뛰기

6.3 내구성과 가용성 모델

Vanilla PostgreSQL에서는 로컬 디스크 fsync가 COMMIT의 내구성을 보장합니다. standby와 failover 도구는 가용성을 보장합니다. Neon은 두 책임을 서로 다른 컴포넌트에 맡깁니다. 내구성은 safekeeper quorum과 object storage가 담당합니다. 가용성은 stateless compute의 재기동이 담당합니다. 이 분리 덕분에 compute가 소실되어도 데이터는 안전합니다. 반면 compute가 재기동하는 동안에는 서비스가 멈춥니다. 이 장에서는 장애 유형별로 어떤 보장이 유지되고 어떤 서비스가 멈추는지 정리합니다.

Commit이 완료되는 조건

Compute의 WAL proposer는 WAL을 safekeeper 세 대에 push합니다. 과반(3 중 2)이 디스크에 기록했다고 응답하면 COMMIT을 클라이언트에 반환합니다. Paxos 기반 합의가 quorum과 단일 primary를 관리합니다. Compute 로컬 디스크의 fsync는 내구성에 관여하지 않습니다.

이후 흐름은 비동기입니다. Pageserver가 safekeeper에서 WAL을 받아 layer 파일로 재구성합니다. layer 파일은 object storage(S3)에 업로드됩니다. Safekeeper는 pageserver가 처리한 뒤 S3에 업로드된 구간의 WAL을 정리합니다. 자세한 LSN 종류(CommitLSN, FlushLSN, VCL, remote_consistent_lsn)는 2.2 Safekeeper와 WAL 내구성에 있습니다.

장애 유형별 영향

장애데이터서비스복구
Compute 노드 소실유지 (WAL은 quorum에 있음)중단새 compute가 같은 timeline에 attach
Safekeeper 1대 소실유지 (나머지 2대가 quorum)지속새 safekeeper 투입 후 WAL 재동기화
Safekeeper 2대 일시 정지유지 (디스크가 남아 있음)쓰기 중단quorum 복구까지 대기
Safekeeper 2대 영구 소실최신 commit 유실 가능쓰기 중단S3와 남은 1대의 WAL 기준으로 복구
Pageserver 소실유지 (S3의 layer + safekeeper WAL)읽기 지연 또는 중단다른 pageserver가 tenant attach, S3에서 layer 다운로드
Object storage 장애최근 구간은 safekeeper와 pageserver 로컬에 존재캐시된 layer 범위에서 지속복구 후 업로드와 다운로드 재개

Safekeeper 항목을 두 줄로 나눈 이유가 있습니다. Quorum은 세 대 중 두 대의 응답으로 commit을 확정하므로 쓰기는 한 대 장애까지만 이어집니다. 두 대가 멈춰도 디스크가 살아 있다면 재기동으로 quorum이 복구되고 데이터도 그대로입니다. 반면 두 대의 저장 매체가 영구히 사라지면 이야기가 달라집니다. 특정 commit의 WAL이 하필 그 두 대에만 기록되어 있었고 S3 업로드가 아직 끝나지 않은 구간이라면, 남은 한 대와 S3에서 그 commit을 찾을 수 없습니다. S3 반영은 commit 경로가 아니라 후속 비동기 단계이기 때문입니다.

Compute 장애가 데이터 손실을 일으키지 않는 이유는 compute에 durable state가 없기 때문입니다. 새 compute는 pageserver에서 basebackup 성격의 초기 tarball과 페이지를 받아 기동합니다. 마지막 commit LSN까지의 상태를 그대로 이어갑니다. 다만 shared_buffers와 LFC가 비어 있습니다. 따라서 6.2의 콜드스타트와 같은 워밍업 비용이 발생합니다.

Pageserver 장애는 읽기 경로에 직접 영향을 줍니다. Neon 클라우드에서는 storage controller가 tenant를 다른 pageserver에 attach합니다. 이후 필요한 layer를 S3에서 받습니다. 이 과정에서 GetPage@LSN 요청이 누적되어 쿼리 지연이 커지거나 잠시 실패합니다. 쓰기는 safekeeper가 수신하므로 계속됩니다. 하지만 pageserver가 따라오지 못하면 backpressure로 인해 쓰기 backend가 대기합니다.

Object storage는 최종 데이터 원본이면서 쿼리 경로에서 가장 먼 계층입니다. S3 장애가 발생해도 최근 데이터는 safekeeper와 pageserver 로컬 디스크에 있습니다. 다만 pageserver는 object storage의 읽기 캐시 역할을 하므로, 서비스가 이어지는 범위는 필요한 layer가 pageserver 로컬에 캐시되어 있는 동안입니다. 캐시에 없는 오래된 layer를 읽어야 하는 쿼리는 demand download가 실패해 오류가 나거나 대기합니다. 업로드가 지연될수록 로컬 디스크 사용량도 늘어납니다. 장애가 길어지면 safekeeper가 WAL을 정리하지 못해 디스크가 가득 찰 위험이 커집니다.

RPO와 RTO로 옮겨 보기

지표Neon 클라우드근거
RPO0 (quorum 사본이 보존되는 장애 범위)quorum ack 이후 반환
RPO (quorum 사본 2대 영구 소실)최신 commit 유실 가능S3 업로드는 commit 경로 밖의 비동기 단계
RTO (compute 장애)compute 재기동 시간문서에 고정 수치 없음, 측정 필요
RTO (pageserver 장애)tenant 재attach + layer 다운로드 시간데이터 크기와 캐시 상태에 의존
과거 시점 복구history window 안 임의 시점Instant restore, branch at LSN

RPO 0은 commit이 반환된 트랜잭션에, 그리고 quorum 사본이 보존되는 장애 범위에 적용됩니다. 설계 밖의 시나리오, 즉 commit WAL을 가진 두 대의 매체가 동시에 영구 소실되는 경우는 위 표의 둘째 줄로 따로 다룹니다.

COMMIT 응답을 받지 못한 트랜잭션은 두 가지로 나뉩니다. 하나는 WAL이 quorum에 도달하기 전에 끊긴 경우로, 이것은 확실히 미commit입니다. 다른 하나는 quorum 기록까지 끝났지만 응답만 유실된 경우입니다. 이때 트랜잭션은 실제로 커밋되어 있으므로 클라이언트 입장에서는 결과 불명 상태입니다. 애플리케이션은 이 둘을 구분할 수 없으므로 재시도 경로에 idempotency key를 두거나, 재시도 전에 트랜잭션 결과를 조회해 확인합니다. 무조건 재시도하면 중복 처리가 생깁니다.

RTO는 문서에서 보장하는 수치가 없습니다. 따라서 도입 전에 compute를 강제로 재시작해 측정합니다. Neon API의 endpoint restart로 재현할 수 있습니다.

Read replica와 리전

Read replica는 같은 branch의 pageserver를 읽는 read-only compute입니다. 데이터 복사가 없으며 생성은 수 초 안에 끝납니다. Safekeeper가 최신 WAL을 replica에도 전달하므로 지연은 비동기 복제 수준입니다. 리전에는 제약이 있습니다. Replica는 같은 리전 안에서만 만들 수 있습니다. 리전을 넘는 구성은 별도 project와 logical replication으로 구성합니다. Free 플랜은 project당 replica 3개까지입니다.

이 구조에서 read replica는 읽기 부하 분산 장치이며 failover 대상은 아닙니다. Primary compute가 종료되어도 replica가 승격되지는 않습니다. 대신 새 primary compute가 기동됩니다.

HA 현황

Neon 문서의 Neon과 Lakebase 비교 표에서 "High availability"는 Neon 쪽에 "Coming soon"으로 표기되어 있습니다. Databricks의 Lakebase Postgres는 엔터프라이즈용 HA와 DR을 강조합니다. 같은 저장 엔진을 사용하더라도 control plane 정책이 다르다는 뜻입니다. 따라서 Neon 클라우드에 다중 compute 자동 failover를 기대한다면 도입 시점의 문서와 SLA를 다시 확인해야 합니다. 이 장의 내용은 2026년 9월 문서 기준입니다. 변경 가능성이 높은 항목입니다.

자체 호스팅에서 사라지는 보장

Part III의 docker compose 구성에는 storage controller와 control plane이 없습니다. 이 차이로 가용성 모델이 달라집니다.

  • Pageserver가 종료되면 tenant를 다른 pageserver로 이전할 주체가 없습니다. 컨테이너가 재시작되어 같은 데이터 디렉터리와 MinIO를 다시 읽을 때까지 읽기가 멈춥니다.
  • Compute가 종료되면 compute_ctl이 재시작합니다. 하지만 새 노드로 이전하거나 endpoint 주소를 변경하는 계층은 없습니다.
  • Safekeeper 교체 시 멤버십 변경을 자동으로 처리하는 절차가 없습니다.
  • MinIO 단일 노드는 S3의 내구성을 대신하지 못합니다.

upstream README에서도 이 구성이 이미지 테스트용이며 운영용이 아니라고 밝힙니다. 자체 호스팅으로 운영 수준의 가용성을 확보하려면 두 가지를 구분해야 합니다. Storage controller는 소스와 온프레미스 배포 문서가 공개되어 있어 직접 배포합니다. 반면 실행 중인 compute를 재설정하는 compute hook과 endpoint 오케스트레이션 계층은 배포자가 구현해야 합니다. Neon 클라우드의 완성된 control plane 서비스는 공개되지 않았습니다. 상세 내용은 3.6 자체 호스팅 한계와 문제 해결을 참고합니다.

논리적 장애와 history window

하드웨어 장애보다 잘못된 DELETE나 실패한 마이그레이션 같은 논리적 손상을 더 자주 겪습니다. Neon에서 이 복구는 백업 복원이 아닙니다. history window 안의 LSN 또는 시각을 지정해 branch를 만듭니다. 또는 neon branches restore로 branch를 되돌립니다. 여기서 두 가지 시간을 구분해야 합니다. Branch나 restore 자체는 메타데이터 작업이라 데이터 크기와 거의 무관하게 끝납니다. 그러나 복구한 branch가 정상 성능으로 쿼리를 받기까지는 compute 기동 시간, shared_buffers와 LFC 워밍업, object storage에서 내려받을 layer 양이 더해집니다. 백업 파일을 풀고 WAL을 재생하는 vanilla PITR과 달리 첫 단계가 상수 시간이라는 점이 차이입니다. 반대로 history window 밖의 시점은 복구할 수 없습니다. 따라서 window 길이가 곧 논리적 장애의 RPO 한계입니다. Free 6시간, Launch 7일, Scale 30일이라는 숫자는 비용 항목인 동시에 복구 정책 항목입니다.

# 잘못된 DELETE 직전 시각으로 새 branch를 만들어 데이터를 확인합니다 (--parent에 시각을 주면 기본 branch의 그 시점)
neon branches create --name recover-0907 --parent 2026-09-07T05:59:00Z
# 확인이 끝나면 운영 branch를 그 시점으로 되돌리고 현재 상태는 이름을 붙여 보존합니다
neon branches restore main ^self@2026-09-07T05:59:00Z --preserve-under-name main-before-restore

운영 점검 항목

Neon 클라우드에서는 컴포넌트 상태를 직접 확인할 수 없습니다. 따라서 콘솔의 compute 상태와 operation 이력을 관측합니다. 자체 호스팅에서는 다음 항목을 주기적으로 확인합니다.

  • Pageserver의 /v1/tenant/{tenant_id}/timeline 응답에서 last_record_lsnremote_consistent_lsn 차이. 차이가 계속 벌어지면 S3 업로드가 지연되고 있습니다.
  • Safekeeper 컨테이너의 /data 디스크 사용량. Pageserver가 처리하지 못하면 WAL이 정리되지 않아 사용량이 늘어납니다.
  • Compute 로그의 backpressure 관련 대기 메시지.

장애 대응 절차 초안

자체 호스팅 환경에서 장애 유형을 판별하는 순서입니다. Neon 클라우드에서는 1번만 사용자가 처리하고 나머지는 플랫폼이 처리합니다.

  1. Compute 접속 실패인지 쿼리 지연인지 구분합니다. 접속 자체가 실패하면 compute 문제일 가능성이 높습니다. 접속은 되지만 특정 테이블 조회가 멈추면 pageserver 문제일 가능성이 높습니다.
  2. curl http://localhost:9898/v1/tenant/{tenant_id}/timeline으로 pageserver의 응답 여부를 확인합니다. 이어 last_record_lsn이 compute의 pg_current_wal_flush_lsn()과 얼마나 떨어져 있는지 확인합니다.
  3. Safekeeper 세 대의 HTTP 포트(7676~7678)에 /v1/status를 호출해 정상인 수를 셉니다. 두 대 이상이면 쓰기는 계속됩니다.
  4. MinIO 콘솔에서 neon 버킷의 최근 객체 시각을 확인해 업로드가 중단된 시점을 찾습니다.
  5. 컴포넌트를 재시작할 때는 safekeeper, pageserver, compute 순서로 기동합니다. Compute 기동 스크립트는 pageserver 6400 포트가 열릴 때까지 대기합니다.
# 각 컴포넌트 상태를 한 번에 확인합니다
curl -s http://localhost:9898/v1/status
for p in 7676 7677 7678; do curl -s "http://localhost:$p/v1/status" | head -c 200; echo; done
docker compose exec -T compute1 psql -h localhost -p 55433 -U cloud_admin -d postgres -c 'select pg_current_wal_flush_lsn()'

실패 사례

Neon 클라우드 대신 자체 호스팅 구성을 staging으로 사용하던 중 pageserver 컨테이너의 볼륨을 재생성한 경우를 가정합니다. MinIO에 layer가 남아 있더라도 pageserver가 tenant를 자동으로 다시 attach하지 않으면 데이터가 사라진 것처럼 보입니다. 원인은 데이터 손실이 아니라 attach 절차가 없다는 점입니다. /v1/tenant/{tenant_id}/location_config로 attach를 다시 요청하면 S3에서 layer를 받아 복구됩니다. 이 절차를 runbook으로 갖추었는지가 자체 호스팅의 실질적인 가용성을 결정합니다.

연습 문제

  1. Part III 환경에서 compute1 컨테이너를 강제로 종료하고 재시작합니다. 이후 마지막 INSERT가 남아 있는지 확인하고 재접속까지 몇 초가 걸리는지 기록합니다.
  2. Safekeeper 한 대를 중단한 상태에서 쓰기가 계속되는지 확인합니다. 두 대를 중단하면 어떤 오류가 발생하는지도 확인합니다.
  3. Pageserver를 중단한 상태에서 LFC에 있는 데이터와 없는 데이터의 SELECT 결과가 어떻게 다른지 관찰합니다.

심화 체크

  • 우리 서비스의 RTO 요구와 compute 재기동 시간을 비교해 측정값을 확보했는가?
  • 리전 장애 시나리오에 대해 logical replication 기반 대안을 설계했는가?
  • 자체 호스팅이라면 tenant re-attach 절차를 문서화했는가?

참고