Ще один cron — найдешевший спосіб сказати «хай це буде не у вебзапиті». Межу так часто обирають невдало. Команда в планувальнику Laravel може слати пошту, перезбирати звіт або опитувати чужий API, доки два запуски не накладуться і повтор не зробить побічний ефект удруге. Процес на Node поруч із цим застосунком доречний, коли код уже живе на Node або коли повтори й падіння задачі не повинні ділити процес із сайтом. Це не лікування повільного запиту.
Рішення йде в такому порядку. Чого не можна лишати у вебзапиті. Що має статися один раз, навіть якщо задачу запустять знову. І лише потім — на чому це виконувати. Ситуації нижче впізнавані. Це не порівняння швидкості Node і PHP і не привід різати маленький застосунок надвоє, бо на схемі два квадрати.
1. Що cron насправді ховає
У заплановану команду вростає ціла задача. Розклад спрацьовує щохвилини. Команда вибирає рядки і робить роботу просто в собі. Поки вона ще йде, наступна хвилина спрацьовує знову. Виходять два воркери, які одне про одного не знають. База спільна, і зовнішній API теж той, який команда викликає.
Cron уже є. Планувальник Laravel — звичне місце для «зроби це пізніше». «Пізніше» перетворюється на «пройди всіх клієнтів у системі, потім поклич третю сторону, потім запиши звіт». Накладання непомітне, доки клієнт не отримає два листи або оцінку не поставлять двічі. Блокування навколо команди зупиняє накладання. Повтору з правилами, окремої черги для зламаних задач і місця для запуску, який довший, ніж блокування готове чекати, воно не дає.
Подивіться на команду, яка вже є:
Якщо чесна відповідь — «одна одиниця роботи, її можна запустити знову, вона довша за запит», вам потрібна черга. Черга може лишитися ларавелівською, на Redis або на базі, з воркером, який ви і так умієте піднімати. Відходити від PHP — наступне рішення, не це.
2. Коли воркером варто зробити Node
Розріз «бо так гарніше» дорого коштує. «Node краще вміє асинхронність» — не архітектура. Node доречний, коли потрібний код уже на Node або коли падіння цієї задачі має бути окремо від викату Laravel: довгий обробник, бібліотека, яка є лише в цій екосистемі, процес, який можна перезапустити, не чіпаючи процеси сайту.
Змішані стеки з’являються, бо сайт і бічна робота не починалися однією кодовою базою. На одній CRM, де кілька клієнтів живуть в одному застосунку, продукт лишився на PHP, а окремий процес вів обробку дзвінка: збирання промпта, структуровану відповідь моделі, звіт, плюс телефонію, повідомлення й облік. Задачі йшли через чергу. Повтор знаходив роботу вже записаною і зупинявся. Другого результату не з’являлося. Межею була обробка дзвінка. Це не заява, що Node швидший за команду Laravel. Цифру «черга проти cron» по тій системі я б не публікував: такого заміру робота не дала.
На успадкованих робочих системах обережність з іншого боку. Обидва середовища виконання вже можуть лежати в репозиторії. Вирішують, що привести до ладу. Другий процес не заводять лише тому, що Node виявився встановленим.
Перш ніж додавати процес, зафіксуйте:
3. Повтор вирішує більше, ніж мова
Обробник будь-якою мовою, який приймає «принаймні один раз» за «рівно один раз», подвоїть побічний ефект. Брокер доставляє знову. Процес падає після побічного ефекту і до підтвердження. Друга доставка знову списує, шле лист або вставляє рядок.
Спершу пишуть основний шлях. Обробник викликає провайдера, потім пише рядок. Падіння між цими двома кроками — дубль. Безпечний повтор означає: друга доставка знаходить роботу вже записаною і зупиняється. На обробці дзвінка вище це було обов’язковим. Воно так само обов’язкове, якщо воркер — черга Laravel на Redis. Мова цього за вас не зробить. Перевірку пишете ви: ключ цього дзвінка, цієї події, цього списання і результат, який можна прочитати назад.
Перш ніж довіряти повтору:
Філія або клієнт у спільній системі належать тому самому рішенню. На тій CRM у кожної філії була своя база, міграції та індекси будували під розділення. Воркер, який відкриває «ту саму» базу і вірить повідомленню, легко запише рядки одного клієнта задачею іншого. Черга цього не лагодить. У повідомленні має бути клієнт, а воркер має відмовитися від з’єднання, яке з ним не збігається.
4. Черга не замість читання запиту
Повільний запит, який «лагодять» перенесенням на Node, був повільним через те, як читають дані. Воркер виконує ті самі запити і віддає той самий обсяг. Людина тепер дивиться на завантаження з підписом «обробляється», а рахунок за CPU переїжджає в інший сервіс.
Черга помітна в архітектурі. Індекс — ні. На одній навчальній платформі в стеку вже були і Laravel, і Node. Заміряні зміни були не про нового воркера. Цикл запитів до бази став одним агрегувальним запитом: операція скоротилася з 12,41 до 2,451 секунди, пам’ять упала на 1,5 ГБ. Середнє навантаження CPU за тиждень знизилося з 82,2% до 2,75%, коли розібрали вузькі місця, включно з пошуком без індексів. Критична операція — з 23 937 мс до 48 мс. Одна відповідь API — з 912 КБ до 2,1 КБ. Кожна цифра про свою операцію. На всю платформу їх не переносять. Черга на Node поруч із Laravel не була б першою правкою для циклу на дванадцять секунд. Цикл переїхав би, усе ще приблизно на дванадцять секунд, у процес, який важче відкрити очима.
В іншому місці прибрали завислий інтерфейс, і критичний процес скоротився приблизно з години до шести хвилин. Це результат того процесу. Доводом на користь другого середовища виконання він не служить.
Спершу прочитайте повільну операцію. Для людини, яка план запиту не відкриє, та сама звичка викладена в матеріалі що заміряти до редизайну. Якщо гальмує звіт, а не сторінка, версія для власника продукту — черги й індекси, коли звіт у CRM довго відкривається.
5. Три різні ситуації
Ситуацію не змішуйте із сусідньою. Історія одного продукту легко стає загальним шаблоном. Не переносьте її, якщо у вас інша.
Перша. Черга поруч із PHP-застосунком для роботи, яку треба повторювати без дублів. Сайт лишається на місці. Обробник веде довгий ланцюжок: оцінка дзвінка, розмова з телефонією, запис звіту. Межа — повідомлення. Копіювати варто межу і правило повтору, а не обов’язкового брокера.
Друга. Node — сам застосунок, не процес збоку. Продукт уже сервіс на Node, зі своїм інтерфейсом. Робота зі швидкості — витоки пам’яті і повільні запити на цьому процесі. Викат — збірка, яка доїжджає туди, де продукт реально працює. Laravel-застосунку, який чекає воркера, немає. Діагностику лишають на процесі, який обслуговує продукт. Друге середовище не заводять, щоб на схемі було два квадрати.
Третя. Фонова робота вже стоїть поруч із запитом, іноді тією самою мовою. В API може бути свій запуск задач без другої екосистеми. Пошуковий індекс, який стискається з гігабайтів до кількох мегабайтів, — це зміна індексу. Воркером вона б не стала. На маленькій системі питання те саме: що саме повільно і чи потрібен ще один процес узагалі.
Коли просити розібрати межу разом
Якщо ви можете назвати задачу, правило повтору і причину, чому воркер на Node чи не на Node, друга думка не потрібна, щоб поставити блокування на cron. Кличте, коли повтор може списати гроші або сповістити двічі, а обробник цього не запобігає, коли Laravel-воркер і обробник на Node обидва підуть у викат і за повідомлення ніхто не відповідає, або коли повільний запит збираються перенести, а не прочитати.
Таку межу я розбираю всередині вже наявних систем на Laravel і Node, зокрема коли обидві вже на продакшені. Послуги — швидкодія, SaaS-бекенди та інтеграції, які зазвичай ці задачі і породжують. Проєкти тримають кожну з цих ситуацій у своєму обсязі і не перетворюють жодну на загальне «пришвидшили». Якщо потрібен розбір однієї команди, яка переросла cron, напишіть мені в LinkedIn. Надішліть ім’я задачі, чого повтор не повинен зробити вдруге, і на чому код уже написаний.