#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 в любом случае это будет означать «НАЧАТЬ или НАЧАТЬ ТРАНЗАКЦИЮ».