使用Sidekiq的并发性导致了一些问题

时间:2015-06-06 18:38:33

标签: ruby-on-rails sidekiq

我在我的rails应用程序中使用Sidekiq一次排队50k +作业。我们的游泳池大小设置为9.

这些工作都是相关的,并且做同样的事情。我们有另一个模型,它有一个计数器。在每个作业期间,我们检查该模型是否具有值大于200的列。如果它大于200,我们创建该模型的另一个实例,其值为0并继续作业。但是,由于我们一次运行9个作业,因此所有9个作业同时读取该列的值大于200,并且所有作业都创建新实例,这是不正确的。

解决此问题的最佳方法是什么?我们基本上希望所有工作都能从最新的价值中读取。

3 个答案:

答案 0 :(得分:1)

我无法发布任何特定代码,因为它在很大程度上取决于您的数据库类型和设置,但您应该尝试数据库锁定。

工作人员在阅读表时应将其锁定,直到完成创建值为0的新记录。您应该锁定表以供读取,以便其他工作人员需要等到这一个工作人员完成。也可以锁定单独的行,但我不知道它是否适用于您的情况。

答案 1 :(得分:1)

假设您的模型被称为Counter

首先,找到/创建一个合适的计数器:

counter = Counter.where('count < 200').first_or_create ...

(注意:这不是原子的!如果你不能有超过1个活跃的计数器,那么请看:
Race conditions in Rails first_or_create
How do I avoid a race condition in my Rails app?

接下来,尝试在数据库中以原子方式递增它:

success = 
  Counter.where(id: counter.id)
         .where('count < 200')
         .update_all('count = count + 1')

如果有效,success == 1则为success == 0。如果有效,请使用计数器,否则请重试。

答案 2 :(得分:1)

Redis更适合此类操作,您可以通过Sidekiq的Redis连接轻松访问它。

value = Sidekiq.redis { |c| c.incr("my-counter") }
if value % 200 == 0
  # create new instance
end

也许这样的事情对你有用。