フォロー新着: 多様なサービスに対応したタイムラインシステムの技術的変遷
こんにちは。ニコニコ共通バックエンド開発担当の小野塚です。
2024年8月8日から順次「フォロー新着」機能がリリースされましたので、技術的な側面についてこれまでの歴史やニコニコに特徴的な点を含めご紹介したいと思います。
フォロー新着とは
フォロー新着とは、フォローしているユーザー、チャンネル(入会しているチャンネルを含む)、マイリストの更新情報をまとめて新着順にタイムラインとして見られる機能です。 2024年9月リリース予定で開発を進めていましたが、前身であるニコレポのシステムがサイバー攻撃によってダウンしたため、代替として急遽前倒しでリリースされました。[1]
フォロー新着システムに至るまでの歴史
今回のフォロー新着のために開発したシステムは、ニコレポ時代から数えると3つ目のタイムラインシステムとなります。 以前のシステムについて公開されている情報も無いようですので、これを機に簡単に紹介します。
初代ニコレポシステム
ニコレポは2009年にリリースされた「ニコニコ動画(9)」の新機能として導入されました。
当時はデータベースというと社内では基本的にRDBとMemcachedしか無いというような時代でした。 そのため、MySQLをマスターとした上でかなりの量のデータをMemcachedにキャッシュ[2]し、参照時にはフォロー対象の全タイムラインを集めて合成するという作りになっていました。
Memcachedにデータが載っていないと速度が出ないことから当時としては大きいキャッシュを用意していましたが、それでもマイページのタイムラインは1週間しか遡ることができない仕様にしていました。 また、キャッシュ再構築の仕組みをいくつも持っていましたが、それでも不整合により度々サポート問い合わせが来ていたようです。
なおこのシステムでは、コンテンツの状態によるアクティビティの出しわけができなかったため、どこに出す場合であっても一律で厳しい掲載基準を適用していました。
2代目ニコレポシステム
2017年にはニコレポという名前はそのままで新しいシステムに更新されました。 その頃にはもうRDBとMemcachedだけという時代でもなくなっていたこともあり、Elasticsearchがデータベースとして採用されました。これによりキャッシュに頼らずフォローしている全タイムラインをその場で合成してレスポンスすることが可能になりました。
検索エンジンであるElasticsearchは強力な機能を持っているため、参照時に検索条件を変えることで柔軟にタイムラインを構成することができました。 例えば、選んだタグの新着情報が流れてくる「タグレポ」という機能がありましたが、初期のものを除きニコレポと同じバックエンドシステムで実現されていました。[3]
初代システムと違って、参照時にファミリーサービスに問い合わせて得たコンテンツ情報を使い、掲載箇所に合わせた表示判定ができるようにもなっていました。 しかし判定を素直に実装した結果、ニコニコ各サービスの複雑なロジックをシステム内に取り込んでしまい、判定ロジックの実装とその後の保守運用に苦しむことにもなりました。
設計方針
フォロー新着のための新しいシステムではこれまでの流れを受けて、以前ご紹介した共通通知システム同様、表示判定をファミリーサービスに行ってもらう方針で設計しました。 ただし通知発生時に判定をしてその場でAPIを叩く・叩かないを決定するだけの共通通知システムと違い、フォロー新着は表示されるデバイス・場面が様々であり、かつ判定タイミングがコンテンツ投稿より後であることから判断材料も増えるため、より粒度の細かい判定が必要でした。
その際に課題となったのは、そのような場合にどう対応するかのルールがニコニコ全体として存在しなかったことです。 サービスを横断してコンテンツを扱うチームは意外と少ないため、そもそもそのようなルールへの需要が顕在化していないようでした。 労力をかけてニコニコ全体としてのルールを作ることも考えましたが、難易度のわりに今後の需要があるかも分からなかったため、今回はフォロー新着専用のやり方とし、今後同様の案件が出てきた時に再検討するということでファミリーサービスには対応を依頼しました。
具体的には、判定に必要な情報を表示先アプリ(PC用ウェブサイト、iOS動画アプリなど)と表示箇所(フォロワーだけが見られるタイムライン、全公開のタイムラインなど)の2軸に整理し、それらを渡すことで判定結果を返してもらうようにしました。 複雑な判定を持っていないサービスの担当者は判定粒度の細かさに驚いていましたが、一方でその粒度を必要とするサービスの担当者にとって負担は大きかったようです。例えばニコニコ動画では、数多くある動画の状態それぞれについて、出すべきかを具体的に詰めていくのに時間がかかったとのことでした。 また、フォロー新着システムが当初想定していたよりもさらに細かい粒度での判定が必要であると後から分かったりすることもありました。
タイムラインシステムを作る2つの手法
さて、フォロー新着システムのようなタイムラインシステムを作る場合、一般的に2つの手法があると言われます。
一つは今までのニコレポシステムのように、ユーザーから参照リクエストがあった際にその場でフォローしている全対象のタイムラインを合成して返すというものです。 参照時に検索条件を変えることで柔軟にタイムラインを構成することがこの手法のメリットとなります。 一方でその場で合成するという都合上、合成できるタイムライン数に上限があるというのがデメリットとなります。[4]
もう一つの手法として、アクティビティが発生した瞬間にそのフォロワーのタイムラインに配るというものがあります。 ユーザーが参照しにきた時には生成が済んでいるため、合成できるタイムライン数に上限がない一方で柔軟性がかなり低くなります。 前者とはメリット・デメリットがほぼ真逆のシステムと言えるでしょう。 また、ストレージ利用量が膨らむこと、アクティビティが発生してから合成後のタイムラインに表示されるまでに遅延が発生することもデメリットとして挙げられます。
フォロー新着システムでは、後者の方法を採用しています。 開発チームからメリット・デメリットとともに両案を提示したところ、ユーザーにもっと気軽にフォローしてほしいという要望が強く、理論上フォロー数に上限の無い後者の方法となりました。
システム構成
システム構成は以下のようになっています。共通通知システムと同様にAWS上に構築し、核となるデータベースとしてAmazon DynamoDBを採用しました。それ以外では共通通知システムから引き続き、アプリケーションの開発言語にGo言語、動作環境としてECS on Fargate、発生したアクティビティやフォロー状態変更イベントのキューとしてAmazon SQSを使用しています。
DynamoDBの利用は今回が初めてでした。慣れ親しんだRDBと比べてテーブル設計やアクセス方法に癖を感じた一方、ユースケースとしてタイムラインがよく挙げられるだけあって、今回のような用途では思った以上に性能特性が適していると感じました。書き込み性能が重要となるシステムにおいて、キャパシティを増やすだけで書き込みがスケールするのは安心ですし、切りの良い時間にアクティビティの発生が集中するため、未使用のキャパシティを5分間溜め込めるバーストキャパシティが有効でした。オートスケーリングもできるため、なだらかな変化であれば読み込みも書き込みも追従させることができます。また、コストと要件によってはオンデマンドモードが有効な場面もあるかと思います。さらに、ネイティブのマネージドサービスだけあってAmazon Auroraと比べ運用が軽く、TTLを使って自動的に古いデータを消すことができるのも、後から保持期間を変更できないこととのトレードオフにはなりますが、便利でした。
ニコニコに特徴的な点
開発していてニコニコが特徴的だと感じたのは、扱っているコンテンツの種類が多様である点です。投稿できるコンテンツとして、動画、生放送番組、ブロマガ記事、漫画、イラスト、立体作品があります。 タイムラインシステムの開発自体はそれほど珍しいことではありませんが、このコンテンツの多様さは珍しいのではないでしょうか。 中でも、生放送番組は参照した時間によって状態が変わる[5]点、そして漫画も個別のエピソードとエピソードの集合体としての漫画作品という階層構造がある点で特徴的です。 なるべく個別の作り込みをしないようにする一方で、生放送番組のように作り込みが避けられない場合は共通ロジックを侵食しないようにする必要がありました。
そしてそれ以上に特徴的だと感じたのはフォローの種別が複数ある点です。フォロー新着で取り扱っているものとして、ユーザーフォロー、チャンネルフォロー(チャンネル入会を含む)、マイリストフォローがあります。その上、元々はコミュニティフォロー[6]にも対応していました。 単にフォローの種別が多いだけであればタイムラインを合成する際に合成対象を増やせばいいだけです。しかしニコニコのフォローでは同一のアクティビティが複数のフォロー種別から流れてくることがあります。現在のフォロー新着でも、マイリスト追加のアクティビティはユーザーフォローとマイリストフォローの両方から取得することができます。そのためユーザーとマイリストの両方をフォローしていた場合、単純に実装すると本来同一のアクティビティが2つ表示されてしまうことになります。
この問題には、タイムラインを合成した際に同一のアクティビティは常に連続して現れるようIDを設計した上で、表示時に重複を検出して除去するという方法で対応しました。また、ページングの境目に来ても重複しないようにもしています。
終わりに
本記事は通常のタイムラインシステム開発と異なる点に焦点を当てたため、開発が困難であったという印象を与えてしまったかもしれません。 実際、共通通知システムの延長線上から想像していたものと比べると複雑になったように思えます。 しかしながら都度解決していくことができる程度であり、開発期間や工数としてはむしろ予定をやや下回り、順調に開発が進んだ部類であったということを記しておきたいと思います。
初代ニコレポは8年間、2代目ニコレポも7年間稼働したシステムでした。フォロー新着システムも負けないくらいこの先のニコニコサービスを支えていけることを期待しています。 ここまで長文をお読みいただきありがとうございました。
[1]: グループ企業の運用するプライベートクラウドが2024年6月に発生したサイバー攻撃で壊滅したため、ニコニコはサービスを継続することができなくなりました。一方で既に一部のシステムが稼働していたAWS環境は無事であったこともあり、プライベートクラウドにあった残りのシステムもほぼ全てAWSに移し替えることでサービスを復旧しました。フォロー新着に関しては初めからAWS上で構築されていたことから、ニコレポシステムを復旧させるよりフォロー新着のリリースを急ぐことでタイムライン機能の復旧をするという判断になりました。 ↩
[2]: キャッシュされた各タイムラインはlinked listになっており、タイムライン中の古いアクティビティから個別に押し出されて消えるという実装でした。 ↩
[3]: つまりアクティビティ発生元以外をキーにしてタイムラインを構成できたということです。 ↩
[4]: そのため、実は入会できるチャンネル数には制限がないのですが、あまりに入会数が多いとニコレポでは一部のチャンネルが表示されないようになっていました。 ↩
[5]: 同じ生放送番組でもいつ見るかによって、放送前、放送中、放送終了とステータスが変わっていきます。また、放送開始アクティビティ発生時には放送終了時刻が確定していません。 ↩
[6]: ニコニコミュニティはサイバー攻撃により終了を余儀なくされたサービスです。特にニコニコ生放送との関わりが強く、一般ユーザーが生放送を行う際には必ず自分が放送権限を持っているコミュニティを指定する必要がありました。そのためユーザー生放送に関連するアクティビティはコミュニティフォロワーとユーザーフォロワーの双方に流れる仕様で開発が進められていました。 ↩
株式会社ドワンゴでは、様々なサービスを支えるバックエンドシステムを一緒につくるメンバーを募集しています。
ドワンゴに興味がある、または応募しようか迷っている方がいれば、気軽に応募してみてください。