Skip to content

Instantly share code, notes, and snippets.

@neohunter
Last active October 25, 2020 23:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neohunter/91dbe6031d099527bbc6c5a75e51d505 to your computer and use it in GitHub Desktop.
Save neohunter/91dbe6031d099527bbc6c5a75e51d505 to your computer and use it in GitHub Desktop.
deadlock order relationships updates before commit
# This avoid Deadlock
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 { |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
}
@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
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment