Skip to Content

Rails - Mailer problem with sidekiq

Posted on

As humans, we have problems with doing things in parallel. The same as coding :v

When a user sends invitation to his friend, we get information from Sidekiq, but this invitation is not found in database. why?

class Invitation < ActiveRecord::Base
  after_save :send_invitation_email

  # (...)

  def send_invitation_email
    AppMailer.invitation_email(
      user_id: user.id, recipient_email: email, invitation_id: self.id
    ).deliver_later
  end
end

The problem is we are using after_save, and after_save isn’t the last one in list Rails callbacks:

before_validation
after_validation
before_save
around_save
before_create
around_create
after_create
after_save
after_commit/after_rollback

So when send_invitation_email is called, I still have id, but this invitation is not persist in the database yet (cause data is not committed to database). Looking below on this scheme actions(1) and (2), are faster than (3)

The solution is changing after_save to after_commit to ensure data is saved in database, or a better way, split this logic to service, SHOULD NOT use callback cause it’s pain to handle latter.

When you move this code to service, BE CAREFULL WITH TRANSACTION. Transaction and mailer will work in parallel, and this can lead to the same problem.

  def perform
    ActiveRecord::Base.transaction do
      admin.update! password: password
      admin.request_password_change!
    end
  rescue ActiveRecord::RecordInvalid
    response_data false
  else
    Admin::AdminMailer.resend_password_admin(admin, password).deliver_later
    response_data true
  end

Use else block to ignore parallel problem!

comments powered by Disqus