Гарантії виконання та ідемпотентність
Durable Workflow v2 чітко розмежовує відтворення workflow та виконання activity:
- Код workflow відтворюється із зафіксованої історії та має бути детермінованим.
- Код activity виконує побічні ефекти щонайменше один раз.
- Рівень стійкої історії записує зафіксовані результати workflow й activity рівно один раз для заданого стійкого ідентифікатора, навіть якщо транспорт доставив роботу кілька разів.
Ці гарантії дозволяють рушію переживати перезапуски worker, завершення оренди, повторну доставку з черги та поступові розгортання без втрати позиції запуску.
Відтворення та повторна спроба — різні операції
Завдання workflow відновлюють стан шляхом відтворення зафіксованої історії, а потім вирішують, що робити далі. Відтворення повторно викликає тіло workflow, але не виконує знову activity, не надсилає повторно signal та не повторює побічні ефекти, уже записані в історії.
Тому код workflow має залишатися детермінованим. Якщо потрібно перейти через
стійку межу, використовуйте безпечні для workflow засоби, зокрема
Workflow::now(),
sideEffect(...), query, update, результати
activity, memo та атрибути пошуку.
Activity виконується щонайменше один раз
Activity — частина системи з побічними ефектами, тому її контракт інший:
- Спробу activity можуть отримати кілька разів.
- Завершення оренди може спричинити повторну доставку іншому worker.
- Worker може завершити зовнішню роботу, втратити оренду та повідомити результат із запізненням.
- Повторна спроба планує нову стійку спробу того самого логічного виконання activity.
Тому повторне спостереження є передбаченою поведінкою, а не умовою помилки. Автор застосунку має забезпечити безпечне повторення тіла activity чи викликів віддаленої системи.
Рекомендації щодо написання наведено в обмеженнях activity, а операторську поведінку відновлення — у збоях та відновленні.
Що відбувається рівно один раз
Durable Workflow не обіцяє, що процес worker бачить роботу з побічними ефектами лише один раз. Він обіцяє, що зафіксовані стійкі факти є авторитетними й не дублюються для того самого стійкого ідентифікатора.
На практиці це означає:
- Зафіксоване рішення workflow зберігається один раз у типізованій історії для ідентифікатора стійкої команди чи кроку, який воно представляє.
- Зафіксований кінцевий результат однієї спроби activity зберігається один раз
для її
activity_attempt_id. - Відтворення читає ці зафіксовані факти та відновлює з них стан workflow, замість повторного виконання зовнішньої роботи.
Цей поділ — основа розуміння моделі:
- Транспорт і worker забезпечують виконання щонайменше один раз.
- Зафіксована стійка історія забезпечує запис рівно один раз для кожного стійкого ідентифікатора.
Завершення оренди та повторна доставка
Завершення оренди — звичайний шлях відновлення в розподілених системах:
- Отримане завдання містить власника оренди та час її завершення.
- Якщо оренда завершується до того, як worker повідомить поступ чи завершення, завдання стає доступним для повторної доставки.
- Тоді інший worker може отримати ту саму логічну роботу.
Повторна доставка не означає, що рушій забув уже зафіксовані факти. Вона означає, що рушій відновлюється після невизначеності на рівні worker чи транспорту.
Коли ви бачите ознаки дублювання виконання, окремо з’ясуйте два питання:
- Чи відбувся побічний ефект більше одного разу?
- Чи записав стійкий стан більше одного зафіксованого результату для того самого стійкого ідентифікатора?
Перше питання вирішує ідемпотентне проєктування activity. Друге належить до контракту рушія.
Типові ідентифікатори ідемпотентності
Ці ідентифікатори — стабільні ключі для усунення дублікатів роботи:
| Ідентифікатор | Що визначає | Типове використання |
|---|---|---|
workflow_instance_id | Один публічний екземпляр workflow | Обробка дублікатів запуску та бізнес-ідентичність виконання |
workflow_run_id | Один конкретний стійкий запуск | Фіксація обраного запуску для query, експорту чи діагностики |
workflow_command_id | Одна зовнішня команда зміни стану | Усунення дублікатів повторних запитів клієнта |
activity_execution_id | Одне логічне виконання activity між повторними спробами | Типовий ключ віддаленої ідемпотентності для зовнішніх побічних ефектів |
activity_attempt_id | Одна конкретна спроба activity | Кореляція, якщо віддаленій системі потрібно розрізняти окремі спроби |
schedule_id | Одне визначення розкладу | Усунення дублікатів за власністю розкладу та ідентичністю тригера |
idempotencyKey потоку повідомлень | Одне логічне надсилання повідомлення з повторними спробами | Запобігання дублюванню прийому повідомлень під час повторних спроб відправника |
Якщо сумніваєтеся, використовуйте activity_execution_id як типовий ключ
ідемпотентності зовнішньої операції. Використовуйте activity_attempt_id лише
тоді, коли зовнішньому отримувачу справді потрібно розрізняти кожну спробу.
use Workflow\V2\Activity;
final class ChargeCard extends Activity
{
public function handle(array $payload): string
{
return app(PaymentGateway::class)->charge(
$payload,
idempotencyKey: $this->activityId(),
attemptCorrelation: $this->attemptId(),
);
}
}
Що розробники мають зробити ідемпотентним
Вам не потрібно робити саме відтворення workflow ідемпотентним. Фреймворк обробляє відтворення, відновлюючи стан із зафіксованої історії.
Вам потрібно забезпечити безпечне повторення зовнішніх ефектів, зокрема:
- викликів оплати чи білінгу
- електронних листів, текстових повідомлень та webhook
- записів до іншої бази даних чи сервісу
- створення чи завантаження файлів
- будь-яких команд, що створюють або змінюють стан поза історією workflow
Поширені підходи:
- Передавайте ключ ідемпотентності до віддаленого API.
- Записуйте до детермінованого цільового ресурсу, наприклад за відомим ключем об’єкта.
- Використовуйте upsert чи транзакцію з ключем за стійким ідентифікатором.
- Робіть дію природно повторюваною, щоб другий виклик нічого не змінював.
Рекомендації операторам
Діагностуючи запуск у Waterline, CLI чи журналах сервера:
- Вважайте повторне спостереження activity після завершення оренди очікуваним, доки стійкий результат спроби не покаже інше.
- Вважайте запізнілі повідомлення про завершення чи помилку гонкою, яку вирішує рушій, а не доказом того, що віддалений побічний ефект не відбувся.
- Вважайте відтворення завдання workflow відновленням, а не повторною спробою на рівні workflow.
- Відсутні сумісні worker, завислі оренди чи повторні ремонти — це операційні ознаки, які потребують дослідження. Вони не є підставою припускати, що тіло workflow має повторно виконувати побічні ефекти.
Найважливіше операторське розмежування — між невизначеністю транспорту та стійким результатом. Durable Workflow показує обидва, щоб ви могли їх розрізнити.
Пов’язані посібники
- Огляд представляє поділ workflow та activity.
- Обмеження workflow описують правила детермінованого написання коду.
- Обмеження activity описують безпеку побічних ефектів та методи ідемпотентності.
- Збої та відновлення описують повторні спроби, застосування тайм-аутів та ремонт.
- Модель виконання activity пояснює взаємозв’язок activity в черзі, локальних activity, сесій worker та закріпленого виконання.
- Локальні activity пояснюють спроби activity в тому самому процесі, heartbeat завдання workflow, повторні спроби та холодне відтворення.
- Закріплене виконання пояснює закріплені кеші відтворення та роль холодного відтворення як резервного шляху коректності.