Ідемпотентність — властивість операції, при якій її повторний виклик з тими ж вхідними даними не змінює стан системи повторно і не створює нових побічних ефектів (side-effects).
Іншими словами: перший виклик змінює систему, а всі наступні — залишають її в тому самому стані, що й після першого виклику.Де це критично:📍
Background Jobs (напр. Sidekiq / ActiveJob): Мережа може відвалитися, джоба перезапуститься. Код має бути готовим до повторного виконання без дублювання транзакцій.
📍
REST API: Запити GET, PUT та DELETE за специфікацією HTTP мають бути ідемпотентними (на відміну від POST).
Приклад у RubyРозглянемо сервіс активації користувача.
❌
Неідемпотентний підхідПри кожному виклику метод оновлює базу даних та генерує нові фонові задачі на відправку пошти. Це створює зайве навантаження і спамить користувача.
class ActivateUser
def call(user)
user.update!(active: true, activated_at: Time.current)
UserMailer.activation_success(user).deliver_later
end
end
✅
Ідемпотентний підхід (з урахуванням конкурентності)Ми робимо умовне оновлення на рівні бази даних через update_all. Метод update_all повертає кількість оновлених рядків (1 або 0). Якщо рядок вже був оновлений іншим паралельним потоком, ми просто виходимо, не відправляючи лист повторно.
class ActivateUser
def call(user)
# Оновлюємо лише якщо active ще false (атомарна операція)
updated_rows = User.where(id: user.id, active: false)
.update_all(active: true, activated_at: Time.current)
return if updated_rows.zero?
# Лист відправиться ОДИН раз, навіть при паралельних запитах
UserMailer.activation_success(user).deliver_later
end
end
Важливо для інтерв‘ю та код-рев‘ю: Ідемпотентність гарантує незмінність стану системи, а не повернутого значення (return value). У другому прикладі перший виклик поверне об’єкт листа, а повторний — nil. Це абсолютно нормально і не порушує ідемпотентність.
TikTok |
Instagram |
Telegram