Der Shop nimmt Bestellungen an, im Admin stehen sie vollständig in der Liste - aber die Bestellbestätigung erreicht den Kunden erst Stunden später. Der Suchindex zeigt Produkte unter alten Namen, die Sitemap ist zwei Tage alt, der Newsletter-Versand kommt nicht vom Fleck. Diese Symptome gehören zusammen, und sie haben in Shopware 6 meist dieselbe Ursache: Die Shopware Message Queue wird vom Admin-Worker im Browser-Tab abgearbeitet statt von echten CLI-Workern auf dem Server. Shopware setzt für die asynchrone Verarbeitung auf die Symfony-Messenger-Komponente; im Auslieferungszustand liegen die Nachrichten in der Datenbank und werden über den Browser verarbeitet, solange jemand in der Administration angemeldet ist - laut Hersteller eine einfache und schnelle Methode für die Entwicklung, für Produktivsysteme aber ausdrücklich nicht empfohlen (Shopware Developer Documentation, Message Queue). Dieser Beitrag ist ein Betriebsleitfaden: was über die Queue läuft, wie Worker als Systemdienst betrieben werden, wie Transporte und Queues getrennt werden, woran sich Rückstau erkennen lässt und ab welcher Queue-Länge ein Alarm sinnvoll ist. Für Shops in unserem Hosting und Wartung gehört genau dieses Setup zum Standardumfang.

Die Symptome: verspätete Mails, alter Index, tote Sitemap

Die Beschwerden, mit denen Betreiber in solchen Fällen ankommen, klingen nach vier verschiedenen Problemen - und sind eines. Alles, was in Shopware 6 nicht unmittelbar im Request erledigt wird, wandert in die Message Queue. Steht deren Verarbeitung still, fallen zuerst die Aufgaben auf, die Kunden sehen, danach die, die Google sieht, und zuletzt die, die der Buchhaltung fehlen.

  • Bestellbestätigungen kommen verzögert oder gebündelt, sobald sich jemand in der Administration anmeldet. Der Versand über die Queue ist konfiguriert, der Verbraucher fehlt.
  • Der Suchindex hinkt hinterher. Preis- und Namensänderungen sind im Frontend erst nach einem manuellen Reindex sichtbar, weil die Indexing-Nachrichten unbearbeitet liegen bleiben - ein Effekt, der sich leicht mit schlechter Suchrelevanz im Shop verwechseln lässt.
  • Die Sitemap ist veraltet. Ihre Erzeugung hängt am Scheduled Task shopware.sitemap_generate mit einem Standardintervall von 86.400 Sekunden (shopware/docs, scheduled-task.md) - der ohne laufenden Worker gar nicht erst ausgeführt wird.
  • Produkt-Exportfeeds bleiben stehen.product_export_generate_task ist mit 60 Sekunden das kürzeste Standardintervall im Auslieferungszustand (shopware/docs, scheduled-task.md) und fällt entsprechend schnell auf.
  • Newsletter- und Serienmails hängen, weil der Mailversand über den Message-Bus läuft, sobald framework.mailer.message_bus gesetzt ist (Shopware Developer Documentation, Message Queue).
  • Die Cache-Invalidierung verzögert sich. Der Task shopware.invalidate_cache steht mit 20 Sekunden Intervall in der Standardliste (shopware/docs, scheduled-task.md) und entscheidet mit darüber, wann geänderte Inhalte im Frontend ankommen.

Bei der Bestellbestätigung ist die Verzögerung nicht nur ein Servicethema. § 312i Absatz 1 Satz 1 Nummer 3 BGB verpflichtet Unternehmer im elektronischen Geschäftsverkehr, den Zugang der Bestellung unverzüglich auf elektronischem Wege zu bestätigen (Bundesministerium der Justiz, § 312i BGB). Eine Bestätigung, die erst am nächsten Morgen den Server verlässt, weil sich dann jemand im Admin anmeldet, lässt sich schwer als unverzüglich einordnen. Dazu kommt der betriebswirtschaftliche Teil: Jede ausbleibende Bestätigung erzeugt erfahrungsgemäß eine Rückfrage im Support - und zwar in dem Moment, in dem der Kunde gerade bezahlt hat und unsicher ist, ob die Bestellung überhaupt angekommen ist (Projekterfahrung). Das kostet doppelt: einmal Bearbeitungszeit, einmal Vertrauen.

Der Shop wirkt dabei vollkommen gesund

Uptime-Checks sind grün, die Startseite lädt schnell, der Checkout funktioniert. Die Queue ist der einzige Teil des Systems, der stillsteht - und sie meldet sich nicht von selbst. Genau deshalb bleibt der Zustand oft wochenlang unbemerkt, bis eine Häufung von Supportfällen, ein veralteter Feed oder eine fehlende Rechnung darauf aufmerksam macht.

