Чому перевірки пошти за розкладом (cron) йдуть не так

Запуск завдання кожні кілька годин здається простим на папері, але реальність у продакшені буває хаотичною. Останній запуск може залишити зайві повідомлення; повторні спроби можуть накопичуватися; повільний воркер може підхопити повідомлення, яке надійшло п'ятнадцять хвилин тому. Ці залишки порушують наївне правило «перемагає останній лист», на яке покладається більшість скриптів.

Локальні тести проходять успішно, бо вони починаються з чистого вхідного ящика та передбачуваного часу. У продакшені той самий код може підхопити не те повідомлення, тихо пропустити сповіщення або надіслати кілька сповіщень одночасно. Команди часто латають проблему довільними затримками, але затримка лише маскує стан гонитви (race condition) і невдовзі призводить до збоїв при вищих навантаженнях або зміні затримки електронної пошти.

Концепція оренди (lease): перетворення вхідного ящика на тимчасовий ресурс

Оренда вхідного ящика (inbox lease) — це крихітний контракт, який має виконувати кожен запуск cron:

  • Ексклюзивне володіння — один запуск отримує один вхідний ящик (або унікальний простір імен всередині нього).
  • Обмеження в часі — оренда фіксує час початку та час закінчення терміну дії.
  • Перевірка мітки (label) — кожен очікуваний лист має мітку, яку перевіряє завдання.
  • Захист від застарілих повідомлень — завдання ігнорує будь-який лист, що виходить за межі вікна оренди, навіть якщо тема збігається.

Замість того, щоб питати «чи надійшов лист?», завдання тепер запитує: «чи надійшов мій лист протягом вікна моєї оренди?». Такий підхід змушує код перевіряти, чи належить повідомлення поточному виконанню, усуваючи взаємозагрязнення між запусками.

Як інтегрувати цей патерн у типовий чотирьохгодинний cron

  1. Створіть ID оренди (lease ID) на початку запуску та збережіть його разом із вибраним ID вхідного ящика.
  2. Застосуйте суворий фільтр під час опитування (polling): відповідність мітці оренди, унікальності отримувача, конкретній темі та, що найважливіше, часовій мітці отримання.
  3. Логуйте метадані оренди — ID оренди, ID вхідного ящика та точний час отримання будь-якого відповідного повідомлення.

Маючи ці три елементи в логах, будь-яка помилка вказуватиме на відсутність оренди, неправильно спрямований вхідний ящик або лист поза часовим вікном, а не на розмите повідомлення «лист не знайдено».

Поширені пастки, які все одно саботують автоматизацію

  1. Повторне використання імен вхідних ящиків для зручних дашбордів — зрозумілі для людини назви виглядають гарно, але вони знову вносять спільний стан (shared state).
  2. Розпорошення правил опитування по різних файлах — несумісні визначення «свіжості» дозволяють старим повідомленням прослизати крізь фільтри.
  3. Пропуск логування lease-ID — без цього ідентифікатора налагодження перетворюються на вгадування — саме те, що робить нестабільні перевірки постійними.

Уникнення цих помилок робить систему надійною, а логи — корисними.

Якщо ізоляція неможлива, посильте фільтри

Якщо створення виділеного вхідного ящика для кожного запуску є непрактичним, компенсуйте це суворішими критеріями:

  • Вікно часу отримання — відхиляйте будь-який лист, отриманий раніше початку оренди.
  • Унікальність отримувача — використовуйте адресу для кожного запуску або унікальний аліас, якщо це дозволяє провайдер.
  • Відбиток теми (subject fingerprint) — вбудовуйте токен, специфічний для запуску, у рядок теми.

Навіть часткова реалізація концепції оренди значно зменшує дрейф стану (state drift) до того, як його налагодження стане занадто дорогим.

Контраргумент: чому порада «просто додай затримку» все ще актуальна

Деякі команди стверджують, що кількох секунд паузи (sleep) між запусками достатньо. Затримка працює, поки затримка пошти залишається в межах буфера, але будь-яке збільшення затримки провайдера, тимчасовий затор або масштабування миттєво руйнують це припущення.

Висновок

Прив'язуючи кожен запуск до власного вхідного ящика (або простору імен), маркуючи очікувані повідомлення та логуючи ідентифікатори оренди, ви усуваєте взаємозагрязнення між запусками, робите помилки помітними та нарешті отримуєте надійність, якої вимагають заплановані сповіщення.