Một job idempotent là job có thể chạy nhiều lần với cùng arguments mà không gây ra kết quả sai — quan trọng vì Sidekiq/Active Job đảm bảo "at least once" delivery, không phải "exactly once".
Không idempotent (nguy hiểm):
ruby
def perform(user_id)
user = User.find(user_id)
user.increment!(:login_count) # chạy 2 lần → đếm sai
ChargeService.charge(user) # charge 2 lần!
endIdempotent (an toàn):
ruby
def perform(order_id)
order = Order.find(order_id)
return if order.paid? # guard: đã xử lý rồi thì bỏ qua
ActiveRecord::Base.transaction do
order.mark_paid!
PaymentGateway.charge(order.payment_token)
end
endKỹ thuật phổ biến: dùng DB unique constraint / Redis lock / upsert để ngăn side effect trùng.
An idempotent job can run multiple times with the same arguments without producing incorrect results — critical because Sidekiq/Active Job guarantees "at least once" delivery, not "exactly once".
Not idempotent (dangerous):
ruby
def perform(user_id)
user = User.find(user_id)
user.increment!(:login_count) # runs twice → wrong count
ChargeService.charge(user) # charges twice!
endIdempotent (safe):
ruby
def perform(order_id)
order = Order.find(order_id)
return if order.paid? # guard: already processed, skip
ActiveRecord::Base.transaction do
order.mark_paid!
PaymentGateway.charge(order.payment_token)
end
endCommon techniques: DB unique constraints, Redis locks, or upsert to prevent duplicate side-effects.