ruby-on-railsheroku

Why can't I update an ActiveRecord inside ensure block?


I have a Rails 5.2.3 (MRI 2.5.5) app hosted on Heroku. I've added some cleanup code inside an ensure block, so that it runs when the app receives the SIGTERM for its daily restart. The cleanup code involves a simple update on an ActiveRecord object. The ensure block runs as expected, but the database transaction itself always rolls back.

The transaction inside ensure works fine during normal operations, but I cannot get it to complete after it has received a SIGTERM. I'm going by what's shown in Heroku's docs.

begin
  ## Do some work...
ensure
  ## Runs (not exclusively) after SIGTERM...
  ## ...except for ActiveRecord update/create etc!

I couldn't find any error message to go by.


Solution

  • As of ActiveRecord 7.2.2, when the update method runs, it eventually calls ActiveRecord::ConnectionAdapters::TransactionManager#within_new_transaction. This method checks if Thread.current.status == "aborting" and issues a rollback_transaction.

    Therefore, if the update operation is inside an ensure block, that is running while the process is exiting, you can encounter this issue. One potential solution is to use update_column or update_columns, which uses a separate process.

    def within_new_transaction(isolation: nil, joinable: true)
      @connection.lock.synchronize do
        transaction = begin_transaction(isolation: isolation, joinable: joinable)
    
        begin
          yield transaction.user_transaction
        rescue Exception => error
          rollback_transaction
          after_failure_actions(transaction, error)
    
          raise
        ensure
          unless error
            if Thread.current.status == "aborting"
              rollback_transaction
            else
              # omitted...