Incerteza
Incerteza

Reputation: 34934

after_commit or after_create for a model where a transaction is used?

I need to create a User and send an email upon registration:

class User < ActiveRecord::Base

  after_commit :send_email, on: :create # I believe it's better than after_create
  # after_create :send_email, on: :create

  class << self
    def create_with_some_params(params)
      #....
      user.transaction do
        if user.save && some_condition
          user.category = category
          raise ActiveRecord::Rollback unless user.category.persisted?
        end  
      end

      rescue => e
        logger.error e.message
      ensure 
        [user, some_data]
      end
    end
   end

  send_email(....)
       #.....
  end
end

At first I used after_create to send an email, sending email was being performed within transaction. And pretty often a timeout occurred. So I started using after_commit because it's called after the transaction and thus the timeout won't happen due to email being slowly.

So in this case with user.transaction do, should I use after_commit instead of after_create to avoid transaction timeouts?

Upvotes: 1

Views: 999

Answers (1)

craig.kaminsky
craig.kaminsky

Reputation: 5598

I prefer after_commit in these scenarios because it does ensure that the entire transaction is done and committed to the DB before it 'acts'.

However, one thing you can do to help with the email slowness is add something like Sidekiq or DelayedJob (background processing) to improve app performance, especially Rails' notoriously slow mailers.

We've been using Sidekiq at work for a couple years and it's been a godsend as our app is a content delivery system!

MyMailer.delay.send_some_email(stuff) # this is how we can asynchronously send an email with Sidekiq

Using a background processor would eliminate the transaction timeouts should you either (a) want to stick with after_create or (b) just want to speed up mailers :)!

Upvotes: 1

Related Questions