I have a simple model, Payments, which has two fields amount and running_balance. When a new payment record is created, we look for the running_balance of its previous payment, say last_running_balance and save last_running_balance+amount as the running_balance of the current payment.
Here are our three failed attempts to implement the Payments model. For simplicity, assume the previous payment always exists and ids are increasing as payments are created.
Attempt 1:
class Payments < ActiveRecord::Base
before_validation :calculate_running_balance
private
def calculate_running_balance
p = Payment.last
self.running_balance = p.running_balance + amount
end
end
Attempt 2:
class Payments < ActiveRecord::Base
after_create :calculate_running_balance
private
def calculate_running_balance
p = Payment.where("id < ?", id).last
update!(running_balance: p.running_balance + amount)
end
end
Attemp 3:
class Payments < ActiveRecord::Base
after_commit :calculate_running_balance
private
def calculate_running_balance
p = Payment.where("id < ?", id).last
update!(running_balance: p.running_balance + amount)
end
end
These implementations may cause race conditions in the system as we are using sidekiq to create payments in the background. Suppose the last payment is payment 1. When two new payments, say payment 2 and payment 3 are created at the same time, their running_balance might be computed based on the running balance of payment 1 because it might be the case that when payment 3 is figuring out its running balance payment 2 has not been saved to the database yet.
In particular, I am interested in a fix that avoids the running condition. I am also keen on looking at other rails apps that implement similar payment systems.