Skip to content

R05 — 실제 이관·복구 검증과 통합 후보 (2026-09-12)

Phase 현황

Phase내용상태
Phase 1선행 및 최신 main·staging·release 통합완료, PR #1580에 포함
Phase 2기존 188개에서 후보 219개까지 전체 DB 이관완료, 실제 사본과 4년 입력 검증
Phase 3복구·구/신 버전 공존·worker 전환완료, 최종 precheck 통과
Phase 4staging Full·배포·smoke·U06Full 5회(실패 3·성공 2) 뒤 staging 15850e76 병합·배포·smoke 완료(Full 34679429859, Deploy 34679946098); U06 실제 15 CASE 미완료(계정 주입 경로 대기)
Phase 5최종 후보 확정·R06 인계미완료

1. 배경

R02·R03·R04·U06과 D13·B02의 개별 검증을 실제 출시 조합으로 연결해야 했다. 기존 운영 수리와 v0.18.0의 타입·기능 경계를 함께 보존하고, 기존 사용자 데이터가 있는 DB에서 전체 이관과 복구를 확인했다.

2. 문제 제기

빈 DB 검사만으로는 실제 이관의 잠금과 원본 보존을 증명할 수 없다. 실제 사본과 4년 입력에서는 통계 계산 중의 테이블 잠금 때문에 DDL이 기존 5초 예산을 넘었다. 통합 과정에서는 요청 취소 누락과 검증 도구의 반환값·병렬 실행 문제도 드러났다.

3. 설계 결정

기존 worker의 claim·compute 잠금과 계산된 출력의 정상 발행이 끝난 뒤 DDL을 시작하도록 순서를 정했다. 사용자 원본 수정, DB 전체 downgrade, 시간 예산 상향, 확률적 재시도는 적용하지 않았다.

선택판단
빈 DB replay만으로 승격실제 이관·잠금·보존을 증명하지 못해 기각
기존 예산으로 전체 이관과 중단 후 재개 검증채택, 실제 사본과 4년 입력의 출처를 구분
실제 배포된 v0.17.1의 93개 파일로 공존 검증채택, 소스 재빌드와 별도 증거로 보존
반복 Full로 오류 수집기각, 승인된 최종 1회의 원 결과를 보존하고 로컬에서 수리
전역 승격 잠금이나 새 검증 체계 추가기존 범위에 필요하지 않아 추가하지 않음

4. 적용한 내용

Phase 1 — 통합 후보

main 1587cb7a와 staging 044a55f8의 날짜 처리, 통계 대기·오류 표시, 모바일 표시, 로그인 및 오류 관측을 v0.18.0의 구조에 연결했다. 요청 저장소는 최신 응답 선택과 진행 중인 전송의 수명을 분리했다. 최종 로컬 단위 검사 3,785건과 persistence 118건, 웹·관리자 빌드를 통과했다.

Phase 2 — 전체 DB 이관

기존 rollout에 worker 전환 준비를 연결했다. 준비는 70초, 개별 migration은 60초, 잠금은 5초로 각각 구분했다. 연결 유실 시 중단과 종료 시 잠금 해제를 검증했으며 migration 원문은 바꾸지 않았다. 실제 사본 44,357행과 4년 입력 33,127행의 값·ID·관계를 대조했다. 중첩 필드를 누락하던 요약 해시를 고치고 표별 전후 증거를 저장했다.

Phase 3 — 복구와 공존

실제 사본에서 세션 3가족 45행의 선택 복원, 중간 실패 후 재개, 영수증 재전송, 비대상 원본 불변, 전체 재계산과의 통계 동치를 확인했다. 실제 구 배포본과 새 앱의 저장·수정·전송 중 후속 수정은 최종 8회 모두 통과했다.

최종 head 7c0a697b의 필수 precheck는 541.266초에 통과했다. pgTAP 122파일·2,444단정, 모든 DB 검사 묶음과 660세션 수렴 검사가 포함된다. Phase 완료 검사는 3924 = 3789 P + 0 F + 135 조건부 제외였다.

Phase 4 — 최종 Full 원 결과

