Hyvä Block-Cache-Strategie: Cache-Tags richtig setzen ohne den Full Page Cache zu brechen
AI generated
Hyvä
phtml
Hyvä · Block-Cache · Full Page Cache · Magento 2
Hyvä Block-Cache-Strategie für Templates
cache_lifetime und getIdentities() richtig kombiniert

Block-Cache und Full Page Cache sind zwei unabhängige Cache-Ebenen, und wer sie verwechselt, produziert entweder Daten-Lecks zwischen Kunden oder einen Cache, der bei jeder Kleinigkeit komplett neu aufgebaut wird. Diese Hyvä Block-Cache-Strategie zeigt, wie cache_lifetime, getIdentities() und gezielte Invalidierung in Magento 2.4.8 mit PHP 8.4 sauber zusammenspielen, ohne den Full Page Cache zu brechen.

16 Min. Lesezeit cache_lifetime · getIdentities() · Cache-Tags · Invalidierung Magento 2.4.8 · Hyvä Themes · PHP 8.4

1. Block-Cache vs. Full Page Cache: zwei Cache-Ebenen, die man nicht verwechseln darf

Eine belastbare Hyvä Block-Cache-Strategie beginnt nicht mit der Frage, wie stark man cachen sollte, sondern mit einer sauberen Trennung von zwei Ebenen, die in der Praxis ständig durcheinandergeworfen werden: dem Full Page Cache und dem Block-Cache. Der Full Page Cache speichert die komplette gerenderte HTML-Antwort, geschlüsselt über URL, Store, Währung und Kundengruppe. Bei einem Cache-Hit wird die Antwort direkt aus Varnish oder dem eingebauten FPC-Backend ausgeliefert, bevor Magentos Blocksystem für diesen Request überhaupt anläuft. Der Block-Cache arbeitet eine Ebene darunter: Er cacht das Ergebnis einzelner Blöcke während des eigentlichen Renderdurchlaufs, gesteuert pro Block über cache_lifetime und Cache-Tags.

Genau hier entfaltet eine durchdachte Block-Cache-Strategie ihren Nutzen, und zwar in zwei Szenarien. Erstens: Der Aufbau oder die Erneuerung eines Full-Page-Cache-Eintrags kostet Zeit proportional zum kompletten Blockbaum, und ein gut konfigurierter Block-Cache beschleunigt genau diesen Regenerationsprozess, nachdem ein FPC-Eintrag abgelaufen ist oder per Tag invalidiert wurde. Zweitens: Auf Seiten, die grundsätzlich nicht öffentlich cachefähig sind, etwa das Kundenkonto, der Warenkorb oder der Checkout, greift der Full Page Cache überhaupt nicht, weil Magento diese Layouts explizit als nicht cachefähig markiert. Auf genau diesen Seiten ist der Block-Cache die einzige Schutzschicht zwischen dem Request und teuren Blöcken wie Navigation, Footer-CMS-Block oder Währungsumschalter.

Ohne bewusste Block-Cache-Strategie gehen Entwickler oft davon aus, dass mit aktiviertem Full Page Cache automatisch alles gecacht ist, und lassen cache_lifetime auf dem Standardwert null stehen. Ein Mega-Menü mit mehreren Kategorie-Abfragen wird dadurch bei jedem Aufruf einer nicht-cachefähigen Seite wie dem Checkout vollständig neu berechnet, obwohl sich die Kategoriestruktur seit Tagen nicht geändert hat.

2. cache_lifetime setzen: welche Blöcke statisch sind, welche niemals gecacht werden dürfen

Der zentrale Hebel jeder Block-Cache-Strategie ist die Methode getCacheLifetime(), die jede von \Magento\Framework\View\Element\AbstractBlock abgeleitete Klasse überschreiben kann. Gibt sie null zurück, verweigert Magentos _loadCache()-Logik jeden Cache-Zugriff für diesen Block, er wird bei jedem Request vollständig neu gerendert. Ein Integer definiert stattdessen die Time-to-live in Sekunden, nach der ein gecachter Eintrag als veraltet gilt, selbst wenn keine Tag-Invalidierung stattgefunden hat. Statische Blöcke wie die Footer-Navigation, ein im Header eingebetteter CMS-Static-Block oder der Sprach- und Währungsumschalter sind gute Kandidaten für einen expliziten Wert wie 86400, da sie sich unabhängig von Kunde oder Session kaum ändern.

