Performance optimierung Indexierung

Hallo Zusammen, hat einer von euch hier schon eine Endlösung für sich gefunden?

Ich habe 1000e Produkte und will mal nur einen Teilimport mit 800 Produkten importieren, doch seitdem ich auch einen haufen Kategorien angelegt habe (ein paar hundert), hängt der dämliche Import ständig bei 50 Produkten und in den Meldungen steht „Noch 2xxx Kategorien verbleibend“.
Hab das mal 3 Tage laufen lassen und er ist nicht fertig geworden.

Habe schon den Adminworker deaktiviert und 2 Cronjobs angelegt, mit massiv Speicherlimit, sowie die Warteschlange mit dem Froschtool und auch direkt über die DB geleert. Bringt alles genau garnichts.

/opt/plesk/php/8.2/bin/php -d memory_limit=4096M shopdomain/bin/console messenger:consume failed async --time-limit=100 --memory-limit=4096M

/opt/plesk/php/8.2/bin/php -d memory_limit=4096M shopdomain/bin/console scheduled-task:run --time-limit=100 --memory-limit=4096M

Wenn man das nicht besser einstellen kann, muss ich wohl selber über die JavascriptAPI irgendwie den Import programmieren :frowning:

scheduled-task:run braucht kaum Speicher, kannst du einfach weglassen.
messenger:consume solltest du vielleicht splitten:

sync: 2-4 GB, 120 Sekunden
async: 2-4 GB, 120 Sekunden
failed: 16+GB, 3600 Sekunden

Außerdem in der config:

shopware:
    dal:
        batch_size: 10

Ich habe meine persönliche Endlösung schon lange gefunden - importiere und aktualisiere unsere Produkte ausschließlich über die API.

Erster Durchgang: Alle Produkte - ohne Index
Zweiter Durchgang: Nur Parents und Einzelartikel - mit Index

Läuft. :slight_smile:

1 „Gefällt mir“

Hi Teddie, vielen dank für die Info, das mit dem Aufsplitten versuche ich gleich noch. Dh ich hätte dann 3 „messenger:consume“ cronjobs richtig?

… messenger:consume sync --time-limit=100 --memory-limit=4096M
… messenger:consume async --time-limit=100 --memory-limit=4096M
… messenger:consume failed --time-limit=100 --memory-limit=4096M

Wenns dann immer noch nicht ordentlich funktioniert, werde ich auch über die API gehen. Das löschen aller Produkte habe ich zB auch schon über die API gemacht, da das im Backend nicht geht. Maximal 100 Produkte auf einmal löschen ist hald sinnlos, wenn man einen Testimport mit ein paar 1000 Produkten versucht und die dann wieder löschen will. Auch so eine Sache die ich nicht verstehe dass man das im Backend nicht machen kann, aber naja.

Soweit korrekt - mehr geht natürlich auch sofern du RabbitMQ verwendest.
Wichtig ist, dass du das Time-Limit nicht zu kurz wählst.

Angenommen es ist ein Index-Prozess und es sind durch einen Zufall 10 Produkte mit jeweils 20.000 Varianten also zusammen 200.000 - da muss die Datenbank schon einiges leisten und sofern die 4 GB reichen kann es immer noch sein, dass 100 Sekunden einfach zu kurz sind. 200.000 ist eigentlich nicht viel, aber him Hintergrund muss der Shop „normal“ auch noch laufen, die Suche, Produkt-Exporte etc.

Also schlägt der Async fehl und landet im Failed und schlägt wieder fehl, weil der Failed genau so kurz ist, wie der Async.

Scheint leider keinen Unterschied bei mir zu machen.

RabbitMQ sagt mir nichts, aber da wir mit Shared Hosting arbeiten und da 100e Webs drauf laufen, werde ich da nichts am Server installieren, von dem ich keine Ahnung habe. Bin egtl Frontendentwickler, aber muss mich notgedrungen mit diesen Backendsachen beschäftigen. :face_with_thermometer:

Ich habe gestern auch noch den dal:refresh:index Befehl in der SSH Konsole versucht, aber der brachte den klassischen Memoryfehler, irgendwas mit „tried to allocated …“. Vlt liegts auch daran und ich bekomme es beim Import nur nicht mit, weil es eben auch keine sonderlich gute Fehlerausgabe im Admin gibt.

Da ich Zeitnah eine Lösung brauche und schon Tage mit dem Import herum probiere, werde ich mich heute oder morgen an die API-Lösung setzen, hilft nichts. :confused:

Hast du die batch_size in der yaml angepasst?

2500 Kategorien sind schon ne Hausnummer… müssen ja etliche Produkte sein… glaube nicht, dass Shared Hosting da auf Dauer sinnvoll ist.

