2016-08-11 3 views
0

У меня есть несколько работников Resque для одного и того же объекта (пользователя). После успешной обработки он должен уменьшить атрибут call_left.ActiveJob/Resque грязный читает. Уровень изоляции транзакции

Он отлично работает с perform_now (следовательно), но дает непредсказуемые результаты с perform_later (параллельно). В журналах есть коммиты с таким же числом calls_left.

Я попытался использовать метод reload и даже установить самый высокий уровень изоляции. Но все еще есть эта проблема.

Как решить?

class DataProcessJob < ActiveJob::Base 
    queue_as :default  
    def perform(user_id, profile_id) 
    User.transaction(isolation: :serializable) do 
     user = User.find(user_id).reload 
     user.data_process(profile_id) 
     user.update(calls_left: user.calls_left-1) 
    end 
    end 
end 

ответ

1

Первый вариант будет использовать блокировки (optimistic или pessimistic). Документация объясняет их различия, и вы можете выбрать тот, который подходит вашему делу. Кроме того, here is a relevant code snippet из документов, которые, вероятно, помогут вам, если вы пойдете с оптимистичной блокировкой.

def with_optimistic_retry 
    begin 
    yield 
    rescue ActiveRecord::StaleObjectError 
    begin 
     # Reload lock_version in particular. 
     reload 
    rescue ActiveRecord::RecordNotFound 
     # If the record is gone there is nothing to do. 
    else 
     retry 
    end 
    end 
end 

Второй вариант будет увеличивать calls_left поле с помощью сырой SQL строки запроса. Базовая БД будет заниматься атомными обновлениями.


последнее, но не менее, вы можете использовать decrement!(:calls_left) метод, чтобы сделать код более читаемым.

+0

замки не работают. оптимистичный не работает, потому что он предназначен для работы в едином процессе. пессимистический - просто нет. Raw SQL, похоже, работает, спасибо. –

+0

На самом деле, Rails реализует оптимистичную блокировку, добавляя поле 'lock_version' к вашей таблице (немного отличающееся от оптимистической блокировки уровня БД). Таким образом, он будет работать в сценариях без единого процесса, если они будут реализовывать проверку версии блокировки в запросе обновления SQL. Но, я не уверен, не заглянул в исходный код. Вы пытались использовать оптимистичную блокировку? Если вы это сделали, и это не сработало, сообщите мне. Было бы неплохо принять это к сведению в будущем. – Uzbekjon

+0

Оптимистическая блокировка включена по умолчанию. Он вызывает «спасение ActiveRecord :: StaleObjectError», если объект устарел, что неверно в моем случае. И это из документации: 'Этот механизм блокировки будет функционировать внутри одного рубинового процесса. Чтобы он работал во всех веб-запросах, рекомендуется использовать lock_version как скрытое поле для вашей формы. ' –

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