Die eigentliche Gefahr einer unbedachten Block-Cache-Strategie liegt in der Kombination aus cache_lifetime und einem unvollständigen Cache-Key. Magentos getCacheKeyInfo() bestimmt, welche Dimensionen den Cache-Schlüssel eines Blocks beeinflussen, etwa Store-ID oder Kundengruppen-ID. Fehlt in diesem Array eine relevante Dimension, während der Block trotzdem einen positiven cache_lifetime zurückgibt, wird ein und derselbe gecachte HTML-Fragment für Requests ausgeliefert, die eigentlich unterschiedliche Inhalte zeigen müssten. Die Grundregel lautet deshalb: cache_lifetime nur für Blöcke setzen, deren getCacheKeyInfo() jede Varianzquelle vollständig abbildet, niemals aber Kunden- oder Session-ID in den Key aufnehmen, da das den Cache faktisch pro Kunde dupliziert und den Sinn des Block-Cache umkehrt.

3. Cache-Tags über getIdentities(): gezielte Invalidierung statt globalem Cache-Flush

Cache-Tags sind der zweite Baustein jeder Block-Cache-Strategie und werden vollständig unabhängig vom Cache-Key über getIdentities() gesteuert. Diese Methode liefert ein Array von String-Tags, das Magentos Full-Page-Cache-Modul Magento_PageCache nutzt, um zu wissen, welche gecachten Seiten oder Blöcke zu welcher Entität gehören. Wird ein Produkt gespeichert, sammelt ein Core-Observer die zugehörigen Tags wie cat_p_123 und ruft darüber gezielt die Invalidierung genau der Seiten und Fragmente auf, die dieses Tag tragen, während alles andere unangetastet im Cache bleibt.

Eigene Blöcke, die \Magento\Framework\DataObject\IdentityInterface implementieren, müssen granulare, aussagekräftige Tags zurückgeben, etwa mironsoft_badge_ gefolgt von der Produkt-ID, niemals aber ein leeres Array oder den bloßen Klassennamen des Blocks. Ein leeres Array bedeutet, dass der Block praktisch nie über eine Entitäts-Speicherung invalidiert wird und bis zum manuellen Cache-Flush veraltete Inhalte ausliefert. Ein zu generisches Tag, etwa die Wiederverwendung des globalen Produkt-Cache-Tags für jedes Produkt unabhängig von der tatsächlichen Beziehung, invalidiert bei jeder einzelnen Produktspeicherung deutlich mehr Seiten als nötig, was in Abschnitt 8 vertieft wird.


<?php

declare(strict_types=1);

namespace Mironsoft\CatalogBadge\Block;

use Magento\Catalog\Model\Product;
use Magento\Framework\DataObject\IdentityInterface;
use Magento\Framework\View\Element\Template;
use Magento\Framework\View\Element\Template\Context;

/**
 * Product badge block that must invalidate whenever stock or price changes.
 */
class ProductBadge extends Template implements IdentityInterface
{
    /**
     * Cache tag prefix used for this block's identities.
     */
    private const CACHE_TAG = 'mironsoft_badge';

