'How to make a thread safe in ruby on rails?
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
Solution 1:[1]
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::OptimisticandLocking::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_countersthat 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_quantityinternally 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.
Solution 2:[2]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | spickermann |
| Solution 2 | barnacle.m |
