Brian
Brian

Reputation: 7145

Do rails Transactions blocks exit after all actions have been committed?

Related to Run rails code after an update to the database has commited, without after_commit, but I think deserving its own question.

If I have code like this:

my_instance = MyModel.find(1)
MyModel.transaction do 
  my_instance.foo = "bar"
  my_instance.save!
end
new_instance = MyModel.find(1)
puts new_instance.foo

Is this a guarantee that new_instance.foo will always output "bar" and not its previous value? I'm looking for a way to ensure that all the database actions that occur in a previous statement are committed BEFORE executing my next statements. Rails has an after_commit hook for this, but I don't want this code executed every time... only in this specific context.

I can't find anything in the documentation on Transactions that would indicate if Transaction blocks are "blocking". If they are blocking, that will satisfy my requirement. Unfortunately, I can't think of a practical way to test this behavior to confirm my suspicions one way or another.

Upvotes: 3

Views: 3214

Answers (1)

Ben
Ben

Reputation: 472

Still researching this, but I think a transaction does block code execution until after the database confirms that it has written. Since "save!" is automatically wrapped in a transaction by Rails, the relevant code should run synchronously. The extra transaction block should be unnecessary.

I don't think Rails returns as soon as it hands off the call to the DB when the DB calls are within a transaction. The confusion I had was with after_save callbacks. After_save callbacks suffer from race conditions because they are in fact part of the transaction that saves are automatically wrapped in, so any code called by an after_save callback is not race condition safe, it is not protected by the transaction. Only after_commit calls are safe. Within the transaction Rails will hand off to the DB and then execute after_save callbacks before the DB has finished committing.

Studying this for more insights:

https://github.com/rails/rails/blob/bfdd3c2182156fa2cb81ed4f048b065a2d6f1341/activerecord/lib/active_record/connection_adapters/abstract/transaction.rb

UPDATE

Changing my answer to "no". It doesn't appear that save! or save blocks execution. From these two resources, looks like this is a common problem:

https://github.com/resque/resque/wiki/FAQ#how-do-you-make-a-resque-job-wait-for-an-activerecord-transaction-commit

https://blog.engineyard.com/2011/the-resque-way

Upvotes: 3

Related Questions