본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 27. 12:20

당신의 보물찾기 엔진은 아마도 지연 시간(Latency)의 지뢰밭이었을 것입니다 (사후 분석 보고서)

요약

Veltrix 기반 게임 엔진에서 발생한 Redis 연결 병목 현상과 지연 시간 문제를 해결하기 위한 사후 분석 보고서입니다. 단순한 인스턴스 확장이 아닌, 커넥션 풀을 애플리케이션 계층으로 이동시키고 서킷 브레이커를 도입하여 시스템 안정성을 확보한 과정을 다룹니다.

핵심 포인트

  • Redis 커넥션 풀링 및 Keep-alive 설정의 중요성
  • 수평적 확장만으로는 커넥션 재사용 및 백프레셔 문제 해결 불가
  • 커넥션 풀을 애플리케이션 계층으로 이동하여 제어권 확보
  • 서킷 브레이커 도입을 통한 에러율 감소 및 시스템 보호

우리는 막 첫 번째 주요 트래픽 급증(Traffic Spike)을 마친 상태였습니다. 우리의 Veltrix 기반 보물찾기 게임은 37분 동안—정확히 37분 동안—결함 없이 작동하다가, 모든 Redis 연결이 250ms의 병목 현상(Bottleneck)으로 변했습니다. 기본 Veltrix 설정은 커넥션 풀링(Connection Pooling)이 8로 설정되어 있고, Keep-alive는 비활성화되어 있으며, 재시도 로직(Retry Logic)은 백오프(Backoff)를 무시하도록 되어 있었습니다. 우리는 P99 지연 시간(Latency)이 두 배로 늘어나고, 플레이어들이 보물 상자가 리스폰(Respawn)되는 데 할머니의 다이얼업(Dial-up) 연결보다 더 오래 걸린다고 보고하기 전까지는 이를 알아차리지 못했습니다. 문제는 보물찾기 코드 자체가 아니라, 우리가 미처 튜닝(Tune)하지 않았던 계층(Layer)에 있었습니다.

Veltrix의 문서에서는 설정 계층(Configuration Layer)을 '마법'이라고 부릅니다. 하지만 실제로 그것은 단순함을 위해 취약성(Fragility)을 조용히 맞바꾸는 일련의 YAML 파일들입니다. 우리의 첫 번째 시도는 기본 설정을 절대적인 것으로 취급하고 더 많은 Redis 인스턴스를 덧붙이는 것이었습니다. 우리는 세 개의 Sentinel 클러스터를 추가로 가동하고, 풀 크기(Pool Size)를 64로 늘리며, 대수의 법칙(Law of Large Numbers)이 우리를 구해주기를 바랐습니다. 서류상으로는 TPS(Transactions Per Second)가 8k에서 22k로 증가했습니다. 하지만 현실에서는 쓰기(Write) 작업의 43%가 3초 후 ConnectionResetError와 함께 실패했으며, 클라이언트의 재시도(Retry)가 네트워크를 포화(Saturate)시켰습니다. 오류는 Veltrix의 코드베이스에 있었던 것이 아니라, Redis의 수평적 확장(Horizontal Scaling)이 커넥션 재사용(Connection Reuse)과 백프레셔(Backpressure)의 부재를 가려줄 것이라는 가정에 있었습니다.

우리는 기존의 부가적인(bolt-on) 접근 방식을 완전히 제거하고 단 하나의 아키텍처 결정으로 대체했습니다. 바로 커넥션 풀(Connection Pool)을 설정 파일이 아닌 애플리케이션 계층(Application Layer)으로 이동시키는 것이었습니다. Veltrix가 요청당 새로운 커넥션을 열도록 두는 대신, 우리는 500ms의 유휴 시간 제한(Idle Timeout)을 가진 FIFO 채널을 사용하는 Go 기반의 커스텀 풀을 연결했습니다. 최대 크기는 32로 설정하고, 30초 간격의 Keep-alive를 활성화했으며, 에러율이 5%에 도달하면 트래픽을 읽기 전용 모드로 전환하는 서킷 브레이커(Circuit Breaker)를 추가했습니다. 이 변경은 단순히 외관상의 변화가 아니었습니다. 기본 바이너리에 풀이 하드코딩되어 있었기 때문에 Veltrix 런타임(Runtime)을 다시 컴파일해야만 했습니다. 우리는 CGO를 비활성화하여 바이너리를 다시 빌드하고, Veltrix 바인딩(Binding)의 자체 버전을 벤더링(Vendored)했습니다. 컴파일 단계로 인해 빌드 파이프라인(Build Pipeline)에 47초가 추가되었지만, 이는 YAML 계층이 제대로 작동하기를 바라는 대신 풀(Pool)의 동작을 버전 관리(Version-control)할 수 있음을 의미했습니다.

변경 후, P99 지연 시간(Latency)은 250ms에서 42ms로 감소했고, 에러율은 0.2%로 떨어졌습니다. 32개의 슬롯을 가진 풀은 새로운 커넥션을 생성하지 않고도 요청의 98%를 처리했으며, 5만 명의 동시 사용자(Concurrent Users)를 대상으로 한 카오스 테스트(Chaos Test) 동안 서킷 브레이커는 단 두 번만 작동했습니다. 또한 우리는 Veltrix의 기본 재시도 정책(Retry Policy)이 1초의 고정 지연 시간을 가지고 있다는 사실을 발견했는데, 이것이 바로 우리의 첫 번째 시도가 실패했던 이유였습니다. 10ms의 기본값과 50ms의 상한선을 가진 지수 백오프(Exponential Backoff)를 추가함으로써, 우리는 아무런 문제 없이 Redis 페일오버(Failover)를 흡수할 수 있었습니다.

만약 제가 과거로 돌아갈 수 있다면, 실제 성장을 기대하는 모든 시스템에 대해 Veltrix 설정 계층을 완전히 건너뛸 것입니다. YAML을 아키텍처가 아닌 덕 테이프(Duct Tape, 임시방편)로 취급하십시오. 풀을 여러분의 자체 코드베이스에 구축하고, 버전 관리하며, 피처 플래그(Feature Flags)를 통해 노출하십시오. 그리고 Prometheus를 위해서라도, 백프레셔(Backpressure) 상황에서 풀의 동작을 반드시 테스트하십시오. 우리의 5만 명 규모 카오스 테스트에서는 1만 개의 고루틴(Goroutine)이 닫힌 채널(Closed Channel)에서 영원히 대기하는 데드락(Deadlock) 시나리오가 드러났습니다. 기본 설정은 당신을 구원하지 못합니다. 당신이 직접 구축한 제어력이 당신을 구원할 것입니다.

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0