Merge Check 34638911663 성공 후 release에 실제 병합된 tree가 로컬 검증 tree bee5422c0449ab9aecf3a47919610bb533157d70와 같음을 확인했다. staging PR #1581의 merge candidate a54d6e8a20b3035b0c2317ce36e92f02f6d1cf04도 같은 tree다.

승인된 Full 34639164713 attempt 1실패했다. 원 결과 댓글에 실패와 미실행을 구분했다. 시간 fixture 3건, CASE 5건, 상세 화면 정렬 1건, CRUD 2건과 별도 cleanup hook 실패가 관측됐다. persistence 실패로 브라우저 shard 4의 13 CASE가 실행되지 않았다. 전체 분모·원인 집계와 로컬 수리는 진행 중이며 전체 성공률을 산정하지 않는다.

자동 Full을 유발하는 PR head 갱신, 추가 Full, staging 병합, R06 재개는 진행하지 않았다. 승인된 1회는 사용 완료 상태다. 과거 후보의 성공을 이번 실패 run에 합산하지 않는다.

Phase 4 — 실패 수리 후보

후속 후보 3bab905c에는 시간 fixture, 실제 dialog 결함 주입, repository 참조, 동기식 적용 영수증, Linux 제목 정렬을 고친 내용이 들어 있다. 별도 재현으로 확인한 auth 계정 삭제와 통계 compute의 교착 상태는 부모→자식 잠금 순서를 일치시켜 해결했다. 두 순서의 DB 회귀를 포함한 worker 10건이 통과했다. 원 Full의 CRUD 두 timeout과 cleanup hook은 같은 원인으로 입증되지 않아 미확정 상태를 유지한다.

새 migration을 포함한 DB220 후보 9d6485e6에서 실제 사본과 4년 이력의 188→220, 전체 32개 이관이 모두 통과했다. 최대 파일은 750ms·327ms, 최대 잠금은 14ms·0ms였고, 원본 23표·영수증·변경 이력·checkpoint의 전후 차이는 0이다. 최신 후보와 이 측정 후보의 차이는 신규 계정 테스트의 날짜 한 곳뿐이며 제품과 DB 입력은 같다.

같은 DB220 후보의 전체 단위 검사는 3789 P / 0 F / 137 조건부 제외, 필수 precheck는 558.384초, 전체 PASS였다. 원 Full의 미실행 브라우저 13건도 별도 실행에서 모두 통과했다. 미실행 empty-account/cardio는 15건 중 날짜 불일치 1건이 실패했고, 기존 고정 fixture 날짜에 맞추자 15건 모두 통과했다. 수정 후 CRUD는 12건 모두 통과했으나 원격 timeout 원인 확인으로 확대하지 않는다. 최종 커밋의 precheck와 실제 구 배포본 공존 결과는 실행 장부에 이어 기록한다.

최종 head 3bab905c의 필수 precheck는 534.750초 전체 PASS, 실제 구 배포본 공존은 6 P + 2 P, 품질 오류0이었다. HQ와 조율해 자기 승격 PR을 닫고 열린 승격 PR 0개를 확인한 뒤 Merge Check 34646255331로 수리만 release에 병합했다. merge 6ea9b2fb와 검증 head의 tree는 8d3b495b644358ef3bc7d0c701ba338f8bd56fa6로 일치한다. 추가 Full·승격·배포 성공을 의미하지 않는다.

Phase 4 — Linux 원본 조사와 claim/auth 추가 교착

원 head 411d8abf의 Linux DB 순서 재현은 DB219와 pgTAP 122파일·2,444단정을 통과했지만 concurrency에서 auth 삭제/compute 및 auth 삭제/claim 교착을 관측했다. 최종 TAP 합계 없이 기존 180초 상한으로 중단돼 뒤의 CRUD는 실행되지 않았다. 환경 문제로 pgTAP 0건이었던 앞선 시도는 무효로 보존했다. 조사 보고에 환경 차이와 단계별 결과를 구분했다.

