Skip to content

Very slow performance for \Magento\Catalog\Api\CategoryLinkRepositoryInterface::save #37739

@ioweb-gr

Description

@ioweb-gr

Preconditions and environment

  • 2.4.1 to 2.4.6

Steps to reproduce

  • Create a large profile with about 700 categories, 6 websites, 14 store views and 200.000 products
  • Create a message queue system that will assign / remove products from the categories
  • Populate the message queue with around 1k messages
  • Set the max message size to something normal like 100
  • Start your consumer and let it run with a logger for checking the time

You will get a result like this

2023-07-08 16:56:48 - AssignSaleCategoryHandler: add GW2990 1119
2023-07-08 16:56:59 - AssignSaleCategoryHandler: add GW2991 1119
2023-07-08 16:57:10 - AssignSaleCategoryHandler: add S20678-16 1119
2023-07-08 16:59:25 - AssignSaleCategoryHandler: add 3024725-003 1119
2023-07-08 16:59:34 - AssignSaleCategoryHandler: add 1WT21015-200 1119
2023-07-08 16:59:41 - AssignSaleCategoryHandler: add 1WT21015-001 1119
2023-07-08 16:59:47 - AssignSaleCategoryHandler: add 1KW21010-365 1119
2023-07-08 17:00:08 - AssignSaleCategoryHandler: add 1WT21018-200 1119
2023-07-08 17:00:15 - AssignSaleCategoryHandler: add 1WT21018-001 1119
2023-07-08 17:00:23 - AssignSaleCategoryHandler: add 1WT21018-100 1119
2023-07-08 17:00:30 - AssignSaleCategoryHandler: add 1AF21022-336 1119
2023-07-08 17:02:41 - AssignSaleCategoryHandler: add 1AF21031-100 1119
2023-07-08 17:04:50 - AssignSaleCategoryHandler: add 1AF21034-330 1119
2023-07-08 17:07:00 - AssignSaleCategoryHandler: add S20729-16 1119
2023-07-08 17:09:10 - AssignSaleCategoryHandler: add 2400005-15011 1119
2023-07-08 17:09:18 - AssignSaleCategoryHandler: add 2400005-16011 1119
2023-07-08 17:09:25 - AssignSaleCategoryHandler: add 2400005-19010 1119
2023-07-08 17:09:32 - AssignSaleCategoryHandler: add N2400002-13013 1119
2023-07-08 17:09:38 - AssignSaleCategoryHandler: add N2400002-16011 1119
2023-07-08 17:09:45 - AssignSaleCategoryHandler: add N2400002-19010 1119
2023-07-08 17:09:52 - AssignSaleCategoryHandler: add DD1579-101 1119
2023-07-08 17:10:05 - AssignSaleCategoryHandler: add 3024877-003 1119
2023-07-08 17:10:16 - AssignSaleCategoryHandler: add CZ5478-100 1119
2023-07-08 17:10:21 - AssignSaleCategoryHandler: add S20689-16 1119
2023-07-08 17:13:12 - AssignSaleCategoryHandler: add FFM0060-60002 1119

As you can see it processed like 30 products in > 15 minutes

The main issue here is that the function

    /**
     * @inheritdoc
     */
    public function save(\Magento\Catalog\Api\Data\CategoryProductLinkInterface $productLink)
    {
        $category = $this->categoryRepository->get($productLink->getCategoryId());
        $product = $this->productRepository->get($productLink->getSku());
        $productPositions = $category->getProductsPosition();
        $productPositions[$product->getId()] = $productLink->getPosition();
        $category->setPostedProducts($productPositions);
        try {
            $category->save();
        } catch (\Exception $e) {
            throw new CouldNotSaveException(
                __(
                    'Error: "%1"',
                    $e->getMessage()
                ),
                $e
            );
        }
        return true;
    }

Will actually resave the whole category every time you assign a product to it including all the product positions and everything.

image
image

In my case as you can see this is triggering observers for rewriting the urls again and it's causing the delay.

Obviously this speed is not acceptable in terms of performance rendering the code unusable.

In our use case we have to manually handle more than 50k messages / day because when products change their price we need to refresh specific categories with them by deleting or adding them to the category.

Moreover by the time these messages are processed more will be added infinitely in a never ending loop of unfinished message processing.

Sample message handler

<?php
/**
 * Copyright (c) 2023. IOWEB TECHNOLOGIES
 */

namespace Ioweb\SaleCategories\Model\Queue\Handler;

use Ioweb\SaleCategories\Api\Data\AssignSaleCategoryMessageInterface;
use Ioweb\SaleCategories\Service\Logger;
use Magento\Catalog\Api\CategoryLinkManagementInterface;
use Magento\Catalog\Api\CategoryLinkRepositoryInterface;

class AssignSaleCategoryHandler
{
    private CategoryLinkManagementInterface $categoryLinkManagement;
    private CategoryLinkRepositoryInterface $categoryLinkRepository;
    private Logger $logger;

    public function __construct(
        CategoryLinkManagementInterface $categoryLinkManagement,
        CategoryLinkRepositoryInterface $categoryLinkRepository,
        Logger $logger
    )
    {
        $this->categoryLinkManagement = $categoryLinkManagement;
        $this->categoryLinkRepository = $categoryLinkRepository;
        $this->logger = $logger;
    }

    /**
     * @param AssignSaleCategoryMessageInterface $message
     * @return string
     */
    public function execute($message)
    {
        $this->logger->info('AssignSaleCategoryHandler: ' . $message->getAction() . ' ' . $message->getSku() . ' ' . $message->getCategoryId());
        switch($message->getAction()){
            case AssignSaleCategoryMessageInterface::ACTION_ADD:
                $this->categoryLinkManagement->assignProductToCategories(
                    $message->getSku(),
                    [$message->getCategoryId()]
                );
                break;
            case AssignSaleCategoryMessageInterface::ACTION_REMOVE:
                $this->categoryLinkRepository->deleteByIds(
                    $message->getCategoryId(),
                    $message->getSku()
                );
                break;
        }
        return 'complete';
    }
}

Expected result

Assignment is really fast.

Actual result

Assignment is unexpectedly really slow.

Additional information

I've opened that back in 2021 and it was dismissed and closed because I wasn't using MessageQueues. Well here we are in 2023 with messageQueues and the underlying problem is still there. Is there any proposed alternative?

Release note

No response

Triage and priority

  • Severity: S0 - Affects critical data or functionality and leaves users without workaround.
  • Severity: S1 - Affects critical data or functionality and forces users to employ a workaround.
  • Severity: S2 - Affects non-critical data or functionality and forces users to employ a workaround.
  • Severity: S3 - Affects non-critical data or functionality and does not force users to employ a workaround.
  • Severity: S4 - Affects aesthetics, professional look and feel, “quality” or “usability”.

Metadata

Metadata

Assignees

Type

No type

Projects

Status

On Hold

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions