Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

Performance-Überlegungen für das Gesamtmodul

Performance-Überlegungen für das Gesamtmodul

~8 Min. Lesezeit Zuletzt aktualisiert am 9. August 2026

Jeder Performance-Fallstrick in diesem Modul wurde bereits an genau der Stelle behandelt, an der er entsteht - EAV-Joins in Block 2, Observer-Overhead in Block 4, Race Conditions in Block 8/10. Dieses Kapitel bündelt sie nicht neu, sondern ordnet sie nach ihrer tatsächlichen Kostenstelle im Gesamtsystem: was bei jedem Checkout mitläuft, was nur bei Katalog-Anfragen zählt, und was der nächtliche Cronjob allein trägt.

Checkout-Pfad: drei Observer auf einem Event

sales_order_place_after trägt inzwischen drei modul-eigene Beobachter: AwardPointsOnOrderPlaced (Kapitel 30), RedeemPointsOnOrderPlaced (Kapitel 63/67) und CreditPurchasedPointsPackageOnOrderPlaced (Kapitel 76) - alle drei synchron, alle drei potenziell mit eigenen Datenbank-Schreibzugriffen. Der eigentliche Kostenfaktor ist dabei fast nie die Beobachter-Infrastruktur selbst (ein einzelner dispatch()-Aufruf ist Mikrosekunden), sondern die Summe der PointsLedgerRepositoryInterface::save()- und CustomerRepositoryInterface::save()-Aufrufe, die jeder Observer potenziell auslöst - im ungünstigsten Fall (Punkte verdient und eingelöst und ein Punkte-Paket gekauft) bis zu drei Ledger-Inserts und drei Customer-Saves in einem einzigen Checkout-Request.

Tipp: Die drei Observer bewusst getrennt zu halten (statt sie in einen einzigen zusammenzufassen) kostet fast nichts an zusätzlicher Zeit, gewinnt aber an Wartbarkeit und - wichtiger - an Fehlertoleranz: Jeder Observer kapselt seine Kernlogik einzeln in try/catch (\Throwable) (Kapitel 30/31/63/76), sodass ein Fehler in einem der drei Pfade die anderen beiden nicht mitreißt.

EAV-Joins: nur dort, wo sie nötig sind

Die Reward-Collection (Kapitel 15) ist der einzige Ort im Modul mit echten EAV-Joins. Kapitel 18 hat bereits die zentrale Regel festgehalten: nie mehr Attribute laden als für die konkrete Ansicht nötig (addAttributeToSelect() statt eines pauschalen addAttributeToSelect('*')), Filter immer über indizierte EAV-Attribute, nie über Textsuche in _text-Tabellen ohne Limit. Der GraphQL-DataProvider (Kapitel 82/83) und der Admin-Grid-Datenlieferant (Kapitel 16) nutzen dieselbe Collection und erben damit dieselbe Disziplin automatisch - eine der stärksten praktischen Begründungen für das Repository-Pattern aus Kapitel 6/79: eine einzige, korrekt optimierte Datenquelle statt drei unabhängiger, potenziell abweichender Implementierungen.

Cache-Nutzung im Gesamtmodul

  • LoyaltyCatalog-Cache-Typ (Kapitel 8): tag-basierte Invalidierung für den Prämienkatalog, wird bei jeder Reward-Änderung automatisch geleert.
  • CacheLedgerListPlugin (Kapitel 38): reiner Request-Cache über ein Instanz-Array - verhindert, dass getListByCustomerId() mehrfach in derselben Anfrage (z. B. einmal fürs ViewModel, einmal fürs Widget) dieselbe Abfrage doppelt ausführt.
  • RedemptionRateLimiter (Kapitel 86): nutzt bewusst den generischen CacheInterface statt des eigenen LoyaltyCatalog-Typs, da Rate-Limit-Zähler eine völlig andere Lebensdauer und Invalidierungslogik brauchen als der Katalog-Cache.
  • LoyaltyFeatureFlagsConfigType (Kapitel 88): hängt sich unter das Cache-Tag des Kern-Konfigurations-Caches, statt einen eigenen Typ zu registrieren - eine bewusste Vereinfachung für einen Configuration Type, der ohnehin selten ändert.

Reconciliation-Abfragen und Indizes

ReversePointsOnCreditmemoSave (Kapitel 31) und ExpirePoints (Kapitel 33) nutzen beide dasselbe Soll-Ist-Muster mit GROUP BY customer_id auf dem Ledger. Solche Aggregations-Abfragen profitieren stark von einem zusammengesetzten Index auf (customer_id, type, expires_at) - deutlich mehr als von Einzelindizes auf jeder Spalte für sich. Wer die db_schema.xml aus Kapitel 3 in einem echten Projekt übernimmt, sollte diesen Index gezielt prüfen: Ein reiner Primärschlüssel auf ledger_id reicht für den nächtlichen Cronjob bei wachsender Kundenzahl nicht aus - der Unterschied zeigt sich meist erst ab einigen Hunderttausend Ledger-Zeilen, dann aber deutlich.

Achtung: Da ExpirePoints außerhalb eines Request-Kontexts läuft (kein Nutzer wartet auf eine HTTP-Antwort), fällt eine fehlende Indizierung im Alltag lange nicht auf - bis der Cronjob irgendwann die konfigurierte schedule_lifetime (Kapitel 32) überschreitet und als "missed" markiert wird. Genau dieses Symptom gehört in die Troubleshooting-Liste aus Kapitel 103.

Querverweise auf die Fallstrick-Kapitel der Serie

  • EAV-Performance im Detail: Kapitel 18.
  • Observer-Overhead und Idempotenz-Guards: Kapitel 29-31, 76.
  • Cache-Typen und ihre jeweilige Invalidierung: Kapitel 8, 38, 86, 88.
  • Race Conditions bei parallelen Einlösungen (bewusst nicht behoben, dokumentiert): Kapitel 63, 81, 86.
  • Reindex-Kosten bei Attribut-Änderungen: Kapitel 19/20 (catalog_product_attribute-Reindex).

Performance ohne Absicherung bringt wenig - Kapitel 101 schließt deshalb mit genau dem Gegenstück: einer Zusammenfassung aller Sicherheitsentscheidungen der Serie.