如何防止Sidekiq对数据库进行并行修改

时间:2013-10-14 08:35:37

标签: ruby-on-rails multithreading sidekiq

我有一名工作人员试图根据某些条件找到照片匹配。每张照片都有一个独特的匹配。我编写了这样的代码:

class PhotoDeliveryWorker
include Sidekiq::Worker

def perform(photo_id)
  photo = Photo.find(photo_id)
  unless photo.match
    matches = Photo.where(some_condition: "some_value")
    match = matches.first

    if match
      # Do something to photo
      photo.match = match

      if photo.save
        match.some_condition = "another_value"
      else
        schedule photo_id
      end
    else
      # Couldn't find a match
      schedule photo_id
    end
  end
end
private
def schedule(photo_id)
  PhotoDeliveryWorker.perform_in 1.hours, photo_id
end
end

如您所见,worker获取第一个传递条件的模型对象,然后更改match以将其从将来的工作者匹配列表中排除。

问题是当一些工作人员一次执行时,他们都获得相同的matches列表,因此修改了同一个实体。但我需要为每张照片添加一个独特的匹配。

我该如何解决?

其他信息:

问:我为什么要雇佣工人?

答:如果我找不到匹配项,我需要稍后重试。

问:为什么我使用Sidekiq多线程?

答:我需要尽可能快地处理照片。

可能我可以在每个人的开头得到当前活跃工人的数量,然后取第一个而不是第一个。但那个解决方案闻起来有点味道,不是吗?

更新

其他问题:我可以使用ActiveRecord锁定这个问题吗?我对with_lock以及所有这些东西都不是很熟悉。

1 个答案:

答案 0 :(得分:0)

解决问题的一个非常简单的方法就是分割工作量,这样工人就不会互相干扰。

如果您有2名工作人员,则可以为他们编号,并在ID photo.id % 2上执行模数。这样你只得到0和1。数字0的工人只能在他的批次上工作,另一名工人的编号为1。通过增加模数,你可以达到你想要的任何数量的工人。