개발자 취준생이 디아블로 2 레저렉션 서버 문제를 보며 든 생각

    디아블로2 레저렉션

    서론

    최근 디아블로 2 레저렉션 (이하 D2R) 서버가 자주 길게 터지는 문제가 발생하여서 한창 논란이 되고 있다...는 것 까지만 알게 되었는데, G식백과 - 디아블로2 레저렉션 성인 셧다운제 사태의 숨겨진 진실이 폭로되다 영상을 보다보니 현 문제에 대한 블리자드 측의 길고 긴 공지가 있다는 것을 확인했습니다.

    한낱 백엔드 개발자 취업준비생의 시점에서 생각해보는 글이기 때문에 내용의 정확성이 보장되지 않습니다. 선배 개발자 분들의 지적은 언제든지 환영합니다.

    레거시 코드와 싱글톤

     

     

    원작에서 일부 업그레이드된 이 서비스는 게임 내 기능의 중추적인 요소를 담당하는데요. 게임 생성/참가, 게임 목록 업데이트/불러오기/분류, 게임 서버 상황 검증, 그리고 캐릭터가 사용 중인 분류에 맞는 게임에 참가할 수 있도록 데이터베이스로부터 캐릭터를 읽어오는 작업 등이 이에 포함됩니다. 무엇보다도 이 서비스는 단일 개체(Singleton)이기에, 모든 플레이어들이 게임 목록을 확인할 때 가장 최신이자 올바른 게임 목록이 표시되도록 처리하는 과정에서 한 번에 하나의 인스턴스로만 구동됩니다. 해당 서비스가 최신 기술에 더 잘 부합하도록 여러 측면에서 최적화를 진행하긴 했지만, 이전에 말씀드린 것처럼 여전히 많은 문제가 게임 생성에서 비롯되고 있습니다.

    D2R은 디아블로2의 코드를 기반으로 하고 있으며, 이 레거시 코드를 쉽게 손 댈수가 없었을 것이기 때문에 기존 디아블로2가 가지고 있던 문제는 고스란히 D2R에도 이어지게 됩니다. 그러나 과거에는 문제가 되지 않았던 내용들이 현대에 문제가 될 수 있으며 이는 현장에서도 많이 일어나는 일이라고 알고 있습니다. 그 중 이번 서버 문제의 원인으로 블리자드가 꼽은 것은 '싱글톤' 구현일 것입니다.

     

    특정한 로직을 싱글톤으로 개발하는 이유는 단순합니다. 매번 새로운 요청이 있을 때 마다 로직을 담당하는 인스턴스를 매번 생성할 필요가 있을까?라는 생각을 선배 개발자들이 했었기 때문일 것입니다. 그 대신, 하나의 인스턴스를 미리 만들어 놓고, 이 인스턴스가 모든 요청을 처리하도록 만들면 효율적으로 동작할 수 있습니다.

     

    제가 현재 배우고 있는 스프링 부트 프레임워크 또한 서비스, 컨트롤러, 각종 설정정보 등을 싱글톤으로 개발하면서 불변 객체로 만들거나 혹은 static 메서드를 호출하여 코드 셔틀처럼 활용하고 있어서 왜 싱글톤이 문제가 되는지 이해가 잘 되지는 않았습니다.

     

    서비스를 싱글톤으로 개발하려면, Stateless, 즉 싱글톤은 어떠한 State도 가지지 말아야 한다고 알려져 있습니다. 그 이유는 멀티 쓰레드 환경에서 동시에 state를 변경하도록 인스턴스에 요청한다면, Race Condition에 의해 State의 정합성을 보장할 수 없기 때문입니다. D2R에서는 게임 생성, 참가, 목록 불러오기 등을 담당하는 서비스를 싱글톤으로 개발했다고 알렸는데, '모든 플레이어들이 게임 목록을 확인할 때 가장 최신이자 올바른 게임 목록이 표시되도록 처리하는 과정' 을 언급한 내용을 읽으며 D2R 게임 목록을 설마 싱글톤 인스턴스 내부에서 관리하도록 구현했을까 의심하게 되었습니다. 그럴리는 없겠지만 이 추측이 맞다면 Critical Section 문제를 해결하기 위해선 한 번에 하나의 요청만 서비스 인스턴스에 접근할 수 있도록 하는 구현밖에 없을거라고 생각하기 때문입니다.

     

    왜 내 계정은 복구가 안 될까?

    D2R은 전 세계 계정의 상태를 관리하기 위해 모든 유저 정보를 단일 정보 저장소 (Single Source of Truth, SSOT)인 글로벌 DB에서 관리하도록 했습니다. D2R 서비스에서 계정 정보의 기준이 되는 것이 이 SSOT가 되는 것입니다. 그러나 하나의 거대한 DB가 전세계 수십만 동접자가 쏟아내는 쿼리를 처리할 수단이 없습니다. 그래서 각 지역을 담당하는 DB를 두고 요청은 지역DB가 받아들이며 일정 주기로 동기화 프로세스를 구동하여 지역의 DB 변경내용을 글로벌 DB에 반영하도록 했습니다.

    엄밀히 말하면 SSOT 아키텍쳐는 SSOT 내에서 관리되는 데이터를 외부 DB에선 값을 복사하여 관리하는 것이 아닌, SSOT를 향한 참조로 관리하는 것이라고 알고 있었어서 정확하게 맞는 설명인진 모르겠으나 D2R의 중요한 정보는 단일 정보 저장소에서 관리한다는 사실만 이해했습니다. D2R에서의 계정 정보의 기준은 바로 이 글로벌 DB가 될 것입니다.

    만약 각 지역을 관할하는 DB가 터지면 가장 최근의 글로벌-지역DB 동기화 이후 기록된 지역 DB의 내용은 글로벌 DB에 반영이 되지 않았기에 계정의 상태는 글로벌 DB에 기록된 시점의 정보로 롤백되는 현상이 벌어지는 것입니다.

    블리자드의 향후 계획

    1) 빈도 제한
    2) 로그인 대기열 생성

     

    다들 백신접종 예약이나 수강신청, 티켓팅 등 다양한 분야에서 겪어봤을 것입니다. 한 번에 서버에 접속할 수 있는 인원을 제한하여 서버가 완전히 터지는 것을 방지하는 해결책입니다.

     

    3) 여러 개의 기능을 담당하는 핵심 요소를 각각의 소규모 서비스로 분리하기 (MSA)

     

    Micro Service Architecture를 줄여서 MSA라고 부릅니다. 기존의 하나의 서버에서 모든 기능을 담당했던 Monolithic 설계의 경우, 부분적인 장애가 전체 서비스의 장애에 영향을 미칠 수 있는 단점을 해소하기 위해 MSA를 채택할 수 있습니다. 기존에 하나의 어플리케이션에 모든 기능이 뭉쳐져 있던 것을 각 기능별로 서비스를 분리하여 구동함으로써, 장애를 격리하는 것 외에도 특정 서비스의 서버만 증설하는 등 Scale-Out에도 용이합니다. 이번 서버 문제처럼 일부 기능의 장애가 서버 전체에 문제를 불러 일으키지 않도록 하기 위해 MSA를 채택하는 것을 언급하지 않았나 생각합니다.

    에필로그

    확률형 아이템 규제법에 기름을 부은 메이플스토리 확률조작 사건이 발생했을 때에도 공지로 소스코드를 올려 버그의 원인을 설명하려는 시도가 있었습니다. 분석은 메이플스토리 인벤 - 개발자가 본 환불 알고리즘 글을 참고하시면 좋을 듯 합니다.

    저는 메이플과 디아블로2를 하지 않기 때문에 크게 와닿는 문제는 아니었지만 현업자의 코드나 현재 운영중인 게임의 인프라의 정보를 보는 것은 신기하고 즐겁습니다. 

    댓글