結論から言うと、ユーザーが増えた時にカスタムフィードが不安定になる原因は2つあります。
- Relayの処理が間に合わない
- フィードの参照アクセスに耐えきれない
これを理解するには、Blueskyのアーキテクチャを順に理解する必要があります。Blueskyのアーキテクチャを理解する上で重要な図は公式から提供されています。
この図において、フィードジェネレータは右中央の青色のところです。SkyFeedであったり、拙作のStarryskyシリーズもここに属します。
この図、ちょっとわかりにくいので、「投稿する」というアクションに絞ってこの図に追記していきます。
投稿をカスタムフィードジェネレーターに反映する
右上にBlueskyのアイコンがありますが、これが公式アプリやWeb、TOKIMEKIなどのサードパーティクライアントを意味しています。投稿すると、AppViewからPDSいわゆるきのこサーバーに投稿が保存されます。その内容は別のユーザーに伝える必要があるため、Relay(Firehose)を通じて別のサーバーに連携されます。フィードジェネレータはRelayからどのような投稿がされたかを初めて知ることができるようになっています。
この例では「投稿」という説明になっていますが、現実には
- 他人が投稿に対するリポストしたり引用したり解除したり
- いいねをつけたり消したり
- 本人が投稿を削除したり
- 誰かが誰かをフォローしたり解除したり
- プロフィールを更新したり
- エログロ投稿を運営さんがラベルつけたり
のようなものも(厳密には細かく違いもありますが)このルートを辿ります。このような「投稿」や「いいね」がつくことを「イベント」と呼びます。新規ユーザーの場合は、当然プロフィールを更新しますし、積極的に他人をフォローします。そのため、既存ユーザーよりもFirehoseから連携されるイベントが多くなることが窺い知れます。
フィードジェネレーター側はFirehoseから全てのイベントを受信するしかない仕様なので、「あらゆるイベントの中で自分のフィードジェネレーターで必要になるイベントを取捨選択し、かつ、いい感じに自身のサーバーに反映する」ことを行なっています。この取捨選択といい感じに反映が間に合わないと、「X時間遅れで取り込み中」といった事象の報告がなされることになります。
平常時の「1万イベントの中から1000件を見つけ出して処理する」こととピーク時の「10万イベントの中から1万イベントを見つけ出して処理する」ことは大きく違ってきてしまい、1万イベントを振り分けること自体にも処理時間を食われてしまうことになります。もちろん1万イベントの中で、自身のフィードにいい感じに反映することも10倍時間がかかります。投稿以外のイベントにも眼を向ける必要がある、ということですね。
現実、BOTや半分ぐらいのフィードジェネレータは投稿のイベントだけで事足りるケースが多いです。Bluesky Meetupでも「全部のイベント受信するの辛いんだけど!」という質問が上がったり、Blueskyのやりとりで運営側に要望が上がっていたりするので、そのうちに「投稿だけほしい」「いいねはいらない」などの大きな括りでフィルターを指定できる日が来るかもしれません
SkyFeedの受信の仕組み
この受信するための一連のSkyFeedの仕組みはオープンソースで公開されています。
SkyFeedは一般的に高速な処理に向いているとされるDartのライブラリで行っていますが、それを自身のサーバーに反映するコストの方がかかっていると思います(パソコンで何らかのファイルを保存するのは時間かかるのと同じ理屈です)。というか動かしたらメモリ8GBの安物M1 mac miniではお話になりませんでした。
カスタムフィードを表示する
表示する際はかなりシンプルです。とあるカスタムフィードBを見たい場合は、AppViewにBを見たいとリクエストすると、どのサーバーにあるかを知っているのでそこにデータを要求します。ジェネレータは求められらフィードの内容をAppViewに返し、Blueskyで参照することができます。
表示するフローにおいて、ユーザーは増えた時に困るのは緑の線のところになります。通常のWebサイトでもアクセスが増えるとダウンしますが、それはBlueskyのカスタムフィードの世界でも起こり得ます。
SkyFeedのような共用?汎用?フィードジェネレータは複数のフィードが稼働していますから、そのアクセス数も桁違いになっているだろうことが窺い知れます。
SkyFeedの表示する仕組み
実はSkyFeedはこの図には近いのですが、少し違う仕組みになっています。具体的には「Firehoseから流れてきたデータを単純に溜め込む箱」と、それを「個別のカスタムフィードごとに分ける箱を作り、AppViewからの表示に備える箱」が2つ分かれています。個別のフィードごとの箱に、5分おきぐらいにRelayの最新データをRegexを用いて、投稿を検索してフィードごとに保存するようにしています。
この仕組みもあり、最初に「私が2つの理由がある」といいましたが、SkyFeedにおいては固有の処理「3.Relayで受信済みのデータを、各フィードに振り分ける処理が遅延している」のことも気にする必要があります。
この振り分ける部分のソースコードも公開されています。
1(緑矢印), 3(オレンジ矢印)の変換サーバーがダウンしている場合や遅延している場合は、フィードは表示されるが内容が古い、という事象が発生します。2(青矢印)がダウンしている場合は、単純にフィードが表示できませんエラーになります。
ここまでお付き合いいただきありがとうございました。