ReactiveCaching
このドキュメントは
reactive_caching.rbを参照しています。
ReactiveCaching 関数は、バックグラウンドでデータを取得してRailsキャッシュに保存し、リクエストされている間だけ最新の状態に保つために使用されます。データがreactive_cache_lifetime の間リクエストされなかった場合、データの更新は停止され、削除されます。
使用例
class Foo < ApplicationRecord
  include ReactiveCaching
  after_save :clear_reactive_cache!
  def calculate_reactive_cache(param1, param2)
    # Expensive operation here. The return value of this method is cached
  end
  def result
    # Any arguments can be passed to `with_reactive_cache`. `calculate_reactive_cache`
    # will be called with the same arguments.
    with_reactive_cache(param1, param2) do |data|
      # ...
    end
  end
end
この例では、#result が最初に呼ばれたとき、nil を返します。しかし、#calculate_reactive_cache を呼び出すためにバックグラウンドワーカーをキューに入れ、 最初のキャッシュの有効期間を 10 分に設定しています。
どのように動作するか
最初に#with_reactive_cache が呼び出されると、バックグラウンド・ジョブがエンキューされ、with_reactive_cache はnil を返します。バックグラウンド・ジョブは#calculate_reactive_cache を呼び出し、その戻り値を保存します。また、reactive_cache_refresh_intervalの後にバックグラウンド・ジョブを再エンキューします。したがって、保存された値は常に最新の状態に保たれます。計算が同時に実行されることはありません。
#with_reactive_cache 値がキャッシュされている間に #with_reactive_cache呼び出すと、キャッシュされた値が返されます。また、キャッシュの有効期間をreactive_cache_lifetime の値だけ延長します。
有効期間が過ぎると、バックグラウンド・ジョブはキューに入れられなくなり、#with_reactive_cache を再度呼び出すと、nil が返され、処理が最初からやり直されます。
ReactiveCachingにハードリミットを設定
パフォーマンスを維持するために、ReactiveCaching を含むクラスにハードキャッシュの制限を設定する必要があります。設定方法の例を参照してください。
詳細については、内部イシューRedis (または ReactiveCache) のソフトリミットとハードリミットをお読みください。
いつ
- 外部APIへのリクエスト(例えばk8s APIへのリクエスト)が必要な場合。外部リクエストの間、アプリケーションサーバのWorkerをブロックし続けることは望ましくありません。
- モデルが大量のデータベース呼び出しや時間のかかる計算を行う必要がある場合。
使用方法
モデルやインテグレーションで
ReactiveCaching 懸念は、モデルだけでなくインテグレーション (app/models/integrations) でも使用できます。
- 
モデルやインテグレーションにこのコンサーンを含めてください。 懸念事項をモデルに含めるには include ReactiveCaching懸念をインテグレーションに含めるには: include Integrations::ReactivelyCached
- モデルまたはインテグレーションにcalculate_reactive_cacheメソッドを実装します。
- キャッシュされた値が必要なモデルまたはインテグレーション内部でwith_reactive_cacheを呼び出します。
- それに応じてreactive_cache_work_typeを設定します 。
コントローラ
ReactiveCaching を使用するモデルやサービスメソッドをコールするコントローラのエンドポイントは、 バックグラウンドワーカーが完了するまで待つべきではありません。
- 
ReactiveCachingを使用するモデルやサービスメソッドをコールする API は、キャッシュの計算中 (#with_reactive_cacheがnilを返すとき) に202 acceptedを返すべきです。
- また、Gitlab::PollingInterval.set_headerでポーリング間隔ヘッダを設定する必要があります。
- APIのコンシューマはAPIをポーリングすることが期待されます。
- ポーリングによるサーバ負荷を軽減するために、ETagキャッシュの実装を検討することもできます。
モデルやサービスに実装するメソッド
ReactiveCaching を含むモデル/サービスで実装すべきメソッドです。
#calculate_reactive_cache (必須)
- このメソッドは実装する必要があります。戻り値はキャッシュされます。
- このメソッドはReactiveCachingによって呼び出されます。
- 
with_reactive_cacheに渡された引数はすべてcalculate_reactive_cacheにも渡されます。
#reactive_cache_updated (オプション)
- このメソッドは必要に応じて実装することができます。
- このメソッドは、キャッシュが更新されるたびにReactiveCachingから呼び出されます。キャッシュが更新され、新しいキャッシュ値が古いキャッシュ値と同じ場合、このメソッドは呼び出されません。このメソッドは、新しい値がキャッシュに格納されている場合にのみ呼び出されます。
- これは、キャッシュが更新されるたびにアクションを実行するために使用できます。
モデルもしくはサービスによって呼び出されるメソッド
これらはReactiveCaching によって提供されるメソッドで、モデル/サービスの内部で呼び出されるべきものです。
#with_reactive_cache (必須)
- 
with_reactive_cacheは、calculate_reactive_cacheの結果が必要な場合に呼び出されなければなりません。
- ブロックは、任意の数の引数を取ることもwith_reactive_cacheできますwith_reactive_cache。with_reactive_cacheに渡された引数はすべてcalculate_reactive_cacheに渡されます。with_reactive_cacheに渡された引数は、キャッシュ・キー名に付加されます。
- 結果がすでにキャッシュされているときにwith_reactive_cacheが呼び出された場合、ブロックが呼び出されてキャッシュされた値が返され、ブロックの返り値はwith_reactive_cacheによって返されます。また、キャッシュのタイムアウトもreactive_cache_lifetimeの値にリセットされます。
- 結果がまだキャッシュされていない場合、with_reactive_cacheはnilを返します。また、calculate_reactive_cacheを呼び出し、結果をキャッシュするバックグラウンド・ジョブもキューに入れます。
- バックグラウンド・ジョブが完了し、結果がキャッシュされると、with_reactive_cacheへの次の呼び出しがキャッシュされた値をピックアップします。
- 
以下の例では、 dataがキャッシュされた値で、with_reactive_cacheに与えられたブロックに渡されます。class Foo < ApplicationRecord include ReactiveCaching def calculate_reactive_cache(param1, param2) # Expensive operation here. The return value of this method is cached end def result with_reactive_cache(param1, param2) do |data| # ... end end end
#clear_reactive_cache! (オプション)
- このメソッドはキャッシュを期限切れ/クリアする必要があるときに呼び出されます。例えば、モデルが修正された後にキャッシュがクリアされるように、モデルのafter_saveコールバックで呼び出すことができます。
- パラメータはキャッシュキーの一部なので、このメソッドはwith_reactive_cacheに渡されるのと同じパラメータで呼び出されなければなりません。
#without_reactive_cache (オプション)
- これは、デバッグの目的で使用できる便利なメソッドです。
- このメソッドはバックグラウンドワーカーではなく、現在のプロセスでcalculate_reactive_cacheを呼び出します。
設定可能なオプション
class_attribute 、調整可能なオプションがいくつかあります。
self.reactive_cache_key
- この属性の値は、dataおよびaliveキャッシュ・キー名のプレフィックスです。with_reactive_cacheに渡されたパラメータが残りのキャッシュ・キー名となります。
- 
デフォルトでは、このキーはモデル名とレコードの ID を使用します。 self.reactive_cache_key = -> (record) { [model_name.singular, record.id] }
- 
dataキャッシュキーは"ExampleModel:1:arg1:arg2"で、aliveキャッシュキーは"ExampleModel:1:arg1:arg2:alive"です。ExampleModelはモデルの名前、1はレコードの ID、arg1とarg2はwith_reactive_cacheに渡されるパラメータです。
- 
この問題をインテグレーション ( app/models/integrations/) に含める場合は、インテグレーションに以下を追加してデフォルトをオーバーライドする必要があります:self.reactive_cache_key = ->(integration) { [integration.class.model_name.singular, integration.project_id] }reactive_cache_keyが上記とまったく同じであれば、代わりに既存のIntegrations::ReactivelyCachedを使用することができます。
self.reactive_cache_lease_timeout
- 
ReactiveCachingはGitlab::ExclusiveLeaseを使って、キャッシュ計算が複数のワーカーによって同時に実行されないようにします。
- この属性はGitlab::ExclusiveLeaseのタイムアウトです。
- デフォルトは 2 分ですが、別のタイムアウトが必要な場合はオーバーライドできます。
self.reactive_cache_lease_timeout = 2.minutes
self.reactive_cache_refresh_interval
- これはキャッシュが更新される間隔です。
- デフォルトは1分です。
self.reactive_cache_refresh_interval = 1.minute
self.reactive_cache_lifetime
- これはリクエストがなければキャッシュがクリアされるまでの時間です。
- デフォルトは 10 分です。このキャッシュ値に対するリクエストが 10 分間ない場合、キャッシュは失効します。
- 有効期限が切れる前にキャッシュ値が要求された場合、キャッシュのタイムアウトはreactive_cache_lifetimeにリセットされます。
self.reactive_cache_lifetime = 10.minutes
self.reactive_cache_hard_limit
- これはReactiveCachingがキャッシュを許可する最大データサイズです。
- デフォルトは1メガバイトです。この値を超えるデータはキャッシュされず、Sentry上でReactiveCaching::ExceededReactiveCacheLimit。
self.reactive_cache_hard_limit = 5.megabytes
self.reactive_cache_work_type
- これはcalculate_reactive_cacheメソッドで実行される作業の種類です。この属性に基づいて、キャッシュジョブを処理する適切なワーカーを選ぶことができます。外部からのリクエスト (Kubernetes や Sentry など) を処理する場合は、必ず:external_dependencyに設定してください。そうでない場合は:no_dependencyに設定してください。
self.reactive_cache_worker_finder
- これは、calculate_reactive_cacheが呼び出されるオブジェクトを見つける、または生成するためにバックグラウンドワーカーが使用するメソッドです。
- 
デフォルトでは、オブジェクトを見つけるためにモデルの主キーを使用します: self.reactive_cache_worker_finder = ->(id, *_args) do find_by(primary_key => id) end
- 
デフォルトの動作は、カスタム reactive_cache_worker_finderを定義することでオーバーライドできます。class Foo < ApplicationRecord include ReactiveCaching self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } def self.from_cache(var1, var2) # This method will be called by the background worker with "bar1" and # "bar2" as arguments. new(var1, var2) end def initialize(var1, var2) # ... end def calculate_reactive_cache(var1, var2) # Expensive operation here. The return value of this method is cached end def result with_reactive_cache("bar1", "bar2") do |data| # ... end end end- この例では、主キーIDはwith_reactive_cacheに渡されたパラメータとともにreactive_cache_worker_finderに渡されます。
- カスタムreactive_cache_worker_finderはwith_reactive_cacheに渡されたパラメータで.from_cacheを呼び出します。
 
- この例では、主キーIDは
