스택 이해 및 느린 요청의 원인
Rails 스택(Nginx → Puma → Rails)에서 상위 레이어 타임아웃을 하위 레이어 처리 시간이 초과하면 499 오류가 발생합니다. 느린 요청은 CPU-bound (Ruby GVL 차단)와 I/O-bound (느린 의존성 대기) 두 가지 유형으로 나뉩니다.
빠른 완화 전략
499 오류를 줄이기 위한 단기적인 해결책은 다음과 같습니다.
- 1. 타임아웃 정렬:
- Puma:
worker_timeout,force_shutdown_after설정. - Nginx:
proxy_connect_timeout,proxy_send_timeout,proxy_read_timeout,send_timeout조정. 클라이언트/CDN 타임아웃은 Nginx보다 짧게 유지.
- Puma:
- 2. 긴 작업 백그라운드 처리:
- 오래 걸리는 작업은 Sidekiq/Resque 큐로 옮기고, 클라이언트에는
202 Accepted응답 즉시 반환.
- 오래 걸리는 작업은 Sidekiq/Resque 큐로 옮기고, 클라이언트에는
- 3. 비용이 많이 드는 읽기 캐싱:
Rails.cache.fetch로 데이터 캐싱, ETag 및 Cache-Control 헤더 추가로 CDN 캐싱 활용.
장기적인 개선 방안
시스템 견고함과 성능 향상을 위한 장기적인 접근 방식입니다.
- 1. CPU vs IO 풀 분리:
Procfile을 통해 Puma 인스턴스를web_io와web_cpu로 분리하고, Nginx에서 라우팅하여 CPU 작업이 I/O 스레드를 차단하지 않도록 함.
- 2. 연결 풀 튜닝:
- DB 연결 풀 크기를 Puma 최대 스레드 수 이상으로 설정하여 병목 현상 방지.
- 3. 캐시 사전 워밍:
- 배포 시 주요 엔드포인트를 호출하여 캐시를 미리 채워 넣음.
- 4. 가시성 확보 (Observability):
Rack::Timeout으로 요청 타임아웃 설정, p95/p99 지연 시간, 499 오류율, Puma 스레드 포화도, DB 연결 풀 대기 시간 등을 모니터링.
타임아웃 계층 구조
클라이언트/CDN (30s) > Nginx proxy_read_timeout (60s) > Rack::Timeout (25s) > DB/HTTP 클라이언트 (10~20s) > Puma worker_timeout (60s) 순으로 타임아웃을 설정하여 균형 잡힌 시스템 구축.