Ohne RabbitMQ, Redis und ElasticSearch bzw. OpenSearch läuft alles über die Datenbank, da können 10 Produkte bereits zum Flaschenhals werden… (vor allem mit MariaDB) außerdem funktionieren so keine parallelen Worker (>1 async etc.)

Hast du die Daten bereits in einer Datenbank etc. könnte da evtl. etwas helfen.

Ja die batch_size habe ich in der shopware.yaml, sofern das richtig ist, gesetzt.

shopware:
    auto_update:
       enabled: false
    admin_worker:
      enable_admin_worker: false
    dal:
        batch_size: 10

In der Kategorietabelle „category“ sind 1741 Kategorien, woher die 2500 kommen weiß ich nicht. Das sind leider so viele, da man im Shop Produkte verschiedener Marken kaufen kann und jede Marke ihre eigene Kategorienstruktur hat.
Wenn ich so darüber nachdenke, als ich noch keine Kategorien angelegt hatte, konnte ich noch 7000 Produkte ohne Probleme importieren, also wird das Problem evtl an der Indexierung der Kategorien liegen.

Die Daten kommen zum großteil als BMEcat Datei, dafür habe ich extra einen Parser schreiben müssen, der die wichtigsten Daten herausholt und als CSV zurück gibt (kleine Webanwendung außerhalb des Shopsystems). Im Endeffekt habe ich jetzt jeweils eine CSV für jede der 6 Marken und für jede Marke ist auch ein eigenes Importprofil angelegt.

Meine Lösung wäre mit ChatGPT ein möglichst einfaches Importscript (Javascript) für eine kleine Webanwendung zu erstellen und zu hoffen das ich damit die Kategorie Indexierung umgehen kann. Da ich im Bezug auf Shop und API keine Erfahrung habe, weiß ich aber nicht was da wieder für Probleme auf mich zukommen werden.

Wenn du da irgendwie helfen könntest, oder Tipps hast, nehme ich die liebend gerne an :slight_smile:

Sieht gut aus.

Kann durchaus sein, dass da viele Faktoren zusammenkommen.

Vermute, dass beim Verarbeiten des Produktes, gleich die Kategorie mit aktualisiert wird und dadurch auch die anderen Produkte in der Kategorie. Wenn da noch die Eigenschaften dazu kommen… oha.
(Spielt für mich auch keine Rolle mehr ob es so war, ist oder wie auch immer.)

Habe den Importer von Shopware von Anfang an gemieden, allein schon wegen der Eigenschaften, die zuerst angelegt werden müssen, bevor das Produkt importiert werden konnte. Zumindest war‘s damals so.

Habe damals alles in PHP geschrieben:

  • Produktdaten werden aufbereitet (SQL-DB) und „fertig“ auf einen lokalen Redis-Server geworfen
  • Immer 100 Produkte werden als Bulk an den Shop übergeben

Resultat: ca. 250 Produkte pro Sekunde

Eigentlich kommen da keine Probleme auf dich zu… musst nur im Header
indexing-behavior: disable-indexing
bzw.
indexing-behavior: use-queue-indexing
angeben, dann sollte das alles kein Problem (mehr) sein.
So kannst du auch die Preis-Updates // Bestandsabgleich etc. machen

Vielen dank!
Bin schon dabei den Import selber umzusetzen. Ich habe jetzt ein PHP Skript, das mir das BMEcat File parsed und ein JSON aller Produkte zurückgibt. Hatte das vorher verwendet um die CSV für das interne Importtool zu erstellen und habe es jetzt einfach auf JSON umgebaut.

Von Redis habe ich null Ahnung, kenne nur den Namen, das wars aber auch schon. Ich werde jetzt versuchen über die Javascript API die Daten zu Importieren bzw Produkte erstellen und updaten.

Eine Frage, vlt hast du da ja auch Ahung davon: Die Bilder der Produkte (nur immer ein Coverbild) sind als Link hinterlegt. Shopware ladet die ja automatisch runter beim Import. Ich muss da wohl über /api/media die Bilder erstellen bzw herunterladen lassen und dann die ID dem jeweiligen Produkt als Coverbild zuweisen oder? Oder macht die API das herunterladen der Bilder auch selber?

