本連載は分散型マイクロブログ用ソフトウェアMisskeyの開発に関する紹介と、関連するWeb技術について解説を行っています。
ここ最近でMisskeyのユーザー数がさらに急激に増えています。そのため、運営者がより少ないコストでサーバーを維持できるよう、Misskeyのスケーラビリティ改善を急いでいます。
今回は、そういった最近のMisskeyのパフォーマンス改善の取り組みについて、検討中のものも含めて紹介したいと思います。
Identicon生成の無効化オプション
Identiconはユーザーが自身のアイコンを設定していないときに代わりに表示されるアイコンで、これはユーザーごとに異なるようになっています。
仕組みとしては、https://
にリクエストされた際に、hoge
部分をシードとしてランダムな画像を生成し、それをレスポンスするようになっています。また、一度生成されたものはキャッシュされます。
ただ新規登録が増えるとidenticon生成の負荷が無視できなくなります。そこでサーバー管理者がユーザーごとのidenticon生成を無効にし、代わりに一律で仮のアイコンを表示するように設定できるようにしました。
child_process.execSyncの回避
Misskeyではサーバーの負荷の状態を見れるウィジェットをユーザーが設置できますが、サーバー負荷を取得するためにMisskeyが使用しているライブラリが、内部的にコストの高いchild_
を頻繁に呼んでいるということが分かったため、これも管理者が無効にできるようにしました。
Node.sync
系の関数はイベントループをブロックしてしまうので使用は極力避けるべきです。
JSON.parseの呼び出しを削減
MisskeyのストリーミングAPIでは、内部的にRedisに接続してサーバーマシン間でイベントのやり取りを行っています。
今までは、ストリーミングのコネクションごとにイベントを受け取ってJSONを解析する処理が行われていましたが、これをストリーミングではなくRedisのコネクションごとに行うようにし、メッセージ解析の回数を減らしました。
JSON.
自体はC++で非常に最適化されて書かれているため本来そこまで負荷の高い処理ではありませんが、塵も積もれば山となるで、呼び出し回数が非常に多い場合はコストが無視できなくなります。
リアクションのポーリング取得
MisskeyはWebSocketを使ったストリーミングによりリアルタイムでコンテンツが更新されることが特徴のひとつですが、コンテンツの更新頻度が高い場合は負荷や通信量が増えてしまいます。
特に
そこで、一定の条件下でリアクションをリアルタイム取得するのではなく、ポーリングで取得するようにできないか検討しています。ポーリングにすれば
リアクション情報のbulk update
MisskeyではDBのクエリを高速化するために、投稿のリアクション情報を投稿自体にもJSON形式で持たせています。
ただ、これについてもフォロワーの多いユーザーの投稿には短い間に大量のリアクションがつくため、投稿レコードの更新処理が頻発します。PostgreSQLにおいてレコードの更新というのは実質的にはレコードの新規挿入+削除と変わらないため、パフォーマンスへの影響は少なくありません。
そこで、まずRedisに新規リアクション情報を保存するようにし、定期的にそこから情報を読み出してまとめて投稿レコードを更新
カスタム絵文字参照の効率化
フロントエンドにおいて、今まではカスタム絵文字の参照にJavaScriptのArray.
やArray.
などを使用して線形探索を行っており、サーバーに登録されているカスタム絵文字の数が多い場合にパフォーマンス悪化の原因になっていました。
そこで、Map
を用いたハッシュテーブル検索に置き換えることで参照を高速化しました。
// before
const emoji = customEmojis.find(x => x.name === 'foo');
// after
const emoji = customEmojisMap.get('foo');
ストールしたWebSocketコネクションの破棄
MisskeyではWebSocketを多用しますが、たまに
そこで、WebSocketのプロトコル制御フレームを利用して定期的にクライアントに対してpingを送信し、pongが返ってこなければコネクションが生きていないと判断してコネクションの破棄を行うようにしました。
また、使用するWebSocket用ライブラリも、今まで使用していたものはあまりメンテナンスされておらず非効率な処理になっている部分もあったため、より活発に開発されているwebsockets/
Misskey Webのバンドルサイズ削減
Misskeyのフロントエンドにおいて、最初に読み込まれるJavaScriptのサイズを以下の改修によって50%ほど削減しました。
- Unicode絵文字の辞書データを最適化かつ最小限化し、ユーザーが任意で追加の辞書情報をダウンロードできるように
- CSS Modulesのコンパイル方法を効率化
- より多くのモジュールをdynamic import化
(=必要になった時だけ読み込む) - 不要なライブラリの削除など
これによって、Misskeyを最初に開いた時の読み込み速度が改善しています。
キャッシュの効率化
バックエンド内で利用する複数のキャッシュにおいて、共通化して持てるデータが存在していたため一部共有するようにし、メモリ使用量を減らしました。
署名アルゴリズムの変更 / 鍵のサイズの変更
ActivityPubでは、各メッセージに電子署名を行いますが、MisskeyはこれまでRSA 4096bitで署名をしていました。しかし、Mastodonなどが使うRSA 2048bitと比べるとセキュリティの高い設定になっていて、署名の際の計算コストがかなり高くなってしまいます。
そこでMisskeyもRSA 2048bitもしくは、ECDSA、EdDSAなどのより効率的な署名アルゴリズムに変更することを検討しています。ただ、すでに作成された鍵のマイグレーションをどう行うかなどの課題があります。
まとめ
今回は最近のMisskeyのパフォーマンス改善の取り組みを紹介しました。ここで紹介した以外にも、細かなパフォーマンス改善は常に行っています。
パフォーマンス改善においては、まずどの処理の負荷が高いのか、どこがボトルネックになっているのかを調べるところから始めます。Web・
JSON.
のように、一見負荷が低い処理であっても、アクセス数が増えて大量に実行されるようになると負荷が問題になる処理というのもあるため、そういった
今後もMisskeyのパフォーマンス改善について特筆すべきものがあれば紹介していきたいと思います。