キャッシュコンテキスト
コンテキストキャッシュ = (リクエスト)コンテキスト依存関係
キャッシュコンテキストはHTTPのVaryヘッダーに類似しています。
なぜ?
キャッシュコンテキストは、キャッシュする必要があるもののコンテキスト依存のバリアントを作成する方法を定義します。キャッシュを作成するコードが読みやすくなり、同じコンテキストのバリエーションが必要な場所ごとに同じロジックを繰り返す必要がなくなります。
例:
- 一部のデータの出力はアクティブなテーマに依存し、テーマごとに異なる結果が出力されます。その場合は、テーマキャッシュコンテキストに応じてキャッシュを使用します。
- パーソナライズされたメッセージを表示するレンダー配列を作成する場合、配列のレンダリングはユーザーに依存します。その場合は、ユーザーキャッシュコンテキストに応じてキャッシュが異なります。
- 一般的に:計算コストの高い情報がサーバー環境に依存する場合、そのためにもキャッシュコンテキストが使用されます。
どのように?
キャッシュコンテキストは、利用可能なキャッシュコンテキストサービスの1つを参照する文字列です(下記参照)。
キャッシュコンテキストは文字列のセット(順序は関係ありません)として渡されるため、string[]として出力されます。セットであるのは、1つのキャッシュアイテムが多くのキャッシュコンテキストに依存(バリエーション)する可能性があるためです。
通常、キャッシュコンテキストはリクエストコンテキストオブジェクト(つまりリクエストから)から取得されます。Webアプリケーションの環境の大部分はリクエストコンテキストから取得されます。結局のところ、HTTPレスポンスは、それを開始したHTTPリクエストのプロパティに大きく依存して生成されます。
ただし、これはキャッシュコンテキストがリクエストから取得される必要があるという意味ではありません。デプロイされたコードに依存する場合もあります。たとえば、deployment_idコンテキストなどです。
次に、キャッシュコンテキストは階層として記述されます。最も単純な例:何かがユーザーごとに異なる場合、ユーザーごとの違いがあるため、単純に権限を使用する必要はなくなります。権限のセットの場合、キャッシュは権限ごとに使用されます。ページの一部がユーザーごとに異なり、別の部分が権限によって異なる場合、Drupalはユーザーごとの違いのみを使用するのに十分賢い必要があります。ここでDrupalは階層情報を使用して、不要なキャッシュバリエーションを作成しないようにできます。
構文
- ドットは親要素と子要素を区切ります
- 複数形のキャッシュ名はパラメーターを使用できることを示します:コロンを追加してから、目的のパラメーターを指定します(パラメーターが指定されていない場合、すべての可能なパラメーターが収集されます。たとえば、すべてのクエリ引数)
Drupal 8コアのキャッシュコンテキスト
Drupal 8コアには、次のキャッシュコンテキスト階層が付属しています:
cookies
:name
headers
:name
ip
languages
:type
protocol_version // Available in 8.9.x or higher.
request_format
route
.book_navigation
.menu_active_trails
:menu_name
.name
session
.exists
theme
timezone
url
.path
.is_front // Available in 8.3.x or higher.
.parent
.query_args
:key
.pagers
:pager_id
.site
user
.is_super_user
.node_grants
:operation
.permissions
.roles
:role
注記。以前のブランチ/リリースでurl.path.is_frontキャッシュコンテキストを使用するには、変更レコードを参照してください。
キャッシュコンテキストが使用されるすべての場所で、この階層全体が指定されます。これには3つの利点があります:
- あいまいさがありません:親キャッシュコンテキストがどこで使用されても、何に基づいているかが明確です
- キャッシュコンテキストの比較(および畳み込み)が簡単になります:a.b.cとa.bの両方が存在する場合、a.bがa.b.cを含むことは明らかであり、したがって、a.b.cを省略できる理由、つまり親オブジェクトに「畳み込む」ことができる理由が明確です
- ツリーの各レベルがツリー全体で一意であることを気にする必要がありません
したがって、この階層からのキャッシュコンテキストの例:
- theme(設定されたテーマに依存)
- user.roles(ロールの組み合わせに依存)
- user.roles:anonymous(現在のユーザーが「anonymous」ロールを持っているかどうか、つまり「匿名ユーザーであるかどうか」に依存)
- languages(すべての言語タイプで異なります:インターフェース、コンテンツ…)
- languages:language_interface(インターフェース言語に依存 - LanguageInterface::TYPE_INTERFACE)
- languages:language_content(コンテンツ言語に依存 - LanguageInterface::TYPE_CONTENT)
- url(URL全体に依存)
- url.query_args(クエリ文字列全体に依存)
- url.query_args:foo(クエリ引数? fooに依存)
- protocol_version(HTTP/1とHTTP/2に依存)
キャッシュコンテキストの最適化/畳み込み/結合/簡素化
Drupalは階層情報を自動的に使用して、キャッシュコンテキストを可能な限り簡素化します。たとえば、ページの一部がユーザーごとに異なり(userキャッシュコンテキスト)、別の部分が権限ごとに異なる場合(user.permissionsキャッシュコンテキスト)、変更はユーザーごとに個別に行われるため、最終結果を権限ごとに変更する意味はありません。
言い換えれば:optimize([user, user.permissions]) = [user]。
user.permissionsがより具体的であるため、実際にuser.permissionsを意味している場合でも、user.permissionsを最適化すると、権限の変更によってuser.permissionsキャッシュコンテキストがページごとにロードされることがなくなります。つまり、権限が変更された場合でも、権限が変更されるたびに変更されるべき場合でも、同じキャッシュバージョンを使い続けます。
そのため、時間とともに変化する可能性のある設定に依存するキャッシュコンテキストは、キャッシュ可能性メタデータを関連付けることができます:キャッシュタグとmax-age。そのようなキャッシュコンテキストが最適化されると、そのキャッシュタグはキャッシュアイテムに関連付けられます。したがって、割り当てられた権限が変更されるたびに、キャッシュアイテムも無効になります。
(「キャッシュ」は本質的に「不要な計算の回避」であることを忘れないでください。したがって、コンテキストの最適化は、コンテキストサービスのgetContext()メソッドの結果をキャッシュすることと見なすことができます。この場合、これは暗黙のキャッシュです(値を保存する代わりに破棄します)が、効果は同じです:キャッシュヒット時にgetContext()メソッドが呼び出されないため、計算が回避されます。そして、何かをキャッシュするとき、そのもののキャッシュ可能性を関連付けます。したがって、コンテキストをキャッシュする場合、タグとmax-ageを関連付けます。)
類似しているがより複雑な例は、node grantsです。node grantsは特定のユーザーに適用されるため、ノード権限キャッシュコンテキストはuser.node_grantsという形式になります。ただし、ノード権限は非常に動的な場合があります(たとえば、時間に依存して数分ごとに変化する場合があります)。これは、サイトに存在するnode grantフックの実装に依存します。したがって、node grantsキャッシュコンテキストをmax-age = 0で使用することをお勧めします。これは、キャッシュできない(つまり最適化できない)ことを意味します。したがって、optimize([user, user.node_grants]) = [user, user.node_grants]。
特定のサイトでは、デフォルトのノード権限キャッシュコンテキスト実装をオーバーライドし、代わりにmax-age = 3600を指定できます。これは、すべてのノード権限フックがアクセス結果を最大1時間キャッシュできることを示します。そのようなサイトでは、optimize([user, user.node_grants]) = [user]と最適化されます。
認識、定義、作成方法?
キャッシュコンテキストは、cache.contextタグが付いたサービスです。したがって、任意のモジュールがキャッシュコンテキストを追加できます。それらは\Drupal\Core\Cache\Context\CacheContextInterfaceまたは\Drupal\Core\Cache\Context\CalculatedCacheContextInterface(パラメーターを受け取るキャッシュコンテキスト用。つまり、:parameterサフィックスを受け取るキャッシュコンテキスト)を実装します。
したがって、使用可能なすべてのキャッシュコンテキストを見つけるために必要なのは、CacheContextInterfaceとCalculatedCacheContextInterfaceに移動し、IDEを使用してすべての実装を見つけることだけです。(PHPStormでは:タイプ階層 → サブタイプ階層。NetBeansでは:インターフェース名を右クリック → 使用箇所を検索 → すべてのサブタイプを検索。)
または、Drupalコンソール(drupal debug:cache:context)を使用して、サイトまたはアプリケーションのすべての現在のキャッシュコンテキストを表示できます:
$ drupal debug:cache:context
Context ID Label Class path
cookies HTTP-Cookies Drupal\Core\Cache\Context\CookiesCacheContext
headers HTTP-Header Drupal\Core\Cache\Context\HeadersCacheContext
ip IP-Adresse Drupal\Core\Cache\Context\IpCacheContext
languages Language Drupal\Core\Cache\Context\LanguagesCacheContext
request_format Anfrageformat Drupal\Core\Cache\Context\RequestFormatCacheContext
route Route Drupal\Core\Cache\Context\RouteCacheContext
route.book_navigation Buchnavigation Drupal\book\Cache\BookNavigationCacheContext
route.menu_active_trails Aktiver Menüpfad Drupal\Core\Cache\Context\MenuActiveTrailsCacheContext
見つけた各クラスには、\Drupal\Core\Cache\Context\UserCacheContextのようなコメントがあります:
Cache context ID: 'user'.
これは、'user'がコードで指定できる実際のキャッシュコンテキストであることを意味します。(または、このクラスが*.services.ymlファイルのどこで使用されているかを見つけて、サービスIDを確認してください。詳細は以下を参照。)
ヒント:Drupalコアのすべてのキャッシュコンテキストの最新の完全なリストは、cache_contextタグが付いたサービスを見るだけで取得できます!
サービスIDは標準化されています。常にcache_context.で始まり、その後にキャッシュコンテキストの親が続き、最後にキャッシュコンテキストの名前が続きます。したがって、たとえば:cache_context(必須プレフィックス)+ route(親)+ book_navigation(このキャッシュコンテキストの名前):
cache_context.route.book_navigation:
class: Drupal\book\Cache\BookNavigationCacheContext
arguments: ['@request_stack']
tags:
- { name: cache.context }
これにより、route.book_navigationキャッシュコンテキストが定義されます。
デバッグ
上記はすべて、キャッシュされるものをデバッグする際の有用な情報です。ただし、もう1つあります:何かがキャッシュキー['foo', 'bar']とキャッシュコンテキスト['languages:language_interface', 'user.permissions', 'route']でキャッシュされているとします。対応するキャッシュアイテムは、CID(キャッシュID)を持つ特定のキャッシュバケットにキャッシュされます:
foo:bar:[languages:language_interface]=en:[user.permissions]=A_QUITE_LONG_HASH:[route]=myroute.ROUTE_PARAMS_HASH
つまり:
- キャッシュキーが最初に、指定された順序でリストされます
- キャッシュコンテキストはアルファベット順に2番目にリストされ、[<キャッシュコンテキスト名>]=<キャッシュコンテキスト値>の形式のCID部分になります
- これらすべてのCID部分はコロンで結合されます
これにより、キャッシュの分析とデバッグが容易になるはずです!
ヘッダー(デバッグ)
そして最後に:特定のレスポンスがどのキャッシュコンテキストに依存しているか(つまり、どのようにバリエーションするか)を確認するのは簡単です:X-Drupal-Cache-Contextsヘッダーを見るだけで十分です!
注記。これらのヘッダーが表示されない場合は、開発用にDrupalインスタンスを設定する必要があります。
動的ページキャッシュ
Drupal 8でキャッシュコンテキストを徹底的に使用することで、Drupal 8にはデフォルトで有効な動的ページキャッシュが付属しています。(以前は「Smart Cache」として知られていました)
内部ページキャッシュ
内部ページキャッシュは、キャッシュコンテキストの実装に関係なく、匿名ユーザーに提供されるすべてのページが同一であることを前提としていることに注意してください。匿名ユーザーに提供されるコンテンツを変更するためにキャッシュコンテキストを使用したい場合は、このモジュールを無効にする必要があり、パフォーマンスに影響を与える可能性があります。