Skip to content

Instantly share code, notes, and snippets.

@TrumpClone
Last active March 21, 2017 15:42
Show Gist options
  • Save TrumpClone/6826bb80fef2cd9f402328a774617a6e to your computer and use it in GitHub Desktop.
Save TrumpClone/6826bb80fef2cd9f402328a774617a6e to your computer and use it in GitHub Desktop.
DB Locker
module Synchronizable
DEFAULT_WAIT_TIME = 10.seconds
extend self
def synchronize(*args, &block)
Transaction.run { with_advisory_lock(*args, &block) }
end
private
def with_advisory_lock(object = self, wait_time: DEFAULT_WAIT_TIME, **params)
lock_name = lock_name_for(object, params)
ActiveRecord::Base.logger.debug "Locking with #{lock_name}"
wait_time = nil if Rails.env.in?(%w[development test])
wrapped_block = proc do
ActiveRecord::Base.uncached do
ActiveRecord::Base._with_advisory_lock(lock_name) do
object.reload if object.is_a?(ActiveRecord::Base)
yield
end
end
end
if WithAdvisoryLock::Base.lock_stack.empty?
# We have to pass Timeout::Error explicitly so that transactions properly work
Timeout.timeout(wait_time, Timeout::Error, &wrapped_block)
else
wrapped_block.call
end
end
def lock_name_for(object, params)
name_parts =
case object
when String, Symbol
[object]
when Class
[object.name.underscore.dasherize]
else
[object.class.name.underscore.dasherize, object.try(:id)]
end
[name_parts, *params].flatten.compact.join("-")
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment