1P by GN⁺ | ★ favorite | 댓글 1개
  • Lichess는 요청 부하 때문에 7-piece Syzygy tablebase 서버의 주기적 RAID 검사가 밀리자, 전체 블록을 훑는 방식 대신 읽기 시 무결성 검증으로 전환함
  • 긴 다운타임 없이 17TiB tablebase를 옮기기 위해 새 서버를 마련했고, 32GiB RAM·2×201GiB NVMe·6×5.46TiB HDD 환경에서 실제 요청 로그를 재생해 검증함
  • 프로덕션에서 기록한 100만 개 요청을 12개 병렬 클라이언트로 재생한 결과, 평균 응답보다 사용자가 체감하는 꼬리 지연시간이 핵심 병목으로 드러남
  • 구현 측면에서는 mmap보다 pread(2)가 오류 처리와 꼬리 지연시간에서 유리했고, POSIX_FADV_RANDOM·MADV_RANDOM 같은 랜덤 접근 힌트는 대체로 역효과였음
  • 제한된 SSD에는 table prefix를 올리고 요청 내부 probe를 병렬화해 느린 디스크 접근을 줄였으며, 벤치마크 개선이 프로덕션 응답 시간에도 이어지는지 확인함

RAID 전체 검사 대신 읽기 시 검증으로 전환

  • Lichess의 7-piece Syzygy tablebase 서버는 tablebase 요청이 많은 동안 주기적 RAID 무결성 검사를 끝내기 어려웠음
  • 새 구성은 dm-integrity on LVM을 사용해 모든 데이터 블록을 주기적으로 검사하지 않고, 블록이 읽힐 때마다 검증
  • 17TiB tablebase를 몇 시간 다운타임 없이 이전하기 위해 새 서버를 따로 구성함
    • 실제 전환 전에 전체 tablebase를 대상으로 제어된 벤치마크를 실행할 수 있었음
    • 이후 새 서버로 전환하고 기존 서버를 퇴역시킴

새 서버 구성

  • RAM은 기존과 같은 32GiB를 유지함
  • 저장장치는 기존 서버에 없던 2×201GiB NVMe를 추가했고, 476GiB 디스크의 나머지 공간은 OS와 작업 공간으로 예약함
  • HDD는 기존 5개에서 6×5.46TiB HDD로 늘어남
  • 운영체제는 Debian bookworm이며, 커널은 Linux 6.1.0-21-amd64 계열임
  • 기본 I/O 스케줄러는 NVMe에서 none, HDD에서 mq-deadline이 선택된 상태였음

RAID 5 설정과 모니터링

  • RAID 5는 단일 디스크 장애에서 복구할 수 있고, 랜덤 읽기를 여러 디스크에 분산할 수 있어 tablebase 서버에 잘 맞음
  • 초기 구성은 다음과 같았음
lvcreate --type raid5 --raidintegrity y --raidintegrityblocksize 512 --name tables --size 21T vg-hdd
  • 초기 테스트 성능은 괜찮았지만, 모니터링이 없었다면 일부 디스크가 같은 수준으로 참여하지 않는 문제를 놓쳤을 수 있음
  • --stripes를 생략하면 모든 물리 볼륨을 기본으로 사용하지 않음
  • 디스크별 읽기 활동 모니터링이 잘못된 RAID 설정을 잡아내는 데 필요했음

실제 요청 로그로 본 병목

  • 정상 조건에서 서버는 초당 10~35개 요청을 받음
  • 프로덕션 환경에서 100만 개 요청을 기록하고, 선택한 시나리오에서 12개 병렬 클라이언트가 이를 순차 제출함
  • table은 지연 열기 방식으로 열리고, 애플리케이션 및 OS 캐시는 점진적으로 채워짐
    • 처음 80만 개 응답 시간은 워밍업으로 제외함
    • 이후 20만 개 요청의 응답 시간을 분석함
  • 평균 응답 시간은 충분히 빠르지만 꼬리 지연시간이 높아 최적화의 초점이 됨
  • ECDF 그래프는 각 응답 시간보다 빠른 요청 비율을 보여주며, x축은 로그 스케일임
  • 그래프에는 클라이언트의 30ms ping time을 반영하기 위해 각 응답 시간에 30ms를 더함
    • 로그 스케일 x축이 낮은 구간의 몇 밀리초 차이를 과하게 강조하지 않도록 하기 위한 처리임

