【问题标题】:How to prevent Sidekiq from parallel modifications to database如何防止 Sidekiq 并行修改数据库
【发布时间】:2013-10-15 14:12:08
【问题描述】:

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

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 以将其从未来的worker 匹配列表中排除。

问题是当几个工人同时执行时,他们都得到相同的matches 列表并因此修改相同的实体。但我需要为每张照片单独匹配。

我该如何解决?

附加信息:

问:为什么我使用工人?

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

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

A:我需要尽快处理照片。

也许我可以在每个人的开头获得当前活跃工人的数量,并参加第 n 场比赛而不是第一次比赛。但是那个解决方案有点味道,不是吗?

更新:

附加问题:我可以使用一些关于 ActiveRecord 锁定的东西来解决这个问题吗?我对with_lock 和所有这些东西不是很熟悉。

【问题讨论】:

  • 您可以为此下拉到原始 SQL:UPDATE photos SET match_id = ? where id = ? and match_id IS NULL。这样,只有第一次更新才会找到并更新记录。
  • 假设一个简单的条件,您可以使用照片 ID 为每个条件保留一个 redis 列表/集,然后使用 BRPOPRPOP 获取匹配的 ID 并确保永远不会重复使用此 ID。编辑:您可能还可以处理包含所有 ID 的列表/集,以确保它不会在 redis 中重用

标签: ruby-on-rails multithreading sidekiq


【解决方案1】:

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

如果你有 2 名工人,你可以给他们编号,然后对 id photo.id % 2 进行取模。这样你就只能得到 0 和 1。编号为 0 的工人仅在他的批次上工作,编号为 1 的工人在另一个上工作。通过增加模数,您可以增加任意数量的工人。

【讨论】:

  • 实际代码要复杂得多,对于每张照片,可能匹配的真实组可能不同。所以他们是不平等的,我不能把这个列表分成每个工人的部分。但是感谢您提出有趣的建议!
  • 另一个简单的解决方案是在匹配项中保留一个工作人员 ID,以便他们可以以这种方式离开。
  • 做这种事情的通常方法是让一个主要的工人向其他工人发放比赛。所以你只有一名工作人员负责寻找匹配项,其余的人则在做艰苦的工作,只分配一个匹配项。
猜你喜欢
  • 1970-01-01
  • 2013-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-28
  • 1970-01-01
相关资源
最近更新 更多