Skip to content

MSG-609 test: 부하테스트를 dev 사양(t3.small) 임시 서버에서 다시 재고 저장 구조·알림·인코딩 비교를 보고서로 남긴다 - #290

Merged
Ss0Mae merged 3 commits into
developfrom
feature/MSG-609-cloud-load-test
Oct 2, 2026

Conversation

@Ss0Mae

@Ss0Mae Ss0Mae commented Oct 2, 2026 •

Copy link
Copy Markdown
Member

🎫 관련 티켓

  • Related MSG-609 (완료 조건 중 "임시 서버·보안 그룹 삭제"가 남아 있어 Closes 는 아닙니다. 아래 리뷰 포인트)

작업 내용

지금까지의 HTTP 부하테스트는 전부 노트북 한 대에서 앱·DB·k6를 같이 띄운 것이라 dev 사양에서 어디서 무너지는지를 몰랐습니다. 같은 VPC에 dev와 같은 t3.small 앱 박스와 t3.medium 부하기 박스를 띄워 같은 시나리오를 다시 쟀습니다. 세 커밋으로 나눴습니다.

1. add 런북·러너·하네스 (load-test/bench-ec2/, load-test/*.py, k6)

  • 앱·부하기 user-data, 벤치 env·compose, 5초 자원 샘플러, 1·2단계 러너, k6 요약과 차트 스크립트. monitoring/README.md에서 이 런북으로 안내합니다.
  • notification-comparison.py 신설: 알림 발송 구조 3단을 격리 컨테이너에서 모사합니다.
  • hotzone-comparison.py에 버킷 없는 Redis(redis_naive) 비교군과 --modes·--scales, encoding-ab.py에 동시 건수·타임아웃·이미지 불일치 허용 env, viewport-ab-benchmark.js의 응답 체크를 현재 계약(data.grids)으로.

2. test 원시 결과 (load-test/evidence/2026-09-29, 2026-09-30, 3.3MB)

  • k6 --summary-export JSON, 앱 박스 샘플러 CSV, 시드 로그, 비교 회차 json·gz. 회차당 수십 MB인 k6 원시 JSON은 이전과 같이 넣지 않았습니다.

3. docs 보고서 7종 (docs/reports/)

재실측 4종(종합 + 뷰포트·핫구역·도감 요약). 한 줄 결론은 세 API 모두 5xx 없이 버텼지만 상한은 노트북의 1/2에서 1/12이고 병목이 API마다 다릅니다.

API 로컬 상한 t3.small 상한 병목
뷰포트 격자 조회 약 286 rps (40 VU p95 96ms) 약 90 rps (p95 651ms) PostgreSQL CPU 1코어, 첫 페이지 쿼리 11ms/건
핫구역 조회 2,000 rps (p99 3.4ms) 800 rps 유지, 무릎 약 930 rps 앱 JVM CPU. Redis는 1%
도감 요약 (헤비 5%) 1,000 rps 통과 60~80 rps에서 붕괴 PostgreSQL CPU 2코어, 헤비 사용자 1건 0.45초

구조 비교 3종은 "그때 왜 그 선택이었나"를 사후 실측으로 확인한 기록이고 코드는 바꾸지 않았습니다.

  • 핫구역 저장 구조 4단: DB 집계 → Redis 원시 신호 → 6시간 버킷 → 버킷 + 캐시. 신호 100만 건, 100 rps에서 성공률 0.7% → 2.9% → 100% → 100%. "Redis라서 빠르다"가 아니라 "쓰기 때 미리 합쳐 둔 것"이 빠릅니다. 버킷 없는 Redis(②)는 DB보다 12배 느렸습니다.
  • 알림 발송 구조 3단: 동기 직접 발송 → DB 폴링 릴레이 → outbox1 + Kafka. 요청 p95는 ① 50ms, ②③ 8ms로 같고, FCM 20초 장애 때 유실은 ① 1,508건, ②③ 0건. 이 두 성질은 outbox의 몫이지 Kafka의 몫이 아닙니다. 처리량은 세 구조 다 FCM 직렬과 폴링 상수에 묶여 초당 20건 안팎입니다.
  • 영상 인코딩 1노드 vs 2노드, 동시 3·6·12건: 완료 시간이 동시 건수에 비례하고 2노드가 모든 규모에서 약 45% 줄입니다(12건 198초 → 107초). 병목은 작업 전달이 아니라 워커 CPU입니다.

🤔 고민한 내용

dev와 같게 둔 것과 다르게 둔 것을 표로 밝혔습니다. 이미지·프로파일·인스턴스 타입·swap·DB/Redis 동거는 같고, SQL·DEBUG 로그를 INFO로 내리고 부트 워밍업·AI·알림·CloudFront를 껐습니다. 부하 대상 API 셋은 이 중 어느 것도 경로에서 쓰지 않습니다. t3.small은 dev와 같은 unlimited 크레딧 모드2였고 벤치 중 잔고가 0이라, 이 숫자를 dev에 대입해도 낙관적이지 않습니다.

포화 구간 판정은 Prometheus가 아니라 샘플러 CSV를 정본으로 썼습니다. 앱이 응답을 못 하는 구간에서는 scrape가 빠져서 Prometheus만 보면 포화가 안 보입니다.

뷰포트 첫 s1·s3 회차는 체크 실패율 100%로 찍혔는데 지연 수치는 살렸습니다. k6 체크가 옛 응답 형태(body 배열)를 보고 있었고 HTTP는 전부 200이었습니다. 고친 뒤 s1을 다시 돌렸습니다(2단계). strategy=A|B는 현재 컨트롤러에 없어 같은 쿼리를 두 번 재는 반복 측정이 됐습니다.

알림 비교에서 Kafka를 깎아내리지 않도록 썼습니다. 알림 PRD 5.1이 "Kafka는 배우려고 넣었다"고 스스로 적어 둔 조건부 승인이라, 실측은 그 판단을 뒤집는 게 아니라 outbox 몫과 Kafka 몫을 숫자로 가르는 데 썼습니다. 다음 튜닝 지점은 릴레이 상수(5s/100건)이고, 행사 시작 팬아웃(MSG-583)이 그 경로입니다.

비용은 약 $0.12였습니다. 사전 추산(하루 $5 미만)보다 훨씬 적었고, 시간이 아니라 크레딧이 변수였습니다.

👀 리뷰 포인트

  • 임시 인스턴스 2대는 종료가 아니라 중지(stop) 상태이고 SG fillmap-bench-sg가 남아 있습니다(EBS 50GB, 월 약 $4.6). 종합 보고서 "정리" 절에 terminate 명령을 적어 뒀습니다. 재실측 계획이 없으면 머지 전후에 지우고 티켓을 닫겠습니다.
  • load-test/evidence/ 3.3MB가 레포에 들어갑니다. MSG-608 증거 폴더(json·gz·png)와 같은 구성입니다. 더 줄여야 하면 gz 29개(2.0MB)를 뺄 수 있습니다.
  • encoding-ab.py의 ALLOW_IMAGE_MISMATCH=1은 dev api가 실험 이미지를 달고 있을 때만 쓰는 우회라 기본값은 여전히 assert입니다.
  • 범위 밖으로 남긴 것: MSG-89 캐시 4전략(미머지 브랜치 필요), MSG-585(스크립트 미커밋), MSG-583(JVM 안 부하기), MSG-494(이미 dev 실측). 종합 보고서 끝에 적었습니다.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VzV5yGk9MqAR1kZH1ULrjU


Generated by Claude Code

Footnotes

  1. outbox 패턴: 외부 발행(큐, 메시지)을 요청 안에서 직접 하지 않고 같은 DB 트랜잭션에 "보낼 것" 행을 남긴 뒤 별도 프로세스가 그 행을 읽어 발행하는 방식. DB 커밋과 발행이 원자적일 수 없는 이중 쓰기 문제를 피합니다. 이번 실측에서 "요청 지연 8ms"와 "장애 시 유실 0건"은 이 행 하나가 만든 성질입니다. ↩

  2. unlimited 크레딧 모드: t3 계열은 평소 CPU 사용량이 기준치(t3.small은 20%) 아래일 때 크레딧을 모아 두고 버스트 때 씁니다. unlimited는 크레딧이 바닥나도 성능을 깎지 않는 대신 초과분을 과금하는 설정입니다. 잔고 0에서 잰 수치라 "크레딧 덕에 잘 나온 값"이 아닙니다. ↩

Ss0Mae and others added 3 commits October 2, 2026 02:53
load-test/bench-ec2/: dev 사양(t3.small) 앱 박스와 부하기 박스를 같은 VPC에 띄우는
user-data·env·compose, 5초 자원 샘플러, 1·2단계 러너, k6 요약과 이야기용 차트 스크립트.
monitoring/README.md에서 이 런북으로 안내한다.

load-test/notification-comparison.py: 알림 발송 구조 3단(동기 직접 발송, DB 폴링 릴레이,
outbox + Kafka + 컨슈머)을 격리 컨테이너에서 모사해 비교하는 하네스.

hotzone-comparison.py에 버킷 도입 전 형태인 redis_naive 비교군과 --modes·--scales 인자,
encoding-ab.py에 동시 건수·타임아웃·이미지 불일치 허용 env, viewport-ab-benchmark.js의
응답 검사를 현재 응답 형태(data.grids)에 맞춘다.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzV5yGk9MqAR1kZH1ULrjU
2026-09-29: 뷰포트·핫구역·도감 요약 k6 요약, 앱 박스 자원 샘플, 시드 로그, 핫구역 저장
구조 4단 비교(hotzone-story). 2026-09-30: 알림 발송 구조 3단 비교와 영상 인코딩
1노드·2노드 규모 회차. 해석은 docs/reports/2026-09-29-*, 2026-09-30-*.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzV5yGk9MqAR1kZH1ULrjU
dev 사양(t3.small) 재실측: 종합, 뷰포트 격자 조회, 핫구역 조회, 도감 요약 조회.
구조 비교: 핫구역 저장 구조 4단(DB 집계 → Redis 원시 신호 → 6시간 버킷 → 버킷 + 캐시),
알림 발송 구조 3단(동기 → DB 폴링 → outbox + Kafka), 영상 인코딩 1노드 vs 2노드.
기존 로컬 보고서는 그대로 두고 클라우드판을 새로 쓴다(티켓 완료 조건).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VzV5yGk9MqAR1kZH1ULrjU
@claude

claude Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Claude finished @Ss0Mae's task in 20s —— View job


PR #290 리뷰

확인한 범위

  • origin/develop 대조: src/·.claude/·마이그레이션 변경 없음. 마이그레이션 번호, developCode 대역, Owner A/B 계약 경합 해당 없음.
  • 변경 성격은 부하 하네스(load-test/), 증거 데이터, 보고서뿐이라 스펙·트랜잭션·시간대 항목은 대상이 아님.
  • PR 본문 주장 대조: "코드는 바꾸지 않았다"는 사실과 일치. encoding-ab.py의 ALLOW_IMAGE_MISMATCH 기본값은 assert 유지로 본문과 일치. bench.env 시크릿은 더미 값.
  • 보고서 수치와 증거 JSON의 개별 대조는 하지 않았음(미검증).

발견

  • P3 · load-test/bench-ec2/run-phase1.sh:4 · 앱 박스 사설 IP 하드코딩(phase2도 동일), env 오버라이드 권장

참고(코드 밖)

  • 임시 인스턴스 2대와 SG가 남아 있어 본문대로 머지 전후 terminate 후 티켓 종료가 필요합니다. 월 약 $4.6 과금이 계속됩니다.

Comment thread load-test/bench-ec2/run-phase1.sh
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

테스트 커버리지

Overall Project 95.9% 🍏

There is no coverage information present for the Files changed

@Ss0Mae
Ss0Mae merged commit 78d8a37 into develop Oct 2, 2026
2 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.

1 participant