Розробник Magento замінив 300 окремих викликів ProductRepositoryInterface::getById() на один запит до колекції. Кількість звернень до бази даних скоротилася з 300 до 1, і сторінка категорії завантажувалася помітно швидше. А яка була ціна? Деякі товари зникли з вітрини, що збило з пантелику і покупців, і власників.

Проблема N+1 на сторінці категорії Magento

На сторінці було представлено 300 товарів. Для кожного з них код отримував два кастомні атрибути за допомогою методу репозиторію getById(), створюючи по одному запиту на кожен товар — класичний патерн N+1. Заміна цих поодиноких завантажень на колекцію, яка витягує всі необхідні рядки одним запитом, різко зменшила кількість запитів. Затримка фронтенду зменшилася, але у списку товарів зникли позиції, які раніше відображалися.

Чому колекція приховує товари, яких немає в наявності

Модуль CatalogInventory у Magento автоматично додає фільтр «в наявності» (in-stock) до будь-якої колекції товарів, створеної для фронтенду. Фільтр активується, коли налаштування магазину Display Out of Stock Products вимкнено — це поширений вибір для продавців, які не хочуть показувати недоступні товари.

Натомість репозиторій ніколи не застосовує фільтр залишків. Він просто перевіряє, чи існує товар, і повертає його, незалежно від рівня запасів. Коли код перейшов від викликів репозиторію до колекції, фільтр залишків непомітно відсіяв кожен товар, кількість якого дорівнювала нулю. Жодних помилок у логах не було; колекція просто повернула менше рядків, ніж очікував розробник.

Що це означає для продавців

Вітрина, яка непомітно приховує SKU, яких немає в наявності, створює кілька проблем:

  • Фронтенд-компоненти отримують відсутні дані, що призводить до порожніх міток або неправильних цін.
  • Відлагодження стає складнішим, оскільки симптом — відсутність товарів — не викликає попереджень.

Оскільки більшість автоматизованих тестів використовують фікстури з товарами в наявності, баг часто залишається прихованим, поки сайт не почне працювати з реальними залишками.

Як зберегти швидкість, не приховуючи залишки

  1. Пропускайте стандартний фільтр залишків – Явно видаляйте фільтр із колекції, якщо вам потрібен кожен товар незалежно від його доступності. Magento пропонує методи для вимкнення або заміни плагіна залишків для конкретного запиту.
  2. Використовуйте репозиторій, коли важлива точність – Репозиторій гарантує, що кожен запитаний ID товару буде повернутий, навіть якщо його немає в наявності. Кешуйте результати або завантажуйте лише необхідні ID, щоб мінімізувати кількість запитів.
  3. Використовуйте resource model або raw SQL – Робіть запити безпосередньо до таблиці товарів для отримання потрібних атрибутів. Це дозволяє обійти всі плагіни колекцій і дає повний контроль над тим, які рядки будуть включені.

Швидкість проти правильності

Заміна шумного циклу N+1 на один запит до колекції здається природною; зменшення затримки є відчутним. Проте швидший запит, який повертає неправильний набір товарів, — це дефект, а не оптимізація. Розробники повинні зважувати чисту швидкість проти ризику показати неповний каталог.

На що звернути увагу далі

  • Відхилення конфігурації – Перевіряйте, чи прапорець Display Out of Stock Products відповідає очікуваній поведінці будь-якого кастомного коду колекцій.
  • Побічні ефекти плагінів – Інші модулі можуть додавати власні фільтри до колекцій. Переглядайте стек плагінів щоразу, коли змінюєте стратегію запитів.

Урок зрозумілий: виправлення проблеми N+1 за допомогою колекції приносить користь лише тоді, коли ви також перевіряєте стандартні фільтри цієї колекції. Ігнорування вбудованого фільтра залишків Magento може непомітно «підрізати» ваш каталог, перетворюючи приріст продуктивності на втрату прибутку.