    /**
     * @param Context $context Block context injected by the framework.
     * @param Product $product Product the badge is rendered for.
     * @param array $data Additional block data.
     */
    public function __construct(
        Context $context,
        private readonly Product $product,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    /**
     * Static blocks can use a long TTL, this block relies on tag invalidation instead.
     *
     * @return int Cache lifetime in seconds.
     */
    public function getCacheLifetime(): int
    {
        // One day is a safe upper bound; getIdentities() invalidates earlier when needed.
        return 86400;
    }

    /**
     * Returns the exact cache tags this block depends on.
     *
     * @return string[] Cache identity tags for targeted invalidation.
     */
    public function getIdentities(): array
    {
        return [
            self::CACHE_TAG . '_' . $this->product->getId(),
            Product::CACHE_TAG . '_' . $this->product->getId(),
        ];
    }
}

4. Personalisierte Blöcke: Private Content und Customer Data Sections statt Block-Cache

Personalisierte Inhalte gehören grundsätzlich nicht in eine Block-Cache-Strategie, sondern in Magentos Private-Content-Mechanismus aus dem Modul Magento_PageCache. Der Full Page Cache speichert eine Seite als öffentliches HTML-Fragment mit Platzhaltern für private Daten wie Kundenbegrüßung, Warenkorb-Zähler oder Wunschlisten-Anzahl. Im Browser lädt customer-data.js diese Abschnitte per Ajax nach und ersetzt die Platzhalter clientseitig, gesteuert über customer_sections.xml, die festlegt, welche Section bei welcher Controller-Aktion invalidiert wird. Dieser Mechanismus existiert genau deshalb, damit Personalisierung niemals über cache_lifetime oder getIdentities() gelöst werden muss.

Ein verbreitetes Anti-Pattern: Statt personalisierte Fragmente über Private-Content-Sections zu führen, setzt ein Entwickler cache_lifetime auf null für den gesamten Mini-Cart-Block. Das verhindert zwar veraltete Daten, wirft aber jeden Cache-Vorteil für diesen Block weg. Schlimmer ist die umgekehrte Variante: Personalisierte Daten landen versehentlich im gecachten, öffentlichen HTML, weil ein Block mit positivem cache_lifetime auf Kundendaten zugreift, die im Cache-Key nicht abgebildet sind, was zu einem echten Daten-Leck zwischen Kunden führt.


<!-- app/design/frontend/Mironsoft/default/Magento_Customer/layout/customer_account.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <!-- The whole account dashboard is not publicly cacheable, so block cache is the only cache layer here -->
    <body>
        <referenceContainer name="content">
            <block class="Mironsoft\CatalogBadge\Block\CustomerGreeting"
                   name="customer.greeting"
                   template="Mironsoft_CatalogBadge::customer/greeting.phtml"
                   cacheable="false">
                <!-- Personalized data never gets a positive cache_lifetime -->
            </block>
        </referenceContainer>
    </body>
</page>

5. ViewModel-Caching-Fallen: warum ViewModels selbst nicht gecacht werden

Eine der häufigsten Fallen in einer modernen Hyvä Block-Cache-Strategie betrifft ViewModels. Ein ViewModel, das über ArgumentInterface injiziert wird, ist ein einfaches PHP-Objekt und keine Block-Instanz. Es implementiert weder getCacheLifetime() noch getIdentities() noch IdentityInterface und wird vom Block-Cache-Subsystem überhaupt nicht berührt. Die Konsequenz: "Der umgebende Block ist gecacht" und "die Berechnung im ViewModel ist teuer" sind zwei unabhängige Tatsachen. Bei einem Cache-Hit läuft das ViewModel schlicht nicht, weil das komplette Block-HTML direkt aus dem Cache kommt. Bei jedem Cache-Miss, sei es beim ersten Rendering, nach Ablauf der TTL oder nach Tag-Invalidierung, führt das ViewModel seine Berechnung dagegen mit voller Kosten erneut aus, ohne eigenes Caching.

Für teure ViewModel-Berechnungen, die an einen Block mit definierter Block-Cache-Strategie gekoppelt sind, etwa die Aggregation von Preisspannen für ein konfigurierbares Produkt, lohnt sich eine eigene Caching-Schicht direkt im ViewModel über \Magento\Framework\App\CacheInterface, mit explizitem Schlüssel aus Produkt-ID, Store-ID und Kundengruppen-ID sowie eigenen Tags, unabhängig vom cache_lifetime des Blocks. Das schafft eine Caching-Ebene, die selbst bei einem Block-Cache-Miss noch greift, was reines Block-Level-Caching nicht leisten kann.

Genauso wichtig ist die Kehrseite: Liest ein ViewModel innerhalb eines Blocks mit positivem cache_lifetime Daten aus \Magento\Customer\Model\Session oder einer anderen besucherspezifischen Quelle, landen diese Daten dauerhaft im gecachten HTML und werden an fremde Kunden ausgeliefert, exakt die gleiche Fehlerklasse wie in Abschnitt 2, nur über das ViewModel statt über eine Block-Eigenschaft. Die Lösung ist identisch: cache_lifetime größer null niemals mit einem ViewModel kombinieren, das Session- oder Kundendaten liest, die nicht im Cache-Key abgebildet sind.

6. Cache-Tag-Vererbung zwischen Eltern- und Kind-Blöcken in Layout-XML

Cache-Tags und cache_lifetime vererben sich in Magentos Blockbaum nicht automatisch, und genau das ist der Teil jeder Block-Cache-Strategie, der Entwickler am häufigsten überrascht. Layout-XML baut über verschachtelte referenceContainer- und referenceBlock-Deklarationen einen Eltern-Kind-Baum aus Blöcken auf. Rendert ein Elternblock mit gesetztem cache_lifetime, ruft er rekursiv toHtml() für alle Kinder auf und cacht das kombinierte HTML als eine einzige Einheit. Das eigene cache_lifetime und getIdentities() eines Kindblocks werden für diesen Render-Vorgang praktisch bedeutungslos, weil die Cache-Entscheidung des Elternblocks bestimmt, was gespeichert wird.

Steckt ein tatsächlich dynamischer Kindblock, etwa ein Warenkorb-Zähler oder ein sitzungsabhängiger Upsell-Hinweis, verschachtelt in einem Elternblock mit gesetztem cache_lifetime, friert der dynamische Inhalt beim ersten Rendering im gecachten HTML des Elternblocks ein. Das korrekte Pattern ist deshalb, echte dynamische Unterblöcke entweder komplett außerhalb der Cache-Grenze des gecachten Elternblocks zu platzieren, etwa in einem eigenen, nicht gecachten Geschwister-Container, oder sie konsequent über Private-Content-Sections aus Abschnitt 4 zu führen, statt sich darauf zu verlassen, dass der Blockbaum das richtige Cache-Verhalten von selbst weiterreicht.


<!-- app/design/frontend/Mironsoft/default/Magento_Theme/layout/default.xml -->
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <!-- Wrong: a cached parent silently freezes the dynamic child's output -->
        <referenceContainer name="header.container">
            <block class="Mironsoft\CatalogBadge\Block\HeaderWrapper"
                   name="header.wrapper.cached"
                   template="Mironsoft_CatalogBadge::header/wrapper.phtml">
                <!-- getCacheLifetime() returns 86400 on this block -->
                <block class="Mironsoft\CatalogBadge\Block\CartCounter"
                       name="cart.counter.dynamic"
                       template="Mironsoft_CatalogBadge::header/cart-counter.phtml"/>
            </block>
        </referenceContainer>