Was in Shopware über die Message Queue läuft

Shopware nutzt Symfony Messenger und Enqueue, um Nachrichten asynchron zu verarbeiten - Aufgaben laufen im Hintergrund und damit unabhängig von Timeouts einzelner Requests oder Systemabbrüchen (Shopware Developer Documentation, Message Queue). Im Auslieferungszustand existieren drei Transporte: async für den Regelbetrieb, low_priority für nachrangige Aufgaben und ein Failure-Transport für Nachrichten, die ihre Zustellversuche aufgebraucht haben. Konfiguriert werden sie über die Umgebungsvariablen MESSENGER_TRANSPORT_DSN, MESSENGER_TRANSPORT_LOW_PRIORITY_DSN und MESSENGER_TRANSPORT_FAILURE_DSN (Shopware Developer Documentation, Message Queue). Wer den E-Commerce-Betrieb plant, sollte diese drei Namen kennen - an ihnen hängt der halbe Tagesablauf des Shops.

Transaktionsmails

Shopware versendet Mails standardmäßig synchron. Wird framework.mailer.message_bus auf messenger.default_bus gesetzt, wandern Bestell-, Storno- und Versandmails in die Queue - laut Hersteller die empfohlene Variante, weil langsame Mailserver sonst die Seitenauslieferung ausbremsen (Shopware Developer Documentation, Performance Tweaks).

Indexierung im Datenmodell

Entity-Indexing-Nachrichten aktualisieren Produkt-, Kategorie- und Suchdaten. Sie sind laut Hersteller vergleichsweise langsam, während andere Nachrichtentypen sehr schnell abgearbeitet werden (Shopware Developer Documentation, Message Queue) - der Hauptgrund für getrennte Queues.

Scheduled Tasks

16 Standardaufgaben bringt Shopware mit - von log_entry.cleanup über cart.cleanup bis shopware.sitemap_generate (shopware/docs, scheduled-task.md). Sie werden über die Queue eingeplant und brauchen einen laufenden Verbraucher, um überhaupt ausgeführt zu werden.

Import, Export und Feeds

Import- und Exportläufe, Produkt-Exportfeeds und App-Aktualisierungen laufen ebenfalls asynchron. Ein Massenimport erzeugt in kurzer Zeit sehr viele Nachrichten - das ist der Punkt, an dem die Trennung der Queues über die Wartezeit der Bestellmails entscheidet.

Die Standard-Scheduled-Tasks decken einen weiten Intervallbereich ab: shopware.invalidate_cache läuft mit 20 Sekunden, product_export_generate_task mit 60 Sekunden, die meisten Aufräumaufgaben täglich mit 86.400 Sekunden, product_keyword_dictionary.cleanup wöchentlich mit 604.800 Sekunden und product_download.media.cleanup mit 2.628.000 Sekunden etwa monatlich (shopware/docs, scheduled-task.md). Eingeplant werden sie durch bin/console scheduled-task:run; seit Shopware 6.5.5.0 gibt es dafür die Option --no-wait, mit der sich der Aufruf sauber in einen Cron-Eintrag alle fünf Minuten setzen lässt, statt einen dauerhaft wartenden Prozess zu halten (Shopware Developer Documentation, Scheduled Task).

Der Planer plant nur - gearbeitet wird woanders

scheduled-task:run legt Nachrichten in die Queue, ausgeführt werden sie von messenger:consume. Läuft nur der Planer, füllt sich die Queue, ohne dass etwas passiert. Läuft nur der Verbraucher, werden Aufgaben gar nicht erst eingeplant. Beide Prozesse gehören zusammen in die Dienstverwaltung - seit Shopware 6.7.2.0 helfen scheduled-task:list und scheduled-task:run-single beim Nachvollziehen des Zustands (Shopware Developer Documentation, Scheduled Task).

Warum der Admin-Worker im Browser-Tab keine Betriebslösung ist

Der Admin-Worker ist ein Mechanismus der Administration: Die Oberfläche hält einen Request offen, arbeitet die konfigurierten Transporte ab, und nach Ablauf des Poll-Intervalls endet der Request, woraufhin die Administration einen neuen startet. Der dokumentierte Standardwert für dieses Intervall liegt bei 30 Sekunden, die abzuarbeitenden Transporte werden explizit aufgeführt (Shopware Developer Documentation, Message Queue):

config/packages/shopware.yaml
# config/packages/shopware.yaml
shopware:
    admin_worker:
        enable_admin_worker: true
        poll_interval: 30
        transports: ["async", "low_priority"]

