docbrown/app/services/bulk_sql_delete.rb
Jamie Gaskins 34a61abdca
Remove txn from with_statement_timeout block (#15088)
Turns out this caused a *different* problem. It would work for the first
delete batch in `BulkSqlDelete`, but subsequent `DELETE` queries would
then get their `statement_timeout`s reset to the global setting. The
issue was that `BulkSqlDelete` uses methods that don't respect nested
transactions, and we were already resetting the value of the setting to
its previous value anyway, so there was really no reason to run it
inside a transaction.

The only failure mode I know of here is that the `ensure` block breaks
because `connection` is severed. In that case, the value is reset anyway
when the connection pool replaces the connection so the failure does not
propagate.
2021-10-15 14:53:35 -04:00

63 lines
1.5 KiB
Ruby

class BulkSqlDelete
STATEMENT_TIMEOUT = ENV.fetch("STATEMENT_TIMEOUT_BULK_DELETE", 10_000).to_i.seconds / 1_000.to_f
def self.delete_in_batches(sql)
ActiveRecord::Base.connection_pool.with_connection do |connection|
perform_and_log(sql) do
unless Rails.env.test?
connection.begin_db_transaction
end
result = ApplicationRecord.with_statement_timeout STATEMENT_TIMEOUT, connection: connection do
connection.exec_delete(sql)
end
connection.commit_db_transaction unless Rails.env.test?
result
end
end
end
def self.perform_and_log(delete_sql)
deleted = 0
loop do
result = 0
duration = Benchmark.realtime do
result = yield
end
log_deletion_batch(delete_sql, result, duration)
deleted += result
break if result.zero?
end
deleted
rescue StandardError => e
log_deletion_error(delete_sql, e)
raise e
end
private_class_method :perform_and_log
def self.log_deletion_batch(statement, count, duration)
Rails.logger.info(
tag: "notification_delete",
statement: statement,
rows_deleted: count,
duration: duration,
)
end
private_class_method :log_deletion_batch
def self.log_deletion_error(statement, exception)
Rails.logger.error(
tag: "notification_delete",
statement: statement,
exception_message: exception.message,
backtrace: exception.backtrace.join("\n"),
)
end
private_class_method :log_deletion_error
end