        <!-- Correct: the dynamic block sits outside the cached parent's toHtml() boundary -->
        <referenceContainer name="header.container">
            <block class="Mironsoft\CatalogBadge\Block\HeaderWrapper"
                   name="header.wrapper.cached"
                   template="Mironsoft_CatalogBadge::header/wrapper.phtml"/>
            <block class="Mironsoft\CatalogBadge\Block\CartCounter"
                   name="cart.counter.dynamic"
                   template="Mironsoft_CatalogBadge::header/cart-counter.phtml"
                   cacheable="false"/>
        </referenceContainer>
    </body>
</page>

7. Debugging: X-Magento-Cache-Debug-Header und Cache-Tags in Varnish und Redis prüfen

Ohne Werkzeuge bleibt jede Block-Cache-Strategie ein Ratenspiel. Magento und die vorgelagerte Cache-Schicht liefern aber brauchbare Debugging-Signale. Der X-Magento-Cache-Debug-Header, aktivierbar unter Stores > Configuration > Advanced > System > Full Page Cache, meldet HIT oder MISS für die Full-Page-Cache-Ebene pro Request. Ein einfacher curl -I gegen eine Kategorie- oder CMS-URL zeigt sofort, ob der FPC überhaupt gegriffen hat, bevor man sich um Block-Level-Tags kümmert.

Der Block-Cache selbst hat kein HTTP-Header-Äquivalent, da er die HTTP-Ebene nie direkt erreicht: Er besteht aus PHP-generierten HTML-Fragmenten im konfigurierten Cache-Backend, in einem Magento-2.4.8-Setup meist Redis. Praktisch prüft man das über redis-cli im Container, etwa mit SCAN gegen die im env.php-Abschnitt cache/frontend konfigurierte Datenbank, oder mit einem temporären Debug-Plugin auf AbstractBlock::_loadCache() und _saveCache(), das Block-ID, Tag-Liste und gespeicherte TTL pro Request protokolliert, niemals aber direkt im Core verändert.


# Check whether the full page cache layer served this request from cache
curl -sI https://shop.example.com/damen/kleider.html | grep -i x-magento-cache-debug
# X-Magento-Cache-Debug: HIT

# Inspect Redis-backed block cache entries via the docker-magento wrapper
bin/cli redis-cli -n 1 --scan --pattern "*mironsoft_badge*"

# Flush only a specific cache type instead of the whole full page cache
bin/magento cache:clean full_page

8. Häufiger Fehler: zu aggressive oder zu grobe Cache-Tags brechen den Full Page Cache

Der häufigste strukturelle Fehler in einer schlecht durchdachten Block-Cache-Strategie ist nicht zu wenig, sondern zu grobes Caching. Zwei symmetrische Fehlermuster treten in der Praxis ständig auf. Zu grobe Tags: Ein Block oder Observer nutzt ein einziges, breites Tag oder verlässt sich auf die nicht überschriebene Standard-Identity, wodurch jedes unabhängige Save-Event weit mehr gecachte Seiten und Blöcke invalidiert als nötig. Die Korrektur eines Tippfehlers in einer einzigen Produktbeschreibung löscht dann versehentlich jede Kategorieseite, die irgendein Produkt referenziert, aus dem Cache.

Zu aggressive Invalidierung: Ein eigener Observer auf catalog_product_save_after ruft CacheInterface::clean() mit dem Typ full_page auf oder, schlimmer, \Magento\Framework\App\Cache\Manager::flush(['full_page']), statt nur die spezifischen Tags des gespeicherten Produkts zu löschen. Das Ergebnis: Jeder Besucher auf jeder Seite trifft gleichzeitig auf einen kalten Cache, was direkt nach jeder Admin-Speicherung einen sichtbaren TTFB-Ausschlag erzeugt, unter Last teilweise so stark, dass es wie ein kurzer Ausfall aussieht. Die Lösung in beide Richtungen ist dieselbe Disziplin: Identities müssen so eng wie die tatsächliche Abhängigkeit sein, und Invalidierung läuft immer über CacheInterface::clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, $tags) mit der exakten Tag-Liste, niemals über einen pauschalen Typ-Flush, es sei denn, eine wirklich globale Änderung erfordert das tatsächlich.

