Розробник Magento замінив 300 окремих викликів ProductRepositoryInterface::getById() на один запит до колекції. Кількість звернень до бази даних скоротилася з 300 до 1, і сторінка категорії завантажувалася помітно швидше. А яка була ціна? Деякі товари зникли з вітрини, що збило з пантелику і покупців, і власників.
Проблема N+1 на сторінці категорії Magento
На сторінці було представлено 300 товарів. Для кожного з них код отримував два кастомні атрибути за допомогою методу репозиторію getById(), створюючи по одному запиту на кожен товар — класичний патерн N+1. Заміна цих поодиноких завантажень на колекцію, яка витягує всі необхідні рядки одним запитом, різко зменшила кількість запитів. Затримка фронтенду зменшилася, але у списку товарів зникли позиції, які раніше відображалися.
Чому колекція приховує товари, яких немає в наявності
Модуль CatalogInventory у Magento автоматично додає фільтр «в наявності» (in-stock) до будь-якої колекції товарів, створеної для фронтенду. Фільтр активується, коли налаштування магазину Display Out of Stock Products вимкнено — це поширений вибір для продавців, які не хочуть показувати недоступні товари.
Натомість репозиторій ніколи не застосовує фільтр залишків. Він просто перевіряє, чи існує товар, і повертає його, незалежно від рівня запасів. Коли код перейшов від викликів репозиторію до колекції, фільтр залишків непомітно відсіяв кожен товар, кількість якого дорівнювала нулю. Жодних помилок у логах не було; колекція просто повернула менше рядків, ніж очікував розробник.
Що це означає для продавців
Вітрина, яка непомітно приховує SKU, яких немає в наявності, створює кілька проблем:
- Фронтенд-компоненти отримують відсутні дані, що призводить до порожніх міток або неправильних цін.
- Відлагодження стає складнішим, оскільки симптом — відсутність товарів — не викликає попереджень.
Оскільки більшість автоматизованих тестів використовують фікстури з товарами в наявності, баг часто залишається прихованим, поки сайт не почне працювати з реальними залишками.
Як зберегти швидкість, не приховуючи залишки
- Пропускайте стандартний фільтр залишків – Явно видаляйте фільтр із колекції, якщо вам потрібен кожен товар незалежно від його доступності. Magento пропонує методи для вимкнення або заміни плагіна залишків для конкретного запиту.
- Використовуйте репозиторій, коли важлива точність – Репозиторій гарантує, що кожен запитаний ID товару буде повернутий, навіть якщо його немає в наявності. Кешуйте результати або завантажуйте лише необхідні ID, щоб мінімізувати кількість запитів.
- Використовуйте resource model або raw SQL – Робіть запити безпосередньо до таблиці товарів для отримання потрібних атрибутів. Це дозволяє обійти всі плагіни колекцій і дає повний контроль над тим, які рядки будуть включені.
Швидкість проти правильності
Заміна шумного циклу N+1 на один запит до колекції здається природною; зменшення затримки є відчутним. Проте швидший запит, який повертає неправильний набір товарів, — це дефект, а не оптимізація. Розробники повинні зважувати чисту швидкість проти ризику показати неповний каталог.
На що звернути увагу далі
- Відхилення конфігурації – Перевіряйте, чи прапорець Display Out of Stock Products відповідає очікуваній поведінці будь-якого кастомного коду колекцій.
- Побічні ефекти плагінів – Інші модулі можуть додавати власні фільтри до колекцій. Переглядайте стек плагінів щоразу, коли змінюєте стратегію запитів.
Урок зрозумілий: виправлення проблеми N+1 за допомогою колекції приносить користь лише тоді, коли ви також перевіряєте стандартні фільтри цієї колекції. Ігнорування вбудованого фільтра залишків Magento може непомітно «підрізати» ваш каталог, перетворюючи приріст продуктивності на втрату прибутку.
