简体   繁体   English

如何在 ruby on rails 中使线程安全?

[英]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 .如果您认为您的计算需要异步运行,您需要考虑使用异步作业框架,如ActiveJobSidekiq

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.有两种方法可以解决这个问题。

  1. 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::OptimisticLocking::Pessimistic It depends a bit on your specific use-case what might be the better approach.这在一定程度上取决于您的具体用例,哪种方法可能更好。

  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM