class MysqlFramework::Scripts::LockManager
Public Class Methods
new()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 8 def initialize @pool = Queue.new end
Public Instance Methods
fetch_client()
click to toggle source
This method is called to retrieve a Redlock client from the pool
# File lib/mysql_framework/scripts/lock_manager.rb, line 65 def fetch_client @pool.pop(true) rescue StandardError # By not letting redlock retry we will rely on the retry that happens in this class Redlock::Client.new([redis_url], retry_jitter: retry_jitter, retry_count: 1, retry_delay: 0) end
release_lock(key:, lock:)
click to toggle source
This method is called to release a lock
# File lib/mysql_framework/scripts/lock_manager.rb, line 44 def release_lock(key:, lock:) return if lock.nil? MysqlFramework.logger.info { "[#{self.class}] - Releasing lock: #{key}." } with_client { |client| client.unlock(lock) } end
request_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay)
click to toggle source
This method is called to request a lock (Default 5 minutes)
# File lib/mysql_framework/scripts/lock_manager.rb, line 13 def request_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay) MysqlFramework.logger.info { "[#{self.class}] - Requesting lock: #{key}." } lock = false count = 0 loop do # request a lock lock = with_client { |client| client.lock(key, ttl) } # if lock was received break out of the loop break if lock # lock was not received so increment request count count += 1 MysqlFramework.logger.debug do "[#{self.class}] - Key is currently locked, waiting for lock: #{key} | Wait count: #{count}." end # check if lock requests have exceeded max request attempts raise "Resource is already locked. Lock key: #{key}. Max attempt exceeded." if count == max_attempts # sleep and try requesting the lock again sleep(retry_delay) end lock end
with_client() { |client| ... }
click to toggle source
This method is called to retrieve a Redlock client from the pool and yield it to a block
# File lib/mysql_framework/scripts/lock_manager.rb, line 73 def with_client client = fetch_client yield client ensure @pool.push(client) end
with_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay) { || ... }
click to toggle source
This method is called to request and release a lock around yielding to a user supplied block
# File lib/mysql_framework/scripts/lock_manager.rb, line 53 def with_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay) raise 'Block must be specified.' unless block_given? begin lock = request_lock(key: key, ttl: ttl, max_attempts: max_attempts, retry_delay: retry_delay) yield ensure release_lock(key: key, lock: lock) end end
Private Instance Methods
default_max_retries()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 90 def default_max_retries @default_max_retries ||= Integer(ENV.fetch('MYSQL_MIGRATION_LOCK_MAX_ATTEMPTS', 300)) end
default_retry_delay()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 94 def default_retry_delay @default_retry_delay ||= Float(ENV.fetch('MYSQL_MIGRATION_LOCK_RETRY_DELAY_S', 1.0)) end
default_ttl()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 86 def default_ttl @default_ttl ||= Integer(ENV.fetch('MYSQL_MIGRATION_LOCK_TTL', 2000)) end
redis_url()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 82 def redis_url ENV.fetch('REDIS_URL') end
retry_jitter()
click to toggle source
# File lib/mysql_framework/scripts/lock_manager.rb, line 98 def retry_jitter @retry_jitter ||= Integer(ENV.fetch('MYSQL_MIGRATION_LOCK_JITTER_MS', 50)) end