Daraus ergeben sich drei Betriebsprobleme. Erstens wird nur verarbeitet, solange jemand angemeldet ist - nachts, am Wochenende und in Betriebsferien steht die Queue. Zweitens erzeugt jede geöffnete Administration einen dauerhaft laufenden PHP-Request; bei mehreren gleichzeitig angemeldeten Mitarbeitenden führt das laut Hersteller zu hoher CPU-Last und stört die saubere Ausführung von PHP-FPM (Shopware Developer Documentation, Message Queue). Drittens hängt die Verarbeitungsleistung am Nutzerverhalten statt an einer Kapazitätsplanung: Wer den Admin-Tab schließt, hält die Hintergrundverarbeitung an, ohne es zu merken.

KriteriumAdmin-WorkerCLI-Worker als Dienst
Läuft wann?Nur bei angemeldeter AdministrationDurchgehend, unabhängig von Anmeldungen
KapazitätAbhängig von offenen Admin-TabsÜber die Anzahl der Dienstinstanzen steuerbar
LastprofilPHP-FPM-Prozesse, CPU-Last im Web-StackEigene Prozesse, vom Web-Stack getrennt
PriorisierungFeste Transportliste in der KonfigurationPro Dienst eigene Transporte und Reihenfolge
Neustart nach DeploymentErst beim nächsten SeitenaufrufGeordnet über stop-workers und Prozessmanager
Failure-TransportNicht abgedecktEigener Verbraucher einrichtbar
Beides gleichzeitig ist die häufigste Fehlkonfiguration

Sind CLI-Worker eingerichtet, sollte der Admin-Worker in der shopware.yaml deaktiviert werden - so steht es ausdrücklich in der Herstellerdokumentation (Shopware Developer Documentation, Message Queue). Laufen beide, konkurrieren sie um dieselben Nachrichten, und die Last im Web-Stack bleibt bestehen, obwohl die Dienste die Arbeit längst übernommen haben:

config/packages/shopware.yaml
# config/packages/shopware.yaml
shopware:
    admin_worker:
        enable_admin_worker: false

Worker als Systemdienst: Limits, Instanzen, geordneter Neustart

Der empfohlene Weg ist der CLI-Worker (Shopware Developer Documentation, Message Queue). Der Befehl messenger:consume verarbeitet einen oder mehrere Transporte und beendet sich nach definierten Grenzen. Das ist gewollt: Ein langlebiger PHP-Prozess sammelt über die Laufzeit Speicher an, deshalb wird er regelmäßig ersetzt statt endlos weiterbetrieben. Zeit- und Speichergrenze sind damit keine Notbremse, sondern die Betriebsart.

Terminal
$ bin/console messenger:consume async --time-limit=60 --memory-limit=128M
$ bin/console messenger:consume async low_priority
$ bin/console scheduled-task:run --no-wait

Die Reihenfolge der Transporte im Aufruf ist eine Prioritätsentscheidung: Der Worker arbeitet den ersten Transport ab und wechselt erst dann zum nächsten, wenn dort nichts mehr wartet (Symfony Documentation, Messenger). messenger:consume async low_priority bedeutet also Mails und Indexierung zuerst, nachrangige Aufgaben danach. Damit der Prozess nach Erreichen seiner Grenzen wieder startet, gehört er unter einen Prozessmanager - systemd oder Supervisor; ein Cron-Aufruf ist die schwächere Alternative (Shopware Developer Documentation, Message Queue). Shopware liefert eine Vorlage für eine instanziierbare systemd-Unit mit:

/etc/systemd/system/shopware_consumer@.service
[Unit]
Description=Shopware Message Queue Consumer, instance %i
PartOf=shopware_consumer.target

[Service]
Type=simple
User=www-data
Restart=always
WorkingDirectory=/var/www/html
ExecStart=php /var/www/html/bin/console messenger:consume --time-limit=60 --memory-limit=512M async low_priority

[Install]
WantedBy=shopware_consumer.target

Mit systemctl enable shopware_consumer@{1..3}.service werden daraus drei parallele Instanzen, die über ein gemeinsames Target gestartet werden (Shopware Developer Documentation, Message Queue). Die Vorlage arbeitet mit --time-limit=60 und --memory-limit=512M: Der Prozess beendet sich nach 60 Sekunden oder bei 512 MB Speicherverbrauch und wird durch Restart=always sofort neu gestartet. Wer Supervisor bevorzugt, steuert die Instanzzahl über numprocs, erlaubt mit startretries mehrere Startversuche und räumt dem Prozess über stopwaitsecs eine Frist ein, die laufende Nachricht noch zu beenden - in der Symfony-Dokumentation beispielhaft mit 20 Sekunden angegeben (Symfony Documentation, Messenger).

Cron als Worker-Ersatz hat einen Haken

Ein periodischer Cron-Aufruf kennt keine Obergrenze für gleichzeitig laufende Worker. Dauert eine Nachricht länger als das gesetzte Zeitlimit, startet der nächste Cron-Lauf trotzdem einen weiteren Prozess - so entsteht eine unbeabsichtigt wachsende Zahl an Workern, worauf die Herstellerdokumentation ausdrücklich hinweist (Shopware Developer Documentation, Message Queue). Prozessmanager wie systemd oder Supervisor verhindern das, weil sie ihre Instanzen zählen.