Kurzes Fazit, für die die auch hier Probleme haben

  1. Von Shopware vorgeschlagenen DBs (MySQL 8.0 oder MariaDB 10.11) verwenden.
    Das war das größte Problem, darauf bin ich leider erst zu ende drauf gekommen. Wir hatten MariaDB 10.3 und das war mit Abstand der größte Flaschenhals
  2. RabbitMQ als MessengerQueue einrichten
    ZB beim Produktimport werden ja alle importierten Produkte nochmal für die Suche indexiert. Das geht zeitlich aber nicht direkt 1 zu 1, darum gibt es die MessageQueue, hier werden die Inexierungsaufgaben zwischenspeichert, damit Shopware sie dann nach und nach abarbeiten kann.
  3. Optional einen eigenen Import programmieren.
    Man hat dann bessere Kontrolle über denn Import, vor allem wenn das zukünftig der Kunde selber machen soll. Ich habe mir zB auch eine „Löschen aller Produkte“ und „Löschen der Bilder von Ordner XY“ Funktionen gebaut. Stichwort Bulk Import/Edit/Delete

PS:
Das Experiment Shopware ist für mich nach 3 Shops jetzt zu ende, meine Nase ist voll :wink: . Es gibt zwar viele coole Features wie der Rulebuilder, aber am Ende gibt es einfach auch noch zu viele Bugs und es fehlen so viele grundlegende Funktionen. Ja, man kann alles anpassen, aber da braucht man einfach einen Programmierer der sich da auskennt. Ich mache zu 80% nur Frontend und hab mit PHP Frameworks keinen Kontakt, für mich waren das zuviele unbezahlte Stunden und zuviele Kompromisse die der Kunden eingehen musste, weil die hälfte nicht geht, verbunden.

Shopware kann sicher viel, aber ohne einen erfahrenen Shopwareprogrammierer an der Seite kann ich die Version6 (noch) nicht empfehlen.

1 „Gefällt mir“

Völlig ohne Ironie: dann berichte doch gerne mit welchem System du, primär als Frontenentwickler, alle die Anforderungen der Kunden umsetzen kannst.

1 „Gefällt mir“

… und was alles nicht geht. :thinking:

Ich hab ja nicht geschrieben das ich „Alle Kundenanforderungen“ damit erschlagen können will, das hätte ich auch nicht erwartet. Aber bei uns hat es an zu vielen Ecken gehakt, die jetzt keine exotischen Wünsche waren und ich ohne Programmierung nicht umsetzen konnte.

Evlt ist Shopware 5 (noch) eine besser Lösung, zumindest liest sich das oft so in den verschiedenen Themen, dass SW5 was konnte und SW6 nicht mehr.

Woocommerce ist meine einzige echte Referenz, da war das mit dem Import weitaus einfacher, aber das System hat andere Limitierungen, darum sind wir ja zu Shopware gewechselt.

Wie gesagt, Shops hab ich abgehakt.

Ja das man eben einen Industriestandard wie BMcat Files nicht importieren kann. Das der Importer einfach abkackt, wenn er ein Bild nicht findet anstatt das Dummybild einzusetzen. Das der Importer einen Netto und Brutto Preis braucht, obwohl sogar bei manueller einpflege das eine aus dem anderen gerechnet wird. Das ich Kateorien erst manuell anlegen muss und dann selber die CSV so zusammen basteln muss, das ich anstatt der Kategorienamen, die ShopwareIDs drinnen stehen habe. Bei Woocommerce ging das alles viel einfacher, aber das soll keine Empfehlung sein. Wir sind extra zu Shopware gewechselt wegen anderer Limitierungen in Woocommerce, die ich jetzt nicht weiter ausführe.

Shopware 6 wird nie ohne tiefergreifendes technisches Wissen sinnvoll nutzbar sein. Shopware 6 hat einfach eine andere strategische Ausrichtung als Shopware 5. Muss man wissen, dann ist (fast) alles gut.

Fazit: Ohne spezialisierte Agentur (oder vergleichbare inhouse-Kompetenzen) macht Shopware 6 keinen Sinn.

Sehr interessanter Thread! Habe mir hier schon einige Infos rausgezogen. :+1:

Ich habe den CLI-Worker laufen und lasse per cronjob den consumer in Dauerschleife laufen:

* * * * * www-data /usr/local/bin/php /var/www/html/htdocs/bin/console messenger:consume failed async --time-limit=60

Unsere 300.000+ Produkte importiere ich via sync-api und habe das Indexing schon deaktiviert via Header-Anweisung indexing-behavior=disable-indexing. Das läuft dann auch halbwegs schnell. Davor hatte ich immer wieder Lock Wait Timeouts.

Das Indexing mache ich dann später über einen separaten cronjob.

Entsprechend der Empfehlungen hier habe ich rabbitMQ in einem extra docker container installiert und in shopware aktiviert über die ENV-Variable MESSENGER_TRANSPORT_DSN. Das funktioniert soweit. Allerdings könnte ich nicht sagen, dass das Indexing jetzt mit rabbitMQ schneller läuft. Ca. 1 Datensatz pro Sekunde.

