Доволі часто на технічних інтервʼю, коли мова заходить про розподілені системи, можна почути питання про такий патерн, як Idempotency. Якщо коротко, Idempotency Key - це унікальний ідентифікатор операції, який дозволяє системі зрозуміти, що вона вже бачила цю операцію, і не виконувати її повторно.
Раніше я вже розбирав ідемпотентність у контексті HTTP-запитів, тому раджу також почитати про неї. Але на співбесіді одного визначення зазвичай недостатньо. Набагато краще показати, яку реальну проблему вирішує цей патерн.
Розберімо це на простому прикладі з оплатою.
Система без Idempotency
Уявімо, що користувач купує товар на сайті. Браузер відправляє запит у наш мікросервіс. Мікросервіс приймає його та публікує повідомлення в брокер, наприклад RabbitMQ.
Припустимо, що для доставки повідомлень використовується модель at-least-once delivery. Це означає, що брокер гарантує доставку повідомлення щонайменше один раз, але за певних обставин одне й те саме повідомлення може бути доставлене повторно.
На схемі вище RabbitMQ передає повідомлення одному з наших воркерів. Він отримує його та виконує бізнес-операцію — у нашому випадку списує гроші з картки користувача.
Після успішної обробки воркер повинен відправити брокеру ACK — підтвердження того, що повідомлення було успішно оброблено.
А тепер уявімо ситуацію:
Причина може бути будь-якою: воркер упав, стався network timeout, процес перезапустився тощо. І тут виникає важливий момент.
RabbitMQ не знає, що гроші вже були списані. Він знає лише одне: повідомлення було відправлене, але ACK не отримано. Оскільки використовується at-least-once delivery, брокер через деякий час доставить це повідомлення повторно — можливо, навіть іншому воркеру.
У результаті одна логічна операція може бути виконана декілька разів. Для платежів це очевидно неприпустимо.
Тут і зʼявляється Idempotency Key
Тепер подивімося на ту саму систему, але вже з використанням Idempotency Key.
Архітектура майже не змінюється. Головна відмінність — тепер кожна логічна операція має унікальний idempotencyKey.
Наприклад: payment:order-123
Перед тим як виконувати бізнес-логіку, воркер намагається зареєструвати цей ключ у швидкому сховищі, наприклад Redis.
Логіка приблизно така:
Що в результаті дає Idempotency
Важливо розуміти: Idempotency не гарантує, що RabbitMQ не доставить повідомлення двічі.
Задача Idempotency — зробити так, щоб повторна доставка одного й того самого повідомлення не призвела до повторного виконання бізнес-операції.
У нашому прикладі RabbitMQ може доставити повідомлення про оплату декілька разів, але завдяки Idempotency Key система розпізнає дублікати і не спише гроші повторно.