perf: N+1-Abfragen auf der Startseite beheben, totes Cache-Verbot aufheben #71

Merged
Phil merged 1 commit from perf/enable-template-caching into main 2026-09-06 21:40:17 +00:00
Owner

Die Startseite brauchte 1,1 s bis zum ersten Byte, Unterseiten 0,25 s. Ursache gemessen: 569 Datenbankabfragen gegen 78 auf /impressum.

Refs #57

Der Fund

config/general.php schaltete Crafts Template-Caching ab, mit dieser Begründung:

->enableTemplateCaching(false)  // Disabled: Causes conflict with Blitz cache (9.9s resource delay)

Blitz ist nirgends installiert — nicht in composer.json, kein vendor/putyourlightson/, kein Plugin-Eintrag im Project-Config, keine Zeile in der plugins-Tabelle. Der Konflikt, wegen dem das Caching abgeschaltet wurde, kann nicht existieren.

Dieselbe Karteileiche steckte in config/app.php: Redis ist dort vollständig als Cache-Backend verdrahtet, mit keyPrefix => 'blitz:'. Und zwei {% cache %}-Blöcke im Code (_system/favicons.twig, _system/meta.twig) waren seit dem Abschalten wirkungslos — jemand hat Caching geschrieben, das nie lief.

Was drin ist

1. Fünf Blocktypen luden ihre Beziehungen einzeln statt gesammelt (N+1). Das ist der eigentliche Fehler, kein Cache-Thema: pl_columnsSection, pl_differencesSection, pl_logosSection, pl_productsSection, pl_reviewsSection und core/content-home holen ihre Relationen jetzt vorgeladen.

2. Template-Caching wieder eingeschaltet, Kommentar korrigiert — er verweist jetzt auf Redis als Backend statt auf ein nicht existierendes Plugin. keyPrefix von blitz: auf craft:; die alten Schlüssel laufen unbenutzt aus.

3. {% cache %} um die drei teuersten unpersonalisierten Sektionen (Spalten-, Logo-, Unterschiede-Block). Gezielt, nicht mit der Gießkanne — Bereiche mit nutzerspezifischem Inhalt bleiben ungecacht.

Belege

Datenbankabfragen der Startseite:
  vorher                    500
  nach Eager-Loading        327   (-35 %)
  mit warmem Cache          215   (-57 %)

Sprachtrennung DE/EN: in beide Reihenfolgen mit geleertem Cache geprueft,
                      keine Vermischung
Content-Invalidierung: Live-Aenderung an einem Entry wird sofort sichtbar

Was NICHT belegt ist

Die TTFB-Verbesserung in Millisekunden. Die lokale Docker-Umgebung war für eine belastbare Zahl zu verrauscht — die echte Wirkung zeigt sich erst unter Produktionslast. Die Abfragezahlen sind gemessen, die daraus folgende Zeitersparnis ist gefolgert.

Zur Einordnung: Google PageSpeed gibt der Seite 100 Punkte. Das misst das Rendern bei einem einzelnen Abruf unter Idealbedingungen, nicht die Serverzeit unter Last. Unter 25 parallelen Anfragen stieg die Startseite auf bis zu 6,1 s, und genau das hat der Seobility-Crawler als vier "nicht abrufbare Seiten" gewertet, obwohl keine einzige einen echten Fehlercode liefert.

Nachzuziehen

