From 260a108391073736ab8f3ceffb7e6582893d6de8 Mon Sep 17 00:00:00 2001 From: Khyojae Date: Wed, 11 Nov 2026 21:26:22 +0900 Subject: [PATCH 1/2] =?UTF-8?q?docs(rpo-rto):=20Q4=20binlog=20=EB=B0=98?= =?UTF-8?q?=EC=B6=9C=20=EC=A3=BC=EA=B8=B0=2060=EC=B4=88=20=ED=99=95?= =?UTF-8?q?=EC=A0=95=20=E2=80=94=20=EC=95=BD=EC=86=8D=20=EC=B5=9C=EB=8C=80?= =?UTF-8?q?=20=EC=86=90=EC=8B=A4=20=EC=95=BD=2061=EC=B4=88(=EA=B0=80?= =?UTF-8?q?=EC=A0=95=20=ED=94=BC=ED=81=AC),=20FLUSH=20=EC=B8=A1=EC=A0=95?= =?UTF-8?q?=EC=9D=80=20=EB=92=A4=EC=97=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 주기가 더하는 손실은 «이미 끝나 리포트까지 본 운동» 이다 — 진행 중 운동은 서버를 잃으면 주기와 무관하게 끊긴다. 가정 피크(1분에 약 4.5건 종료)에서 60초 약 4.5건 · 300초 약 23건. 300초 → 60초 비용 차이는 S3 요청 요금 월 1달러 미만(업로드 1번 1초 미만 실측). FLUSH 가 쓰기를 막는 시간은 안 쟀다 — 결정을 먼저 하고 측정을 뒤에 붙인다(사용자 선택). FLUSH 명령 자체가 52~56ms 라 19초 기준까지 여유가 크다는 추론을 근거로 적고, 측정 결과가 크면 다시 연다. Co-Authored-By: Claude Opus 5.5 --- docs/decisions/README.md | 2 +- docs/decisions/rpo-rto-targets.md | 32 +++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/docs/decisions/README.md b/docs/decisions/README.md index 5a2337a5..9af274c1 100644 --- a/docs/decisions/README.md +++ b/docs/decisions/README.md @@ -132,7 +132,7 @@ - [binlog-shipper-ops.md](./binlog-shipper-ops.md) — 운영 binlog 반출기 — 측정용 스크립트를 운영에서 믿고 맡길 것으로 (분기점 — 방향 «서버 옆 업로더 직접 구현», D1~D8 추천까지·채택 없음) - [major-version-upgrade-policy.md](./major-version-upgrade-policy.md) — 메이저 버전업 2건(springdoc 3.x·Gradle 9) (보류 — §8에 "재지 않은 것" 다수, 테스트·실측 안 됨) - [replication-lag-and-semisync.md](./replication-lag-and-semisync.md) — 복제 — "한 대가 죽어도 된다"를 지연과 대가로 (진행중 — 조건부 결론, 다른 AZ는 안 잼) -- [rpo-rto-targets.md](./rpo-rto-targets.md) — RPO·RTO 목표 — "얼마 걸리나"에서 "얼마여야 하나"로 (진행중 — 방향 확정: 백업+binlog 서버 밖, 리플리카 보류, 반동기 미채택. 반출·백업 주기 숫자 미정) +- [rpo-rto-targets.md](./rpo-rto-targets.md) — RPO·RTO 목표 — "얼마 걸리나"에서 "얼마여야 하나"로 (진행중 — 방향 확정: 백업+binlog 서버 밖, 리플리카 보류, 반동기 미채택. 목표 RTO 확정, 반출 주기 60초 확정(FLUSH 측정 뒤 재확인), 백업 주기·보존 기간 미정) - [reverse-proxy-and-tls.md](./reverse-proxy-and-tls.md) — 리버스 프록시·TLS 종료 — nginx를 넣어야 하나 (진행중 — 이슈 일부 등록, CORS PATCH 누락은 여전히 미등록) ## 포트폴리오·전략·서사 diff --git a/docs/decisions/rpo-rto-targets.md b/docs/decisions/rpo-rto-targets.md index 873adaeb..5798bd1c 100644 --- a/docs/decisions/rpo-rto-targets.md +++ b/docs/decisions/rpo-rto-targets.md @@ -1,9 +1,9 @@ # RPO·RTO 목표 — «얼마나 잃어도 되고, 얼마 안에 돌아와야 하나» 를 정하는 분기점 -작성일: 2026-11-07 · 갱신: 2026-11-08 (§8-1 읽기 : 쓰기 비율 가정 추가 · §6-2 측정 둘 — 재생 시간·호스트 상실 RPO · 측정 셋 — 새 서버 준비·반출 업로드·리플리카 승격) -상태: 🟡 **방향 확정 · 목표 RTO 확정(11-08, §6-3) · 나머지 숫자 미정.** 구성 2(백업 + binlog 서버 밖 반출) 채택, 리플리카 보류, 반동기 미채택 -(2026-11-07 사용자 confirm). **반출 주기·백업 주기·보존 기간 숫자는 아직 없다** — 추천안이 숫자를 정하지 -않았고, 근거 없이 채우지 않는다(§6 Q4·Q6). +작성일: 2026-11-07 · 갱신: 2026-11-11 (Q4 반출 주기 60초 확정) · 2026-11-08 (§8-1 읽기 : 쓰기 비율 가정 추가 · §6-2 측정 둘 — 재생 시간·호스트 상실 RPO · 측정 셋 — 새 서버 준비·반출 업로드·리플리카 승격) +상태: 🟡 **방향 확정 · 목표 RTO 확정(11-08, §6-3) · 반출 주기 60초 확정(11-11, Q4) · 나머지 숫자 미정.** 구성 2(백업 + binlog 서버 밖 반출) 채택, 리플리카 보류, 반동기 미채택 +(2026-11-07 사용자 confirm). **백업 주기·보존 기간 숫자는 아직 없다** — 근거 없이 채우지 않는다(§6 Q6). +반출기를 운영에서 어떻게 돌리나는 [`./binlog-shipper-ops.md`](./binlog-shipper-ops.md). 연관: [`./backup-restore-rto-rpo.md`](./backup-restore-rto-rpo.md) (복구 시간 실측) · [`./replication-lag-and-semisync.md`](./replication-lag-and-semisync.md) (복제 지연·반동기 대가) · [`../tasks/31-production-readiness-plan.md`](../tasks/31-production-readiness-plan.md) §DB 백업 정책 · @@ -139,10 +139,23 @@ Flyway 마이그레이션의 테이블 기준. «재생성» 칸은 코드·문 `exercise_rejected_attempts` 용도, `pose_data` 분석 전 유실)은 **확인 전**이다 — 확인 결과가 A~C 로 올라가도 Q4 결정(구성 2)은 바뀌지 않는다(전체가 한 RPO 라서) - [x] **Q3. RPO 를 하나로** → 🟢 **하나로 둔다.** 저장소를 나누지 않는다(§7) -- [~] **Q4. 호스트 상실 RPO** → 🟢 **구성 2 채택** — 백업 + binlog 를 주기적으로 서버 밖에. RPO = binlog 반출 주기. - 🟡 **반출 주기 숫자는 미정** — 추천안이 정하지 않았다. 고르는 근거는 «주기를 줄일수록 늘어나는 것» - (업로드 횟수·`FLUSH BINARY LOGS` 빈도·작은 binlog 파일 수) — 11-08 에 쟀다: 가정 피크에서 반출 1번에 업로드 - 0.48~0.71초·FLUSH 52~56ms, 하루 횟수는 60초 주기 1,440번 · 300초 주기 288번(§6-2). FLUSH 가 쓰기를 막는 시간과 요금은 안 쟀다 +- [x] **Q4. 호스트 상실 RPO** → 🟢 **구성 2 채택** — 백업 + binlog 를 주기적으로 서버 밖에. RPO = binlog 반출 주기. + 🟢 **반출 주기 60초** (2026-11-11 사용자 confirm). 약속하는 최대 손실 = **약 61초**(주기 60초 + 업로드 1초 미만, + 🔴 가정 피크 기준 — 최대 속도로 쓰면 업로드가 1GB 파일당 2.3~12초로 늘어 그만큼 더해진다). + - **주기가 정하는 것** — 서버를 잃으면 진행 중인 운동은 주기와 무관하게 끊긴다(DB·AI 가 한 서버, 복구 약 37분). + 주기가 더하는 손실은 **«이미 끝나 리포트까지 본» 운동**이다. 가정 피크(동시 약 68세션 · 세션 15분 → 1분에 약 4.5건 종료)에서 + 최악 기준: 30초 약 2건 · **60초 약 4.5건** · 300초 약 23건 · 900초 약 68건 (어차피 끊기는 약 68건 대비 +3% · +7% · +33% · +100%) + - **주기를 줄이는 비용** — 반출 1번 업로드 0.48~0.51초 · FLUSH 명령 52~56ms(60초 주기, 실측 §6-2), 하루 1,440번. + S3 요청(올리기 + 목록) 월 약 8.6만 건 ≈ 월 0.4달러 — 1,000건당 0.005달러(미국 동부 단가)로 계산, 서울 단가 미확인. + 저장 요금은 쓰는 양이 같아 주기와 무관 + - **왜 60초** — 300초 → 60초는 비용 차이가 월 1달러 미만인데 추가 손실이 약 23건 → 약 4.5건. 60초 → 30초는 줄어드는 게 + 약 2건인데 안 잰 FLUSH 비용이 두 배가 된다 + - 🟡 **조건 — FLUSH 가 쓰기를 막는 시간을 안 쟀다.** 결정을 먼저 하고 측정은 뒤에 붙인다(사용자 선택). 근거: FLUSH 명령 자체가 + 52~56ms(최대 속도 68~178ms)라 막히는 시간도 그 근처일 것으로 추론 — 문제 기준(AI→Spring 재시도 한도 약 19초)까지 100배 이상 여유. + **다시 여는 조건**: 로컬 측정에서 FLUSH 순간 커밋 지연이 FLUSH 명령 시간을 크게 넘으면 EC2 로 올려 재고 Q4 를 다시 본다. + 같은 측정에 `PURGE BINARY LOGS`(반출기 D4)도 넣는다 + - 반출 업로드 값(0.48~0.71초)은 rig 가 실패를 성공으로 기록할 수 있는 결함(#855)이 있었지만, S3 객체 42개와 이름·크기를 대조해 + 전부 성공한 업로드였음을 확인했다 - [x] **Q5. 목표 RTO** → 🟢 **장애 종류별 · 시간대별 목표 확정, 리플리카(3) 보류 유지** (2026-11-08 사용자 confirm, §6-3) - [~] **Q6. 백업 주기·보관 위치·보존 기간** → 🟢 **위치는 서버 밖**(구성 2 의 정의). 🟡 **주기·보존 기간은 미정** — 운영 호스트가 정해지지 않았다(`31-production-readiness-plan.md:218` 과 같은 이유) @@ -302,3 +315,6 @@ DAU 와 비교해 판단을 닫지 않는다는 원칙과도 어긋난다([[feed 리플리카(3) 는 **보류 유지** — 줄이는 게 기술 시간 약 7분뿐이고 사람 시간이 대부분이라서. 근거로 쓴 같은 날 실측 둘: [프로세스 재기동](../../loadtest/results/process-restart-rto-2026-11-08/README.md)(쉼 1.3초 · 가정 피크 1.9~2.4초 · 최대 쓰기 33.6~35.1초, 잃은 0) · [페일오버 앱 전환](../../loadtest/results/failover-app-reconnect-2026-11-08/README.md)(앱 재시작 약 106초 · DNS 5~30초, 단 네트워크 끊김에서 요청 약 14개가 약 16분 매달림, 잃은 0). +- 2026-11-11: 🟢 **Q4 반출 주기 60초 확정** (사용자 confirm). 약속 최대 손실 약 61초(가정 피크). 근거는 «주기가 더하는 손실 = 이미 끝난 + 운동» 계산(60초 약 4.5건 · 300초 약 23건)과 비용(월 1달러 미만 차이). 🟡 FLUSH 가 쓰기를 막는 시간은 안 쟀다 — 측정은 결정 뒤에 + 붙이고, 크면 다시 연다. 반출기 운영 설계는 [`binlog-shipper-ops.md`](./binlog-shipper-ops.md)(분기점, PR #854). From a8d892bc586215cc2b5614534c7efa8ad1fdc224 Mon Sep 17 00:00:00 2001 From: Khyojae Date: Wed, 11 Nov 2026 22:03:27 +0900 Subject: [PATCH 2/2] =?UTF-8?q?docs(rpo-rto):=20Q4=20=EB=A6=AC=EB=B7=B0=20?= =?UTF-8?q?=EB=B0=98=EC=98=81=20=E2=80=94=2061=EC=B4=88=EB=8A=94=20?= =?UTF-8?q?=EB=B0=98=EC=B6=9C=20=EC=84=B1=EA=B3=B5=20=EC=A1=B0=EA=B1=B4?= =?UTF-8?q?=EB=B6=80,=204.5=EA=B1=B4=EC=9D=80=20=ED=8F=89=EA=B7=A0=20?= =?UTF-8?q?=EA=B8=B0=EC=A4=80,=20=EC=9E=AC=EC=98=A4=ED=94=88=EC=97=90=2019?= =?UTF-8?q?=EC=B4=88=20=ED=95=9C=EB=8F=84=20=EC=A7=81=EC=A0=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 약 61초는 «매 주기 반출 성공» 동안의 약속. 실패 구간엔 마지막 성공 뒤 전부를 잃고 상한이 없다 — 그 구간을 줄이는 건 주기가 아니라 반출 멈춤 알림(binlog-shipper-ops D6, #673~680) - 주기별 추가 손실 건수를 «최악» 에서 «평균 종료율 기준» 으로 정정. 종료가 몰리면 더 많을 수 있고 집중도는 모른다 - 다시 여는 조건: ① 19초 넘는 커밋이 하나라도 → 바로 재오픈 ② 명령 시간보다 길게 막힘 → 추론 깨짐, EC2 재측정 Co-Authored-By: Claude Opus 5.5 --- docs/decisions/rpo-rto-targets.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/docs/decisions/rpo-rto-targets.md b/docs/decisions/rpo-rto-targets.md index 5798bd1c..d81ee5c5 100644 --- a/docs/decisions/rpo-rto-targets.md +++ b/docs/decisions/rpo-rto-targets.md @@ -142,9 +142,14 @@ Flyway 마이그레이션의 테이블 기준. «재생성» 칸은 코드·문 - [x] **Q4. 호스트 상실 RPO** → 🟢 **구성 2 채택** — 백업 + binlog 를 주기적으로 서버 밖에. RPO = binlog 반출 주기. 🟢 **반출 주기 60초** (2026-11-11 사용자 confirm). 약속하는 최대 손실 = **약 61초**(주기 60초 + 업로드 1초 미만, 🔴 가정 피크 기준 — 최대 속도로 쓰면 업로드가 1GB 파일당 2.3~12초로 늘어 그만큼 더해진다). + 🔴 **약 61초는 «반출이 매 주기 성공하는 동안» 의 약속이다.** 반출이 실패하는 동안(S3 장애 · 자격증명 만료 · 반출기 멈춤)에 + 서버를 잃으면 손실 = **마지막으로 반출에 성공한 뒤 지난 시간 전부**이고 상한이 없다. 그 구간을 짧게 만드는 것은 주기가 아니라 + 반출 멈춤 알림이다 — 지표·실패 처리는 [`binlog-shipper-ops.md`](./binlog-shipper-ops.md) D6·§6, 알림 경로는 #673~680 (🔴 아직 없음) - **주기가 정하는 것** — 서버를 잃으면 진행 중인 운동은 주기와 무관하게 끊긴다(DB·AI 가 한 서버, 복구 약 37분). 주기가 더하는 손실은 **«이미 끝나 리포트까지 본» 운동**이다. 가정 피크(동시 약 68세션 · 세션 15분 → 1분에 약 4.5건 종료)에서 - 최악 기준: 30초 약 2건 · **60초 약 4.5건** · 300초 약 23건 · 900초 약 68건 (어차피 끊기는 약 68건 대비 +3% · +7% · +33% · +100%) + 주기 하나를 통째로 잃을 때 **평균 종료율 기준**: 30초 약 2건 · **60초 약 4.5건** · 300초 약 23건 · 900초 약 68건 + (어차피 끊기는 약 68건 대비 +3% · +7% · +33% · +100%). 🟡 **최악 건수가 아니다** — 종료가 몰리면(예: 같은 시각에 시작한 + 그룹 운동) 한 주기 안에 더 많이 끝날 수 있다. 종료 집중도는 실사용 기록이 없어 모른다 — 주기 사이 비교(비율)로만 쓴다 - **주기를 줄이는 비용** — 반출 1번 업로드 0.48~0.51초 · FLUSH 명령 52~56ms(60초 주기, 실측 §6-2), 하루 1,440번. S3 요청(올리기 + 목록) 월 약 8.6만 건 ≈ 월 0.4달러 — 1,000건당 0.005달러(미국 동부 단가)로 계산, 서울 단가 미확인. 저장 요금은 쓰는 양이 같아 주기와 무관 @@ -152,7 +157,10 @@ Flyway 마이그레이션의 테이블 기준. «재생성» 칸은 코드·문 약 2건인데 안 잰 FLUSH 비용이 두 배가 된다 - 🟡 **조건 — FLUSH 가 쓰기를 막는 시간을 안 쟀다.** 결정을 먼저 하고 측정은 뒤에 붙인다(사용자 선택). 근거: FLUSH 명령 자체가 52~56ms(최대 속도 68~178ms)라 막히는 시간도 그 근처일 것으로 추론 — 문제 기준(AI→Spring 재시도 한도 약 19초)까지 100배 이상 여유. - **다시 여는 조건**: 로컬 측정에서 FLUSH 순간 커밋 지연이 FLUSH 명령 시간을 크게 넘으면 EC2 로 올려 재고 Q4 를 다시 본다. + **다시 여는 조건** (둘 중 하나라도): + ① FLUSH(또는 PURGE) 순간 커밋 지연이 **19초 한도를 넘는 커밋이 하나라도 나오면** → Q4 를 바로 다시 연다(문제 기준 자체를 넘은 것) + ② 커밋 지연이 **FLUSH 명령 시간보다 길게** 나오면 → «막히는 시간 ≈ 명령 시간» 이라는 위 추론이 깨진 것이다. 로컬 절대값은 + 못 믿으므로(2코어 박스) EC2 로 올려 다시 재고 ① 로 판정한다. 같은 측정에 `PURGE BINARY LOGS`(반출기 D4)도 넣는다 - 반출 업로드 값(0.48~0.71초)은 rig 가 실패를 성공으로 기록할 수 있는 결함(#855)이 있었지만, S3 객체 42개와 이름·크기를 대조해 전부 성공한 업로드였음을 확인했다