Nach jedem Deployment müssen die Worker neu starten, sonst arbeiten sie mit dem Code, der beim Prozessstart geladen wurde. Symfony sieht dafür bin/console messenger:stop-workers vor: Der Befehl setzt eine Markierung im Cache, jeder Worker beendet die laufende Nachricht und danach sich selbst, der Prozessmanager startet ihn mit dem neuen Code neu (Symfony Documentation, Messenger). Der Aufruf gehört hinter Code-Rollout und Cache-Aufwärmung. Zwei Details entscheiden über den Erfolg: Läuft die Anwendung auf mehreren Hosts, muss der Cache ein gemeinsam genutzter Adapter sein, damit die Markierung alle Prozesse erreicht (Symfony Documentation, Messenger). Und wer opcache.validate_timestamps=0 gesetzt hat - eine Empfehlung aus den Shopware-Performance-Hinweisen -, muss den OPcache beim Deployment aktiv leeren, sonst laufen Web-Prozesse und Worker weiter auf altem Bytecode (Shopware Developer Documentation, Performance Tweaks).

Deployment-Reihenfolge, die sich bewährt hat

Code ausrollen, Datenbankmigrationen ausführen, Cache aufwärmen, OPcache zurücksetzen, messenger:stop-workers aufrufen, Prozessmanager die Worker neu starten lassen, erst danach den Traffic umschalten. Wie sich dieser Ablauf ohne Wartungsfenster fahren lässt, beschreibt der Beitrag zu Zero-Downtime-Deployments mit Blue-Green; wer den laufenden Betrieb abgeben möchte, findet die Abgrenzung im Überblick zum Managed Hosting für Online-Shops.

Transport-Wahl: Datenbank als Standard, Redis oder AMQP unter Last

Standardmäßig nutzt Shopware den Doctrine-Transport, der die Nachrichten in der Datenbank ablegt. Der Hersteller bezeichnet das als einfache Lösung, die für die Entwicklung gut geeignet ist, für Produktivsysteme aber nicht empfohlen wird, und verweist auf spezialisierte Broker, die die Datenbank entlasten (Shopware Developer Documentation, Message Queue). In der Praxis trägt die Datenbank kleinere Shops mit überschaubarem Nachrichtenaufkommen trotzdem zuverlässig - der Wechsel lohnt sich, sobald mehrere Worker parallel arbeiten, Massenimporte zur Routine gehören oder die Datenbank ohnehin an ihrer Schreibgrenze läuft.

TransportGeeignet fürBetriebsaufwandTypische Grenze
Datenbank (Doctrine)Standardfall, moderates NachrichtenaufkommenKein zusätzlicher DienstSchreiblast und Zeilensperren bei vielen Workern
RedisHohe Frequenz, kurze NachrichtenEigener Dienst, Persistenz klärenSpeicherbedarf, Verhalten bei Neustart
AMQP-BrokerViele Verbraucher, mehrere Queues, RoutingEigener Dienst mit eigener AdministrationZusätzliche Komponente im Betriebsmodell

Unabhängig vom gewählten Transport gibt es einen Detailpunkt, der unter Last spürbar wird: Symfony Messenger prüft bei jeder Verbindung, ob die Queue existiert, und legt sie bei Bedarf an. Shopware empfiehlt, das über ?auto_setup=false an der Verbindungs-URL abzuschalten und die Queues stattdessen einmalig beim Deployment mit bin/console messenger:setup-transports anzulegen (Shopware Developer Documentation, Performance Tweaks). Wenn Redis ohnehin im Spiel ist, lohnt der Blick auf die übrigen Einsatzfelder - zusammengefasst im Beitrag zu Redis-Caching für Shopware.

Die Datenbank als Queue hat einen sichtbaren Preis

Beim Doctrine-Transport liegen die Nachrichten in der Tabelle messenger_messages. Jeder Worker fragt sie in kurzen Abständen ab und sperrt Zeilen für die Dauer der Verarbeitung. Mit steigender Worker-Zahl wächst damit die Schreiblast auf genau der Datenbank, die parallel den Shop bedient. Wer ohnehin an der Datenbank arbeitet, prüft den Lebenszyklus der Datenbankversion sinnvollerweise gleich mit.

Queues trennen, damit der Massenimport nicht den Mailversand blockiert