9. Best Practices für eigene Module: eigene Cache-Tags registrieren und gezielt invalidieren

Eigene Module brauchen eine bewusste Block-Cache-Strategie von Anfang an, nicht als nachträglichen Fix. Beim Bau eines eigenen Blocks für Hyvä, mit PHP 8.4 und Constructor Property Promotion, der \Magento\Framework\View\Element\Template erweitert und \Magento\Framework\DataObject\IdentityInterface implementiert, definiert man eine stabile Tag-Präfix-Konstante wie const CACHE_TAG = 'mironsoft_badge'; und baut getIdentities() aus diesem Präfix plus der relevanten Entitäts-ID, nach dem gleichen Muster wie Magentos eigenes Product::CACHE_TAG. Ein passender Observer auf das relevante *_save_after-Event löst exakt die betroffenen Tags auf und ruft das injizierte CacheInterface::clean() ausschließlich mit diesen Tags auf.

Hängt die Invalidierung von mehr als der Entitäts-ID selbst ab, etwa bei einem lagerbestandsgetriebenen Badge, das invalidiert werden muss, sobald die verkaufbare Menge eines Produkts einen Schwellenwert unterschreitet, hakt man sich gezielt in ein passendes Event wie cataloginventory_stock_item_save_after ein, statt sich an catalog_product_save_after anzuhängen, und löst innerhalb dieses Observers genau die betroffenen Produkt- und Kategorie-Tags auf, bevor clean() aufgerufen wird. So bleibt eine wachsende Zahl von Modulen komponierbar, ohne dass eines von ihnen versehentlich zum Grund wird, warum die gesamte Block-Cache-Strategie in globale Full-Page-Flushes abrutscht.


