Magento – Magento 2.3 extreamly slow with over 100 cart lines

cartmagento2PHPproduct

I am working in a Magento 2.3 installation with avarage cart size of 100-150 lines.
Getting the visible cart items from Quote is slower over time ( as products count rises )

I've search thought Magento core and i've found that the delay is caused when it tries to get the Quote from the database (?)

in $quote = $this->quoteRepository->getActive($this->getQuoteId());

public function getQuote()
{
    $this->_eventManager->dispatch('custom_quote_process', ['checkout_session' => $this]);

    if ($this->_quote === null) {
        $quote = $this->quoteFactory->create();
        if ($this->getQuoteId()) {
            try {
                if ($this->_loadInactive) {
                    $quote = $this->quoteRepository->get($this->getQuoteId());
                } else {
                    $quote = $this->quoteRepository->getActive($this->getQuoteId());
                }

                /**
                 * If current currency code of quote is not equal current currency code of store,
                 * need recalculate totals of quote. It is possible if customer use currency switcher or
                 * store switcher.
                 */
                if ($quote->getQuoteCurrencyCode() != $this->_storeManager->getStore()->getCurrentCurrencyCode()) {
                    $quote->setStore($this->_storeManager->getStore());
                    $this->quoteRepository->save($quote->collectTotals());
                    /*
                     * We mast to create new quote object, because collectTotals()
                     * can to create links with other objects.
                     */
                    $quote = $this->quoteRepository->get($this->getQuoteId());
                }
            } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
                $this->setQuoteId(null);
            }
        }

        if (!$this->getQuoteId()) {
            if ($this->_customerSession->isLoggedIn() || $this->_customer) {
                $customerId = $this->_customer
                    ? $this->_customer->getId()
                    : $this->_customerSession->getCustomerId();
                try {
                    $quote = $this->quoteRepository->getActiveForCustomer($customerId);
                    $this->setQuoteId($quote->getId());
                } catch (\Magento\Framework\Exception\NoSuchEntityException $e) {
                }
            } else {
                $quote->setIsCheckoutCart(true);
                $this->_eventManager->dispatch('checkout_quote_init', ['quote' => $quote]);
            }
        }

        if ($this->_customer) {
            $quote->setCustomer($this->_customer);
        } elseif ($this->_customerSession->isLoggedIn()) {
            $quote->setCustomer($this->customerRepository->getById($this->_customerSession->getCustomerId()));
        }

        $quote->setStore($this->_storeManager->getStore());
        $this->_quote = $quote;
    }

    if (!$this->isQuoteMasked() && !$this->_customerSession->isLoggedIn() && $this->getQuoteId()) {
        $quoteId = $this->getQuoteId();
        /** @var $quoteIdMask \Magento\Quote\Model\QuoteIdMask */
        $quoteIdMask = $this->quoteIdMaskFactory->create()->load($quoteId, 'quote_id');
        if ($quoteIdMask->getMaskedId() === null) {
            $quoteIdMask->setQuoteId($quoteId)->save();
        }
        $this->setIsQuoteMasked(true);
    }

    $remoteAddress = $this->_remoteAddress->getRemoteAddress();
    if ($remoteAddress) {
        $this->_quote->setRemoteIp($remoteAddress);
        $xForwardIp = $this->request->getServer('HTTP_X_FORWARDED_FOR');
        $this->_quote->setXForwardedFor($xForwardIp);
    }

    return $this->_quote;
}

The delay is on average about 15-25 seconds. Is there a way to store this kind of data in a caching storage like Redis ? I've already set Redis as a storage cache with no luck.

Is anyone else experiencing this kind of issue? Magento Changelog claims that carts should be fine when containing about 300 lines

https://devdocs.magento.com/guides/v2.2/release-notes/ReleaseNotes2.2.0CE.html

The project runs on a dedicated server with 32gb of RAM and 8 Cores XEON Cpu

UPDATE

After some more investigation i've found that the delay is on Quote.php line 1380-1391

public function getItemsCollection($useCache = false)
{
    if ($this->hasItemsCollection()) {
        return $this->getData('items_collection');
    }
    if (null === $this->_items) {
        $this->_items = $this->_quoteItemCollectionFactory->create();
        $this->extensionAttributesJoinProcessor->process($this->_items);
        $this->_items->setQuote($this);
    }
    return $this->_items;
}

The weird thing is that there isn't any traffic on Redis CLI monitor when requesting this collection, shouldn't this kind of data be stored in Redis?

UPDATE 2

There seems to be a "plugin" or something around getItemsCollection function. I've stopped execution in it and the response was immediate, if i let it returns the $this->items is shows the delay. Tried to use xdebug in order to find what causes the delay with no luck. Xdebug followed execution until Collection.php where i lost track. Mysql profiler showed around 1500 queries running in cart page ( i think this is a huge number ) although none of them lasted longer than ~5ms ( there where some around 15ms ).

So i've installed a fresh magento installation with luna theme and imported the sample products using composer.
The new vm has MySql 5.7, PHP-FPM 7.2 and redis.
Enabled production mode, all caches except page_cache and set the deafult cache to redis
Added 46 items in cart and the result for just the Document load was ~13s

enter image description here

I can't accept that Magento can't handle this kind of cart size. This time is not acceptable from the end-users.

No clue from where to start searching.

UPDATE 3

Upgraded server ( CPU + more ram ) with no results.
Any idea on how to cache the whole quote ? There seems to be no way of serializing the Object due to Closures.
I am trying to figure if a short caching in redis ( or a longer one for the checkout page only ) makes a difference.

Already tried serialize, json_encode and base_64 for the Quote

Best Answer

Thank you for answering my questions in comments section.

You've got a few options.

More resource on stack. More CPU. But even the you are going to struggle to get close to numbers you are talking about. Using nginx or maybe varnish will offer more improvements.

Redis will help on high traffic stores. So if you've got a lot of traffic on top of large baskets it's safe to leave on and will offer improvment.

You've done SQL tuneup. Which is great.

But caches will only offer improvements. They aren't a solution.

The other option would be to attempt to streamline magento. Something like this.

https://www.integer-net.com/make-magento-2-small-again/

Finally contine to profile and investigate code. Without extensions installed it's going to be reasonably optimal.

It's also worth mentioning that it's likely that the figures from magento are on commerce (not open source) which has load balancing and split database https://devdocs.magento.com/guides/v2.3/config-guide/multi-master/multi-master.html

Good luck.

Related Topic