Der wichtigste Betriebsschritt nach dem Abschalten des Admin-Workers ist die Trennung der Queues. Der Grund ist schnell erzählt: Ein Katalogimport mit 50.000 Positionen erzeugt in wenigen Minuten sehr viele Indexing-Nachrichten. Liegen Bestellmails in derselben Queue, stehen sie hinter diesem Berg - der Kunde wartet auf die Bestätigung, während der Worker Produktdaten indexiert. Shopware weist ausdrücklich darauf hin, dass es sinnvoll sein kann, Nachrichten auf einen anderen Transport zu leiten, um die Worker-Zahl für einen bestimmten Nachrichtentyp zu begrenzen, Datenbanksperren zu vermeiden oder den Mailversand zu priorisieren (Shopware Developer Documentation, Message Queue).

Routing auf einen eigenen Indexing-Transport
# config/packages/framework.yaml
framework:
    messenger:
        transports:
            async: "%env(MESSENGER_TRANSPORT_DSN)%"
            entity_indexing: "%env(MESSENGER_TRANSPORT_INDEXING_DSN)%"
        routing:
            '*': async

# config/packages/shopware.yaml
shopware:
    messenger:
        routing_overwrite:
            'Shopware\Core\Framework\DataAbstractionLayer\Indexing\EntityIndexingMessage': entity_indexing

shopware.messenger.routing_overwrite überschreibt - anders als die reine Symfony-Routing-Konfiguration - eine bestehende Zuordnung, statt eine weitere hinzuzufügen. Genau das braucht man für Indexing-Nachrichten, die standardmäßig über das AsyncMessageInterface auf async geleitet werden. Die Option wurde mit Shopware 6.6.4.0 und 6.5.12.0 eingeführt (Shopware Developer Documentation, Message Queue). Die Umsetzung eigener Nachrichtentypen und Handler gehört in die individuelle Entwicklung und sollte zusammen mit dem Betriebskonzept entstehen, nicht danach.

  1. Dienst 1 - Mail und Regelbetrieb:messenger:consume async, zwei bis drei Instanzen, kurze Zeitlimits. Zielgröße ist die Wartezeit bis zur Bestellbestätigung.
  2. Dienst 2 - Indexierung:messenger:consume entity_indexing, begrenzte Instanzzahl, höheres Speicherlimit. Zielgröße ist Durchsatz ohne Sperrenstau in der Datenbank.
  3. Dienst 3 - Nachrangiges:messenger:consume low_priority, eine Instanz. Aufräumarbeiten und alles, was eine Verzögerung von Stunden verträgt.
  4. Dienst 4 - Failure-Transport: ein eigener Verbraucher mit längerem Intervall. Ohne ihn bleiben fehlgeschlagene Nachrichten unbearbeitet liegen - darauf weist die Herstellerdokumentation ausdrücklich als Warnung hin (Shopware Developer Documentation, Message Queue).

Die eigentliche Frage ist nicht, wie viele Worker ein Shop braucht, sondern welche Nachricht hinter welcher anderen warten darf.

XICTRON Entwicklungsteam

Skalierung: Worker-Zahl bestimmen und Rückstau erkennen

Auf die Frage nach der richtigen Worker-Zahl gibt Shopware bewusst keine pauschale Antwort: Sie hängt von der Menge der eingereihten Nachrichten und ihrem Typ ab - Produktindexierung ist eher langsam, andere Nachrichten werden sehr schnell verarbeitet; der Hersteller empfiehlt, die Queue zu beobachten und die Zahl entsprechend anzupassen (Shopware Developer Documentation, Message Queue). Das ist keine Ausflucht, sondern die einzige belastbare Antwort - und sie setzt voraus, dass der Rückstau überhaupt gemessen wird.

Die aussagekräftigste Kennzahl ist dabei nicht die Anzahl wartender Nachrichten, sondern ihr Alter. Eine Queue mit 5.000 Einträgen, die in vier Minuten abgearbeitet ist, ist unauffällig. Eine Queue mit 40 Einträgen, von denen der älteste seit zwei Stunden wartet, ist ein Störungsfall. Beim Doctrine-Transport lässt sich beides aus der Tabelle messenger_messages ablesen: die Zeilenzahl je queue_name und der älteste available_at-Zeitpunkt ohne gesetztes delivered_at. Bei Redis oder einem AMQP-Broker liefern die Bordmittel des jeweiligen Dienstes dieselben beiden Werte.

BeobachtungUrsache liegt in der QueueUrsache liegt im Shop
Queue-LängeWächst dauerhaft, auch nachtsSteigt in Lastspitzen und fällt danach
Ältester EintragAlter wächst linear mit der UhrzeitAlter bleibt im Minutenbereich
Worker-ProzesseKein Prozess aktiv oder alle dauerhaft belegtProzesse laufen und sind zeitweise unbeschäftigt
FehlerbildMails und Index verspätet, Seiten schnellSeiten langsam, Fehlerrate steigt
Erste MaßnahmeDienste prüfen, Instanzen erhöhen, Queues trennenRessourcen, Datenbank und Caching prüfen