claim 교착은 현재 release 6ea9b2fb에서도 재현됐다. pending claim이 auth 부모를 job/run보다 먼저 잠그고 삭제 중인 부모를 건너뛰도록 고치자 양방향 회귀 1 P/1 F → 2 P, 관련 worker 12 P가 됐다. DB221 replay와 SQL 일치, 전체 check 3789 P/0 F/139 조건부 제외를 확인했다. 새 low/function migration은 20260915073000이며 권한·원본·lease·재시도·시간 예산은 유지했다. BUG-142, 최종 검증·병합 장부, 구조화 증거에 연결한다.

원 Full CRUD의 기다린 SQL과 GoTrue cleanup 실패 원인은 미확정이다. 로컬에서 추가 발견한 claim 결함을 원 Full 원인으로 소급하지 않으며 본문 수리 9/11, cleanup 0/1을 유지한다. 과거 DB220의 32개 populated 이관과 새 DB221은 입력이 같지 않다. 추가 Full·staging/Production·R05 최종 수락은 미완료다.

claim 수리 release 반영 — 2026-09-12 KST. PR #1583Merge Check 34651970271 성공 후 2026-09-11T22:00:32Z7ab3dcdf709cc8ecd3d99a855b8c7b8013434731로 실제 병합됐다. 검증 head 95f3b6f7와 merge의 tree는 417f325c4b75b843d2f5393f8d175ed1a42b6b8f로 일치한다. 자기 #1581 CLOSED와 열린 동일 release 승격 PR 0개를 확인하고 정상 Merge Check를 사용했다. BUG-142를 같은 턴에 기록했다. 추가 Full·승격·staging/Production 배포는 실행하지 않았으며 R05 Phase 4·5는 미완료다.

Phase 4 — 최신 DB221 전체 이관 갱신

고정 release 7ab3dcdf에서 기존 실제 사본과 4년 RPE10+수정 이력에 188→221 전체33개를 각각 한 번 적용해 모두 통과했다. 원본23표의 값·ID·관계·source와 영수증·수정 이력·checkpoint 차이0, 중단 후 재개와 정의 일치도 통과했다. 최대 파일은 299ms·317ms, 최대 잠금은 0ms·0ms이며 기존60초/5초 기준을 유지했다. 전체 적용 wall은 74,291ms·75,001ms로 별도 기록하고 새 상한은 두지 않았다.

앱·SQL을 수정하지 않았고 이전32개 실패/성공을 보존했다. 최신 실행 장부, 33개 증거, 후보/입력 바이트를 연결한다. 오래된 사본/RPO, 원 Full CRUD 미확정 및 R05 staging 수락 미완료 경계는 유지한다.

