Письмо клиенту ушло...
 
Уведомления
Очистить всё

Письмо клиенту ушло, а заказ откатился. Колбэк внутри транзакции?

4 Сообщения
4 Пользователи
7 Реакции
471 Просмотры
tatiana.golubeva
Сообщения: 5
Автор темы
Дружит с bundler бэкенд-разработчица
Присоединился: 1 год назад
[#86]

Пришла в Ruby из Go полгода назад и ловлю странное. При оформлении заказа клиенту уходит письмо «заказ принят», а заказа в базе нет: транзакция откатилась из-за ошибки при списании остатков. Письмо отправляется из колбэка модели:

class Order < ApplicationRecord
  after_create :send_confirmation

  private

  def send_confirmation
    OrderMailer.confirmation(self).deliver_now
  end
end

Создание заказа и списание остатков завёрнуты в один transaction. Rails 8.0, MySQL 8.


3 ответа
GalinaKorneva
Сообщения: 30
Читает чужие PR бэкенд-разработчица
Присоединился: 4 года назад

after_create выполняется внутри транзакции, до коммита. Письмо уже ушло, потом транзакция откатилась, а вернуть письмо нельзя. Нужен after_create_commit: он вызывается только после успешного коммита, а при откате не вызывается совсем.

after_create_commit :send_confirmation

Если потом переведёте письмо на deliver_later, ставьте джобу тоже только после коммита, иначе воркер может взять её раньше, чем заказ станет виден, и упадёт на десериализации. Правило простое: всё, что уходит наружу (письма, вебхуки, запросы к платёжке), только после коммита.


Ответ
kostya.sokurov
Сообщения: 73
Держит прод бэкенд-разработчик, senior
Присоединился: 4 года назад

Одна ловушка с этими колбэками. Если повесить after_create_commit и after_update_commit на один и тот же метод, сработает только последний: оба внутри превращаются в after_commit, и второй затирает первый. Пишите after_commit :send_confirmation, on: [:create, :update] или after_save_commit. А когда колбэков после коммита становится больше трёх, я бы вынес оформление заказа в отдельный объект и вызывал письмо явно, читать такое проще.


Ответ
Валерий40
Сообщения: 33
Читает чужие PR фулстек-разработчик
Присоединился: 4 года назад

Тоже на этом спотыкался после Laravel. Там для очереди есть отдельная настройка «после коммита», и я по привычке ждал, что Rails сделает так же сам.


Ответ