v2 ECS 마이그레이션 테스트 결과 - 100-hours-a-week/KTB4-17th-wiki GitHub Wiki
1차 테스트 결과
70 message/s · 200 vu · 메시지 10만 건
| 지표 | 결과 |
|---|---|
| 테스트 조건 | 30분, 200명·1:1 채팅방 100개, 실제 10만 건 전송 |
| 종합 판정 | FAIL |
| 연결 전환 | 기존 사용자 100명 모두 Green 이동 성공 |
| HTTP | 성공 99,999건, 전환 중 실패 1건 |
| 중복 수신 | 1,450회 |
2차 테스트 결과
35 message/s · 200 vu · 메시지 6만 900건
| 지표 | 결과 |
|---|---|
| HTTP 성공·DB 저장 | 60,899건, 전환 중 실패 1건 |
| HTTP 전송 오류 | 1건 |
| reload 구간 서버 RST | 1건 |
| 온라인 상대방 메시지 미수신 | 0건 |
| 최종 메시지 누락 | 0건 — 재접속 중 놓친 13건은 이력 조회로 복구 |
| WebSocket 중복 수신 | 0건 |
1건 실패의 원인
nginx reload 시 유휴 상태인 HTTP keep-alive 연결을 닫는 과정에서 발생한 경쟁 조건이다.
nginx는 해당 연결의 소켓을 닫고, OS는 FIN을 전송하며 TCP 종료 절차를 진행한다.
클라이언트는 연결 종료를 아직 인지하지 못한 상태에서 기존 연결로 POST 요청을 보낸다.
POST 패킷은 서버까지 도착하지만, 서버에서는 이미 해당 소켓을 닫았기 때문에 HTTP 요청으로 처리되지 않고 OS가 RST를 반환한다.
이번 요청은 Spring에 도달하지 않았고, 클라이언트의 재시도도 없어서 메시지가 저장되지 않았다.
해결 방법
nginx의 keepalive_min_timeout 설정을 주어서 해당 문제를 해결한다.
https://github.com/nginx/nginx/pull/456
3차 테스트 결과
| 지표 | 결과 |
|---|---|
| HTTP 성공·DB 저장 | 60,900건 전부 |
| HTTP 전송 오류 | 0건 |
| reload 구간 서버 RST | 0건 |
| 온라인 상대방 메시지 미수신 | 0건 |
| 최종 메시지 누락 | 0건 |
| 수신 지연 p95 | 14.67초 |
| 수신 지연 p99 | 36.36초 |
| WebSocket 중복 수신 | 14건 |
과한 지연 시간의 원인
서버 처리량의 한계도 있겠지만, 블루-그린 전환 전에 DB에 rabbit_published 컬럼이 추가되고 Green으로 전환되는 사이에 쌓인 Outbox들이 Green 컨테이너에 순간적인 부하를 주기 때문에 안정화 전까지 지연 시간이 치솟는다.
해결 방법
nginx의 트래픽을 Green으로 보내기 직전에 DB 쿼리를 통해 동기화를 한 번 진행하자.
4차 테스트 결과
| 지표 | 결과 |
|---|---|
| HTTP 성공·DB 저장 | 60,896건 — 전송 실패 4건 |
| HTTP 전송 오류 | 4건 (ECONNRESET) — 모두 reload 이전 발생, DB 미저장 |
| 온라인 상대방 메시지 미수신 | 0건 |
| 최종 메시지 누락 | 0건 |
| 수신 지연 p95 | 2.16초 |
| 수신 지연 p99 | 13.89초 |
| WebSocket 중복 수신 | 1건 |
4건 실패의 원인
nginx reload와 별개로, 75초 동안 사용하지 않은 HTTP keep-alive 연결을 종료하는 과정에서 경쟁 조건이 발생했다.
nginx가 FIN을 보낸 뒤, HTTP 클라이언트가 연결 종료를 처리하기 전에 기존 연결로 POST 요청을 보냈고 서버는 RST를 반환했다.
해당 요청들은 Spring에 도달하지 않았으며, 재시도가 없어 DB에 저장되지 않았다.
→ keep-alive 설정을 끄고 보내보자.
5차 테스트 결과
| 지표 | 결과 |
|---|---|
| HTTP 성공·DB 저장 | 60,900건 |
| HTTP 전송 오류 | 0건 |
| 온라인 상대방 메시지 미수신 | 0건 |
| 발송한 메시지의 최종 누락 | 0건 |
| 수신 지연 p95 | 2.24초 |
| 수신 지연 p99 | 16.87초 |
| WebSocket 중복 수신 | 1건 |
참고자료
https://github.com/nginx/nginx/pull/456
https://nginx.org/en/docs/control.html#reconfiguration
새 워커 프로세스를 시작하고 기존 워커 프로세스에 정상적으로 종료하도록 요청하는 메시지를 보냅니다.
기존 워커 프로세스는 수신 소켓을 닫고 이전 클라이언트에 서비스를 계속 제공합니다.
모든 클라이언트에 대한 서비스가 완료되면 기존 워커 프로세스가 종료됩니다.
https://iximiuz.com/en/posts/reverse-proxy-http-keep-alive-and-502s/