Phase 4 — 두 번째 Full과 CRUD 대기 경계 수리 (#1584)

오너의 재실행 지시로 release 7ab3dcdfFull 34662653471을 실행했다(옛 head의 전이 run 34662652541은 자동 취소, 제품 테스트 0건). 잡 12 = 성공 10 + 실패 2. 단위 3928 = 3789 P + 0 F + 139 S, DB 동시성 51 P, pgTAP 122파일·2,444단정, persistence 118 P, 브라우저 53 + viewport 22 = 75 P. 실패는 CRUD 본문 12 = 10 P + 2 timeout과 계정 정리 hook 1 F, 후속 신규 계정·cardio 15건 미실행. 실행 슬롯 합계 4,199 = 4,043 P + 2 F + 139 S + 15 U, 96.28%.

원인은 e2e의 통계 대기가 모든 계정의 큐가 빌 때까지 기다리는 전역 방식이라 다른 계정 잡 하나에 묶인 것이었다(실제 DB에서 다른 계정 pending 잡을 잠금으로 붙잡아 10 P + 2 timeout 재현). PR #1584로 대기를 그 계정의 통계만 기다리는 10초 예산 방식으로 바꾸고 계정 삭제 직전에도 자기 통계를 마무리하게 했다(같은 조건 12 P / 8.0초). 정리 hook 실패는 취소된 테스트의 전역 대기가 백그라운드에서 계속 돌며 GoTrue 연쇄 삭제와 잠금 순서로 충돌한 경로가 유일한 후보이며 원격 로그 부재로 확정하지 않았다. release 7add2144로 반영, 승격 PR #1581은 닫아 두었다.

Phase 4 — 세 번째 Full과 CASE-025 제품 결함 수리 (#1585, BUG-143)

2026-09-12 13:00 KST부터 Claude 세션 080ed475가 이어받아 staging 병합·Full 통과를 맡았다. 로컬에서 CI와 같은 DB 순서 7분 41초 전체 통과, e2e 3종 5회 반복 15/15, 다른 계정 잡 잠금 조건 통과를 확인한 뒤 PR #1581을 재개방해 Full 34674181914를 실행했다. 잡 12 = 성공 9 + 실패 3. 직전 2회의 실패 지점(CRUD 12 P·신규 계정 9 P·cardio 6 P)은 통과했고, 단위 3,929 = 3,789 P + 0 F + 140 S, pgTAP 122/2,444, DB 동시성 52 P, persistence 118 P, 브라우저·viewport 75 = 74 P + 1 F. 실행 슬롯 합계 4,201 = 4,060 P + 1 F + 140 S, 96.64%. 실패 1건은 CASE-025 두 번째 reload의 10초 timeout이었다.

로컬 재현(10회 중 1회, RPC 응답을 1.5초 늦추면 6회 중 3~4회)과 Chromium tracing으로 원인을 확정했다: 저장 직후 피드 새로고침이 진행 중일 때 reload가 오면 문서 이탈 신호가 요청을 끊고, 피드 상태가 idle로 돌아가자 화면 로드 정책이 즉시 재요청 → 즉시 거절 → 저장소 알림 → 동기 재렌더 → 다시 idle … 이 beforeunload 처리의 microtask 안에서 무한히 돌아(React 동기 flush 1,787회) 탭이 멈춘다. 테스트가 아니라 사용자에게도 저장 직후 새로고침·탭 닫기·앱 전환 시 화면이 얼어붙는 제품 결함이다(BUG-143). PR #1585가 자원 저장소 fetch에 "떠나는 문서 위에서는 요청을 시작하지도 구독자를 깨우지도 않는다"는 공통 규칙을 넣었다(문서 이탈 신호는 documentLifecycle.ts로 분리). 결정적 재현 조건에서 3~4/6 → 0/6, 조용한 환경 10회 10/10, precheck 전체 통과 뒤 release e4cd999a로 반영했다. 이 PC의 self-hosted runner가 이전 세션 종료 뒤 offline이어서 다시 띄운 것도 기록한다.

Phase 4 — 네 번째 Full과 staging 승격

수리 반영 뒤 자동 실행된 Full 34677735108(head e4cd999a, 15:17~15:27 KST)이 12/12 잡 성공했다. 단위 3,931 = 3,791 P + 0 F + 140 S(조건부), pgTAP 122/2,444, DB 동시성 52 P, CRUD·신규 계정·cardio 27 P, persistence 118 P, 브라우저·viewport 75 P. 실행 슬롯 합계 4,203 = 4,063 P + 0 F + 140 S, 96.67%(실행분 100%). 직전 3회 Full의 실패 지점(CRUD timeout·정리 hook·CASE-025 reload)이 모두 통과했다. main·staging이 release의 조상임을 확인하고 merge commit으로 승격해 staging 0f46583a(15:28 KST, tree = release tree 6ad89619)가 됐다.

Phase 4 — staging 배포 실패와 원격 스키마 게이트 수리 (#1586)

승격 직후의 2-Staging Deploy 34678245398은 database 잡의 마지막 단계 Remote contract gate 에서 실패했다(잡 8 = 성공 1 + 실패 1 + 미실행 6). 마이그레이션 33개는 정상 적용됐고 게이트가 function:public.start_wodup_import_batch 를 없다고 판정했다 — 실제로는 20260914010000 이 dump 꼴("public"."fn"("uuid", …))로 지운 함수인데, 게이트 파서가 따옴표 있는 함수 문장을 읽지 못해 옛 기대를 남긴 도구 결함이다. 이 게이트는 배포에서만 돌아 Full 4회로는 드러나지 않았다.

로컬 샌드박스에서 같은 판정을 재현한 뒤 PR #1586이 함수 create/drop/rename 문장의 따옴표를 허용하도록 고쳤다. 수리 후 샌드박스 missing 0(기대 함수 489 = 원격 489), Production 읽기 전용 조회(main 장부)도 missing 0(406 = 406), 단위 31/31, precheck 전체 통과. release 8621d677로 반영했다.

Phase 4 — 다섯 번째 Full과 staging 배포

게이트 수리를 실은 새 승격 PR #1587Full 34679429859(head 8621d677, 15:56~16:07 KST)이 12/12 잡 성공했다. 단위 3,932 = 3,792 P + 0 F + 140 S(회귀 테스트 +1), pgTAP 122/2,444, DB 동시성 52 P, CRUD·신규 계정·cardio 27 P, persistence 118 P, 브라우저·viewport 75 P — 실행 슬롯 합계 4,204 = 4,064 P + 0 F + 140 S, 96.67%(실행분 100%). merge commit 으로 병합해 staging 15850e76(16:08 KST, tree = release tree 6d8fcf43)이 됐고, 2-Staging Deploy 34679946098(16:08~16:12 KST)이 database(남은 마이그레이션 0, Remote contract gate 489 = 489·missing 0) → functions → frontend → smoke(CRUD 왕복 TAP 12/12) 순으로 성공했다. 같은 tree 의 Full 성공 기록과 staging 배포·smoke 증거가 갖춰져 승격 ① 조건을 충족한다. 기록.

작업 중 드러난 것

초기 잠금 실패 2회, unused 검사 누락, DB fixture의 발행 경합, 병렬 Git 등록 오류, 최종 Full 실패를 모두 보존했다. 실제 Full 경계에서 드러난 누락은 사전 검증 누락으로 기록한다. 원인이 확인된 항목과 cleanup 등 아직 확인되지 않은 항목을 구분한다. 세 번째 Full의 CASE-025 reload timeout은 테스트가 아니라 제품 결함(BUG-143)이었고, staging 배포의 Remote contract gate는 Full CI에 없는 검사라 배포에서만 드러났다(게이트 도구 결함, #1586).

5. 적용 결과

항목전 → 후
전체 이관 최대 잠금사본 9,450ms·4년 24,387ms 실패 → 각각 0ms, 전체 31개 통과
migration 시간기존 파일별 60초 유지, 최종 최대 359ms·346ms
전체 이관 경과 시간실제 사본 70.191초·4년 입력 69.825초, 파일별 예산과 별도
원본과 관계 보존실제 사본 23표 차이 0, 영수증 1,044개·변경 이력 31개·체크포인트 1개 보존
실제 사본 복구65.022초, 준비 94.650초 별도. 선택·비대상 원본, 통계 동치, 정리 통과
실제 구/신 배포본 공존통계 대기 도구 오류로 최초 6 F → 최종 8 P / 0 F / 0 S / 0 U
Phase 완료 검사병렬 Git 등록 오류 1 F → 3,789 P / 0 F / 135 조건부 제외
후보 통합검증 head 7c0a697b → release 411d8abf, 동일 tree
최종 stagingFull 1회 실패 → Full 4회(실패 3·성공 1, 실행 슬롯 성공률 96.28% → 96.67%)로 staging 0f46583a 병합, 배포 1회 실패(게이트 도구 결함) → 수리 후 재승격 PR #1587 Full 12/12 · 2-Staging Deploy 34679946098 성공(DB → Edge → 프런트 → smoke 12/12), staging 15850e76 = release tree 6d8fcf43

6. 이번 개선으로 향상된 것

기존 사용자의 기록·영수증·관계를 보존하는 이관과 복구 근거를 실제 입력에 연결했다. 계산 중인 통계 트랜잭션이 끝난 뒤 DDL을 실행하도록 순서를 정해 기존 잠금 예산을 지켰다. 증거에는 실행 출처, 후보별 입력 동등성, 원래 실패를 함께 남겼다.

남은 것

R05의 U06 실제 15 CASE, 최종 후보 확정은 미완료다. U06 계정은 HQ의 기존 요청에 답변을 기다리고 있다. R04의 혼합 쓰기 예산 초과와 지연 악화, 비교 기준 차이, 24시간을 넘은 사본, 원본 Motra·InBody 파일 부재, 최신 네이티브·서명·실제 제공자 인증 미검증도 유지한다. Production과 R06은 오너의 명시적 재개 전까지 대기한다.