Zwei weitere Sektionen haben denselben N+1-Fehler: Partner-Logos und Video-Testimonials. Sie konnten nicht angefasst werden, weil eine parallel laufende Session (PR #65) gerade in diesen Dateien arbeitet. Gehört als eigenes Ticket nachgezogen, sobald sie frei sind.

Blitz als echter Seiten-Cache steht separat an — das Plugin muss dafür erst installiert werden. Blockiert derzeit ebenfalls durch die parallele Session, weil die Plugin-Installation in config/project/project.yaml schreibt.

🤖 Generated with Claude Code

https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh

Die Startseite brauchte 1,1 s bis zum ersten Byte, Unterseiten 0,25 s. Ursache gemessen: 569 Datenbankabfragen gegen 78 auf `/impressum`. `Refs #57` ## Der Fund `config/general.php` schaltete Crafts Template-Caching ab, mit dieser Begründung: ```php ->enableTemplateCaching(false) // Disabled: Causes conflict with Blitz cache (9.9s resource delay) ``` **Blitz ist nirgends installiert** — nicht in `composer.json`, kein `vendor/putyourlightson/`, kein Plugin-Eintrag im Project-Config, keine Zeile in der `plugins`-Tabelle. Der Konflikt, wegen dem das Caching abgeschaltet wurde, kann nicht existieren. Dieselbe Karteileiche steckte in `config/app.php`: Redis ist dort vollständig als Cache-Backend verdrahtet, mit `keyPrefix => 'blitz:'`. Und zwei `{% cache %}`-Blöcke im Code (`_system/favicons.twig`, `_system/meta.twig`) waren seit dem Abschalten wirkungslos — jemand hat Caching geschrieben, das nie lief. ## Was drin ist **1. Fünf Blocktypen luden ihre Beziehungen einzeln statt gesammelt** (N+1). Das ist der eigentliche Fehler, kein Cache-Thema: `pl_columnsSection`, `pl_differencesSection`, `pl_logosSection`, `pl_productsSection`, `pl_reviewsSection` und `core/content-home` holen ihre Relationen jetzt vorgeladen. **2. Template-Caching wieder eingeschaltet**, Kommentar korrigiert — er verweist jetzt auf Redis als Backend statt auf ein nicht existierendes Plugin. `keyPrefix` von `blitz:` auf `craft:`; die alten Schlüssel laufen unbenutzt aus. **3. `{% cache %}` um die drei teuersten unpersonalisierten Sektionen** (Spalten-, Logo-, Unterschiede-Block). Gezielt, nicht mit der Gießkanne — Bereiche mit nutzerspezifischem Inhalt bleiben ungecacht. ## Belege ``` Datenbankabfragen der Startseite: vorher 500 nach Eager-Loading 327 (-35 %) mit warmem Cache 215 (-57 %) Sprachtrennung DE/EN: in beide Reihenfolgen mit geleertem Cache geprueft, keine Vermischung Content-Invalidierung: Live-Aenderung an einem Entry wird sofort sichtbar ``` ## Was NICHT belegt ist **Die TTFB-Verbesserung in Millisekunden.** Die lokale Docker-Umgebung war für eine belastbare Zahl zu verrauscht — die echte Wirkung zeigt sich erst unter Produktionslast. Die Abfragezahlen sind gemessen, die daraus folgende Zeitersparnis ist gefolgert. Zur Einordnung: Google PageSpeed gibt der Seite 100 Punkte. Das misst das Rendern bei einem einzelnen Abruf unter Idealbedingungen, nicht die Serverzeit unter Last. Unter 25 parallelen Anfragen stieg die Startseite auf bis zu 6,1 s, und genau das hat der Seobility-Crawler als vier "nicht abrufbare Seiten" gewertet, obwohl keine einzige einen echten Fehlercode liefert. ## Nachzuziehen Zwei weitere Sektionen haben denselben N+1-Fehler: **Partner-Logos** und **Video-Testimonials**. Sie konnten nicht angefasst werden, weil eine parallel laufende Session (PR #65) gerade in diesen Dateien arbeitet. Gehört als eigenes Ticket nachgezogen, sobald sie frei sind. Blitz als echter Seiten-Cache steht separat an — das Plugin muss dafür erst installiert werden. Blockiert derzeit ebenfalls durch die parallele Session, weil die Plugin-Installation in `config/project/project.yaml` schreibt. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh
Redis-Cache-Prefix von 'blitz:' auf 'craft:' korrigiert — Blitz ist nie
installiert worden (DB-Check: kein Eintrag in `plugins`, keine
composer.json-Abhängigkeit), der alte Präfix war eine Karteileiche.
Bestehende blitz:*-Redis-Keys altern jetzt unbenutzt über TTL/Eviction
aus, das ist beabsichtigt.

enableTemplateCaching wieder auf true — der Kommentar zum vermeintlichen
Blitz-Konflikt war falsch, es gibt keinen Blitz.

Fünf Blocktypen der Startseite hatten N+1-Queries durch fehlendes
.with()/.eagerly() bzw. mehrfach ausgeführte, ungecachte Element-Queries
in Schleifen (columnsSection, differencesSection, logosSection,
reviewsSection, productsSection). Gemessen per general_log: 500 Queries
pro Seitenaufruf vor dem Fix, 327 danach.

{% cache %} um die drei teuersten, unpersonalisierten Blöcke
(columnsSection ~100 Queries, logosSection ~19, differencesSection ~17)
mit explizitem site-scoped Key, Invalidierung über Crafts automatische
Element-Query-Erkennung statt fester TTL. Warmer Cache: 215 Queries
(-57% ggü. 500 vorher).

pl_partnerSection.twig und pl_videoTestimonialSection.twig haben
denselben N+1-Fehler (partnerLogo/videoFile/posterImage je Zeile ohne
.with()), aber beide Dateien haben nicht-committete Änderungen einer
parallelen Session — nicht angefasst, siehe Folge-Issue.

Refs #57

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017w4iMK5VNobPwXUWXmLKKh
Phil merged commit 990a3e989b into main 2026-09-06 21:40:17 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io!71
No description provided.