<?php

declare(strict_types=1);

namespace Mironsoft\CatalogBadge\Observer;

use Magento\Catalog\Model\Product;
use Magento\Framework\App\CacheInterface;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

/**
 * Invalidates only the cache tags affected by a saved product, never the whole full page cache.
 */
class InvalidateProductBadgeCache implements ObserverInterface
{
    /**
     * @param CacheInterface $cache Framework cache used for targeted tag cleaning.
     */
    public function __construct(
        private readonly CacheInterface $cache
    ) {
    }

    /**
     * Cleans exactly the tags belonging to the saved product's badge block.
     *
     * @param Observer $observer Event observer carrying the saved product.
     * @return void
     */
    public function execute(Observer $observer): void
    {
        /** @var Product $product */
        $product = $observer->getEvent()->getProduct();

        // Targeted invalidation: only this product's badge tag is cleaned.
        $this->cache->clean(
            \Zend_Cache::CLEANING_MODE_MATCHING_TAG,
            ['mironsoft_badge_' . $product->getId()]
        );
    }
}

Entscheidungen im direkten Vergleich: Die folgende Übersicht fasst die wichtigsten Weichenstellungen einer Block-Cache-Strategie zusammen und zeigt, welches Pattern in der Praxis tatsächlich funktioniert.

Aufgabe Falsch Empfohlenes Pattern Effekt
cache_lifetime setzen null für statische Blöcke belassen Expliziten TTL setzen, z.B. 86400 Kein unnötiges Rendering auf Nicht-FPC-Seiten
getIdentities() implementieren Leeres Array oder Klassenname als Tag Granulare Tags pro Entität, z.B. cat_p_123 Gezielte statt globale Invalidierung
Invalidierung nach Save Kompletten Full Page Cache flushen CacheInterface::clean() mit Tag-Liste Cache-Hit-Rate bleibt für unbeteiligte Seiten erhalten
Personalisierte Daten cache_lifetime auf personalisiertem Block Private Content / Customer Data Section Kein Daten-Leck zwischen Kunden
Kind-Block im gecachten Parent Dynamischen Block ungeprüft verschachteln Dynamische Blöcke aus der Cache-Grenze herausziehen Keine eingefrorenen Session-Daten im Cache

Mironsoft

Hyvä-Entwicklung, Cache-Audits und Magento-2-Betrieb

Block-Cache und Full Page Cache spielen nicht zusammen?

Wir analysieren cache_lifetime, getIdentities() und Cache-Tag-Invalidierung in eurem Hyvä-Shop und entwickeln eine belastbare Block-Cache-Strategie, die den Full Page Cache nicht bricht.

Cache-Tag-Audit

Analyse aller Block-Klassen auf cache_lifetime, getIdentities() und Daten-Leck-Risiken

Umsetzung

Gezielte Invalidierung über CacheInterface statt globalem Cache-Flush nach jedem Save

Monitoring

