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.
63 lines
1.5 KiB
Ruby
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
|