SMOOSYのメッセージキャッシュ機構

こんにちは。アーキテクトのQZ西垣です。

先日、2018年4月に会員管理システム「SMOOSY:スムージー」のクラウド版をリリースしました。
学協会の運営にかかせない「会員情報」「会費管理」「コミュニケーション」を一元的に把握し、事務局業務を ”スムース” にしたい。学術に特化したシステム・サービスを提供するアトラスだから知っている”できたらいいな” を解決するクラウド型の会員管理システムがSMOOSYです。
最近はそのSMOOSYの機能拡張にかかりきりです。
さて、今回はSMOOSYに実装したメッセージキャッシュ機構について解説します。

なぜメッセージキャッシュ機構が必要なのか

まずは、なぜメッセージキャッシュ機構が必要なのか、その理由を解説します。

SMOOSYでは、メッセージをプロパティファイルで一元管理しています。
それだけではメッセージの自由度が低くなってしまうため、学会ごとにカスタムメッセージを登録する機能も実装しています。
カスタムメッセージはデータベースに格納していますが、メッセージが要求されるたびにデータベースを参照すると、パフォーマンスに悪影響が出てしまいます。
そこで、データベースのキャッシュとしてRedisを使用し、さらに1回のリクエストのスコープに限定してWebサーバのインメモリにもメッセージをキャッシュしています。

また、SMOOSYでは、一部の機能をシングルページアプリケーションとして実装しているため、JavaScriptから扱いやすい形でのメッセージが必要となります。
そのアプローチとして、全メッセージを返却するWebAPIを実装していますが、メッセージを参照するたびにWebAPIを呼び出しメッセージを変数に格納すると、やはりパフォーマンスに悪影響が出てしまいます。
そこで、WebAPI自体にHTTPのキャッシュ制御を実装すると同時に、JavaScriptでセッションストレージをキャッシュとして使用しています。

メッセージのカスタム化とパフォーマンスを両立させるために、これらの機構が必要なのです。

サーバサイドのメッセージのキャッシュ機構

メッセージソースはメッセージを要求されるたびに、カスタムメッセージ管理コンポーネントからのメッセージ取得を試みます。
その際、以下のように処理が実行されます。

  1. Redisにメッセージキャッシュが存在するか確認※
  2. Redisに存在する場合
    1. Redisからカスタマイズメッセージを取得し返却※
  3. Redisに存在しない場合
    1. データベースから全カスタマイズメッセージを取得※
    2. データベースから取得した全カスタマイズメッセージをRedisに格納
    3. 全カスタマイズメッセージから該当のカスタマイズメッセージを返却

※Spring Frameworkのキャッシュ機構を使用して、リクエスト単位でWebサーバのインメモリにキャッシュ

クライアントサイドのメッセージのキャッシュ機構

クライアントサイドのJavaScriptでは、メッセージ管理オブジェクトでメッセージを一元管理しています。
メッセージ管理オブジェクトからメッセージを取得する際、以下のように処理が実行されます。

  1. メッセージ管理オブジェクト内のキャッシュを確認
  2. キャッシュが存在する場合
    1. キャッシュからメッセージを取得
  3. キャッシュが存在しない場合
    1. セッションストレージから最終更新日時を取得
    2. メッセージ取得のWebAPIを呼び出す。最終更新日時が取得できていれば、HTTPヘッダに付与
    3. WebAPIのレスポンスが更新なしの場合
      1. セッションストレージから全メッセージを取得し、メッセージ管理オブジェクト内のキャッシュに格納
      2. キャッシュからメッセージを取得
    4. WebAPIのレスポンスが更新ありの場合
      1. レスポンスから取得した全メッセージを、セッションストレージに格納
      2. レスポンスから取得した全メッセージを、メッセージ管理オブジェクト内のキャッシュに格納
      3. キャッシュからメッセージを取得

HTTPのキャッシュ機構とセッションストレージを使用することにより、WebAPIの呼出回数最小化とレスポンス高速化を実現しています。

メッセージキャッシュ機構を実現するインターフェイス、クラス

以下のインターフェイス、クラスによって、サーバサイドのメッセージキャッシュを実現しています。

  • MessageSourceComponent
  • MessageSourceComponentImpl
  • CustomMessageManagementComponent
  • CustomMessageDatabaseComponent
  • CustomMessageRedisComponent
  • PrimitiveRedisComponent

MessageSourceComponent

Spring Frameworkで定義されているMessageSourceインターフェイスとは別に、SMOOSYで独自に定義したインターフェイスです。
このインターフェイスには、最終更新日時取得と全メッセージ取得の2つのメソッドを定義しています。

MessageSourceComponentImpl

Spring Frameworkで定義されているMessageSourceと、前述のMessageSourceComponentを実装するクラスです。
カスタムメッセージに関する処理はCustomMessageManagementComponentに委譲しています。

CustomMessageManagementComponent

カスタムメッセージを管理するコンポーネントです。
最終更新日時取得と全カスタムメッセージ取得の2つのメソッドを定義しています。

  • 最終更新日時取得
    • メッセージプロパティファイルとカスタムメッセージの最終更新日時で、より新しい方を返却
  • 全カスタムメッセージ取得
    • Redisから全カスタムメッセージを取得し返却
    • Redisにカスタムメッセージが存在しない場合、データベースから取得後Redisに格納した上で返却

データベースとRedisに関する処理はそれぞれCustomMessageDatabaseComponentCustomMessageRedisComponentに委譲しています。

CustomMessageDatabaseComponent

データベース上のカスタムメッセージを管理するコンポーネントです。
全カスタムメッセージ取得メソッドを定義しています。
このコンポーネントのメソッドには、Spring Frameworkのキャッシュ機構によるリクエスト単位のキャッシュが適用されています。

CustomMessageRedisComponent

Redis上のカスタムメッセージを管理するコンポーネントです。
最終更新日時取得と全カスタムメッセージ取得の2つのメソッドを定義しています。

PrimitiveRedisComponent

Redisのプリミティブ操作を実装したコンポーネントです。
このコンポーネントのメソッドには、Spring Frameworkのキャッシュ機構によるリクエスト単位のキャッシュが適用されています。

おわりに

SMOOSYのメッセージキャッシュ機構は、過去のプロジェクトからの反省と、Spring Frameworkの機能を活かした構成となっています。
今後も技術的蓄積や新たな技術の導入によって、SMOOSYは拡張され続ける予定です。
技術的に興味深い実装を行なった際には、また当ブログにアップする予定ですので、どうぞご期待ください。