mmap보다 유리했던 pread(2)

  • Syzygy tablebase 구현체인 shakmaty-syzygy는 table 파일을 여는 방식과 읽는 방식을 교체할 수 있는 인터페이스를 제공함
  • 주요 후보는 두 가지였음
    • mmap: table 파일을 메모리에 매핑하고, 해당 메모리 영역 접근 시 디스크 읽기가 투명하게 발생함
    • pread(2): 읽기마다 시스템 호출을 수행하고, 반환값으로 읽기 오류를 보고함
  • mmap은 매핑 후 추가 시스템 호출이 필요 없지만, 읽기가 일반 메모리 접근처럼 보이기 때문에 오류를 시그널 같은 out-of-band 방식으로 처리해야 함
  • 서버 구현에서는 더 견고한 오류 처리만으로도 pread 사용을 정당화할 수 있었고, 벤치마크에서도 관심 시나리오에서 pread 성능이 더 좋았음
  • 가능한 원인 중 하나는 메모리 매핑된 단일 데이터 블록 접근이 페이지 경계를 넘을 때 두 번의 디스크 읽기로 이어질 수 있다는 점임
  • 체스 엔진에 곧바로 pread를 적용할 필요는 없음
    • 엔진 매치의 tablebase 사용은 보통 모든 WDL table을 충분히 빠른 저장장치에 둘 수 있을 때 이뤄짐
    • 이 경우 일반적인 응답 시간 범위는 해당 그래프에 보이지 않을 정도이고, 시스템 호출 오버헤드를 줄이는 메모리 매핑이 더 나음

랜덤 접근 힌트의 역효과

  • posix_fadvise(fd, 0, 0, POSIX_FADV_RANDOM)와 메모리 맵에 해당하는 힌트는 결과적으로 대부분 역효과였음
  • POSIX_FADV_RANDOM은 파일 접근이 랜덤이고 자동 read-ahead가 유용하지 않을 가능성이 높다고 OS에 알려, 페이지 캐시 압박을 줄이기 위한 힌트임
  • 사람들이 엔드게임을 분석할 때의 tablebase 접근 패턴은 예상보다 랜덤하지 않을 수 있음
  • 체스 엔진에서는 probe가 서로 다른 가능한 엔드게임에 더 흩어질 수 있어 결과가 달라질 수 있음

제한된 SSD에 올릴 table prefix

  • table probe는 먼저 포지션을 table header의 인코딩 정보를 바탕으로 정수 인덱스로 인코딩함
  • 이후 해당 인덱스의 결과가 들어 있는 압축 데이터 블록을 찾아야 함
  • Syzygy는 올바른 항목 근처를 가리키는 sparse block length list를 제공하고, 이어서 block length list로 관련 데이터 블록을 찾음
  • table section 크기는 다음과 같음
Table section WDL DTZ Total
Headers and sparse block length lists 38GiB 9GiB 47GiB
Block length lists 274GiB 64GiB 339GiB
Compressed data blocks 8433GiB 8458GiB 16891GiB
  • SSD 공간을 적응형 캐시 계층으로 사용해 hot list entry와 data block을 캐시할 수도 있음
  • 꼬리 지연시간을 줄이는 목표에서는 최악의 경우를 고려해 sparse block length list와 block length list를 SSD에 두는 방식이 적합함
  • 이 배치는 hot/cold 여부와 관계없이 table probe당 느린 디스크 읽기를 최대 1회로 제한할 수 있음
  • 해당 서버에서는 RAID 1 미러링을 하기엔 SSD 공간이 충분하지 않았고, 선택적 최적화라는 이유로 중복성을 포기하고 RAID 0을 사용함

요청 내부 probe 병렬화

  • 체스 엔진의 일반적인 tablebase 요청은 단일 WDL 값에 대한 요청임
  • 사용자 인터페이스에서는 모든 수에 대한 DTZ 값을 표시하려고 함
  • Syzygy 내부의 capture 해소까지 포함하면 평균 요청은 23 WDL probe70 DTZ probe를 발생시킴
  • 초기 구현은 요청 처리 자체는 병렬화했지만, 각 요청 내부의 probe는 순차 실행함
  • 더 세밀한 병렬성은 낮은 지연 구간에서 오버헤드를 만들지만 꼬리 지연시간을 크게 줄임
  • 디스크가 실제로 많은 병렬 읽기를 물리적으로 처리할 수는 없어도, I/O 스케줄러가 각 요청을 더 빨리 끝내도록 읽기를 계획할 가능성이 높아짐
  • 이 방식은 디스크 헤드가 다음 요청 섹터에 도달할 때까지의 시간을 줄이도록 관련 디스크 접근 순서를 더 잘 계획하게 함

