Ідемпотентні та детерміновані workflow: у чому різниця?
Детермінована поведінка ухвалює ті самі рішення за тих самих вхідних даних та історії. Ідемпотентна поведінка має той самий очікуваний ефект, незалежно від того, чи операція виконується один раз або кілька. Детермінованість робить одне виконання передбачуваним. Ідемпотентність робить повторні виконання безпечними. Жодна властивість не передбачає іншу.
| Питання | Детермінованість | Ідемпотентність |
|---|---|---|
| Що залишається незмінним? | Рішення за тих самих вхідних даних та історії | Очікуваний зовнішній ефект після одного чи багатьох викликів |
| Навіщо це Durable Workflow? | Відтворення має відновити той самий шлях workflow | Повторні спроби й повторна доставка не мають дублювати побічні ефекти |
| Де це потрібно? | У коді workflow та оркестрації | В activity та системах, які вони викликають |
| Чи забороняє це введення-виведення або випадковість? | Так, у коді workflow, що відтворюється, якщо результат не записаний | Ні, activity може використовувати введення-виведення, час чи випадковість і все одно забезпечувати безпечне повторення ефекту |
Властивості незалежні
Детермінованість без ідемпотентності
Розгляньмо операцію, що збільшує лічильник:
increment(counter):
counter.value = counter.value + 1
За того самого початкового значення лічильника ця операція завжди виконує те саме обчислення, тому вона детермінована. Вона не ідемпотентна: два виклики збільшують лічильник двічі, тоді як один виклик збільшує його один раз.
Ідемпотентність без детермінованості
Тепер розгляньмо activity, яка встановлює замовленню відомий стан, але використовує випадкову затримку між спробами під час звернення до сервісу:
archive(order_id):
wait(random_backoff())
PUT /orders/{order_id}/status {"status": "archived"}
Час і кількість внутрішніх спроб можуть різнитися, тому ця реалізація не детермінована. Але операція може бути ідемпотентною: після одного успішного виклику чи кількох замовлення архівоване. Відповідь, затримка та внутрішній шлях не мають бути однаковими. Однаковим має бути очікуваний зовнішній ефект.
Чому код workflow має бути детермінованим
Durable Workflow відновлює стан оркестрації шляхом відтворення зафіксованої історії. За тих самих вхідних даних workflow та історії код workflow має планувати ті самі activity, timer та очікування в тому самому порядку. Читання реального часу, випадкове значення, запит до поточної бази даних чи мережева відповідь усередині тіла workflow можуть обрати іншу гілку під час відтворення та створити рішення, які не відповідають історії.
Перенесіть ці операції до activity або використовуйте засіб workflow, який записує значення в історію. Тоді відтворення повертає записаний результат activity чи побічного ефекту, замість повторного виконання зовнішньої операції. Детермінованість не вимагає, щоб кожен виклик activity давав універсальну відповідь поза часом. Вона вимагає, щоб відтворена оркестрація ухвалювала рішення, узгоджені з її записаною історією.
Правила написання наведено в обмеженнях workflow, а модель відтворення — у принципі роботи.
Чому activity та побічні ефекти мають бути ідемпотентними
Activity переходять через стійку межу до баз даних, платіжних API, поштових провайдерів, об’єктних сховищ та інших систем. Вони можуть доставлятися повторно після тайм-ауту, повторної спроби, збою worker чи завершення оренди. Worker може навіть завершити зовнішню зміну й зазнати збою до того, як Durable Workflow отримає його повідомлення.
Спроєктуйте очікуваний ефект activity так, щоб наступна спроба була безпечною:
- надсилайте стабільний ключ ідемпотентності до віддаленого API
- використовуйте upsert за стійким бізнес-ідентифікатором або ідентифікатором виконання
- записуйте за детермінованою назвою об’єкта чи ресурсу
- використовуйте природно ідемпотентну операцію, наприклад встановлення значення чи видалення відомого ресурсу
Ключ ідемпотентності не робить код activity детермінованим і не заважає початку наступної спроби. Він дозволяє зовнішній системі розпізнати, що повторні спроби представляють ту саму логічну операцію.
Контракти повторних спроб, повторної доставки та стійкої історії наведено в обмеженнях activity, моделі виконання activity та гарантіях виконання та ідемпотентності.
Одна межа для всіх SDK
Той самий підхід діє незалежно від того, чи worker написано на PHP, Python або Rust:
- Зберігайте оркестрацію безпечною для відтворення: за тих самих вхідних даних та історії видавайте ті самі стійкі команди.
- Розміщуйте мережеві виклики, годинники, випадковість та змінний зовнішній стан за межею activity.
- Надавайте кожній activity із побічними ефектами стабільну ідентичність, за якою цільова система може усувати дублікати.
Синтаксис мови змінюється. Межа між детермінованістю та ідемпотентністю залишається.