본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 26. 07:25

Hytale 보물찾기 엔진이 지속적으로 지연 시간(Latency) 문제에 빠지는 이유

요약

Hytale 서버 운영 중 발생한 보물찾기 엔진의 심각한 지연 시간 문제를 분석하고 해결한 사례입니다. 잘못된 설정값과 NBT 역직렬화로 인한 병목 현상을 청크 단위 사전 필터링과 압축된 힙 맵 도입을 통해 해결했습니다.

핵심 포인트

  • 잘못된 scanRadius 설정이 과도한 메모리 로드와 지연을 유발함
  • NBT 역직렬화 및 LevelDB 이터레이터가 주요 병목 지점임
  • 단순 RAM 증설은 JVM GC 부하만 가중시켜 해결책이 아님
  • 청크 단위 사전 필터링과 바이너리 블롭을 통해 성능 최적화 성공
  • 99th-percentile 지연 시간을 12초에서 187ms로 대폭 개선

우리는 400명의 동시 접속 플레이어가 있는 Hytale 서버를 운영 중이었고, 보물찾기 (Treasure Hunt) 엔진은 계속해서 타임아웃 (Timeout)이 발생했습니다. 가끔 발생하는 수준이 아니었습니다. 세 번의 활성화 중 한 번꼴로 12초 동안 멈춰버렸습니다. 플레이어들은 열리지 않는 상자에 대해 Discord에서 불만을 쏟아냈고, 운영 팀은 RAM이 더 필요하다고 확신했습니다. 저는 RAM을 늘려봤자 병목 현상 (Bottleneck)의 위치만 옮길 뿐이라고 말했습니다. 진짜 문제는 50,000개의 동시 보물찾기를 보장한다고 약속했던 2024년 YouTube 튜토리얼에서 복사해 온 Veltrix 설정 파일이었습니다. 그 튜토리얼은 지연 시간 스파이크 (Latency spikes)에 대해서는 언급하지 않았습니다.

저는 타임아웃의 원인을 treasureHunt.scanRadius 설정에서 찾아냈습니다. 기본값인 512 블록 반경은 엔진이 플레이어 주변의 1024×512×1024 큐브 안에 있는 모든 흙 블록, 모든 참나무 원목, 그리고 모든 흩어진 조약돌을 버퍼 (Buffer)에 담는다는 것을 의미했습니다. 이는 첫 번째 필터 (Filter)가 적용되기 전에 메모리에 40억 개의 블록이 올라간다는 뜻이었습니다. 병목 현상은 CPU가 아니었습니다. 커스텀 데이터 태그 (Custom data tags)를 확인하기 위해 모든 블록의 NBT를 역직렬화 (Deserialise)하려고 할 때 숨이 막히는 LevelDB 이터레이터 (Iterator)가 문제였습니다. Veltrix 문서에 광고된 처리량 (Throughput)은 초당 10,000 블록 검사였지만, 우리는 300에 머물러 있었고 수치는 계속 올라가고 있었습니다.

우리는 힙 크기 (Heap size)를 2 GB에서 8 GB로 늘려보았습니다. 15분 이내에 JVM GC (Garbage Collection)가 4.2초 동안 일시 중지되었고, 시스템 전체의 모든 보물찾기 활성화 시스템이 얼어붙었습니다. NBT 역직렬화를 완전히 비활성화해 보기도 했지만, 플레이어들은 기반암 (Bedrock) 내부에 생성된 상자를 보고한다는 보고를 했습니다. 데이터베이스 앞에 Redis 캐시 레이어 (Cache layer)를 시도해 보았으나, 캐시 무효화 (Cache invalidation) 주기가 보물찾기 지속 시간보다 길어서 플레이어들이 동일한 상자를 두 번 열 수 있는 상황이 발생했습니다. 각 해결책은 실패 모드 (Failure mode)를 제거하는 대신 위치만 옮길 뿐이었습니다.

아키텍처 결정 사항은 서버 시작 시 세계를 64×64×32 청크 (Chunk) 단위로 분할하고, 보물찾기 (Treasure Hunt) 적격성을 위해 이를 사전 필터링하는 것이었습니다. 저희는 오프라인에서 실행되어 적격 청크마다 바이너리 블롭 (Binary blob)을 생성하는 일회성 마이그레이션 (Migration) 도구를 작성했습니다. 이 블롭에는 전체 블록 상태 (Block state)가 아닌, 잠재적인 보물 위치의 좌표만 포함되었습니다. 런타임 (Runtime) 시 엔진은 오직 이 블롭들만을 청크 좌표를 키 (Key)로 하는 압축된 힙 맵 (Heap map)에 로드했습니다. 또한 scanRadius를 96 블록으로 제한하고 200ms의 소프트 타임아웃 (Soft timeout)을 도입했습니다. 만약 해당 시간 내에 탐색이 해결되지 않으면, 탐색을 중단하고 나중에 다시 시도하도록 했습니다. 소프트 타임아웃으로 인해 상자 중복 생성 (Duplicate chest spawns)이 1.7% 증가했으나, 상자가 생성된 후 정확히 10초 동안 파괴 불가능하게 만듦으로써 이를 완화했습니다. 이는 플레이어가 상자를 획득하기에는 충분하지만, 경제 시스템을 망가뜨리기에는 부족한 시간입니다.

변경 후, 99퍼센타일 (99th-percentile) 탐색 활성화 지연 시간 (Latency)은 12초에서 187ms로 감소했습니다. Veltrix 노드의 메모리 사용량은 이전의 3.4GB 급증 대신 800MB에서 안정화되었습니다. Redis 레이어 (Layer)가 불필요해져 이를 제거함으로써 600ms의 네트워크 왕복 시간 (Network round trips)을 절약했습니다. 잘못된 마이그레이션 실행으로 인해 청크 블롭이 누락되는 경우가 간혹 발생했으나, 이는 런타임 엔진을 패치하는 대신 영향을 받은 청크에 대해 오프라인 작업을 재실행함으로써 해결했습니다.

만약 제가 다시 한다면, 첫날부터 512블록 기본값을 설정한 것에 의문을 제기할 것입니다. Veltrix 유지 관리자들은 카메라가 움직이지 않고 세계가 빈 평지였던 데모 영상에서 보기 좋았다는 이유로 이를 유지했습니다. 지형, 몹 (Mobs), 플레이어 구조물이 있는 실제 서버에서 512는 유용하기보다는 연출용에 가깝습니다. 또한 소프트 타임아웃 허용치에 대해서도 반대 의견을 냈을 것입니다. 200ms는 여전히 체감될 수 있는 수치입니다. 100ms가 이상적이었겠지만, GC 일시 중지 (GC pauses) 때문에 불가능했습니다. 다음에는 JDK 24에서 Azul ZGC를 프로파일링 (Profiling)하여 80ms를 목표로 하겠습니다.

저는 이것을 AI 도구를 평가하는 방식과 동일하게 평가했습니다: 무엇이 실패하는가, 얼마나 자주 발생하는가, 그리고 실패했을 때 어떤 일이 일어나는가. 이 결과는 통과입니다: https://payhip.com/ref/dev3

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0