Shopware 6 mit OpenSearch: "all shards failed" (search_phase_execution_exception, Status 503)"
Im Log eines Shopware-6-Shops tauchen auf einmal kritische Einträge auf, ausgelöst von ganz normalen Aufrufen einer Kategorieseite. Die Storefront läuft vielleicht noch, vielleicht auch nicht. Und die Meldung selbst verrät auf den ersten Blick erstaunlich wenig: kein Grund, keine fehlgeschlagenen Shards, nur ein Status 503.
Genau dieses "wenig" ist aber der wichtigste Hinweis.
Die Fehlermeldung
{"error":{"root_cause":[],"type":"search_phase_execution_exception","reason":"all shards failed","phase":"query","grouped":true,"failed_shards":[]},"status":503} [] {"file":"/var/www/shop/vendor/shopware/elasticsearch/Framework/ElasticsearchHelper.php","line":51,"class":"Shopware\\Elasticsearch\\Framework\\ElasticsearchHelper","callType":"->","function":"logAndThrowException","url":"/kategorie/unterkategorie/"}
Die Zeilennummer in ElasticsearchHelper.php hängt von deiner Shopware-Version ab. Die Methode ist immer dieselbe.
Was dahintersteckt
"all shards failed" ist in OpenSearch nur die Hülle. Die eigentliche Ursache steht normalerweise in root_cause, und bei einer kaputten Query (Tippfehler im Query DSL, Sortierung auf einem text-Feld) findest du dort auch etwas, meistens zusammen mit Status 400.
Hier ist root_cause leer. failed_shards auch. Wie daraus ein 503 wird, sieht man im OpenSearch-Quellcode in SearchPhaseExecutionException::status(): Gibt es keine einzige Shard-Failure und keine weitere Ursache, antwortet OpenSearch mit SERVICE_UNAVAILABLE. Übersetzt heißt das: Die Suchanfrage ist nicht an einem Shard gescheitert. Sie hat überhaupt keinen aktiven Shard gefunden, den sie hätte fragen können.
Das verschiebt das Problem von Shopware weg in den Cluster. Auf einem eigenen Server mit einem einzelnen OpenSearch-Knoten reicht dafür schon ein Neustart zur falschen Zeit. Es gibt keinen zweiten Knoten, der einspringt.
Und dann ist da noch die Frage, warum der Shop manchmal trotzdem weiterläuft. logAndThrowException() schreibt die Meldung als critical ins Log, wirft sie aber nur weiter, wenn SHOPWARE_ES_THROW_EXCEPTION=1 gesetzt ist (oder in der Test-Umgebung). Sonst fällt Shopware laut Doku still auf die MySQL-Suche zurück. Für Besucher sieht der Shop dann normal aus, nur Listings und Suche werden langsamer und liefern andere Treffer. Wer nicht ins Log schaut, merkt davon unter Umständen wochenlang nichts.
Häufige Ursachen im Überblick
Derselbe Fehlertext kommt in zwei Varianten vor, und der Status sagt dir, welche du hast.
Status 503, leerer root_cause: Der Cluster hat ein Problem
Das ist die Variante von oben. Auf selbst betriebenen Servern mit nur einem Knoten sind das die üblichen Verdächtigen, grob nach Häufigkeit sortiert (das ist Erfahrung, keine Statistik):
- Platte voll oder über dem Watermark. Ab dem High Watermark weist OpenSearch keine Shards mehr zu. Shopware trägt seinen Teil bei, weil jeder Reindex eine neue Index-Generation anlegt und die alten liegen bleiben, bis jemand
es:index:cleanupausführt. - OOM-Killer oder zu großer Heap. Wenn sich OpenSearch, MySQL und PHP-FPM den RAM teilen, wird OpenSearch als größter Prozess gern als Erstes beendet. Danach hängen die Shards in der Recovery oder bleiben unassigned.
- Neustart, Recovery läuft noch. Nach einem Reboot, einem Paket-Update oder einem Dienst-Neustart stehen die Shards eine Weile auf
INITIALIZING. Jede Suche in diesem Fenster endet mit genau diesem 503. - Zuweisungsversuche aufgebraucht. Nach mehreren Fehlschlägen hört OpenSearch auf, den Shard zuzuweisen. Er bleibt unassigned, bis du
_cluster/reroute?retry_failed=trueaufrufst, auch wenn die eigentliche Ursache längst behoben ist. - Beschädigte Shard-Daten. Nach einem harten Absturz, einer vollgelaufenen Platte während eines Schreibvorgangs oder einem Dateisystemfehler startet der Primary-Shard nicht mehr, und der Index wird rot.
- OpenSearch-Update. Nach einem Versionssprung starten Shards manchmal nicht sauber. Wenn der Fehler direkt nach einem Update auftaucht, schau zuerst ins OpenSearch-Log.
Status 400, gefüllter root_cause: Die Query passt nicht
Hier erreicht die Anfrage die Shards, aber die Shards lehnen sie ab. Der Cluster ist gesund, das Problem liegt bei dem, was Shopware (oder ein Plugin) abfragt:
- Mapping passt nicht mehr zur Query, etwa nach einem Shopware-Update, neuen Custom Fields oder einem Plugin, das indexierte Felder erweitert. Abhilfe schafft
bin/console es:mapping:updateoder ein vollständiger Reindex. - Sortierung oder Aggregation auf einem
text-Feld statt auf einemkeyword-Feld. Das kommt fast immer aus eigenem Plugin-Code, der die Criteria erweitert. - Kaputte Query-Syntax oder Skripte aus einem Plugin, das in die Suche eingreift.
In root_cause steht bei dieser Variante meist schon, welches Feld betroffen ist.
Eine Sache taucht in Foren oft als Erklärung auf, ist aber keine: Replicas auf einem Ein-Knoten-Setup. Die machen einen Index gelb, nicht rot, und die Suche läuft weiter. Mehr dazu unten.
Prüfen, ob es wirklich das ist
Frag zuerst den Cluster selbst. Ersetze localhost:9200 durch den Wert aus OPENSEARCH_URL in deiner .env und ergänze Zugangsdaten, falls das Security-Plugin aktiv ist.
curl -s "localhost:9200/_cluster/health?pretty"
curl -s "localhost:9200/_cat/indices?v&health=red"
curl -s "localhost:9200/_cat/shards?v" | grep -v STARTED
Steht bei status ein red und tauchen Shopware-Indizes (sie beginnen mit deinem SHOPWARE_ES_INDEX_PREFIX) in der Liste der roten Indizes auf, bist du richtig. Die dritte Zeile zeigt dir, welche Shards UNASSIGNED oder noch INITIALIZING sind.
Warum ein Shard nicht zugewiesen wird, erklärt OpenSearch dir selbst:
curl -s "localhost:9200/_cluster/allocation/explain?pretty"
Ohne Angabe eines bestimmten Shards nimmt sich die API einen nicht zugewiesenen Shard vor. Die Begründung im Feld explanation ist meistens eindeutig: Disk-Watermark überschritten, zu viele fehlgeschlagene Zuweisungsversuche, kein Knoten verfügbar.
Parallel lohnt der Blick in die Logs von OpenSearch und des Systems:
journalctl -u opensearch --since "1 hour ago"
dmesg -T | grep -i -E "killed process|out of memory"
df -h
Auf Shopware-Seite zeigt bin/console es:status den Zustand des Index aus Sicht des Shops.
Die Lösung
Welche das ist, hängt davon ab, was allocation/explain und die Logs sagen. Die häufigsten Fälle:
OpenSearch war weg oder startet noch. Wenn der Knoten nur neu gestartet ist, warte die Recovery ab und prüf _cluster/health erneut. Hat der OOM-Killer zugeschlagen, hilft kein Neustart auf Dauer. Dann ist der Heap in jvm.options zu groß für den verfügbaren RAM, oder der Server ist schlicht zu klein für OpenSearch, MySQL und PHP-FPM zusammen.
Die Platte ist voll. Platz schaffen, dann die Zuweisung erneut anstoßen:
curl -s -X POST "localhost:9200/_cluster/reroute?retry_failed=true&pretty"
Ohne retry_failed versucht OpenSearch Shards, die schon zu oft gescheitert sind, nicht noch einmal.
Alte Shopware-Indizes fressen dabei gern mehr Platz als nötig, weil nach jedem Reindex eine neue Generation angelegt wird. Aufräumen kannst du mit:
bin/console es:index:cleanup
Der Index ist nicht mehr zu retten. Dann baust du ihn neu auf. Solange das läuft, arbeitet die Storefront mit dem MySQL-Fallback (oder wirft Fehler, wenn SHOPWARE_ES_THROW_EXCEPTION=1 gesetzt ist). Plan das also nicht mitten im Tagesgeschäft ein.
bin/console es:index
bin/console messenger:consume async --time-limit=600
bin/console es:create:alias
es:index schreibt die Daten nur in die Queue. Ohne laufende Worker passiert nichts. Wenn deine Worker ohnehin per Supervisor oder systemd laufen, brauchst du den zweiten Befehl nicht manuell.
Ein Knoten, aber Replicas konfiguriert. Das allein macht einen Index nur gelb, nicht rot, und erklärt diesen Fehler also nicht. Aufräumen solltest du es trotzdem, damit echte Probleme im Health-Status nicht im Dauergelb untergehen. Shopware erlaubt das in config/packages/elasticsearch.yml:
elasticsearch:
index_settings:
number_of_shards: 1
number_of_replicas: 0
Die Einstellung greift für neu angelegte Indizes, also beim nächsten es:index.
Wenn das nicht hilft
Der Cluster ist grün, und der Fehler kommt trotzdem? Dann schau noch einmal genau auf den Status. Steht dort 400 statt 503, bist du in der zweiten Variante aus dem Ursachen-Überblick, und die Suche nach dem kaputten Shard führt ins Leere.
Als Workaround, und nur als solcher, kannst du in der .env SHOPWARE_ES_ENABLED=0 setzen, bis der Cluster wieder stabil ist. Der Shop läuft dann komplett über MySQL. Das behebt nichts, verschafft dir aber Ruhe, während du die eigentliche Ursache suchst.
In dem Shop, aus dem die Meldung oben stammt, lief die Suche auf einem selbst betriebenen OpenSearch. Genau dort ist der Weg über _cluster/health und allocation/explain der schnellste, weil kein Hoster-Dashboard den Zustand für dich anzeigt.
Das Tückische an diesem Fehler ist der stille Fallback: Die Storefront funktioniert weiter, nur eben schlechter, und das einzige Signal ist eine Zeile im Log, die niemand liest. ShopSignal behält Elasticsearch bzw. OpenSearch in Shopware-6-Shops im Blick und meldet sich, wenn die Suche ausfällt, statt darauf zu warten, dass es jemandem an den Suchergebnissen auffällt.