2.2 일관된 분기점과 복구
사고 직전의 DB를 분기해 조사하려면 직전이라고 판단한 기준을 남겨야 합니다. snapshot 생성 시각, WAL 위치, 업무 transaction의 commit 시각은 서로 다른 정보입니다. 새 환경에서 SQL이 실행되는지 검증해야 합니다. 조사하려던 변경이 포함되었는지도 별도로 검증해야 합니다. 이 장에서는 복구 가능한 저장 상태와 원하는 업무 상태를 나누어 분기점을 정합니다.
분기 요청에는 원본 식별자와 기준 시점을 함께 기록합니다. 일반 PostgreSQL 복구에는 cluster와 timeline의 계보를 사용합니다. Neon에는 branch와 storage timeline의 조상 관계를 사용합니다. Dolt에는 데이터 버전 commit을 사용합니다. 이 식별 정보를 유지하면서 실제 기동과 조회 결과까지 연결하는 방법을 검토합니다.
Crash consistency와 SQL 상태
실행 중인 PostgreSQL의 일관된 파일시스템 snapshot을 살펴봅니다. 이는 정상 종료된 데이터 디렉터리와 같은 상태를 뜻하지 않습니다. 공식 문서는 이 사본으로 기동하면 WAL을 재생한다고 설명합니다. 비정상 종료 뒤의 복구와 같은 방식입니다. 여기서 crash consistency는 복구에 필요한 저장 상태가 서로 맞는다는 의미입니다. 이 저장 상태를 바탕으로 재기동과 복구를 수행합니다. snapshot 완료 직후 데이터 파일이 최종 SQL 상태를 표현하는지는 별개입니다.
WAL은 이 복구 과정의 근거입니다. PostgreSQL은 checkpoint 이후의 WAL 기록을 재생해 일관된 상태를 복구합니다. 그러므로 snapshot의 데이터 파일과 필요한 WAL을 함께 확보해야 합니다. snapshot 전 CHECKPOINT는 복구 시간을 줄이는 방법입니다. 다만 여러 볼륨의 시점을 일치시키는 기능을 대신하지는 못합니다. checkpoint 실행 여부와 snapshot 범위는 다른 검증 항목입니다.
조사 대상이 잘못된 주문 변경이라고 가정하면 두 질문을 나누어야 합니다. 첫째는 저장 사본으로 PostgreSQL을 정상 기동했는지입니다. 둘째는 그 사본에서 문제의 주문 변경이 보이는지입니다. 정상 기동에 성공해도 두 번째 질문의 답은 목표 시점에 따라 달라집니다. 사고 분석 기록에는 복구 로그를 남기는 방법을 제안합니다. 해당 주문을 식별하는 업무 조건과 조회 결과도 함께 남깁니다.
그림은 서로 다른 준비 경로를 나타내며, 동일한 복구 명령의 여러 표기가 아닙니다. 따라서 각 경로의 성공 조건은 해당 경로에 맞게 적용합니다. 작업 기록에서는 저장소의 시점 고정, 복구 처리, 업무 검증의 완료 상태를 분리합니다. 장애 조사자가 SQL 결과를 인계받을 때 확인한 단계를 알 수 있도록 검증 범위를 명시합니다.
Snapshot 기동 범위
PostgreSQL 파일시스템 snapshot은 전체 cluster 범위를 고려해야 합니다. 데이터 디렉터리, 별도 위치의 WAL, tablespace가 여러 파일시스템에 나뉘는 경우가 있습니다. 이때는 정확히 같은 시점의 snapshot을 확보하는 것이 문제입니다. 한 디렉터리의 복사 성공만 확인하는 절차로는 이 배치를 검증하지 못합니다. 운영 파일 배치와 실제 snapshot 대상을 먼저 대응시켜야 합니다.
동시 snapshot이 불가능한 경우 PostgreSQL 문서는 두 경로를 제시합니다. 서버를 정지한 동안 사본을 확보하는 경로입니다. 또는 continuous archiving을 이용한 base backup 절차를 선택합니다. 각 경로의 조건은 구분해 적용합니다. 실행 중 파일을 순차 복사한 결과를 일관된 snapshot으로 취급하지 않습니다. 사용한 백업 절차가 요구하는 WAL과 완료 조건을 따릅니다.
실무 검토에서는 저장 범위와 실행 범위를 별도 목록으로 작성하는 방법을 제안합니다. 저장 범위에는 cluster의 파일 배치와 snapshot 식별자를 적습니다. 실행 범위에는 PostgreSQL major version을 적습니다. 필요한 extension 바이너리와 연결 설정의 준비 상태도 적습니다. DBLab의 이미지 선택도 원본 major version과 수집 방식의 조건을 따릅니다. 따라서 파일 사본만 확보한 상태를 실행 환경 완성으로 기록하지 않습니다.
dataset 밖의 extension 바이너리나 secret 파일, OS scheduler가 있습니다. 이들이 물리 snapshot에 포함되지 않는다는 판단은 파일 배치에 근거한 추론입니다. 제품의 모든 복제 규칙으로 확정하려면 실제 배치에서 경로를 확인해야 합니다. 원본 설정의 유지 여부와 원본 연결의 제거 여부를 가정하지 않습니다. branch에서 사용할 설정을 직접 대조합니다.
기동 시험에서는 대표 테이블 하나의 조회 외에도 작업 목적에 맞는 검증 SQL을 정합니다. 별도 tablespace에 둔 객체와 필요한 extension을 포함합니다. 다만 이 검사는 파일 배치의 일관성을 대체하는 증명이 아닙니다. 사본 생성 조건과 사용 범위 확인을 함께 남겨야 합니다. 그래야 이후 같은 절차를 반복할 때 성공 조건을 재현할 수 있습니다. 세부 구성 예시는 ZFS thin clone 노트를 참고합니다.
WAL 위치와 두 종류의 Timeline
LSN은 PostgreSQL WAL 안의 바이트 위치입니다. 같은 WAL 계열 안에서 기록 위치를 표현하는 데 사용합니다. replication 또는 recovery 진행량도 표현합니다. 다른 cluster의 데이터 상태를 비교하려면 이 숫자 외에 원본과 이력 계보도 확인해야 합니다. 두 환경이 같은 LSN을 출력해도 계보가 다르면 동일한 상태라는 근거가 되지 않습니다.
PostgreSQL은 archive recovery를 완료하면 이후 WAL을 구분할 새 timeline을 생성합니다. timeline history 파일에는 어느 timeline의 어느 지점에서 갈라졌는지 기록합니다. 여러 복구 경로 중 재생할 계열을 식별하기 위해 이력 파일과 WAL을 함께 다룹니다. 운영 기록에는 timeline ID를 남깁니다. 어떤 원본 cluster와 backup에 대응하는지도 함께 남깁니다.
Neon의 storage timeline은 Pageserver가 관리하는 저장 이력입니다. PostgreSQL의 WAL timeline ID와는 다른 식별자입니다. Neon의 공개 glossary도 두 개념을 구분합니다. 자식 storage timeline은 조상과 분기 LSN을 참조합니다. PostgreSQL 로그의 timeline 숫자를 Neon branch ID 대신 사용해서는 안 됩니다. 두 값이 일대일로 대응하는지도 별도로 확인해야 합니다.
| 비교 항목 | PostgreSQL timeline | Neon storage timeline |
|---|---|---|
| 설명 대상 | 복구 후 구분되는 WAL 계열 | Pageserver의 저장 이력 |
| 분기 계보 자료 | Timeline history 파일 | 조상 timeline과 분기 LSN |
| 함께 기록할 식별 정보 | 원본 cluster와 backup | 원본 branch와 저장 계보 |
장애 보고서에 LSN 하나만 적혀 있다면 먼저 해당 값의 출처를 확인합니다. 어느 서버에서 읽었는지, 기록 위치인지 복구 목표인지, 어느 timeline의 값인지 구분합니다. 이 절차는 제품 간 LSN 변환 공식을 제시하는 것이 아닙니다. 같은 이름의 위치 정보를 서로 다른 맥락에 적용하는 일을 막기 위한 기록 방식입니다.
Neon의 조상 탐색과 page 재구성은 Timeline과 Branch 내부에 정리되어 있습니다. 총론에서는 제품별 식별 정보를 복구 요청에 보존하는 데 집중합니다. 성공한 조회 결과도 그 요청에 연결합니다. 로그, 제어 API, SQL의 시점 표기가 다른 경우가 있습니다. 이때는 어느 자료를 기준으로 판정했는지도 보고서에 적습니다.
복구 목표와 Commit 기준
PostgreSQL PITR는 base backup과 이어지는 WAL로 목표 상태를 복구하는 경로입니다. Neon은 보존된 저장 이력의 과거 LSN에서 branch를 만드는 방식으로 PITR를 지원합니다. 목표가 과거 데이터의 확인이라는 점은 같지만, 필요한 자료와 준비 절차는 다릅니다. PITR라는 기능명만으로 두 경로의 복구 시간이나 보존 비용을 동일하게 취급하지 않습니다.
PostgreSQL은 복구 목표로 시각, transaction ID, LSN 등을 지정하는 설정을 제공합니다. recovery_target_inclusive는 해당 목표의 변경을 포함할지 정합니다. 포함하지 않으면 경계 전에 멈춥니다. 잘못된 transaction 직전을 조사한다면 목표 식별과 포함 여부를 함께 기록해야 합니다. transaction ID의 숫자 순서와 commit 완료 순서도 다른 경우가 있습니다. 따라서 ID만 정렬해 업무 변경 순서를 추정하지 않습니다.
시간 기반 요청은 업무의 의미를 표현하는 데 편리하지만 검증 조건은 더 구체적으로 작성합니다. 예를 들어 잘못된 변경 전이라는 요구에는 제외할 변경과 남겨야 할 정상 변경을 함께 적습니다. 복구 후 두 조건을 각각 조회합니다. 그러면 너무 이른 상태를 선택해 정상 변경까지 누락한 경우도 구분할 수 있습니다. 이는 목표 상태를 확인하는 절차 제안이며, 복구 기능의 보장 범위를 늘리는 설명은 아닙니다.
Dolt의 버전 관리 commit은 DB 상태의 root 참조와 부모 commit의 관계를 이력으로 남깁니다. 일반 SQL transaction commit과는 별개입니다. 따라서 분석 환경을 특정 데이터 버전에 고정할 때는 Dolt commit 식별자를 기록합니다. SQL의 COMMIT 성공 기록만으로는 부족합니다. branch 이름이 이후 변경되더라도 검증에 사용한 기준 버전을 식별하기 위해서입니다.
DBLab의 commit도 같은 단어를 쓰지만 의미를 따로 확인합니다. DBLab은 4.0부터 사용자 요청 snapshot과 명명된 branch를 제공합니다. 그 이전부터 자동 snapshot을 생성했습니다. 사용자가 commit한 시점만 보존한다고 설명하면 자동 snapshot 경로가 빠집니다. 작업 기록에서는 PostgreSQL transaction commit을 별도 항목으로 표시합니다. Dolt 데이터 버전 commit과 DBLab snapshot 요청도 각각 구분합니다.
보존 범위와 재현 검증
복구 가능한 과거 범위와 장애 시 허용할 데이터 손실은 서로 다른 질문입니다. 보존 기간은 어느 과거까지 접근할지 정합니다. RPO는 허용할 손실의 시간 목표를 나타냅니다. 보존 기간이 길다는 설명을 최신 변경의 손실이 작다는 근거로 사용하지 않습니다. 필요한 과거 시점과 복구 목표를 각각 정해야 합니다.
이 장의 검증 설계에서는 분기점 기록에 원본, 생성 모드, 시점 식별자, 보존 조건을 묶습니다. 이어서 기동 결과와 업무 SQL 결과를 붙입니다. 첫 항목은 요청의 재현에, 두 번째 항목은 실제 성공 판정에 사용합니다. 제품이 반환한 생성 시각과 데이터의 기준 시각이 다르다면 두 값을 별도로 보존합니다. 보고서에는 기준 시각을 명시합니다.
보존 만료 경계도 정상 분기와 분리해 시험하는 방법을 제안합니다. 범위 안의 시점을 요청한 결과와 범위 밖의 시점을 요청한 결과를 각각 기록합니다. 실패한 요청을 최신 상태로 자동 대체하면 원래 조사하려던 상태를 잃습니다. 따라서 대체 여부가 명확히 드러나도록 절차를 설계합니다. 이 시험은 실패 처리의 검토 항목이며, 특정 제품이 자동 대체한다는 주장은 아닙니다.
버그 재현용 분기라면 데이터 기준과 코드 및 설정 버전을 함께 고정합니다. 데이터 상태를 제대로 선택했더라도 재현 조건에 필요한 입력을 정해야 결과를 비교하기 쉽습니다. 복구 성공 보고서는 SQL 접속 여부와 목표 데이터 확인을 구분해 작성합니다. 이후 compute 준비 시간과 첫 쿼리 시간은 준비 시간과 자원 비용의 측정 경계로 넘깁니다.