[Snack] 디스코드가 1조개의 메시지를 저장하는법
서문
작년 (2022),
디스코드는 기존의 사용하고 있는 데이터베이스인 Cassandra에서, 새로운 데이터베이스인 ScyllaDB로 모든 데이터를 옮겼다. (migrate)
이는, 디스코드가 서비스에서 중요하게 생각하는 3가지 요소 때문이다.
- 커지고 작아지는게 자유로운지 (확장성 - Scalable)
- 오류가 있어도, 다시 복구 가능한지 (Fault-tolerant)
- 유지보수가 적은지 (maintanance)
디스코드는 2017년에도, MongoDB에서 Cassandra로 옮긴 전적이 있지만,
유지보수 측면에서 많은 어려움을 겪었다고 한다.
이 때문에 디스코드는, 더 편하고 (유지보수 극혐), 심지어 더 빠르기까지한 ScyllaDB를 선택했다.
여기서 말하는 "모든" 데이터는 상상할 수 없을 정도로 크다 (메시지만 1조)
하지만, 이렇게 큰 데이터를 디스코드는 단 "9일"만에 새로운 곳으로 전부 옮겼다.
디스코드가 "어떻게" 이런 작업을 했고,
그 들이 겪은 어려움을 해결한 과정을 살펴보면서,
실제 서비스에서 중요한 요소들과
전체적인 과정에 대한 디스코드의 인사이트를 살펴보자.
Cassandra 란? (간략)
수많은 컴퓨터들을 하나의 서비스(cluster)로 합쳐서 관리하기 위해 최적화된 DB.
- Java로 만들어졌다.
문제 상황
1. 읽기 응답시간
Cassandra의 특징중에 하나는,
쓰기(write)보다, 읽기(read)가 더 오래걸린다는 것이다.
이는 현대 모든 서비스의 구조적 한계 때문이다. ( 그렇다면 해결방법이 없다는거 아냐? )
쓰기작업을 할때는, 속도가 빠른 memory(in-memory-disk)에 쓰고, 이후에 disk 로 옮기는 작업을 하므로,
실제 사용자에게 빠른 응답을 줄수 있는 반면,
읽기작업을 할때는, 무조건 disk에 먼저 접근해야하므로,
실제 사용자에게 응답을 주는 시간이 상대적으로 느릴 수 밖에 없는 구조이다.
(그렇다고 memory만 쓰면 또 너무 비싸니까..)
2. Cassandra의 작동방식
요청하는 정보가 중복되는지 여부와 관계없이, 모든 요청에 대한 응답을 보내준다는 것
( 의문점 : 정확히 어떤 정보가 중복되는가? 그렇게 많이 중복되나? )
바로 이 부분이 디스코드 서버에, 과부화를 일으켰다
아래의 내용은, 실제로 디스코드가 사용하고 있던, Cassandra의 메세지 table 이다.
CREATE TABLE messages (
channel_id bigint,
bucket int,
message_id bigint,
author_id bigint,
content text,
PRIMARY KEY ((channel_id, bucket), message_id)
) WITH CLUSTERING ORDER BY (message_id DESC);
디스코드는 하나의 채널당, 3개의 DB 프로그램(node)를,
클러스터로 사용해쓰기(write) 작업을한다.
실제로 사람이 적은 채널은, 상대적으로 적은 요청을 DB에 보내므로 문제가 없지만,
10명 채널 -> 3개 DB -> 원할하네~
채널에 사람이 많으면 많을수록, 그 채널의 3개의 노드에 요청이 몰려, 과부하되어 버린다
1만명 채널 -> 3개 DB -> ?????
이로인해, 해당 채널의 모든 작업이 느려지고, ( 디스코드는 이 부분을 hot partition이라고 부른다.)
사용자에게 바로 나타나버렸다.
Cassandra가 DB 데이터를 최적화하는 도중에는, 유지보수 할 수 없어 기다려야만 하는데,
해당노드가 있는, DB 클러스터를 유지보수 해야하는 일이 점점 늘어나게 되면서,
이 기다리는 시간이 곧바로 응답 시간 지연으로 이어졌다.
이 때문에 디스코드 개발자들은 최적화 작업을 수동으로 해야했다.
- 실제 운영되고 있는 노드를, 사용자 요청 손실없이 분리
- Cassandra 에서 최적화 정보를 얻기위해 다시 붙임 ㅋㅋ
- 더 이상 해당 노드에 작업이 없을때까지 반복
이게 끝이 아니다.
JVM Garbage Collector 특성상, 메모리를 드롭할 때 해당 노드의 모든 작업을 멈춰야 하는데,
이것도 응답지연으로 이어지기 때문에
메모리 돌려줘! -> 일단 작업 멈춰 == 응답지연
개발자들은 매 시간마다 GC를 조정하고, Heap 메모리 관리 설정을 반복적으로 바꿔야만 했다.
( 개발자들이 거의 상시대기(on-call) 했다고 함 ㄷㄷ )
디스코드가 DB를 변경하고, ScyllaDB를 선택한 이유
카산드라가 Java로 만들어졌고,
JVM의 GC (Garbage Collector)가,
메모리를 OS에게 돌려줄때, 다른 작업을 멈춰야했기 때문이다.
( Java 개발자들은 이 부분을 해결하는 새로운 GC를 개발중인걸로 알고있다 )
ScyllaDB 란? (간략)
Cassandra를 호환하는 DB
Cassandra 보다 빠름
Cassandra 보다 고치기 쉬움
구조상 작업 환경 분리가 편함 ( 위에 최적화 작업 관련 ㅋㅋ )
C++로 작성되었기 때문에 GB가 없다.
아직 위에서 언급한 hot partition 문제는 해결하지 않았다.
(3개의 DB노드에 요청이 몰려서 터질려하는 문제)
디스코드는 이를 해결하기 위한 방법으로,
요청을 받는 API와, DB클러스터 사이에서
중간다리 역할을 해줄 새로운 프로그램(middleware)을 만들기로 결정했다.
마무리
다음에는, 이 Middleware의 모습를 살펴보고,
이 것이 얼마나 효과적인 결과를 가져왔는지 알아보자.