Discordは、応答性の高いユーザー体験を維持しながら、単一サーバーで100万人以上のオンライン・ユーザーにサービスを提供するためにプラットフォームを最適化した。同社は、システム観測可能性とパフォーマンスチューニングに支えられた一連のパフォーマンスとスケーラビリティの改善で、何十億ものメッセージ通知を扇状に流す役割を担うギルドコンポーネントを進化させた。
Discordプラットフォームの主要要素は、Erlang VM上で動作する関数型言語Elixirを使って実装されている。Elixirベースのコンポーネントは、ユーザーへのメッセージ通知のルーティングと配信を担当している。ギルドサーバーは、Discordコミュニティの様々なビジネスフローを管理する中心的なハブである。ギルドプロセスは、WebSocket接続を使用してユーザーデバイス上のクライアントアプリにメッセージを配信する多くのセッションプロセスと相互作用する。アーキテクチャのもう一つの重要な要素には、Pythonで書かれたAPIサービスがあり、ScyllaDBにメッセージを永続化する役割を担っている。
Discordプラットフォームのメッセージフロー(出典: Discord Engineering Blog)
これまでの設計の選択とプラットフォームの制約を考えると、Discordチームはギルドプロセスが増え続けるオンラインユーザーを確実に処理し続けなければならなかった。DiscordのスタッフソフトウェアエンジニアであるYuliy Pisetsky氏は、サーバーパフォーマンスに関するユーザーエクスペリエンスへの配慮について話している。
全体的なスループットへの懸念に加え、サーバーの規模が大きくなるほど遅くなるオペレーションもあります。ほとんどすべての操作を素早く行えるようにすることは、サーバーがレスポンスよく感じられるようにするために重要です。メッセージが送信され、他の人がすぐにそれを見るべきであり、誰かがボイスチャンネルに参加し際には、すぐに参加し始められるべきです。高価な操作の処理に何秒もかかると、そのような経験は損なわれます。
エンジニアたちは、システムのパフォーマンスを理解するためにかなりの時間を費やした。ギルドプロセスのイベント処理ループを計測し、メッセージ処理時間に関する主要なメトリクスを取得した。プロセスのスタックトレースを使用して、チームは分析を行い、メッセージ処理の待ち時間の要因を探した。また、大規模オブジェクトのメモリー使用量を効率的に見積もるヘルパーライブラリーを作成し、メモリー使用量の最適化に役立てた。観測可能性のデータを武器に、いくつかの最適化を実施し、いくつかのメッセージタイプの処理時間を大幅に短縮した。
チームは、ギルドプロセスの作業量を減らすことで、いくつかの大きな成果を得た。これは、パッシブセッション(ユーザーがメンバーであるいくつかのコミュニティと交流しないセッション)の通知を無効にすることで実現した。この変更だけで、ファンアウトの作業量は90%削減され、ユーザー数の増加に必要な余裕ができた。
開発者は、ギルドプロセスとセッションプロセスの間でより効率的なメッセージの受け渡しを支援するために、リレープロセスの新しいレイヤーを導入した。リレープロセスがビジネスフローの一部を処理することで、ギルドプロセスがより多くのユーザーを処理できるようになった。
リレー・プロセス層(出典: Discord Engineering Blog)
その他の最適化には、インメモリデータベースであるETSを使用して、プロセス間でメンバーのリストを安全に保存・共有することや、受信ノードへのファンアウトを行うために別の送信プロセスを作成するなどが含まれる。
HNのスレッドでは、ブログ記事の著者が、Discordによって実装された機能強化の詳細について興味を持つコミュニティからの質問に答えている。