Import / Speicher Fresser

@dominikmank
OK ist ja schon einmal schön, dass Shopware da dran arbeitet allerdings bringt die asynchrone Lösung einem größeren B2B-Shop überhaupt nichts - wenn dieser ständig mit SAP kommuniziert und ein Produkt 1,5GB Speicher frisst, brauchen wir bei 5000 Produkten ja Arbeitsspeicher im TB Bereich - ich bin wirklich großer Fan von SW6 aber an den Insert/Update/Upsert/Delete Befehlen ist noch viel Arbeit reinzustecken bevor größere Kunden damit arbeiten können.
Generell führt der memory-Fresser dazu, dass es auch im Backend zu Fehlern beim Speichern/Löschen/Hinzufügen von Artikeln mit vielen Varianten kommt.
Zum Beispiel sowas:

php.CRITICAL: Fatal Error: Out of memory (allocated 824180736) (tried to allocate 663552 bytes) {"exception":"[object] (Symfony\Component\ErrorHandler\Error\OutOfMemoryError(code: 0): Error: Out of memory (allocated 824180736) (tried to allocate 663552 bytes) at /home/meinedomain.com/apitest/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:262)"} [] [2022-01-10T12:33:26.426271+00:00] request.CRITICAL: Uncaught PHP Exception Symfony\Component\ErrorHandler\Error\OutOfMemoryError: "Error: Out of memory (allocated 824180736) (tried to allocate 663552 bytes)" at /home/meinedomain.com/apitest/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php line 262 {"exception":"[object] (Symfony\Component\ErrorHandler\Error\OutOfMemoryError(code: 0): Error: Out of memory (allocated 824180736) (tried to allocate 663552 bytes) at /home/meinedomain.com/apitest/vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOStatement.php:262)"} []
 https://meinedomain.com/api/_action/sync Payload: {key: "write", action: "upsert", entity: "product",…} action: "upsert" entity: "product" key: "write" payload: [{id: "c1000001070000000000000000086388", versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",…}] 0: {id: "c1000001070000000000000000086388", versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425",…} description: "22x28cms" id: "c1000001070000000000000000086388" versionId: "0fa91ce3e96a4bc2be4bd9ce752c3425"

Wir sind jetzt bei 32 GB Arbeitsspeicher und es reicht immer noch nicht, um ein simples Update-Statement auszuführen für ein Lagerbestandsupdate.

Edit:
Noch einmal um klar zu stellen, in welchen Dimensionen wir hier sprechen - ich habe soeben versucht ein Produkt mit 625 Varianten zu aktualisieren - also lediglich das:

$productData = [
                    'id' => $product->getId(),
                    'stock' => $stock,
                    'deliveryTimeId' => $deliveryTimeId,
                    'restockTime' => $restockTime
 ];

Für eine Variante hat es 1,3GB benötigt:
ProductNumber: CGP9128234
1028.5 megabytes
2330.5 megabytes after productRepository->upsert

Es scheint so, als ob ein Update der anderen Varianten des Artikels dann keine gravierenden Folgen für den Arbeitsspeicher haben (da diese vermutlich durch ein Update der einen Variante bereits vorgemerkt für das Indexing sind)

Wenn ich aber 50 dieser Produkte mit einer Anzahl von 600+ Varianten habe, benötige ich mindestens 50*1,3GB = 65GB für das Lagerbestandsupdate von 5000 Produkten

Hinzu kommt, dass der Lagerbestand bei zwei unserer B2B-Kunden alle 30 Minuten aktualisiert werden müsste - hier könnte noch über 1 Stunde verhandelt werden - ist momentan unmöglich mit SW6

@Moritz_Naczenski
Habt ihr ein Testsystem mit einer solchen Anzahl an Produkten und vor allem Produkten mit vielen Varianten (500+)? Ansonsten kann ich euch gerne eines von uns bereitstellen.

@dominikmank
Vielen Dank für den Tipp.
Ich habe jetzt die einzelnen Methoden einmal auskommentiert und es liegt definitiv an dieser Zeile:

if ($message->allow(self::SEARCH_KEYWORD_UPDATER)) {
                //$this->searchKeywordUpdater->update(array_merge($ids, $childrenIds), $context);
            }

Wenn ich diesen auskommentiere, kommt der gesamte Import mit 300MB RAM klar!

Ich habe das wieder aktiviert und bin ich die Datei platform/SearchKeywordUpdater.php at eb827637a1bb6edd35487dfe59b2b761dcccf685 · shopware/platform · GitHub

Hier liegt das Problem am Iterator:

while ($products = $iterator->fetch()) {
            /** @var ProductEntity $product */
            foreach ($products as $product) {
                // overwrite fetched products if translations for that product exists
                // otherwise we use the already fetched product from the parent language
                $existingProducts[$product->getId()] = $product;
            }
        }

Bei diesen Zeilen wächst der RAM.
Weiter geht es mit der Datei:

$result = $this->repository->search(clone $this->criteria, $this->context);

Und hier ist dann die nächste Ebene des Problems.

Damit landen wir dann hier:

796.5 megabytes repo search before → RepositoryIterator.php
796.5 megabytes → EntityRepository.php
Dispatched EventName product.search.result.loaded → EntityRepository.php
830.5 megabytes after 3 → EntityRepository.php
830.5 megabytes repo search after → RepositoryIterator.php

→ könnte also irgendein Problem beim Event „product.search.result.loaded“ sein?

EntityRepository.php:

public function upsert(array $data, Context $context): EntityWrittenContainerEvent
    {
        $affected = $this->versionManager->upsert($this->definition, $data, WriteContext::createFromContext($context));
        $event = EntityWrittenContainerEvent::createWithWrittenEvents($affected, $context, []);
        $this->eventDispatcher->dispatch($event);

        return $event;
    }

Ohne $this->eventDispatcher->dispatch($event); keine memory Probleme.
Gleiche Probleme auch bei den Event dispatchern in den Methoden update(), create() - delete() vemutlich auch aber habe ich nicht getestet.