ruby on rails -在使用Sidekiq执行异步作业时处理过时的数据



为了异步处理事件并创建活动提要,我使用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 worker在每次电影更新时执行工作:

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/手动传入所有需要的数据,并相应地调整worker:

MovieUpdatedWorker.perform_async(
  movie: self,
  score: score,
  likes_count: likes.count
)

这可以工作,但需要重新实现/复制依赖于scorepopular?等数据的所有方法(想象一个应用程序有比这两个/三个可移动的部分更多的方法)。

这也不能很好地扩展,因为序列化的对象在Redis中会占用很多空间。

2/在传递给worker的记录上存根一些方法:

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

这不是功能,但您可以想象处理实际记录的便利性,而不必重新实现现有方法,并处理实际数据。

您是否看到了其他"冻结"对象数据并异步处理它们的策略?你觉得这两个人怎么样?

我不会说数据过时,因为您实际上拥有它的最新版本,只是它不再流行。听起来你想要的是旧版本。

如果你不希望数据被更改,你需要以某种方式缓存它。要么像您说的那样,直接将数据传递给作业,要么您可以在数据库中添加某种形式的数据版本控制,并传递对旧版本的引用。

我认为传递数据你需要Redis是一个合理的方式。您可以只序列化您真正关心的属性,例如score。

相关内容

  • 没有找到相关文章

最新更新