Queue worker — це процес, який постійно “слухає” чергу і виконує jobs. Веб-запит лише ставить job у чергу, а воркер забирає її і виконує у фоні. Як тільки ви починаєте використовувати черги для медіа, нотифікацій, інтеграцій чи індексації, у вас з’являється три реальні проблеми: задачі інколи падають, інколи зависають, інколи дублюються. Саме для цього потрібні retries, timeouts і failed jobs — це механізми, які роблять систему керованою, а не “інколи працює”.
Retries — це повторні спроби виконати job, якщо вона завершилася помилкою. Вони потрібні, бо більшість падінь у чергах — тимчасові: зовнішній API недоступний, тимчасова помилка мережі, блокування в БД, short spike на сервері, короткий збій storage. Якщо ви не маєте retries, ви втрачаєте операції. Якщо маєте — система відновлюється сама.
Але retries легко перетворити на проблему, якщо job не ідемпотентна. Ідемпотентність означає: повторне виконання не повинно створювати дубль або робити шкоду. Наприклад, “згенерувати thumbnail” — можна повторити хоч 10 разів, якщо ви перевіряєте, що файл уже існує або просто перезаписуєте без побічних ефектів. А от “опублікувати в соцмережі” без маркера “вже опубліковано” може створити 3 однакових пости, якщо job повториться. Тому перед тим як ставити retries, ви маєте закласти захист від дублікатів: або поле в БД published_to_social_at, або збереження external_post_id, або “lock” на дію.
Timeouts — це ліміт часу, після якого job вважається завислою/надто довгою. Воркери не можуть дозволити задачам висіти вічно, бо тоді вони перестають обробляти чергу. Таймаут — це ваша гарантія, що навіть якщо щось пішло не так (FFMpeg завис, файл битий, API не відповідає), воркер не “застряг” назавжди. Після таймауту job зазвичай вважається невдалою і або повторюється (retry), або потрапляє у failed jobs.
Тут важливо розуміти різницю між “job довга” і “job зависла”. Довга — це коли ви свідомо робите важку роботу (транскодинг, великі імпорти). Для таких задач ви ставите більший timeout і, бажано, окрему чергу/окремих воркерів, щоб вони не блокували дрібні задачі. Зависла — це коли процес не рухається. Таймаут потрібен саме для другого, але його значення має відповідати реальності: якщо FFMpeg інколи займає 3 хвилини, а ви поставили timeout 60 секунд — ви самі створюєте нескінченні падіння і повтори.
Failed jobs — це задачі, які остаточно “не пройшли” після всіх спроб або впали з помилкою, яку не можна виправити повтором (наприклад, файл битий, неправильний формат, відсутній запис у БД, некоректні дані). Failed jobs важливі тим, що вони перетворюють невидимі проблеми на видимі. Без failed jobs ви просто “втрачаєте” роботу і не розумієте, чому в користувача не з’явилося прев’ю або чому лист не відправився. З failed jobs у вас є факт: що впало, коли впало, з якою помилкою, і ви можете повторити вручну після виправлення причини.
Правильна робота з failed jobs — це не “подивилися і забули”. У продакшені має бути мінімальний процес: якщо job важлива для бізнесу (медіа, платежі, публікації), то failed jobs мають або логуватися з високим рівнем (error), або тригерити алерт. Для навчання достатньо зрозуміти концепт: failed jobs — це черга проблем, які треба розбирати, інакше система буде “тихо ламатися”.
Тепер як це має виглядати як система, а не як набір опцій. Ви маєте кілька типів черг/воркерів. “Легка” черга: email, нотифікації, невеликі задачі — короткі timeout, швидкі retries. “Важка” черга: медіа/FFMpeg — довші timeout, інші retries, часто менша паралельність, щоб не вбити CPU. Це дає передбачуваність: важкі задачі не душать легкі.
Далі — стратегія retries. Ретрай не повинен бити в стіну кожні 2 секунди. Якщо зовнішній сервіс лежить, вам треба розумне “розведення” спроб у часі. На концептуальному рівні: кілька спроб з паузами, бажано зі зростанням паузи (backoff). Це знижує навантаження і підвищує шанс, що наступна спроба буде успішною. Для локального навчання достатньо усвідомити: retries мають бути обмежені і “розумні”, а не безкінечні.
Ще одна критична тема — “нескінченні цикли” і “отруєні” jobs. Якщо job завжди падає через дані (наприклад, файл пошкоджений), то retries тільки марнують ресурси. Тому job має вміти відрізняти тимчасові помилки (мережа, 429 rate limit, тайм-аут API) від фатальних (невалідний файл, відсутній запис) і поводитись по-різному: для тимчасових — retry, для фатальних — відразу fail з чітким повідомленням і зміною статусу в БД.
Практична ознака здорової системи: після падіння job у вас залишається правильний стан в БД. Наприклад, медіа має статус failed і повідомлення помилки; публікація має статус “не опубліковано” і причину; лист не відправився — є запис у логах/черзі. Це важливіше, ніж “щоб не падало”, бо падіння будуть завжди — питання в керованості.
Висновок: retries — це страховка від тимчасових збоїв, timeouts — захист від зависань, failed jobs — журнал проблем, які треба бачити і розбирати. Щоб це працювало надійно, jobs мають бути ідемпотентними (без дублікатів при повторі), важкі задачі — відокремлені в свої черги/воркери, а помилки — класифіковані на тимчасові й фатальні. Якщо вибудувати це як стандарт, черги перестають бути “магією” і стають інструментом, який реально тримає систему стабільною під навантаженням.