프로덕션 확인과 원자료

  • 벤치마크 시나리오의 최적화가 실제 프로덕션에도 도움이 되는지 응답 시간 차트로 확인함
  • 원자료는 lila-tablebase-bench에 공개되어 있음

댓글과 토론

Hacker News 의견들
  • Lichess는 좋은 와인처럼 가만히 감탄하게 되는 서비스임. 체스 커뮤니티에 정말 훌륭하고, 매일 쓰면서 기능과 성능에 계속 자극받고 있음
    특히 제한된 예산의 1~2명 규모 팀이라는 걸 알면 더 놀라움

    • 무료이고 오픈소스이며, 돈을 요구하지도 않고 앞으로도 그럴 일이 없다는 점도 빠뜨리면 안 됨. 많은 사람이 기부하고, 지출도 공개되어 있으며 앱도 있음
    • 더 많은 최종 사용자용 오픈소스 소프트웨어가 Lichess처럼 사용자 친화적이고, 잘 설계되고, 잘 유지보수되면 좋겠음
    • 나도 그렇게 느낌. 최근 새 베타 모바일 앱은 더 깔끔하고 햅틱 피드백까지 있어서 멋짐
    • 언젠가 Lichess만큼 가치 있고 멋진 걸 만들고 싶음
  • 응답 시간마다 30ms를 더한 ECDF를 보여준 부분이 흥미로웠음
    상수 추가가 인위적으로 보일 수 있지만, 실제로는 30ms 핑을 가진 클라이언트 관점에서 결과를 보는 방식이고, 로그 스케일 x축이 낮은 구간의 몇 ms 차이를 과장하지 않게 해줌. 표준 기법일 수도 있지만 꽤 똑똑한 요령처럼 보임

  • 비용 절감이 꼭 필요했는지, 아니면 그냥 박스 하나에 20TB SSD를 넣고 끝내면 안 되는 다른 이유가 있었는지 궁금함. 4TB SSD도 대략 300달러이고, HP나 Dell SFF 드라이브도 훨씬 비싸진 않음
    아마 테스트와 최적화 자체에 흥미가 있었던 것 같고, 제품 관점이라면 제한된 시간을 다른 프로젝트에 썼을 것 같음

    • Lichess는 비영리이고 자원봉사자가 많아서, 대부분의 영리 회사와는 시간 대비 하드웨어 비용의 균형이 다를 가능성이 큼
    • Lichess는 기부와 자원봉사로만 운영되는 비영리 단체임. 직원은 비영리 단체를 만든 한 명뿐이고, 실력에 비해 다른 직장에서 벌 수 있는 돈보다 훨씬 적게 가져가는 것처럼 보임
      조직이 프랑스 기반이라는 점도 비용에 어떤 영향을 주는지는 모르지만 언급할 만함
    • 이 작업으로 최대 응답 시간을 한 자릿수 규모로 줄였음. 프로젝트가 1주, 길어도 2주 걸렸고 일부 사용자의 응답 시간이 15초에서 1.5초로 줄었다면 충분히 가치 있음
      이보다 시간 투자를 더 잘 정당화하려면 사용자 경험이 더 나쁜 프로젝트가 있거나, 영리 조직에서 다른 곳에 돈 벌 기회가 있고 고객 고통에는 별 관심 없다고 인정하는 경우 정도일 것임
    • “재미로 테스트와 최적화”라고 생각하는 엔지니어는 IT 말고는 다른 산업에 거의 없을 듯함
      너무 강력하고 싼 하드웨어와, 그냥 “오늘은 여기까지” 하고 싶어 하는 게으른 사람 쪽이 결합된 결과처럼 보임. 자기 일에 자부심을 가지라는 말도 있지 않나
    • 제품 관점에서는 말이 되지만, Lichess는 제품을 가진 영리 회사가 아니라 실제로 비영리 조직으로 운영되므로 결정을 이해하려면 관점을 바꿔야 함
  • 이 최적화에는 의문스러운 선택이 몇 가지 있음. 최적화 이유는 입출력 활동이 너무 많아서 RAID 검사가 완료되지 못한다는 것임
    글만 봐서는 17TiB 데이터에 대한 RAID 검사가 실제로 끝난 적이 있는지 불명확함. 대신 주기적 RAID 검사를 끄고, 데이터를 읽을 때 페이지 단위로 오류 검사를 하도록 바꿨는데 두 방식은 같지 않고 중요한 데이터라면 둘 다 써야 함
    데이터를 읽으려 할 때만 손상을 발견하면 오래된 데이터 손상이 계속 남을 수 있고, 백업 보관 기간을 넘어 원본을 복구하지 못할 수도 있음. 여기에 RAID 0으로 바꾼 점도 깔려 있는데, 가장 빠른 선택이긴 해도 그 NVMe 구성이 그런 부하를 견딜 거라고 꽤 크게 믿는 셈임
    백업이 잘 되어 있기를 바람. 좋은 해결책은 임시 서버를 띄워 백업을 복원하고 전체 데이터 검사를 수행한 뒤, 성공하면 백업·복원 절차와 파일 무결성도 함께 검증하는 것임. 그래도 주 서버에서 RAID 검사를 끝낼 여유는 확보해야 하고, 성능 때문에 RAID 0을 쓰지는 않는 편이 좋음

    • 두 방식이 같지 않은 건 맞지만, 이 사용 사례에는 충분함. 데이터 손상을 발견하면 파일을 버리고 다시 다운로드하거나 재생성하면 되기 때문임
      이건 자유롭게 구할 수 있는 데이터셋이고, 크기만 좀 큼. https://en.wikipedia.org/wiki/Endgame_tablebase가 더 잘 설명해줌. 그래서 백업도 하지 않음
  • lishogi도 있지만 아직은 규모가 작아서 이런 최적화가 필요할 정도는 아님
    체스 변형 중에는 쇼기가 가장 재미있고, 샹치는 그 정도는 아님

  • lichess가 여성 lich라는 뜻이라고 보면 되는 건가 싶음. baron/baroness 같은 식으로

    • 귀족 칭호는 비교 대상으로 별로임. 남성 전용 어근이 실제로 있는 드문 예라서 그렇고, 대부분의 단어는 어근이 중성이며 남성형이나 여성형이 있더라도 접사가 필요함
      엄밀히는 남성 lich는 “werlich”, 여성 lich는 “wiflich”이고 복수형에는 “-en”이 붙음. 다만 언데드에게 성별은 대체로 무관하니 중성형이 압도적으로 많이 쓰임
      “lichess”는 독일어와 프랑스어 어근이 뒤섞인 괴상한 조합이라서, 자연스럽게 영어의 다른 단어들과 구분이 안 됨
    • Libre chess, 즉 자유롭고 오픈소스인 체스라는 뜻임
  • 공정한 비교는 아니지만, Lichess 팀의 엔지니어링 품질에는 정말 감탄하게 됨. 주 경쟁자는 GCP 이전을 자랑하면서도 인기 증가에 따라 반복적인 장애를 겪었고, 인원은 100배쯤 더 많을 거라고 봄
    Lichess의 약점은 모바일 앱이었지만, Flutter로 다시 만든 v2는 아직 베타인데도 이미 꽤 좋음
    그리고 Thibault가 자기 보수로 연 6만 달러도 안 가져간다는 점도 기억해야 함

    • 급여를 올린다고 미안해할 필요는 없다고 봄. 연 20만 달러로 올려서 삶을 더 편하게 만들면 장기적으로 프로젝트에도 좋을 수밖에 없음
    • Lichess는 나 같은 캐주얼 체스 플레이어가 다른 사람과 빠르게 한 판 두기에 훌륭한 서비스임. 거의 기다릴 일이 없음
      다만 Lichess를 어떻게 발음하는지 궁금함. Lie chess인지, Le chess인지, League chess인지
    • Lichess는 Wikipedia가 코드와 조직 양쪽에서 얼마나 효율적일 수 있었는지 보여주는 좋은 예처럼 보임
    • Chess.com에 개발자가 얼마나 많은지 꽤 과대평가하는 것 같음