2,000개가 넘는 Discord 서버에서 봇을 운영하며 배운 것
코드를 쓸 때 생각하는 것과 실제 규모에서 다르게 작동하는 네 가지를 이야기해요.

서버 세 개에서 돌아가는 Discord 봇과 2,000개가 넘는 서버에서 돌아가는 같은 봇은, 크기만 다른 같은 프로그램이 아니에요. 작을 때는 전혀 문제없던 가정 몇 가지가, 일정 규모를 넘으면 찾기 가장 어려운 버그가 돼요.
실제로 장애를 일으킨 네 가지를 소개할게요.
1. 장애는 고르게 퍼지지 않고, 숨어요
AlphaCloud는 샤드라고 부르는 여러 프로세스에 나뉘어 돌아가요. 어떤 서버가 어떤 샤드에 들어갈지는 Discord가 정하고, 각 샤드는 자기 서버만 알아요.
여기서 밖에서 보면 우연처럼 보이는 종류의 장애가 생겨요: 어떤 서버에서는 환영 메시지가 작동하고, 어떤 서버에서는 안 돼요. 로그에 오류도 없고, 경보도 없고, 아무것도 없어요. 원인은 시간 제한이 없는 네트워크 호출 하나였어요. 상대방이 연결을 받아 놓고 아무 말도 하지 않으면, 코드는 프로세스가 살아 있는 내내 기다려요. 그 뒤의 일은 전혀 일어나지 않고, 오류가 발생하지 않으니 아무것도 오류를 보고하지 않아요.
교훈은 평범하고 보편적이에요: 밖으로 나가는 모든 호출에는 상한이 필요해요. 영원히 기다리는 호출은 실패하는 호출보다 나빠요. 실패는 적어도 눈에 보이니까요.
2. 재시작은 해결책이 아니라 마감 기한이에요
원인을 몰랐을 때는 영향을 받은 샤드를 재시작하면 확실히 나아졌어요. 바로 그게 재시작의 문제예요: 효과가 있으니, 원인을 찾아야 한다는 압박이 사라져요.
이제 우리는 "재시작하면 나아지나요?"를 치료가 아니라 진단으로 봐요. 나아진다면 고장 난 상태가 메모리에 있다는 뜻이고, 코드 어딘가에 그 상태를 쌓기만 하고 치우지 않는 곳이 있다는 뜻이에요.
반대도 마찬가지예요: 재시작을 견뎌야 하는 건 모두 데이터베이스에 있어야 해요. 수천 명의 멤버에게 역할을 바꾸는 것처럼 여러 단계로 된 작업이 진행 중인데 프로세스가 중간에 죽으면, 그 작업이 그냥 사라지면 안 돼요. 다음에 시작할 때 감지되고 이어서 진행되어야 해요.
3. Discord의 제한은 프로세스가 아니라 봇 단위로 적용돼요
Discord는 봇이 초당 보낼 수 있는 요청 수를 제한해요. 당연해 보이는 가정은 이거예요: 프로세스가 많을수록 처리량도 많다. 틀렸어요. 제한은 프로세스가 아니라 봇에 걸려 있어요. 샤드 세 개가 각자 전체 예산을 쓰면 허용량의 세 배가 되고, 그러면 Discord는 세 개를 모두 제한해요.
그래서 우리 프로세스는 각자 전체가 몇 개인지 계산하고 자기 몫만 가져가요. 당연하게 들리지만 그렇지 않았고, 이 버그는 오랫동안 보이지 않았어요. 각 프로세스는 따로 보면 완벽하게 얌전해 보였으니까요.
관련된 함정이 하나 더 있어요: 반복 작업은 5분마다처럼 고정된 간격으로 실행돼요. 모든 샤드가 동시에 시작하면, 영원히 같은 순간에 작업을 실행해요. 그 뒤로 모든 프로세스는 시작할 때 무작위 오프셋을 받아요.
4. 사람들이 실제로 설정하는 건 예상과 달라요
기술적인 부분은 절반이에요. 나머지 절반은 이거예요: 대부분의 서버에서는 기본값이 곧 설정이에요. 기본값이 잘못된 모듈은, 설정 페이지가 아무리 좋아도 대다수 서버에서 잘못 설정되어 있어요.
여기서 우리에게 두 가지가 나와요. 첫째, 모든 기본값은 설정 자체보다 더 많은 고민이 필요해요. 둘째, 새 모듈에 대해 더 좋은 질문은 "여기서 무엇을 설정할 수 있나"가 아니라 "아무도 아무것도 설정하지 않으면 이 모듈은 무엇을 하고, 그게 일반적인 서버에 맞는가"예요.
이게 처음엔 정반대처럼 들리는 결정의 이유이기도 해요: v1.8.0부터 봇이 보내는 거의 모든 메시지를 자유롭게 디자인할 수 있어요. 하지만 경고 DM의 사유나 티켓 패널의 카테고리 메뉴 같은 필수 블록은 옮길 수는 있어도 지울 수는 없어요. 디자인의 자유는 주지만, 서버가 실수로 작동하는 것처럼 보이는데 실제로는 작동하지 않는 걸 만들 정도까지는 아니에요.
공통점
네 가지 중 세 가지는 옷만 다를 뿐 같은 생각이에요: 이 정도 규모에서는 조용한 실패가 시끄러운 실패보다 비싸요. 충돌은 보고되고 고쳐져요. 그냥 아무것도 하지 않게 된 기능은 몇 달 동안 그대로일 수 있고, 수고스럽게 글을 써 준 누군가의 버그 신고로 알게 돼요.
그래서 이제 새 코드에 대해 가장 먼저 묻는 질문은 "이게 작동하나요"가 아니라 "작동하지 않으면 어떤 모습이고, 우리가 알아챌 수 있을까요"예요.
더 많은 글
Discord 서버 4,000개 지켜보기: Grafana, PostHog, 그리고 그것들이 보여 준 것
트래픽의 절반을 조용히 만들어 낸 반복 작업, 하루 300만 건의 쓸모없는 요청, 1분 만에 끝난 공격. 우리 모니터링이 보는 것과 일부러 보지 않는 것을 이야기해요.
비하인드AI 제공업체가 모델을 종료했을 때
모델 교체, 오류 없이 돌아온 빈 답변, 그리고 갑자기 몇 배로 뛴 청구서. 무엇이 고장 났는지, 그리고 원인이 왜 그렇게 보이지 않았는지 이야기해요.
비하인드AlphaCloud가 거의 모든 기능을 무료로 두는 이유
대부분의 Discord 봇은 가장 유용한 기능을 구독 뒤에 잠가 둬요. 우리는 일부러 그러지 않아요. 그리고 그건 생각보다 너그러움과는 별 관계가 없어요.