Skip to content

[Test] 격리 복원에서 업로드 손실과 문서 기능 재개 시간 계측 - #562

Merged
Gimini-3 merged 1 commit into
developfrom
test/dr-functional-recovery-measurement
Oct 18, 2026
Merged

Gimini-3 merged 1 commit into
developfrom
test/dr-functional-recovery-measurement

Conversation

@Gimini-3

Copy link
Copy Markdown
Contributor

목적

Closes #560

#561의 판정 계약으로 실제 OpenSQL·GCS 복원을 실행하고, HTTP 성공 응답이 어디까지 복원됐는지와 문서 기능을 다시 확인하기까지의 시간을 같은 회차에서 연결합니다.

검증 절차는 PASS지만 무손실 복원은 아닙니다. 성공 응답 65건 중 33건 복원·32건 DB 미복원입니다. 기능 코호트 5건과 문서 기능 6종은 통과했습니다. 후속 Worker 처리로 최초 손실 기록을 바꾸지 않았습니다.

전체 결과·그림·증거 색인 · 실제 절차 · 준비·재시도

테스트 환경

대상 실제 실행 조건
제품 코드 Java 17, 3bff887a5f6abdf4155957a658599f8f0366a141 JAR. 제품 Java 코드 변경 없음
DB 실제 OpenSQL(PG 17.8 기반)·vector 0.8.1. 기존 3노드 내 전용 논리 DB. 물리 기준 백업은 클러스터 단위
앱·캐시 로컬 전용 JVM·Valkey. 복원 뒤 Worker 동시 처리 상한 2
모델 실제 BGE-M3·1,024차원. 프록시는 실제 응답 대기만 제어하며 벡터·검색 결과를 합성하지 않음
저장소 source·preserved·database의 별도 시험 버킷 3개. generation·SHA-256·크기 고정
백업 Barman 3.19.1·PG 클라이언트 17.8, file·rsync 포함. 영구 replication slot·Patroni 설정 변경 없음
복원 호스트 기존 비동기 Standby를 직렬 재사용. 별도 PGDATA·포트·소켓. 원래 DB와 동시 실행하지 않음
보호·원복 리더·동기 Standby 유지, 대상 역할·프로세스 시작 정보 재확인, 1,800초 자동 원복 장치

실행 ID는 DR-RESTORE-20261018-FORMAL1입니다. 준비는 2026-10-18 12:32 UTC 이후, 요청 구간은 12:49–13:01 UTC(한국 시각 21:49–22:01)입니다. 공개 앱·개인 환경 파일·OpenProxy 경로를 전환하는 시험은 아닙니다. Ollama 생성 답변은 범위 밖입니다.

테스트 방법

  1. 사전 기준 고정: 완료 문서 4건과 실제 BGE 응답 대기 중 시험 JVM을 종료한 Job 1건을 준비했습니다. 중단 Job은 PROCESSING·Attempt 1개·Chunk 3개·Embedding 0개였습니다. Worker를 끈 API로 기준 백업을 만들고 Barman 24/24·백업 DONE·verify 종료 코드 0을 확인했습니다.
  2. 성공 응답 원장: 손실 관측 업로드 60건을 목표 11초 간격으로 보내되, 요청 키·파일 해시·크기를 전송 전에, HTTP 응답·ID 4종을 응답 직후 외부 원장에 기록했습니다. 전송 중 업로드 정지가 있어 실제 간격은 원장으로 판정합니다.
  3. 외부 보존: 준비 전송 1회와 300초 예약 구간 2회를 실행했습니다. 경계에서 WAL 전환을 제어했고 완성 WAL만 보존했습니다. 두 예약 시작 지연은 5.005초·2.033초, 전송 시간은 73.514초·63.774초였습니다.
  4. 원본 경계와 복원: T0에 시험 API·수집기·원본 DB 터널을 종료했습니다. 드라이버의 source GCS 거부와 리스너 포트 2개 닫힘을 확인한 뒤 입력을 고정했습니다. 네트워크 없는 새 도구 컨테이너에서 외부 identity·백업·WAL을 import·verify하고 새 PGDATA를 만들었습니다.
  5. 최초 감사 후 활성화: 복원 전용 소켓·data_directory를 확인한 뒤, user_id + request_key로 응답 ID·관계를 조회하고 보존 객체 바이트를 대조했습니다. 최초 감사 단독 저장·fsync 뒤에만 복원 DB 버킷 재지정·Flyway·Worker를 허용했습니다.
  6. 실제 HTTP·최종 정합성: 복원된 계정으로 새 로그인해 다운로드·검색·Job 재개·신규 업로드/인덱싱/다운로드를 확인했습니다. 최종 감사는 별도 파일이며 최초 손실을 덮어쓰지 않습니다.
  7. 원복·정리: 원래 Patroni 프로세스·3노드 역할·설정 해시를 확인했습니다. 시험 DB·역할·프로세스·터널·활성 객체를 정리한 뒤 시험 버킷 3개와 임시 ADC 복사본도 제거했습니다.
