Cache-Kontexte
Cache-Kontexte = (Request-)Kontextabhängigkeiten
Cache-Kontexte sind ähnlich wie der HTTP-Header Vary.
Warum?
Cache-Kontexte definieren, wie kontextabhängige Varianten von etwas, das gecacht werden soll, erstellt werden. Der Code zur Erstellung von Caches wird dadurch leichter lesbar, und dieselbe Logik muss nicht an jeder Stelle wiederholt werden, an der gleiche Kontextänderungen benötigt werden.
Beispiele:
- Die Ausgabe einiger Daten hängt vom aktiven Theme ab, und es werden unterschiedliche Ergebnisse für verschiedene Themes ausgegeben. Dann verwendet man Cache mit Abhängigkeit vom Cache-Kontext des Themes.
- Wenn ein Render-Array eine personalisierte Nachricht zeigt, hängt das Rendering vom Benutzer ab. Dann hängt der Cache vom Cache-Kontext des Benutzers ab.
- Typischerweise: Wenn eine kostenintensive Information von der Serverumgebung abhängt, wird dafür ebenfalls ein Cache-Kontext verwendet.
Wie funktioniert das?
Ein Cache-Kontext ist ein String, der auf einen der verfügbaren Cache-Kontext-Services verweist (siehe unten).
Cache-Kontexte werden als Mengen von Strings übergeben (Reihenfolge spielt keine Rolle) und als string[] dargestellt. Das sind Mengen, da ein Cache-Element von mehreren Cache-Kontexten abhängen kann (variieren kann).
Cache-Kontexte stammen normalerweise aus dem Request-Kontext-Objekt (also aus der Anfrage). Der Großteil der Webanwendungsumgebung stammt aus dem Request-Kontext. HTTP-Antworten werden größtenteils abhängig von den Eigenschaften der sie auslösenden HTTP-Anfragen erzeugt.
Das bedeutet jedoch nicht, dass Cache-Kontexte immer von der Anfrage kommen müssen – sie können auch vom ausgeführten Code abhängen, z.B. vom Kontext deployment_id.
Zweitens sind Cache-Kontexte als Hierarchie beschrieben. Ein einfaches Beispiel: Wenn etwas für jeden Benutzer unterschiedlich ist, brauchen wir nicht nur Zugriffsrechte, denn es gibt Unterschiede pro Benutzer. Für Zugriffsrechte wird der Cache für jede Berechtigung gesetzt. Wenn ein Seitenabschnitt für jeden Benutzer unterschiedlich ist und ein anderer für Berechtigungen, muss Drupal klug genug sein, um Unterschiede nur pro Benutzer zu verwenden. Drupal nutzt also die Hierarchie-Information, um unnötige Cache-Variationen zu vermeiden.
Syntax
- Punkte trennen Elternelemente von Kinderelementen
- Ein pluraler Cache-Name zeigt an, dass ein Parameter verwendet werden kann: Nach einem Doppelpunkt wird der gewünschte Parameter angegeben (wenn kein Parameter angegeben ist, werden alle möglichen Parameter gesammelt, z.B. alle Query-Argumente)
Cache-Kontexte im Drupal 8 Core
Der Drupal 8 Core liefert folgende Cache-Kontext-Hierarchie:
cookies :name headers :name ip languages :type protocol_version // Ab Version 8.9.x verfügbar. request_format route .book_navigation .menu_active_trails :menu_name .name session .exists theme timezone url .path .is_front // Ab Version 8.3.x verfügbar. .parent .query_args :key .pagers :pager_id .site user .is_super_user .node_grants :operation .permissions .roles :role
Hinweis: Um den Cache-Kontext url.path.is_front in älteren Versionen zu verwenden, siehe Änderungsmitteilung.
Überall wo Cache-Kontexte verwendet werden, wird diese gesamte Hierarchie genutzt, was drei Vorteile bietet:
- Keine Mehrdeutigkeit: Es ist klar, worauf sich der übergeordnete Cache-Kontext bezieht, egal wo er verwendet wird.
- Vergleich (und Reduzierung) von Cache-Kontexten wird einfacher: Sind z.B. a.b.c und a.b vorhanden, so beinhaltet a.b automatisch a.b.c, sodass a.b.c weggelassen werden kann.
- Man muss nicht dafür sorgen, dass jeder Baumlevel im gesamten Baum eindeutig ist.
Beispiele aus der Hierarchie:
- theme (abhängig vom aktiven Theme)
- user.roles (abhängig von der Rollenkombination)
- user.roles:anonymous (abhängig davon, ob der aktuelle Nutzer die Rolle „anonym“ hat oder nicht, d.h. ob er anonym ist)
- languages (unterscheidet alle Sprachtypen: Interface, Inhalt ...)
- languages:language_interface (abhängig von der Interface-Sprache - LanguageInterface::TYPE_INTERFACE)
- languages:language_content (abhängig von der Inhaltssprache - LanguageInterface::TYPE_CONTENT)
- url (abhängig von der gesamten URL)
- url.query_args (abhängig von der gesamten Query-String)
- url.query_args:foo (abhängig vom Query-Argument foo)
- protocol_version (abhängig von HTTP 1 versus 2)
Optimierung / Zusammenfassung / Vereinfachung von Cache-Kontexten
Drupal nutzt automatisch die Hierarchie-Information, um Cache-Kontexte zu vereinfachen. Wenn sich z.B. ein Teil der Seite für den Nutzer ändert (Cache-Kontext user) und ein anderer für Berechtigungen (Cache-Kontext user.permissions), macht es keinen Sinn, das Endergebnis für Berechtigungen zu ändern, da Berechtigungsänderungen pro Benutzer variieren.
Anders gesagt: optimize([user, user.permissions]) = [user].
Obwohl user.permissions spezifischer ist, optimiert Drupal user.permissions so, dass Änderungen an Berechtigungen nicht dazu führen, dass user.permissions für jede Seite geladen wird. Das heißt, auch wenn sich Berechtigungen ändern, wird dieselbe gecachte Version weiterverwendet, obwohl sie sich ändern sollte.
Deshalb können Cache-Kontexte, die von sich ändernder Konfiguration abhängen, Cache-Metadaten binden: Cache-Tags und max-age. Wenn so ein Cache-Kontext optimiert wird, werden seine Cache-Tags mit dem Cache-Element assoziiert. Somit wird der Cache ungültig, sobald die zugewiesenen Berechtigungen sich ändern.
(„Caching“ bedeutet grundsätzlich „Vermeidung unnötiger Berechnungen“. Daher kann die Kontext-Optimierung als Caching des Ergebnisses der getContext()-Methode eines CacheContext-Services gesehen werden. Dies ist ein implizites Caching (Wert wird verworfen statt gespeichert), aber der Effekt ist derselbe: Beim Cache-Hit wird getContext() nicht aufgerufen, Berechnungen werden vermieden. Und da wir Dinge cachen, verknüpfen wir deren Cachebarkeit, weshalb wir bei Cache-Kontexten Tags und max-age binden.)
Ein komplexeres Beispiel sind node grants. Diese sind benutzerspezifisch, daher hat der Cache-Kontext der Node-Berechtigungen die Form user.node_grants. Allerdings sind Node-Berechtigungen oft sehr dynamisch (z.B. zeitabhängig, wechseln alle paar Minuten), abhängig von den node grant Hooks der Seite. Daher ist es besser, für diesen Cache-Kontext max-age = 0 zu setzen, d.h. er ist nicht cachebar (nicht optimiert). Daraus folgt: optimize([user, user.node_grants]) = [user, user.node_grants].
Einige Seiten können die Standard-Implementierung des Node Permissions Cache-Kontexts überschreiben und stattdessen max-age = 3600 setzen, wenn alle node grant Hooks erlauben, die Zugriffsresultate bis zu einer Stunde zu cachen. In diesem Fall gilt: optimize([user, user.node_grants]) = [user].
Wie erkennt, definiert und erstellt man Cache-Kontexte?
Cache-Kontexte sind Services mit dem Tag cache.context. Daher kann jedes Modul Cache-Kontexte hinzufügen. Sie implementieren \Drupal\Core\Cache\Context\CacheContextInterface oder \Drupal\Core\Cache\Context\CalculatedCacheContextInterface (für Cache-Kontexte mit Parametern, d.h. mit Suffix :parameter).
Um alle verfügbaren Cache-Kontexte zu finden, suchen Sie einfach alle Implementierungen von CacheContextInterface und CalculatedCacheContextInterface. (In PHPStorm: Typ-Hierarchie → Untertyp-Hierarchie; in NetBeans: Rechtsklick auf Interface → Verwendung suchen → Alle Untertypen finden.)
Alternativ können Sie die Drupal-Konsole nutzen (drupal debug:cache:context), um alle aktuellen Cache-Kontexte Ihrer Site oder Anwendung anzuzeigen:
$ 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
In jeder dieser Klassen finden Sie Kommentare wie bei \Drupal\Core\Cache\Context\UserCacheContext:
Cache context ID: 'user'.
Das bedeutet, dass 'user' der tatsächliche Cache-Kontext ist, den Sie im Code angeben können. (Alternativ können Sie in der *.services.yml-Datei nachschauen, wo diese Klasse als Service registriert ist und den Service-Identifier ansehen.)
Tipp: Eine aktuelle vollständige Liste aller Core Cache-Kontexte erhalten Sie, indem Sie alle Services mit dem Tag cache_context anschauen!
Der Service-Identifier ist standardisiert. Er beginnt immer mit cache_context., gefolgt von den Eltern des Cache-Kontexts und dem Namen des Cache-Kontexts. Zum Beispiel:
cache_context.route.book_navigation: class: Drupal\book\Cache\BookNavigationCacheContext arguments: ['@request_stack'] tags: - { name: cache.context }
Dies definiert den Cache-Kontext route.book_navigation.
Debugging
Die oben genannten Informationen sind hilfreich beim Debuggen von gecachten Inhalten. Ein weiterer wichtiger Punkt: Angenommen, etwas wird mit Cache-Keys ['foo', 'bar'] und Cache-Kontext ['languages:language_interface', 'user.permissions', 'route'] gecacht. Dann wird das Cache-Element mit folgender Cache-ID (CID) im Cache-Container gespeichert:
foo:bar:[languages:language_interface]=en:[user.permissions]=A_QUITE_LONG_HASH:[route]=myroute.ROUTE_PARAMS_HASH
Das bedeutet:
- Cache-Schlüssel werden in der angegebenen Reihenfolge zuerst aufgelistet
- Cache-Kontexte werden alphabetisch sortiert und als Teile der CID in der Form [<cache context name>]=<cache context value> angehängt
- Alle Teile der CID sind durch Doppelpunkte getrennt
Das erleichtert die Analyse und das Debuggen von Caches.
Header (Debugging)
Zuletzt: Es ist einfach ersichtlich, von welchen Cache-Kontexten eine Antwort abhängt (und somit variiert), indem man nur den Header X-Drupal-Cache-Contexts
betrachtet!
Hinweis: Wenn Sie diese Header nicht sehen, müssen Sie Drupal für die Entwicklungsumgebung konfigurieren.
Dynamischer Seiten-Cache
Die umfassende Nutzung von Cache-Kontexten in Drupal 8 ermöglicht es, dass Drupal 8 mit aktiviertem dynamischem Seiten-Cache (früher „Smart Cache“ genannt) ausgeliefert wird.
Interner Seiten-Cache
Beachten Sie, dass der interne Seiten-Cache davon ausgeht, dass alle Seiten, die anonymen Nutzern bereitgestellt werden, identisch sind, unabhängig von den implementierten Cache-Kontexten. Wenn Sie Cache-Kontexte verwenden möchten, um Inhalte für anonyme Nutzer zu variieren, muss dieses Modul deaktiviert werden, was die Leistung beeinträchtigen kann.
Siehe auch
Drupal’s online documentation is © 2000-2020 by the individual contributors and can be used in accordance with the Creative Commons License, Attribution-ShareAlike 2.0. PHP code is distributed under the GNU General Public License.