[英]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.我有一个计算,但我的计算不是线程安全的,它必须使用increment或increment_counter,但我不知道如何实现它。 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结尾
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
.如果您认为
@purchase
或@product
对象在该代码被命中之前发生变化,我建议您使用.reload
。 If you believe your calculation needs to run asynchronously you need to consider using an async job framework like ActiveJob or Sidekiq .如果您认为您的计算需要异步运行,您需要考虑使用异步作业框架,如ActiveJob或Sidekiq 。
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.两者都读取
@product.quantity
,都计算new_quantity
,然后您最终得到其中一个请求将新值写入数据库,而另一个稍慢的请求覆盖该值。
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
and Locking::Pessimistic
. Ruby on Rails 支持
Locking::Optimistic
和Locking::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.另一种方法可能是在 SQL 中执行更新,以原子方式更新记录。 Rails has the method
update_counters
that would allow you to calculate and update the record in the database like this: Rails 有
update_counters
方法,可以让你像这样计算和更新数据库中的记录:
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.但是这种方式可能(取决于你的
check_quantity
内部所做的)可能仍然会导致竞争条件问题,因为该方法中的检查不会知道任何其他请求在之前立即通过检查并在之后立即更新数据库。
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.
但选择哪种方法取决于您的具体用例以及您需要的安全级别、用户体验和数据完整性。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.