Beim Erhöhen der Instanzzahl gibt es eine natürliche Grenze: Ab einem gewissen Punkt konkurrieren die Worker um dieselben Datenbanksperren, und der Durchsatz steigt nicht mehr proportional zur Prozesszahl. Das ist der Moment, in dem entweder der Transport gewechselt oder ein Nachrichtentyp auf einen eigenen Transport gelegt wird - nicht der Moment für den vierten und fünften Worker auf derselben Queue. Wie sich die übrigen Stellschrauben im System zueinander verhalten, ordnet der Beitrag zur Shopware-6-Performance-Optimierung ein.

Zwei Zahlen genügen für den Anfang

Wartende Nachrichten je Queue und Alter der ältesten wartenden Nachricht. Wer nur diese beiden Werte in ein Dashboard bringt, erkennt einen stehenden Worker binnen Minuten statt erst über Kundenbeschwerden. Alles Weitere - Durchsatz pro Minute, Fehlerquote, Laufzeit je Nachrichtentyp - ist Feinschliff, der ohne diese Basis wenig bringt.

Fehlerbehandlung: Failure-Transport, Wiederholungen und Alarme

Scheitert die Verarbeitung einer Nachricht, wird sie in den Failure-Transport verschoben. Die Standardkonfiguration wiederholt die Zustellung dreimal; scheitert sie erneut, wird die Nachricht gelöscht (Shopware Developer Documentation, Message Queue). In Symfony ist die Wiederholungsstrategie parametrisiert: max_retries steht auf 3, die erste Wartezeit auf 1.000 Millisekunden, der Multiplikator auf 2 - die Abstände wachsen also exponentiell, und max_delay deckelt sie bei Bedarf (Symfony Documentation, Messenger). Für einen kurzen Ausfall des Mailservers ist das genau richtig; für einen Ausfall über mehrere Minuten reicht es nicht.

Ohne Verbraucher auf dem Failure-Transport verschwinden Bestellmails still

Die Herstellerdokumentation formuliert es als ausdrückliche Warnung: Der CLI-Worker muss auch für die failed-Queue eingerichtet werden, sonst werden fehlgeschlagene Nachrichten nicht weiterverarbeitet (Shopware Developer Documentation, Message Queue). Praktisch heißt das: Eine Bestellbestätigung, deren Mailserver während eines dreiminütigen Ausfalls nicht erreichbar war, liegt danach in einer Tabelle, in die keiner schaut - und der Kunde wartet weiter.

Terminal
$ bin/console messenger:failed:show --stats
$ bin/console messenger:failed:show 20 -vv
$ bin/console messenger:failed:retry --force
$ bin/console messenger:failed:remove 20

Symfony bringt für den Umgang mit dem Failure-Transport eigene Befehle mit: messenger:failed:show listet die Einträge und zeigt mit -vv den vollständigen Fehler, messenger:failed:retry stellt einzelne oder alle Nachrichten erneut zu, messenger:failed:remove entfernt sie (Symfony Documentation, Messenger). Sinnvoll ist außerdem --failure-limit am Verbraucher: Der Worker beendet sich nach einer definierten Zahl von Fehlern, statt in einer Schleife dieselbe kaputte Nachricht zu verarbeiten (Symfony Documentation, Messenger). In Verbindung mit Restart=always entsteht daraus ein sauberer Selbstheilungsmechanismus für vorübergehende Störungen.

  • Kein Worker-Prozess aktiv - Alarm nach 60 Sekunden. Dieser eine Alarm erklärt die Mehrzahl aller Symptome aus dem ersten Abschnitt.
  • Alter der ältesten wartenden Nachricht über 5 Minuten in der Mail-Queue, über 30 Minuten in der Indexing-Queue. Schwellen je Queue getrennt setzen, sonst ist der Alarm entweder taub oder lästig.
  • Queue-Länge über einem definierten Wert - der Wert ergibt sich aus dem gemessenen Durchsatz, nicht aus dem Bauchgefühl.
  • Zuwachs im Failure-Transport - jeder neue Eintrag ist ein Ereignis, kein Betriebszustand.
  • Scheduled Task ohne Ausführung im erwarteten Intervall - eine Aufgabe mit 60-Sekunden-Intervall, die seit einer Stunde nicht lief, zeigt einen stehenden Planer an.

Kennzahlen fürs Monitoring und typische Fallstricke

