How Discord Scaled Elixir to 5,000,000 Concurrent Users

Bài tóm tắt Discord 2017 2. Tăng trưởng Độ khó: Nâng cao

Discord · 2017 · 2. Tăng trưởng · Độ khó: Nâng cao — bài gốc

Bối cảnh

  • Discord dùng Elixir/Erlang VM (BEAM) từ prototype đầu tiên. Sau 2 năm: gần 5 triệu user đồng thời, hàng triệu event/giây (2017).
  • Kiến trúc lõi là pub/sub: mỗi user mở WebSocket và có một process session; mỗi guild (server Discord) là một process guild trên node khác. Sự kiện trong guild được fan-out tới mọi session.
  • Thiết kế ban đầu cho nhóm ≤25 người. Khi xuất hiện guild 30.000 người online (ví dụ /r/Overwatch), process guild không kịp xử lý hàng đợi; có lúc phải tắt bớt tính năng bằng tay.

Thay đổi chính

Ba thư viện mã nguồn mở, mỗi cái giải một điểm nghẽn đo được:

  1. Manifold (fan-out): một lệnh send/2 tốn 30–70μs, nên publish một event trong guild lớn mất 0,9–2,1 giây. Manifold gom PID theo node đích, gửi một message tới mỗi node, rồi node đó tự phân phối theo số core. Vẫn giữ thứ tự event, còn giảm traffic giữa các node.
  2. FastGlobal (dữ liệu chia sẻ đọc nhanh): tra consistent-hash ring khi hàng loạt user kết nối lại. Qua process thì ~12μs/lần; ETS ~7μs nhưng phải copy (17,5 giây cho một server session khởi động lại). FastGlobal biên dịch dữ liệu thành module hằng số để mọi process đọc không phải copy (~0,3μs), tổng còn ~750ms. Đánh đổi: mỗi lần đổi ring mất tới 1 giây để build lại.
  3. Semaphore (giới hạn đồng thời): khi lookup nhanh lên, gần 5 triệu session "giẫm đạp" 10 process guild registry. Timeout + retry chồng chất làm phình hàng đợi, OOM cả VM và gây sự cố dây chuyền. Họ dựng semaphore bằng :ets.update_counter/4 để từ chối sớm khi quá tải. Họ cố ý không dùng circuit breaker để tránh trạng thái ngừng gọi hẳn.

Vì sao

Process Erlang về bản chất là đơn luồng. Discord thấy shard process guild là việc quá lớn, nên chọn chia nhỏ phần việc nóng nhất (gửi message, tra ring, gọi registry).

Bài học cho SWE

  • Làm một đường nóng nhanh hơn có thể đẩy nghẽn sang chỗ khác. Lookup chậm trước đó vô tình là back-pressure; bỏ nó đi thì registry bị giẫm đạp.
  • Đo bằng micro giây ở đường nóng rồi nhân với quy mô (500.000 session/VM × 5 guild/user) để thấy chi phí thật.
  • Retry không có giới hạn đồng thời biến một sự cố nhỏ thành sự cố dây chuyền. Xem Hot partition và thundering herd.
  • Discord nói nếu làm lại họ vẫn chọn Elixir: hiểu sâu runtime giải được bài toán mà không phải đổi ngôn ngữ.

Liên quan