"Real time communication at scale with Elixir at Discord" の解説と感想を雑に書いていく
Real time communication at scale with Elixir at Discord こちらの記事が Elixir な話ではなく Erlang VM に寄せた話が多かったので、せっかくなので DeepL で翻訳した内容を引用しつつ解説やら感想を雑に書いてみます。
この記事を書いているのは José Valim さん、つまり Elixir の作者です。彼は Erlang VM の改善のとても積極的です。 Erlang/OTP の Pull-Request に頻繁に登場します。
Discord は 2015 年の設立当初 Elixir と Python が選択されました。Elixir が採用されたのは Discord の CTO が Discord を起業する前に Guildwork という World of WarcraftやFinal Fantasy XIなどのMMORPG(多人数参加型ロールプレイングゲーム)で大規模なグループ活動を行うゲーマーのニーズを満たすために構築されたソーシャルネットワークおよびウェブホスティングサービス Erlang を利用していたことが理由のようです。
Discord でもっとも有名な Elixir ライブラリは fastglobal です。fastglobal は mochiglobal を Elixir に移植した者です。
mochiglobal は mochiweb の開発者である Bob Ippolito 氏が開発した Erlanb VM に超高速なグローバル KVS を用意する仕組みです。code モジュールをハックして実現しています。読み込みは早いですが、削除は恐ろしく遅いです。
fastglobal の README にあるベンチマークを見えてもらえばわかりますが、ets と比べて 20 倍速いです。
ちなみにこの機能はすでに Erlang VM では Persistent Term として標準ライブラリとして実装されています。
Discordの最も人気のあるサーバーの中には、60万人近くのユーザーがいることになります。これらのサーバーでは、ある瞬間に20万人以上のアクティブなユーザーに遭遇する可能性は低くありません。誰かがユーザー名を変更した場合、Discordはその変更を接続しているすべてのユーザーにブロードキャストしなければなりません (DeepL による翻訳)
Discord のサーバに 60 万人というのは、 Slack に 60 万人が参加しているというイメージを持って下さい。この数字がどれだけすごいかがわかると思います。
すべてのサーバーで1,200万人以上の同時接続ユーザーと交信し、1秒間に2,600万件以上のWebSocketイベントをクライアントに送信しており、Elixirがこれらすべてをサポートしています
全ての接続は WebSocket です。同時接続数が 1200 万人というのは、任天堂が資料で公開していたプッシュ通知サーバへの同時接続数は1000万台+ でしたので、相当の数です。任天堂のプッシュ通知サーバは片方向とのことでしたが、Discord は双方向でさらに任意でクライアントがメッセージが送れるというのは驚異としか言いようありません。
リアルタイムのコミュニケーションという点では、Erlang VMはその仕事に最適なツールです。Erlang VMは分散システムを構築するための優れたツールと推論を備えた非常に汎用性の高いランタイムです。技術的に言えば、言語は自然に適合していました
これは実績から間違いありません。 WhatsApp や任天堂や LINE 、Zoom など、多くのリアルタイムなコミュニケーションを実現する環境で Erlang VM が利用されています。
Discordは400~500台のElixirマシンでクラスタを実行しています。おそらく、最も印象的な偉業は、Discord のチャット インフラストラクチャ チームが 5 人のエンジニアで構成されていることです。5人のエンジニアが20以上のElixirサービスを担当しており、数百万人の同時ユーザーを処理し、毎秒数千万のメッセージをプッシュすることができます
少人数で大規模なリアルタイムなコミュニケーションシステムを運用できるというのは WhatsApp が 5 億ユーザを超える中で、数人でインフラを支えていた事をイベントで発表しています。ちなみに WhatsApp は利用者が現在 20 億ユーザを超えています。
Erlang Factory 2014 - That's 'Billion' with a 'B': Scaling to the Next Level at WhatsApp - YouTube
任天堂の方も、人数は教えて頂けませんでしたが少人数で同時接続 1000 万を実現するサーバを運用しているとのことでした。
ユーザー間のコミュニケーションを確立するシグナリングとしても知られているオーディオおよびビデオサービスのコントロールプレーンとしてElixirを使用しています。その後、C++がメディア・ストリーミングを担当し、合計で1000以上のノード上で実行されます
Discord はメッセージだけでなく、音声や映像のやりとりをする際に必要なシグナリングサーバとしても Elixir を利用しています。実際に音声や映像のやりとりは C++ で書かれています。
デフォルトでは、Distributed Erlangは完全にメッシュ化されたネットワークを構築しますが、-connect_all falseフラグを設定することで、Erlang VMにトポロジのアウトライン化を任せることもできます。Discordチームはこのオプションを設定して部分的にメッシュ化されたネットワークを構築し、etcdがサービスの発見と共有設定のホスティングを担当します
Erlang VM が持つ便利なフルメッシュ機能は使用していないようです。フルメッシュを避けるのは任天堂も同じことをやっていました。
後ほど追記
DiscordはWebSocket接続やTCPサーバを処理するためにCowboyを使用しています。また、データバーストを管理し、バックプレッシャーやロードシェディングなどの負荷調整を行うためには、過去に詳しく説明したGenStageを使用しています。
Discord は Cowboy を利用しています。ちなみに Heroku も Cowboy を利用しています。
後ほど追記で GenStage について書く。
Discord が Elixir と Rust の間に安全なブリッジを提供する Rustler プロジェクトを使用して、1100 万人の同時使用ユーザーにスケールアップしたときがそうでした。彼らはRustlerを使用して、Rustで構築されたカスタムデータ構造をElixirサービスに直接フックしました。
Rustler は NIF (Erlang VM の C 拡張) を Rust で実現するプロジェクトです。Discord は Rust を利用して Erlang VM の課題になりやすい性能を改善しました。
Elixir + Rustler については最近ではモンストなどを提供している XFLAG Studio が WebTransport (QuicTransport) サーバを実現しています。
"最初の頃は、Erlang VMが提供するすべての保証を受け入れるのが大変でした。データの競合や並行性の問題を心配していましたが、それは起こり得ないことでした。最終的に、彼らはこれらの保証を心に留め、より生産性が高く、プラットフォームとそのツールに頼ることができるようになったのです。Erlang VMが提供するイントロスペクションツールはクラス最高です。クラスタ内のどのVMプロセスを見ても、そのメッセージキューを見ることができます。リモートシェルを使ってどのノードに接続しても、ライブシステムをデバッグすることができます。これらすべてが、数え切れないほど役立っています
- メッセージ保証
- 生産性
- メッセージキュー
- リモートシェル
- ライブデバッグ
Erlang VM の魅力がぎゅっと詰まっています。
DiscordでやっていることはElixirなしではできません。DiscordでやっていることはElixirなしでは実現できないでしょう。もしC++のコードベースだったら、5人のエンジニアでこれを構築することはできないでしょう。
C++ は書けないのでなんとも言えませんが、Erlang VM を利用する事で少人数で大規模なリアルタイムなコミュニケーションシステムを構築する事が可能になるのはとても同意です。