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 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
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_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!