Skip to content

Instantly share code, notes, and snippets.

@neohunter
Created August 12, 2024 16:07
Show Gist options
  • Save neohunter/aa1c0cdd8abd655b35203e99a75f505d to your computer and use it in GitHub Desktop.
Save neohunter/aa1c0cdd8abd655b35203e99a75f505d to your computer and use it in GitHub Desktop.
# This incorporates sorting transaction callbacks for `belongs_to` to avoid # accidental deadlocks due to `touch: true` configuration. # # This sort the `updated_at` queries first by table name. As records within a # single table could also appear in any order it also sort by id. Lastly, # it sorts looking up the longest `belongs_to` chain that …
# frozen_string_literal: true
# This incorporates sorting transaction callbacks for `belongs_to` to avoid
# accidental deadlocks due to `touch: true` configuration.
#
# This sort the `updated_at` queries first by table name. As records within a
# single table could also appear in any order it also sort by id. Lastly,
# it sorts looking up the longest `belongs_to` chain that a model can appear in
# and update the deepest models first.
module ActiveRecord
module ConnectionAdapters
class Transaction
def before_commit_records
return unless records && @run_commit_callbacks
records.uniq.sort_by(&DeadlockPreventionOrder).each(&:before_committed!)
end
end
class DeadlockPreventionOrder
include Comparable
def self.to_proc
method(:new).to_proc
end
def self.association_depth(model, stack: [])
@association_depth ||= {}
@association_depth.fetch(model.name) do |key|
depths = model.reflect_on_all_associations(:belongs_to)
.select { |belonging| belonging.options[:touch] }
.map do |belonging|
if belonging.polymorphic?
1
elsif stack.include?(belonging.klass) || belonging.table_name == model.table_name
0
else
association_depth(belonging.klass, stack: [model].concat(stack)) + 1
end
end
@association_depth[key] = depths.max || 0
end
end
delegate :association_depth, to: :class
attr_reader :object
def initialize(record)
@object = record
end
def <=>(other)
by_depth(other) || by_table(other) || by_id(other)
end
private
def by_depth(other)
(association_depth(other.object.class) <=> association_depth(object.class)).nonzero?
end
def by_table(other)
(object.class.table_name <=> other.object.class.table_name).nonzero?
end
def by_id(other)
(object.id <=> other.object.id) || 0
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment