Drupal (Datenbank)-Cache und seine Tücken

Drupal hat seit Version 8 ein deutlich ausgefeilteres Caching-System als noch in Vorgängerversionen. Das funktioniert im Allgemeinen ziemlich gut und in den meisten Fällen (kleinere Websites, Websites mit überschaubarem Traffic) muss man sich groß keine Gedanken darüber machen.

In einer Standard-Drupalinstallation wird das Caching über die Datenbank abgewickelt. Hier gibt es diverse Tabellen (die alle mit cache_ im Namen starten) die für das Caching verantwortlich sind.

Für große bzw. traffic-starke Seiten empfiehlt Drupal andere Lösungen als den Datenbank-Cache (z. B. Memcached, Redis, Varnish, etc.). Diese Empfehlung wird zwar nicht allzu offensiv ausgesprochen, aber mindestens implizit und in entsprechend informierten Kreisen (Betreiber von großen Seiten) ist das auch hinlänglich bekannt.

Der Datenbank-Cache von Drupal hat(te) ein Problem mit mehr oder weniger unbegrenztem Wachstum der Cache-Tabellen in der Datenbank und damit einhergehend auch großen Speicherplatzanforderungen. Dem wurde mit einer Änderung in Drupal 8.4 Rechnung getragen. Hier wurde eine Limitierung der maximal in einer Cache-Tabelle möglichen Zeilen eingeführt. Dieses Limit liegt per Default bei 5000 Zeilen und kann über das Setting database_cache_max_rows gesteuert werden (auf Ebene des Cache insgesamt oder auch auf Ebene von Cache-Bins, was mehr oder weniger einzelnen Datenbank-Tabellen entspricht). Der Change Record dazu. Wenn man das Setting auf "-1" setzt ist die Limitierung ausgeschaltet und das entspricht der Einstellung vor der Einführung der Limitierung.

Die Implementierung dieses Mechanismus zur Limitierung der Zeilen im Datenbank-Cache kann allerdings in einem Setup mit "binlogs" zu einem Problem führen. Grob gesagt wird in den Datenbank-Binlogs jede Transaktion in der Datenbank (also primär INSERT, UPDATE, DELETE) gespeichert. Das erlaubt das nahezu lückenlose Wiederherstellen von Daten und wird auch für Replikation benötigt.

Die Implementierung des Mechanismus zur Limitierung der Zeilen im Datenbank-Cache basiert auf einem Drupal-Cron-Task. Jedes Mal wenn der Task ausgeführt wird, prüft Drupal, ob mehr Zeilen als über das Setting definiert, in den Cache-Tabellen gespeichert sind und wirft die überschüssigen Zeilen raus. Das erfolgt über ein DELETE-Statement und findet damit auch Niederschlag im binlog. Es ist sehr wahrscheinlich, dass die Cache-Tabellen nun immer wieder ein Stück über das Limit wachsen und mit dem Cron-Task wieder zurückgestutzt werden. Das bedeutet INSERT-Statement für das Einfügen in die Cache-Tabelle und DELETE-Statement um wieder zurück auf das Limit zu gelangen. Das ist ein Endlos-Kreislauf der sich in den binlogs verewigt. Das kann zu enormem Wachstum der binlogs führen und damit ähnliche Probleme (Speicherplatz-Mangel) auslösen wie das Verhalten des Datenbank-Caches vor Einführung des Zeilen-Limits.

Möchte oder kann man nun nicht auf ein alternatives Cache-Backend wechseln, kann es ggf. sinnvoll sein die Limitierung wieder aufzuheben (oder sehr hoch zu setzen) und den Cache in regelmäßigen Abständen über drush cr zu leeren. Der Cache Clear über drush bzw. Backend hat den Vorteil, dass hier ein TRUNCATE-Statement zum Einsatz kommt und dieses das binlog nicht belastet.

Besser ist natürlich ein alternatives Cache-Backend zu verwenden.

In einem konkreten Fall ergab sich folgendes Problem:

Das binlog ist sehr stark gewachsen (vermutlich auch ausgelöst durch verstärktes Crawling der Seiten durch neue AI-Bots) und machte eine schnelle Lösung erforderlich. Die Lösung war das Caching auf memcached umzustellen. Das löste das Problem mit der Größe der binlogs. Allerdings traten nun schwer zu reproduzierende Fehler beim Speichern von Inhalten auf. Es erwies sich, dass diese Fehler aber klar mit der Umstellung auf memached zusammenhingen. Nun begann eine relativ aufwändige Fehlersuche. Es ließ sich kein konkretes Problem in memcached erkennen. Dies führte dazu, dass nach einem Weg gesucht wurde das Caching so einzurichten, dass das Speicher-Problem umgangen werden konnte und gleichzeitig die Datenbank-Tabellen und die binlogs größenmäßig in einem definierten Rahmen blieben.

Es erwies sich, dass cache_page die mit Abstand größte Cache-Tabelle von der Größe auf der Festplatte ist. cache_page ist nur für das Caching für anonyme Benutzer zuständig und daher konnte cache_page nach Memcache ausgelagert werden (nur angemeldete Redakteure bearbeiten das System). Die anderen Cache-Tabellen wurden zurück in die Datenbank verlagert und das Limit auf unendlich gestellt. Danach wurde das Wachstum der Cache-Tabellen beobachtet und ermittelt, dass ein Cache Clear alle zwei Wochen ausreichen würde um die Datenbank-Cache-Tabellen größenmäßig im Rahmen zu halten. Durch den Cache Clear wird das binlog nicht belastet und das Wachstum des binlog blieb damit absolut unauffällig.

Diese Lösung bzw. dieser Workaround konnte erfolgreich genutzt werden bis ggf. die Ursache des Problems mit memcached ermittelt oder eventuell ein anderes Cache-Backend (z. B. Redis) getestet werden kann.

Neuen Kommentar schreiben

Filtered HTML

  • Internet- und E-Mail-Adressen werden automatisch umgewandelt.
  • Zulässige HTML-Tags: <a> <em> <strong> <cite> <blockquote> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • HTML - Zeilenumbrüche und Absätze werden automatisch erzeugt.
  • To post pieces of code, surround them with <code>...</code> tags. For PHP code, you can use <?php ... ?>, which will also colour it based on syntax.

Plain text

  • Keine HTML-Tags erlaubt.
  • Internet- und E-Mail-Adressen werden automatisch umgewandelt.
  • HTML - Zeilenumbrüche und Absätze werden automatisch erzeugt.