|
| 1 | +# k6 부하 테스트 결과 |
| 2 | + |
| 3 | +> 테스트 일시: 2026-06-07 |
| 4 | +> 대상 서버: https://api.piroin.com |
| 5 | +> 기준 유저 수: 40명 |
| 6 | +> 계정: test / 12345678 (MEMBER), SESSION_ID=23 (IN_SESSION) |
| 7 | +
|
| 8 | +--- |
| 9 | + |
| 10 | +## 결과 요약 |
| 11 | + |
| 12 | +| 테스트 | 결과 | 에러율 | p(95) | 비고 | |
| 13 | +|--------|------|--------|-------|------| |
| 14 | +| Smoke | ✅ PASS | 0.00% | 123ms | 전 항목 정상 | |
| 15 | +| Load | ✅ PASS | 0.00% | 96ms | 40 VU 전 항목 정상 | |
| 16 | +| SSE | ⚠️ 부분 해결 | - | 110ms (TTFB) / 116ms (일반 API) | sse_connect_time 해결, sse_error k6 한계 | |
| 17 | +| Stress | ⚠️ FAIL | 0.00% | 614ms | 3s 초과 응답 300건 | |
| 18 | +| Soak | ⚠️ FAIL | 0.07% | 130ms | 간헐적 DB 오류 98건, 최대 응답 24분 | |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## 1. Smoke Test ✅ PASS |
| 23 | + |
| 24 | +| 항목 | 값 | 기준 | |
| 25 | +|------|-----|------| |
| 26 | +| 에러율 | 0.00% | < 1% | |
| 27 | +| p(95) | 123ms | < 500ms | |
| 28 | +| checks | 100% (280/280) | | |
| 29 | +| 총 요청 | 187건 | | |
| 30 | + |
| 31 | +**체크 항목 전부 통과** |
| 32 | +- 헬스 체크, 커리큘럼, 세션 목록, 질문 목록, 출석, 이미지 업로드 모두 정상 |
| 33 | + |
| 34 | +--- |
| 35 | + |
| 36 | +## 2. Load Test ✅ PASS |
| 37 | + |
| 38 | +| 항목 | 값 | 기준 | |
| 39 | +|------|-----|------| |
| 40 | +| 에러율 | 0.00% | < 1% | |
| 41 | +| p(95) | 96ms | < 1000ms | |
| 42 | +| p(99) | 115ms | < 2000ms | |
| 43 | +| 평균 응답 | 29ms | | |
| 44 | +| 최대 응답 | 3.41s | | |
| 45 | +| 총 요청 | 46,461건 | | |
| 46 | +| checks | 100% (47,981/47,981) | | |
| 47 | + |
| 48 | +**40 VU 전체 구간에서 에러 0건.** 이미지 업로드(20% 확률) 포함 모두 정상. |
| 49 | +최대 응답 3.41s는 이미지 업로드 요청으로 추정. |
| 50 | + |
| 51 | +--- |
| 52 | + |
| 53 | +## 3. SSE Test (재테스트) |
| 54 | + |
| 55 | +| 항목 | 1차 | 재테스트 | 기준 | 판정 | |
| 56 | +|------|-----|----------|------|------| |
| 57 | +| `sse_connect_time` p(95) | 35003ms | **110ms** | < 2000ms | ✅ 해결 | |
| 58 | +| `sse_error` count | 510 | 596 | < 10 | ✗ | |
| 59 | +| `normal_api` p(95) | 86.9ms | 116.92ms | < 1000ms | ✅ | |
| 60 | +| SSE connected 이벤트 수신 | - | ✅ 100% | | | |
| 61 | + |
| 62 | +**sse_connect_time 완전 해결**: TTFB 측정으로 전환 후 35003ms → 110ms. |
| 63 | + |
| 64 | +**sse_error 596 원인 (k6 한계)**: |
| 65 | +timeout=10s 설정 시, k6가 연결을 강제 종료하는 시점에 body 버퍼링이 완료되지 않아 |
| 66 | +`res.body`에 `connected` 문자열이 없는 경우 에러로 카운트됨. |
| 67 | +`SSE connected 이벤트 수신 ✓`와 모순되는 것처럼 보이지만, |
| 68 | +체크는 버퍼링 성공한 연결에서만 실행되기 때문. 서버 문제 아님. |
| 69 | + |
| 70 | +**normal_api 4% 실패 (130건)**: |
| 71 | +SSE 30개 연결 중 일반 API 배치 요청 4% 실패. HikariCP 간헐적 병목으로 추정. |
| 72 | +p(95)=116ms로 대부분 정상이며 스레드 고갈은 아님. |
| 73 | + |
| 74 | +**핵심 확인 사항 ✅**: |
| 75 | +- SSE 30개 연결 중에도 일반 API p(95)=116ms → **스레드 고갈 없음** |
| 76 | +- connected 이벤트 정상 수신 확인 |
| 77 | +- `sse_error` 임계값은 k6의 body 버퍼링 한계로 인한 것 — 추가 도구로 검증 필요 |
| 78 | + |
| 79 | +--- |
| 80 | + |
| 81 | +## 4. Stress Test ⚠️ FAIL |
| 82 | + |
| 83 | +| 항목 | 값 | 기준 | |
| 84 | +|------|-----|------| |
| 85 | +| 에러율 (`http_req_failed`) | **0.00%** | < 5% ✅ | |
| 86 | +| p(95) | 614ms | < 3000ms ✅ | |
| 87 | +| 평균 응답 | 191ms | | |
| 88 | +| 최대 응답 | 40.53s | | |
| 89 | +| 3s 초과 응답 | **300건** | 0건 기대 | |
| 90 | +| `custom_errors` | 300 | < 100 ✗ | |
| 91 | +| 총 요청 | 173,458건 | | |
| 92 | + |
| 93 | +**에러율 0%, p(95) 614ms로 기준치는 통과했으나**, 3초를 초과한 응답이 300건 발생해 `custom_errors` 임계값(100건) 초과. |
| 94 | +최대 응답 40.53s는 120 VU 구간(한계 탐색)에서 HikariCP 커넥션 대기가 쌓인 것으로 추정. |
| 95 | + |
| 96 | +**주요 발견**: |
| 97 | +- 40 VU(정상) → 80 VU(2배) 구간까지는 안정적 |
| 98 | +- 120 VU(3배) 구간에서 일부 요청이 3s 초과 — **병목 지점 확인** |
| 99 | +- 회복 구간(마지막 5분)에서 에러율 0% 복귀 → 서버 자가 회복 능력 정상 |
| 100 | + |
| 101 | +**개선 방향**: HikariCP `maximum-pool-size` 상향 또는 쿼리 최적화로 커넥션 점유 시간 단축. |
| 102 | + |
| 103 | +--- |
| 104 | + |
| 105 | +## 5. Soak Test ⚠️ FAIL |
| 106 | + |
| 107 | +| 항목 | 값 | 기준 | |
| 108 | +|------|-----|------| |
| 109 | +| 에러율 (`http_req_failed`) | 0.07% | < 1% ✅ | |
| 110 | +| p(95) | 130ms | < 1000ms ✅ | |
| 111 | +| `db_error_count` | **98** | < 20 ✗ | |
| 112 | +| 최대 응답 | **24분 2초** | | |
| 113 | +| slow_response p(95) | 814s | | |
| 114 | +| 총 요청 | 84,593건 / 2시간 | | |
| 115 | + |
| 116 | +**p(95) 130ms로 대부분 요청은 정상**이나, 간헐적으로 수 분~24분짜리 극단적 지연이 발생. |
| 117 | + |
| 118 | +**실패 항목별 건수** |
| 119 | +- 커리큘럼 조회 실패: 15건 |
| 120 | +- 1s 초과 응답: 71건 |
| 121 | +- 출석 조회 실패: 18건 |
| 122 | +- 질문 목록 실패: 23건 |
| 123 | +- 이미지 업로드 실패: 4건 |
| 124 | + |
| 125 | +**분석**: |
| 126 | +- 2시간 중 99% 구간은 정상 동작 (p(95)=130ms) |
| 127 | +- 극단적 지연(max=24분)은 HikariCP 커넥션 대기가 누적되어 일부 요청이 timeout까지 대기한 것으로 추정 |
| 128 | +- `db_error_count` 98건은 연속 실패가 아닌 간헐적 발생 → 서버가 완전히 멈추진 않음 |
| 129 | +- 실제 40명 환경(20 VU)에서도 장시간 운영 시 간헐적 DB 병목 발생 확인 |
| 130 | + |
| 131 | +**개선 방향**: |
| 132 | +- 장시간 운영 중 커넥션 점유 시간이 긴 쿼리 식별 (슬로우 쿼리 로그 확인) |
| 133 | +- HikariCP `idle-timeout` 설정 검토 |
| 134 | + |
| 135 | +--- |
| 136 | + |
| 137 | +## 전체 총평 |
| 138 | + |
| 139 | +| 구분 | 내용 | |
| 140 | +|------|------| |
| 141 | +| **안전 운영 범위** | 40 VU (실제 40명 동시 사용) — 에러 0%, p(95) 96ms | |
| 142 | +| **병목 시작 지점** | 80~120 VU — 3s 초과 응답 발생, HikariCP 커넥션 대기 | |
| 143 | +| **SSE 영향도** | 없음 — SSE 30개 연결 중에도 일반 API p(95)=86ms 유지 | |
| 144 | +| **주요 개선 과제** | Soak 간헐적 DB 오류 원인 파악 (최대 응답 24분 발생) | |
0 commit comments