그림 1 — 성공 응답의 검증 단위

클라이언트: 요청 키·파일 SHA 기록
  → DocumentUploadFacade: GCS 후보 저장·검증
  → DocumentUploadService: DB Transaction
      문서·버전·FileObject 관계 + Outbox·EmbeddingJob + 완료 응답
  ← HTTP 201 + ID 4종 → 외부 ACK 원장

복원 뒤 user_id + request_key 조회
  → DB ID·관계 일치? → 보존 generation·SHA·크기 일치?
  ├─ 모두 일치: RESTORED (인덱싱 완료라는 뜻은 아님)
  └─ DB 없음: DB_MISSING / 구조·원본·해시 오류는 별도 분류
그림 2 — 최초 감사와 Worker 사이의 관문

고정 외부 입력 → 빈 카탈로그 import·verify → 실제 OpenSQL 기동
  → 읽기 전용 SQL·보존 원본 대조
  → initial-snapshot + initial-audit 단독 저장·fsync
      ├─ 기능 코호트 미복원·구조/원본/해시 오류: 활성화 금지·정리
      └─ 기능 5/5 복원: 최초 손실 32건 보존 → 활성화 허용
          → 복원 DB만 버킷 재지정 → Flyway·Worker → 인증 HTTP
          → final-audit 별도 저장 → 원래 클러스터 복귀

관측 결과

항목 실제 결과
ACK / HTTP 오류 / 결과 불명 65건 / 0건 / 0건
최초 복원 33건 = 기능 5건 + 손실 관측 28건
최초 DB 미복원 32건. 이 중 원본만 보존된 요청은 27건
동결 원본 60건
동결 WAL·history WAL 7개 + history 1개
연속 복구 ACK 경계 → T0 412.061초. 중간 누락 뒤 복원된 ACK 0건
최종 감사 복원 33문서 + 신규 1문서 = 34문서
미완료 Job / 중복 Chunk / 중복 Embedding / 누락 Embedding 각각 0건
계약 회귀 40개 메서드 통과, 오류 0건

T0는 13:01:13.892692 UTC, cutoff는 13:01:24.539 UTC로 10.646초 차이입니다. 복원 연산자 자체는 100.672초이며 전체 기능 복구 시간과 구분합니다.

기능 표본·결과 cutoff 이후 T0 이후
기존 다운로드 4건 HTTP 200·크기·SHA 일치 176.992초 187.638초
기존 검색 기대 문서 Top 20 포함 1/1 192.841초 203.487초
중단 Job 재개 같은 Job INDEXED·Job 1개·Attempt 2개 238.483초 249.130초
새 업로드 HTTP 201 246.122초 256.769초
새 인덱싱 INDEXED 571.408초 582.054초
새 다운로드 HTTP 200·SHA 일치 573.186초 583.832초

검색은 장애 전에 실제 합성 Chunk를 고정하고 기대 문서 포함을 검사했습니다. 정확한 순위·유사도 점수·답변 문장 일치는 요구하지 않았습니다. 기능 검사는 순차 실행이므로 실제 확인 시각이며, 더 이른 최소 준비 시각을 추정하지 않습니다. 새 인덱싱에는 복원된 대기 Job 처리 시간이 포함됩니다.

그림 3 — 관측 손실과 기능 재개는 별도 축

