2015-05-14 2 views
1

Чтобы асинхронно обрабатывать события и создавать фид активности, я использую глобальный идентификатор Sidekiq и Ruby on Rails.Работа со устаревшими данными при выполнении асинхронных заданий с Sidekiq

Это хорошо работает для большинства видов деятельности, однако некоторым из них требуются данные, которые могут измениться к моменту выполнения задания.

Вот полностью выдуманный пример:

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? (представьте себе приложение с гораздо большим количеством этих двух/трех подвижных частей).

Это не очень хорошо масштабируется, поскольку сериализованные объекты могут занимать много места в Редисе.

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

Я бы не сказал, что данные были устаревшими, так как на самом деле у вас будет самая новая версия, только что она больше не популярна. Похоже, вы действительно хотите устаревшую версию.

Если вы не хотите, чтобы данные были изменены, вам нужно как-то кэшировать их. Либо, как вы говорите, передайте данные непосредственно на задание, или вы можете добавить некоторую форму управления версиями данных в базе данных и передать ссылку на старую версию.

Я думаю, что передача данных, необходимых вам в Redis, является разумным способом. Вы можете сериализовать только те атрибуты, которые вам действительно нужны, например оценка.

+0

Вы правы, я исправил заголовок и перефразировал вопрос. Благодаря! –

Смежные вопросы