X-Magento-Cache-Debug, Redis-Tags und Varnish-Logs dauerhaft im Blick

10. Zusammenfassung

Eine wirksame Block-Cache-Strategie ist kein einzelner Schalter, sondern die konsequente Trennung mehrerer Verantwortlichkeiten: cache_lifetime nur für Blöcke mit vollständigem Cache-Key, granulare getIdentities()-Tags statt leerer Arrays oder globaler Klassennamen, Personalisierung ausschließlich über Private Content und Customer Data Sections, eigenes Caching im ViewModel für teure Berechnungen und eine bewusste Prüfung der Cache-Grenzen zwischen Eltern- und Kind-Blöcken in Layout-XML. Jede dieser Maßnahmen für sich verhindert eine bestimmte Fehlerklasse. Zusammen ergeben sie den Unterschied zwischen einem Full Page Cache, der zuverlässig trägt, und einem, der bei jeder kleinen Änderung komplett neu aufgebaut werden muss.

Der zweite entscheidende Baustein ist Sichtbarkeit: Ohne den X-Magento-Cache-Debug-Header, ohne einen Blick in die Redis-Cache-Keys und ohne die Disziplin, Invalidierung immer über gezielte Tags statt über pauschale Flushes durchzuführen, bleibt jede Block-Cache-Strategie Theorie. Erst die Kombination aus sauberer Implementierung und kontinuierlicher Kontrolle macht den Full Page Cache zu einem verlässlichen Fundament statt zu einer Fehlerquelle.

Hyvä Block-Cache-Strategie: Das Wichtigste auf einen Blick

cache_lifetime

Nur für Blöcke mit vollständigem Cache-Key setzen, niemals Kunden- oder Session-ID in den Key aufnehmen.

getIdentities()

Granulare Tags pro Entität statt leerem Array oder globalem Klassennamen.

Personalisierung

Private Content und Customer Data Sections statt cache_lifetime auf dynamischen Blöcken.

Debugging & Fehler

X-Magento-Cache-Debug prüfen, Invalidierung immer über CacheInterface::clean() mit exakter Tag-Liste.

11. FAQ: Block-Cache-Strategie

1Unterschied zwischen Full Page Cache und Block-Cache?
Full Page Cache speichert die komplette HTML-Antwort pro URL, Block-Cache cacht einzelne Blöcke über cache_lifetime und getIdentities().
2Wann cache_lifetime setzen?
Nur wenn der Block keine kundenspezifischen Daten zeigt und getCacheKeyInfo() jede Varianzquelle vollständig abbildet.
3Warum invalidiert mein Block nie?
getIdentities() liefert vermutlich ein leeres Array oder ein zu allgemeines Tag statt granularer Entitäts-Tags.
4Wie den Mini-Cart korrekt cachen?
Über Private Content und Customer Data Sections, per Ajax nachgeladen, nicht über cache_lifetime auf dem Block.
5Werden ViewModels mitgecacht?
Nein, ViewModels laufen bei jedem Cache-Miss neu, sofern keine eigene Caching-Schicht ergänzt wird.
6Dynamischer Block in gecachtem Parent?
Der Inhalt friert beim ersten Rendering ein. Dynamische Blöcke gehören außerhalb der Cache-Grenze des Eltern-Blocks.
7Wie prüfe ich den Full Page Cache?
Mit curl -I und dem X-Magento-Cache-Debug-Header, der HIT oder MISS meldet.
8Warum bricht ein globaler Flush die Performance?
Er löscht den kompletten Full Page Cache, wodurch jeder Besucher gleichzeitig einen kalten Cache trifft und die TTFB kurzzeitig ansteigt.
9Cache-Tags im eigenen Modul registrieren?
IdentityInterface implementieren, stabile Tag-Präfix-Konstante definieren und per Observer gezielt invalidieren.
10Reicht CLEANING_MODE_MATCHING_TAG immer aus?
Für fast alle Fälle ja, ein pauschaler Typ-Flush ist nur bei wirklich globalen Änderungen gerechtfertigt.