Für die Messseite braucht es keine große Werkzeugkette. Shopware liefert seit 6.7.8.0 einen eigenen Endpunkt für Queue-Statistiken: GET /api/_info/message-stats.json ersetzt das ältere GET /api/_info/queue.json, das gemeinsam mit der incrementbasierten Statistik als veraltet markiert und für 6.8.0.0 zur Entfernung vorgesehen ist. Der Hersteller begründet den Wechsel damit, dass die alten Werte durch fest verdrahtete Multiplikatoren und fehlende Dekremente in Randfällen oft ungenau waren (Shopware Release Notes 6.7.8.0). Wer bisher gegen queue.json alarmiert hat, sollte die Umstellung einplanen - inklusive der ebenfalls als veraltet markierten Optionen shopware.admin_worker.enable_queue_stats_worker und shopware.increment.message_queue (Shopware Release Notes 6.7.8.0).

  • Wartende Nachrichten je Queue - getrennt nach async, low_priority, eigenen Transporten und dem Failure-Transport
  • Alter der ältesten wartenden Nachricht je Queue - die eigentliche Führungsgröße im Betrieb
  • Durchsatz in Nachrichten pro Minute, ebenfalls je Transport
  • Anzahl laufender Worker-Prozesse und ihre Laufzeit seit dem letzten Neustart
  • Zuwachs im Failure-Transport pro Stunde als eigener Zähler
  • Zeit von der Bestellung bis zum Versand der Bestätigung als fachliche Kennzahl - die einzige Zahl aus dieser Liste, die auch außerhalb der IT jeder versteht

OPcache nach dem Deployment

Mit opcache.validate_timestamps=0 prüft PHP Dateiänderungen nicht mehr - eine Empfehlung aus den Shopware-Performance-Hinweisen, die einen aktiven Cache-Reset beim Deployment voraussetzt (Shopware Developer Documentation, Performance Tweaks). Ohne diesen Schritt laufen Web-Prozesse und Worker weiter auf altem Bytecode.

Worker mit altem Code

Ein Worker lädt den Code beim Start. Ohne messenger:stop-workers nach dem Deployment verarbeitet er Nachrichten mit der Vorversion - das erzeugt Fehlerbilder, die sich lokal nicht nachstellen lassen, weil dort längst der neue Stand läuft (Symfony Documentation, Messenger).

Doppelt eingerichtete Cronjobs

Ein Cron-Eintrag aus der Erstinstallation plus ein systemd-Dienst aus dem Relaunch: Beide arbeiten dieselbe Queue ab, die Zahl der Prozesse ist keinem mehr klar, und unter Last überholen sich die Läufe (Shopware Developer Documentation, Message Queue). Ein Inventar aller Hintergrundprozesse gehört in die Betriebsdokumentation.

Scheduled Tasks ohne Verbraucher

Der Planer reiht Aufgaben ein, aber kein messenger:consume arbeitet sie ab. Die Tabelle scheduled_task füllt sich, das Frontend zeigt alte Daten. Seit Shopware 6.7.2.0 lässt sich der Zustand mit scheduled-task:list direkt prüfen (Shopware Developer Documentation, Scheduled Task).

Wie diese Kennzahlen in ein durchgängiges Bild aus Verfügbarkeit, Antwortzeiten und Fehlerraten passen, beschreibt der Beitrag zum Shop-Monitoring für Verfügbarkeit und Performance. Zwei weitere Betriebsthemen greifen direkt in die Queue: die Invalidierung des HTTP-Caches, die an einem Scheduled Task hängt und die wir im Beitrag zum Cache-Rework und HTTP-Cache in Shopware einordnen, sowie die Absicherung der Schnittstellen, über die viele dieser Nachrichten überhaupt erst entstehen - dazu passt der Beitrag zum Absichern der Store API im Headless-Shop.

Die Queue gehört in den Betrieb, nicht in den Browser-Tab

Die asynchrone Verarbeitung ist in Shopware 6 kein Randthema, sondern der Pfad, über den Bestellbestätigungen, Suchindex, Sitemap, Feeds und Aufräumaufgaben laufen. Der Auslieferungszustand mit Admin-Worker und Datenbank-Transport ist für die Entwicklung gedacht - der Hersteller sagt das selbst (Shopware Developer Documentation, Message Queue). Der Übergang in den Produktivbetrieb besteht aus wenigen, klar benennbaren Schritten: Admin-Worker aus, Worker als Dienst mit Zeit- und Speichergrenze, getrennte Queues für Mail, Indexierung und Nachrangiges, ein eigener Verbraucher für den Failure-Transport, ein geordneter Neustart nach jedem Deployment und zwei Kennzahlen im Monitoring. Keiner dieser Schritte ist aufwendig - aufwendig ist der Zustand davor.

XICTRON richtet dieses Setup für Shopware-Shops ein und übernimmt den laufenden Betrieb: Wir deaktivieren den Admin-Worker, legen die Worker als Systemdienste mit passenden Limits an, trennen die Queues nach fachlicher Dringlichkeit, hängen den Failure-Transport an eine Alarmierung und binden den geordneten Neustart in das Deployment ein. Ob der Datenbank-Transport ausreicht oder ein eigener Broker sinnvoll ist, entscheiden wir anhand des gemessenen Nachrichtenaufkommens statt nach Schema. Sprechen Sie uns über die Kontaktseite an, lassen Sie den Bestand im Rahmen einer Beratung aufnehmen oder sehen Sie sich an, was unser Hosting und Wartung abdeckt. Für Anpassungen im Shop selbst - eigene Nachrichtentypen, Handler, Routing - steht unsere Shopware-Entwicklung bereit.

