i have a calculation, but my calculation is not thread-safe, it must be using increment or increment_counter, but I have no idea how to implement it. here's my code
def create
if @purchase.check_quantity(@product, purchase_params[:quantity].to_i)
return render :new
end
#calculation is not thread-safe
new_quantity = @product.quantity - purchase_params[:quantity].to_i
@purchase.assign_attributes(purchase_params)
if @purchase.save
@product.update(:quantity => new_quantity)
redirect_to product_url(@product)
else
flash[:error] = @purchase.errors.full_messages.join(', ')
render :new
end
end
CodePudding user response:
I'm not sure what about your code is "not thread-safe" but I don't believe your issue is thread safety.
If you think the @purchase
or @product
objects are changing just before that code gets hit, I'd recommend using .reload
. If you believe your calculation needs to run asynchronously you need to consider using an async job framework like ActiveJob or Sidekiq.
CodePudding user response:
You already noticed that you might run into race conditions in which two requests to your application enter that method almost at the same time. Both read @product.quantity
, both calculate new_quantity
and then you end up with one of those requests writing the new value into the database, and the other, slightly slower request overrides that value.
There are two approaches to solve this issue.
Lock the record for an update. Another process trying to update the same record at the same time will then raise an error. Ruby on Rails supports
Locking::Optimistic
andLocking::Pessimistic
. It depends a bit on your specific use-case what might be the better approach.An alternative might be to do the update in SQL that atomically updates records. Rails has the method
update_counters
that would allow you to calculate and update the record in the database like this:Product.update_counters(@product.id, quantity: -purchase_params[:quantity].to_i)
But this way might (depending on what your
check_quantity
internally does) might still cause race condition issues because the check in that method will not be aware of any other requests passing the check immediately before and updating the database immediately after.
Both methods might help to solve the issue. But which method to choose depends on your specific use case and the level of security, user experience, and data integrity you need.