【发布时间】:2015-05-14 23:01:12
【问题描述】:
为了异步处理事件并创建活动提要,我使用 Sidekiq 和 Ruby on Rails 的全局 ID。
这适用于大多数类型的活动,但其中一些活动需要的数据可能会在作业执行时发生变化。
这是一个完全虚构的例子:
class Movie < ActiveRecord::Base
include Redis::Objects
value :score # stores an integer in Redis
has_many :likes
def popular?
likes.count > 1000
end
end
每次更新电影时,Sidekiq 工作人员都会执行一项工作:
class MovieUpdatedWorker
include Sidekiq::Worker
def perform(global_id)
movie = GlobalID::Locator.locate(global_id)
MovieUpdatedActivity.create(movie: movie, score: movie.score) if movie.popular?
end
end
现在,想象一下 Sidekiq 落后了,在它有机会执行其工作之前,电影的 score 在 Redis 中更新,一些用户不喜欢这部电影,popular 方法现在返回 false。
Sidekiq 最终使用更新的数据。
我正在寻找方法来安排作业,同时确保在执行作业时所需的数据不会改变。一些想法:
1/ 手动传入所有需要的数据并相应地调整工人:
MovieUpdatedWorker.perform_async(
movie: self,
score: score,
likes_count: likes.count
)
这可行,但需要重新实现/复制所有依赖数据的方法,例如 score 和 popular?(想象一个应用程序远不止这两个/三个可移动部分)。
这也不能很好地扩展,因为序列化的对象可能会占用 Redis 中的大量空间。
2/ 在传递给工作人员的记录上存根一些方法:
MovieUpdatedWorker.perform_async(
global_id,
stubs: { score: score, popular?: popular? }
)
class MovieUpdatedWorker
include Sidekiq::Worker
def perform(global_id, stubs: {})
movie = GlobalID::Locator.locate(global_id)
# inspired by RSpec
stubs.each do |message, return_value|
movie.stub(message) { return_value }
end
MovieUpdatedActivity.create(movie: movie, score: movie.score) if movie.popular?
end
end
这不是功能性的,但您可以想象处理实际记录的便利性,无需重新实现现有方法并处理实际数据。
您是否看到其他“冻结”对象数据并异步处理它们的策略?你觉得这两个怎么样?
【问题讨论】:
标签: ruby-on-rails ruby asynchronous sidekiq