Quellen und Studien

Dieser Artikel basiert auf der Shopware Developer Documentation - Hosting/Infrastructure: Message Queue und Scheduled Task sowie Hosting/Performance: Performance Tweaks (developer.shopware.com) -, der Shopware Documentation zu Message Queue and Scheduled Tasks (docs.shopware.com), dem shopware/docs GitHub Repository mit den Dateien guides/hosting/infrastructure/message-queue.md und guides/hosting/infrastructure/scheduled-task.md, den Shopware Release Notes 6.7.8.0 (developer.shopware.com/release-notes) sowie der Symfony Documentation zu Messenger: Sync und Queued Message Handling (symfony.com/doc/current/messenger.html). Die rechtliche Einordnung der Bestellbestätigung stützt sich auf § 312i BGB in der Fassung des Bundesministeriums der Justiz (gesetze-im-internet.de). Als (Projekterfahrung) gekennzeichnete Angaben stammen aus eigenen Betriebsprojekten. Versions- und Konfigurationsangaben können sich durch spätere Herstellerreleases ändern.

Häufige Fragen zur Shopware Message Queue

In der Regel ja. Die Herstellerdokumentation empfiehlt ausdrücklich, den Admin-Worker in der shopware.yaml zu deaktivieren, sobald der CLI-Worker konfiguriert ist (Shopware Developer Documentation, Message Queue). Laufen beide parallel, konkurrieren sie um dieselben Nachrichten, und die CPU-Last durch offene Administrationen bleibt bestehen - obwohl die Dienste die Arbeit bereits übernommen haben.

Eine pauschale Zahl wäre erfahrungsgemäß irreführend. Shopware selbst verzichtet auf eine allgemeine Empfehlung und verweist darauf, dass die passende Anzahl von Menge und Typ der Nachrichten abhängt - Produktindexierung ist langsam, andere Nachrichten sind sehr schnell (Shopware Developer Documentation, Message Queue). Die mitgelieferte systemd-Vorlage zeigt beispielhaft drei Instanzen. Sinnvoll ist, mit zwei bis drei Instanzen zu starten, Länge und Alter der Queue zu messen und danach anzupassen.

Er funktioniert grundsätzlich, hat aber einen dokumentierten Haken: Cron kennt keine Obergrenze für gleichzeitig laufende Worker und wartet nicht, bis ein vorheriger Prozess beendet ist. Dauern Nachrichten länger als das gesetzte Zeitlimit, kann so eine unbeabsichtigt wachsende Zahl an Workern entstehen (Shopware Developer Documentation, Message Queue). Für den Planer scheduled-task:run --no-wait ist ein Cron-Eintrag dagegen üblich und sinnvoll.

Weil Seitenauslieferung und Hintergrundverarbeitung zwei getrennte Systeme sind. Ist der Mailversand über den Message-Bus konfiguriert, hängt die Bestätigung an einem laufenden Verbraucher (Shopware Developer Documentation, Message Queue). Ohne CLI-Worker wird sie erst dann versendet, wenn sich jemand in der Administration anmeldet. Rechtlich ist das relevant, weil § 312i Absatz 1 Satz 1 Nummer 3 BGB eine unverzügliche elektronische Bestätigung des Zugangs der Bestellung verlangt (Bundesministerium der Justiz, § 312i BGB).

Sie werden in den Failure-Transport verschoben und typischerweise dreimal erneut zugestellt; scheitert die Verarbeitung erneut, wird die Nachricht gelöscht (Shopware Developer Documentation, Message Queue). Deshalb gehört auch für diesen Transport ein eigener Verbraucher eingerichtet, und der Zuwachs sollte überwacht werden. Mit messenger:failed:show, messenger:failed:retry und messenger:failed:remove lassen sich die Einträge einsehen, erneut zustellen oder entfernen (Symfony Documentation, Messenger).

Erfahrungsgemäß erst dann, wenn Messwerte dafür sprechen. Shopware nutzt standardmäßig den Doctrine-Transport, bezeichnet ihn als gute Wahl für die Entwicklung und empfiehlt für Produktivsysteme einen spezialisierten Dienst, der die Datenbank entlastet (Shopware Developer Documentation, Message Queue). In der Praxis lohnt der Wechsel typischerweise, sobald mehrere Worker parallel arbeiten, regelmäßig Massenimporte laufen oder die Datenbank bereits an ihrer Schreibgrenze arbeitet.