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.
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...