为了异步处理事件并创建活动源,我使用的是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
这不起作用,但您可以想象处理实际记录的便利性,不必重新实现现有方法,以及处理实际数据。
您是否看到其他策略来“冻结”对象数据并异步处理它们?你怎么看待这两个?
答案 0 :(得分:0)
我不会说数据是陈旧的,因为你实际上已经拥有它的最新版本,只是它不再流行。听起来你真的想要陈旧的版本。
如果您不希望数据发生更改,则需要以某种方式对其进行缓存。您可以这样说,直接将数据传递给作业,或者您可以在数据库中添加某种形式的数据版本,并将引用传递给旧版本。
我认为传递Redis所需的数据是一种合理的方式。您可以仅序列化您真正关心的属性,例如得分。