Manche haben geschrieben, dass sie mehrere Worker parallel laufen lassen. Wie mache ich das? Bin in dem Thema ein newbie. Einfach den messenger:consume mehrfach im cronjob starten lassen? Oder gibt es dazu eine ENV oder yaml-Einstellung?

Und meine zweite Frage wäre:
Wie oft und was indexiert ihr, wenn ihr die Indexierung vom Import abgekoppelt habt? Ist die Indexierung überhaupt noch notwendig, wenn man z.B. elasticsearch verwendet?

Genau so… oder mit Supervisord.
Wenn du nur einen Worker hast bringt RabbitMQ nichts. :wink:

Die Indexierung ist „immer“ notwendig, sonst kommen die Informationen auch nicht in ElasticSearch.
Manuell indexiere ich eigentlich gar nichts… nur, wenn ich merke, dass etwas fehlt.
Ansonsten werden Produkt-Kinder ohne Indexierung importiert, die Produkt-Eltern mit Indexierung, da dadurch die Kinder mit indexiert werden. Der Server macht die Arbeit also nur einmal und nichts doppelt.

Vielen Dank für Deine Rückmeldung.

RabbitMQ
Zwischenzeitlich habe ich RabbitMQ mit cronjobs und supervisord ausprobiert. Das hat keinen großen Unterschied in der Performance gemacht.
Ich habe mit unterschiedlichen Worker-Anzahlen getestet. Da schießt die CPU schnell auf 95% bei 8 CPUs. Hab mich jetzt so auf 4 Worker eingependelt. Da komme ich beim Abarbeiten der Indexierung max. auf 2/sek. Das dauert ca. 40-50 Min. Auch mit mehr Workern ist das nicht schneller geworden, die blockieren sich dann wohl gegenseitig.

dal-Indexierung vs. elasticsearch

Die Indexierung ist „immer“ notwendig, sonst kommen die Informationen auch nicht in ElasticSearch.

Es gibt ja auch noch

bin/console es:index

Ich frage mich, ob es ausreicht, diesen Befehl regelmäßig laufen zu lassen anstelle des

bin/console dal:refresh:index --only=product.indexer --use-queue

Der reine elasticsearch-Index ist schneller als der dal-Index. Dauert aktuell ca. 20 Min.

Varianten bzw. Eltern/Kind-Konstellationen haben wir nicht. Nur über 300.000 Produkte, die permanent ergänzt, geändert, gelöscht, neu bepreist werden. Also eine sehr hohe Dynamik.

Ich tendiere aktuell dazu, nur den elasticsearch-Index statt dem dal-Index laufen zu lassen. Den es:index würde ich dann jede Stunde mal drüber laufen lassen. Den dal vielleicht nur einmal die Woche zur Sicherheit. Oder fehlen mir dann irgendwo Daten oder ich hab Inkonsistenzen?

Sofern du über die API die Daten aktualisierst werden werden die Index(e) eigenständig aktualisiert - einfach über die API mit „indexing-behavior: use-queue-indexing“ einspielen.

Habe mir die Programmierung nicht angesehen.
Es fühlt sich für mich immer an wie Daten → DAL → ES bzw. bei es:index DAL → ES - zumindest ergeben nur so einige Phänomene sinn. (Müsste ich halt erst nachlesen ob‘s wirklich so ist.)

ES ist halt generell extrem schnell, das geht sogar bei unseren 1,6 Mio Produkten recht zügig.

Wenn du Änderungen über die API realisierst, dann sind manuelle Indexierungen (eigentlich) gar nicht nötig.
Habe vor einem halben Jahr die Indexierung das letzte Mal manuell angestoßen und auch nur, weil ein Plugin-Update das nötig gemacht hat.

2 bis 4 Worker reichen auch vollkommen… kann auch sein, dass du irgendwo da einen Flaschenhals hast, sei es bei der Hardware oder bei der Konfiguration, denn 2 pro Sekunde klingt bisschen mager… bzw. wie kommst du auf 2 pro Sekunde?
Dauer / Produkte? 300000 Produkte durch 50 Minuten mal 60 Sekunden wären ja 100 Produkte pro Sekunde, welche Indexiert werden.

1 „Gefällt mir“

einfach über die API mit „indexing-behavior: use-queue-indexing“ einspielen.

Ja, das wäre vielleicht auch noch eine Überlegung. Aktuell spiele ich es über die Bulk-API mit disable-indexing ein und verwende dann cronjobs zum indexieren. Werde mal die Performance mit use-queue-indexing ausprobieren, dann könnte ich ja die cronjobs weglassen.

wie kommst du auf 2 pro Sekunde?

Ja, also 2 messages/sekunde, d.h. 100 Produkte (50 Produkte pro message)