65 ACK: 앞 33건 RESTORED │ 뒤 32건 DB_MISSING
  마지막 복원 ACK 12:54:21.832 UTC → T0까지 412.061초
  보존 원본 60건 중 DB도 복원 33건 / 원본만 보존 27건

T0 → 다운로드 +187.638초 → 검색 +203.487초 → Job 재개 +249.130초
   → 새 업로드 +256.769초 → INDEXED +582.054초 → 해시 대조 +583.832초
   → 최종 34문서의 미완료·중복·누락 각각 0 → 원복·정리

최초/최종 감사·이벤트·동결 입력·실패 회차·정리 근거는 결과 아티팩트 표에 연결했습니다. 전체 비공개 준비 자료를 일괄 공개하지 않고 필요한 36개만 선별했습니다. 파일별 해시·알려진 내부 식별자·이메일·IP·경로·인증 패턴 검사를 수행했고, 공개 사본으로 최초 감사 65건을 동일하게 재계산했습니다.

검증 과정에서 확인된 사항

  • 전송 완료와 최신성은 다릅니다. 마지막 외부 전송 뒤에도 실제 복원 DB는 ACK 33건까지만 보존했습니다. 마지막 재생 LSN 0/25000000은 동결한 마지막 WAL 끝과 일치합니다. 마지막 재생 트랜잭션은 12:54:21.655964 UTC입니다. 수신·archiver·외부 전송 간 최신성 차이의 세부 원인은 분리하지 않았습니다. 이 실험을 RPO 300초 달성이나 무손실 백업으로 보고하지 않습니다.
  • 판정기 상태 계약을 수정했습니다. 기존 공통 목록은 실제 문서 INDEXING·버전 EMBEDDING 등을 거부했습니다. 문서·버전·Job 목록을 구분했고, Java enum 18종·다른 엔티티 상태 3종을 독립 입력으로 시험했습니다. 수정 전 4개 메서드 실패 → 수정 후 40/40 통과입니다.
  • 첫 리허설은 미완료입니다. DR-RESTORE-20261018-REHEARSAL1은 OpenSQL 기동에서 중단됐고 최초 감사·기능 시험을 못 했습니다. 기동 진단 R1은 FATAL: license is invalid를 확인했으며, R2는 같은 호스트·백업 바이트에서 기존 OPENSQL_LICENSE_PATH만 전달해 실제 기동을 확인했습니다. 라이선스 수정·재발급·제품 결함/만료 판정은 하지 않았습니다. 첫 리허설에서 미수집한 stderr를 후속 오류로 소급 채우지 않았습니다.
  • 준비 문제와 재시도를 보존했습니다. DB별 백업 함수 권한, Docker 바인드 잠금, file 도구 누락, chunked body 프록시, 초기 JVM readiness 한도, 원인 미확정 새 SQL 연결 오류를 구분했습니다. 상세
  • 원복 확인: 리더 1대·동기 Standby 1대·비동기 Standby 1대로 복귀했고 Patroni 설정 해시가 같습니다. 개인 .env 두 파일이 같고 사용자용 원본 버킷은 보존했습니다. 원래 ADC를 철회하지 않았습니다. GCS 활성 객체·시험 버킷을 삭제했어도 soft delete 정책에 따른 보존 세대가 남을 수 있으므로 물리 소거라고 하지 않습니다.
  • 미검증 범위: 공개 서비스 전환·전체 사이트 소실·원본 버킷 IAM 차단·Ollama 생성 답변·OpenProxy·장기 WAL 보존·수신 중단 경보·Primary 교체 재연결은 포함하지 않았습니다. 기존 호스트와 제어 경로가 남은 격리 실험 한 회차이며 공개 RTO·운영 RPO 보장이 아닙니다.

리뷰 초점은 상태 계약의 엔티티 경계, 최초 감사 후에만 활성화된 순서, 32건 미복원을 숨기지 않은 판정, T0/cutoff 분리, 정리 증거입니다.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: DocGrid/docgrid/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e8520e22-ece6-477b-add7-93dd839cdd2c
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Gimini-3
Gimini-3 merged commit 3eb46eb into develop Oct 18, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Test] DB·GCS 복원에서 업로드 성공 응답 보존과 기능 재개 계측

1 participant