Как отменить все изменения, выполненные в асинхронной задаче, когда она вызывает ошибку в Sidekiq?

#ruby-on-rails #ruby #sidekiq

Вопрос:

Я использую Sidekiq и Rails (6.0.3.7). У меня есть работник, который выполняет асинхронную задачу, которая последовательно создает много данных в моей базе данных. Так что в основном это происходит, например:

  • Сначала он создает пользователя, затем создает категорию сообщений, затем создает сообщение, а затем создает 10 комментариев.

Иногда этот процесс завершается неудачей на полпути, возможно, при создании категории сообщений или при создании сообщения.

Я хочу, чтобы в случае сбоя задачи в любой заданный момент все данные, которые уже были созданы в указанной задаче, были удалены. Другой подход может заключаться в том, что все данные создаются только после того, как я уверен, что процесс не завершился неудачно. Таким образом, в принципе, он должен был бы «проверить создание» всего, прежде чем фактически записывать данные в базу данных.

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

Есть ли какой-либо подход или метод, который я мог бы использовать для этого на моем текущем работнике, не слишком возясь с фактическим кодом? Какой-то метод «двойной проверки» уже реализован в Sidekiq? Что вы рекомендуете мне изучить?

Заранее спасибо за любую помощь, которую вы можете мне оказать в этом вопросе.

Ответ №1:

Во-первых, здорово, что вы разрабатываете свои задачи так, чтобы они были «все или ничего». Основным и предпочтительным подходом является использование транзакций базы данных, поскольку именно для этого они и были разработаны. Откройте транзакцию перед началом создания сущности и зафиксируйте ее после выполнения всех проверок.

 Account.transaction do
    balance.save!
    account.save!
end
 

Обратите внимание, что методы bang (те, которые имеют трейлинг ! ) предназначены для создания исключений. Исключение автоматически отменит транзакцию, и это то, что вам нужно.

N. B. Попробуйте сделать свою задачу идемпотентной, что означает возврат одного и того же результата независимо от количества вызовов с одинаковыми входными результатами. Это может сэкономить много времени в будущем.

Комментарии:

1. Спасибо! Это именно то, что мне было нужно, мне потребовалось время, чтобы лучше понять это, но теперь мне это нравится! На самом деле я просто Использовал ActiveRecord::Base.transaction . Знаете ли вы, есть ли какая-либо причина, по которой я бы НЕ хотел этого делать? Я сделал это, потому что я создаю несколько объектов из разных моделей в рамках метода, поэтому я не мог понять, какую модель я должен был использовать, чтобы написать ее, как это сделали вы Account.transaction .

2. Я рад, что это было полезно! =) Между ними нет реальной разницы, так как все классы сущностей являются потомками ActiveRecord::Base . С точки зрения общей базы данных SQL в любом случае это будет означать «НАЧАТЬ или НАЧАТЬ ТРАНЗАКЦИЮ».