【发布时间】: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 列表/集,然后使用
BRPOP或RPOP获取匹配的 ID 并确保永远不会重复使用此 ID。编辑:您可能还可以处理包含所有 ID 的列表/集,以确保它不会在 redis 中重用
标签: ruby-on-rails multithreading sidekiq