Callbacks outside of the database transaction
Callbacks are a fantastic way to trigger behaviour at a specific time in the lifecycle of an ActiveRecord object. If for example you want to do something before every save you would use the before_save callback. All of these callbacks run inside of a transaction which ensures data integrity. If something goes wrong, everything is rolled back.
But there are some cases where you want to run the callback after the transaction is committed. One example are expensive operations like image thumbnailing which would block the transaction too long. Another example is asynchronous processing in a queue. In an application which is using resque as the queueing system I just had the problem that every once in a while a job failed because the ActiveRecord object could not be found. The reason for this was the transaction: the resque job was queued in an after_create callback and in some rare cases the resque worker started the job faster than the database could finish the transaction! This could also happen when updating a search index or in any other task which depends on the updated state of the database.
In Rails 3 we can use the after_commit callbacks, which are triggered right after the commit of the transaction.
# triggers after the commit of every save after_commit :method_name # triggers after the commit of a create after_commit :method_name, :on => :create # triggers after the commit of an update after_commit :method_name, :on => :update # triggers after the commit of a destroy after_commit :method_name, :on => :destroy
When using Rails 2
As the application I am working on is still on Rails 2.3 I am now using the after_commit gem which provides this functionality with a slightly different syntax also for earlier versions of Rails (for all versions between 1.2 and 2.3).
# triggers after the commit of every save after_commit :method_name # triggers after the commit of a create after_commit_on_create :method_name # triggers after the commit of an update after_commit_on_update :method_name # triggers after the commit of a destroy after_commit_on_destroy :method_name
Callbacks for rollback
Both the after_commit gem and Rails 3 are also providing additional callbacks which trigger on a rollback of the transaction. These callbacks could be very useful for cleaning up things outside of the database.