Eine optimale Performance ohne Abstriche bei der Dynamik und Aktualität eines CMS. Wie Drupal 8 das ewige Problem der Cache-Invalidierung löst und damit für schnellere Ladezeiten auf Newsportalen sorgt.
Das Problem: Aktualität versus Performance
Die typische Startseite eines Newsportals hat unzählige Artikel. Top-News, verschiedene Ressorts und Regionen, Meistgelesen und Meistkommentiert, Bildergalerien, Videos und so weiter. Alle diese News zu laden und darzustellen braucht Zeit und die Seite wird langsam. Bei den meisten Anfragen werden jedoch die genau gleichen Artikel ausgegeben.
Also werden identische Bereiche in einem Cache zwischengespeichert, damit sie bei der nächsten Anfrage wieder verwendet werden können. Das führt dazu, dass die Seite schneller aufgebaut ist. Bei der folgenden Abbildung können die einzelnen Artikel, wie auch die Regionen Sport und Wirtschaft sowie die ganze Seite im Cache gespeichert werden.
Das führt jedoch zum nächsten Problem. Wird ein neuer Artikel veröffentlicht oder aktualisiert, sollen die Leser das so schnell wie möglich sehen. Gibt es einen Banküberfall in der Region, gilt es, schneller darüber zu berichten als die Konkurrenz.
Häufig werden solche Caches zeitbasiert umgesetzt. Eine Seite wird dann z.B. nur alle 15 minuten einmal aufgebaut, wird dann aber auch innerhalb 15 Minuten unverändert und ggf. veraltet ausgeliefert. Alternativ müssen die Caches automatisch invalidiert werden, wenn neue Inhalte verfügbar sind.
Drupal 7 hat damit Mühe. Der sogenannte Page Cache, der komplett aufgebaute Seiten speichert, wird mit jeder neuen oder geänderten News standardmässig gelöscht und muss wieder komplett aufgebaut werden. Spezifische Invalidierungen benötigen viel Code oder komplexe Konfiguration und detailliertes Verständnis von internen Prozessen.
Die Lösung in Drupal 8
Drupal 8 bringt eine Reihe von Werkzeugen, mit denen diese Probleme wesentlich effizienter gelöst werden können.
Render Cache
Ein solches Werkzeug ist der sogenannte Render Cache. Jeder dargestellte Artikel wird automatisch als eigenständiges Element im Cache gespeichert. Views und Blöcke, die diese Artikel auflisten, werden ebenfalls im Cache gespeichert. Die ganze Seite wird dann zusätzlich im Page Cache gespeichert und lässt sich auch in Varnish oder einem CDN wie Fastly cachen.
Cache Kontexte und Cache Tags
Den entscheidenden Unterschied im Vergleich zu Drupal 7 machen aber zwei neue Konzepte, die Cache Tags und Cache Kontexte.
Kontexte ermöglichen es, verschiedene Cache-Variationen der Ausgabe eines Artikels zu verwalten. Ein Inhalt muss nur deklarieren, welche Kontexte einen Einfluss haben können auf die Ausgabe, den Rest übernimmt Drupal. Ein Beispiel sind unterschiedliche Berechtigungen. Einem Editor soll ein Direkt-Link zum Editieren angezeigt werden, einem normalen Leser nicht. Also werden die Caches nach den Benutzer-Berechtigungen variiert. Ein weiteres Beispiel ist die Zeitzone: Das Artikel-Datum ist in einer anderen Zeitzone anders, der Artikel muss also pro Zeitzone variiert werden.
Tags werden eingesetzt um Einträge im Cache zu invalidieren. Jeder Artikel hat beispielsweise einen Cache Tag. Tags und Kontexte sind “sticky”. Wird ein Artikel in einer Region dargestellt, wird auch der Cache des Blocks und der Seite getaggt. In der folgenden Abbildung hat Sport und Wirtschaft die Tags für die jeweils enthaltenen Artikel sowie den speziellen List-Tag, welcher bei jeder Artikel-Änderung invalidiert wird.
Drupal 8 weiss dadurch, wo ein Artikel dargestellt wird. Es ist nicht mehr nötig, alle Seiten zu invalidieren, wenn ein Artikel ändert, sondern nur die spezifischen Seiten, die diesen Artikel darstellen.
Fastly unterstützt ebenfalls Cache Tags. Die Cache Tags werden via HTTP Header an Fastly weitergegeben. Wird ein Artikel aktualisiert, wird das über eine API an Fastly gemeldet, und alle Seiten, die diesen Artikel anzeigen, werden wieder von Drupal angefragt.
Umsetzung und Optimierungen für NP8
Standardmässig hat Drupal 8 im Moment jedoch nur einen Cache Tag für “Liste von Inhalten”, der immer invalidiert wird, sobald ein Artikel erstellt oder geändert wird. Das ist für NP8 zu generisch und invalidiert immer noch zuviel. Wir haben eine Reihe von NP8 spezifischen Cache Tags definiert, z.b. “News im Ressort Wirtschaft in den letzten 3 Tagen”, oder “Breaking News”. Diese Tags können dann in den Views verwendet werden, damit nur die Listen invalidiert werden, die den Artikel auch wirklich anzeigen.
Damit müssen die Caches nicht mehr zeitbasiert invalidiert werden. Praktisch alle Seiten können beliebig lange Zeit von Fastly gespeichert und ausgeliefert werden. Bei einer relevanten Änderung werden sie trotzdem innerhalb von Sekunden aktualisiert.
NP8 verwendet als Referenz-Hosting platform.sh. Sämtliche benötigten Tools stehen bei diesem Hosting zur Verfügung. Mit aktuellem Cache ist die resultierende Performance für anonyme Benutzer vergleichbar mit der einer statischen HTML-Seite und trotzdem ist der Funktionsumfang des CMS Drupal in keiner Weise eingeschränkt.
Beispiel
Alle Inhaltsregionen sind derzeit im Cache (grün). Jetzt wird ein neuer Artikel (Artikel 7) erstellt im Ressort Sport. Dadurch werden die List-Tags node_list und node:sport (NP8 spezifisch) invalidiert. Da Artikel 7 neu ist, gibt es noch keinen node:7 Tag der invalidiert werden müsste. Dadurch wird die Startseite und die Sport-Region invalidiert, nicht jedoch Wirtschaft.
Zusätzlich wird auch noch Artikel 2 bearbeitet und somit zusätzlich zu den List-Tags auch noch node:2 invalidiert.
Beim nächsten Aufruf der Startseite wird beim Laden der entsprechenden Cache-Einträge festgestellt, dass die Startseite sowie Sport invalidiert wurde (rot), und sie werden neu aufgebaut. Sport stellt jetzt Artikel 7, 1 und 2 dar, 3 verschwindet aus der Übersicht. Für Artikel 7 gab es noch keinen Eintrag im Cache (gelb), 2 wurde invalidiert, aber Artikel 1 kann immer noch aus dem Cache dargestellt werden, da sich zwar die Position, nicht aber der Inhalt verändert hat.
Genau gleich wie dieses vereinfachte Beispiel funktionieren auch beliebig komplexe Übersichtsseiten mit 10 Regionen und Dutzenden von Artikeln. Änderungen führen nur zu einzelnen Invalidierungen und der Grossteil der Seite muss nicht neu aufgebaut werden.
Limitationen
Enthält die Darstellung von Artikeln etwas das vom Benutzer abhängig ist, dann funktioniert der Ansatz mit den Cache Kontexten nicht mehr, bzw. entstehen soviele Kontexte, dass es nicht mehr effizient ist. Bei NP8 sind dies Paywall-Informationen, die dem Benutzer anzeigen, ob er bestimmte Artikel lesen darf oder nicht. Solche Probleme können zum Beispiel mit Javascript gelöst werden. Ein separater Blogeintrag wird detaillierter darauf eingehen.
Links / Read More
D8 Cacheablity: https://www.drupal.org/developing/api/8/render/arrays/cacheability
Cache contexts: https://www.drupal.org/developing/api/8/cache/contexts
NP8 List Cache Tags: https://github.com/md-systems/views_custom_cache_tag