class MysqlGetlock

Constants

PSEUDO_INFINITE_TIMEOUT
TIMEOUT
VERSION

Attributes

key[R]
logger[R]
mysql2[R]
timeout[R]

Public Class Methods

new(mysql2:, key:, logger: nil, timeout: TIMEOUT) click to toggle source
# File lib/mysql_getlock.rb, line 13
def initialize(mysql2:, key:, logger: nil, timeout: TIMEOUT)
  self.mysql2 = mysql2
  set_key(key)
  set_logger(logger)
  set_timeout(timeout)
end

Private Class Methods

session_keys() click to toggle source
# File lib/mysql_getlock.rb, line 161
def self.session_keys
  @session_keys
end

Public Instance Methods

lock() click to toggle source
# File lib/mysql_getlock.rb, line 29
def lock
  _lock(@timeout)
end
locked?() click to toggle source
# File lib/mysql_getlock.rb, line 57
def locked?
  results = mysql2.query(%Q[select is_used_lock('#{key}')], as: :array)
  !!results.to_a.first.first
end
mysql2=(mysql2) click to toggle source

Use this setter if you reconnect mysql2 (which means renew Mysql2::Client instance), but still want to use same MysqlGetlock instance

# File lib/mysql_getlock.rb, line 22
def mysql2=(mysql2)
  @mysql2 = mysql2
  @mysql_version = nil
  @multiple_lockable = nil
  @infinite_timeoutable = nil
end
self_locked?() click to toggle source

return true if locked by myself

# File lib/mysql_getlock.rb, line 63
def self_locked?
  results = mysql2.query(%Q[select is_used_lock('#{key}')], as: :array)
  lock_id = results.to_a.first.first
  return nil if lock_id.nil?
  results = mysql2.query(%Q[select connection_id()], as: :array)
  self_id = results.to_a.first.first
  self_id == lock_id
end
synchronize() { || ... } click to toggle source
# File lib/mysql_getlock.rb, line 72
def synchronize(&block)
  raise LockError unless lock
  begin
    yield
  ensure
    unlock
  end
end
try_lock() click to toggle source
# File lib/mysql_getlock.rb, line 33
def try_lock
  _lock(0)
end
unlock() click to toggle source
# File lib/mysql_getlock.rb, line 37
def unlock
  if !multiple_lockable? and (current_session_key and current_session_key != key)
    raise Error, "get_lock() was issued for another key '#{current_session_key}', please unlock it beforehand"
  end

  results = mysql2.query(%Q[select release_lock('#{key}')], as: :array)
  release_current_session_key
  case results.to_a.first.first
  when 1
    logger.info { "#{log_head}Released a mysql lock '#{key}'" } if logger
    true
  when 0
    logger.info { "#{log_head}Failed to release a mysql lock since somebody else locked '#{key}'" } if logger
    false
  else # nil
    logger.info { "#{log_head}Mysql lock did not exist '#{key}'" } if logger
    true
  end
end

Private Instance Methods

_lock(timeout) click to toggle source
# File lib/mysql_getlock.rb, line 83
def _lock(timeout)
  if !multiple_lockable? and (current_session_key and current_session_key != key)
    raise Error, "get_lock() is already issued in the same connection for '#{current_session_key}'"
  end

  if timeout == 0 # try_lock
    # no wait
  else
    logger.info { "#{log_head}Wait #{timeout < -1 ? '' : "#{timeout} sec "}to acquire a mysql lock '#{key}'" } if logger
  end

  results = mysql2.query(%Q[select get_lock('#{key}', #{timeout})], as: :array)
  case results.to_a.first.first
  when 1
    logger.info { "#{log_head}Acquired a mysql lock '#{key}'" } if logger
    set_current_session_key(key)
    true
  when 0
    if timeout == 0 # try_lock
      logger.info { "#{log_head}A mysql lock is already acquired by the other session '#{key}'" } if logger
    else
      logger.info { "#{log_head}Timeout to acquire a mysql lock '#{key}'" } if logger
    end
    release_current_session_key
    false
  else # nil
    logger.info { "#{log_head}Unknown Error to acquire a mysql lock '#{key}'" } if logger
    release_current_session_key
    false
  end
end
current_session_key() click to toggle source
# File lib/mysql_getlock.rb, line 165
def current_session_key
  MysqlGetlock.session_keys[mysql2.object_id]
end
infinite_timeoutable?() click to toggle source

Before MySQL 5.5.8, a negative timeout value means infinite timeout on Windows. As of 5.5.8, this behavior applies on all platforms.

# File lib/mysql_getlock.rb, line 144
def infinite_timeoutable?
  return @infinite_timeoutable unless @infinite_timeoutable.nil?
  major, minor, patch = mysql_version
  @infinite_timeoutable = (major > 5) || (major == 5 && minor > 5) || (major == 5 && minor == 5 && patch >= 8)
end
log_head() click to toggle source
# File lib/mysql_getlock.rb, line 132
def log_head
  "PID-#{::Process.pid} TID-#{::Thread.current.object_id.to_s(36)}: "
end
multiple_lockable?() click to toggle source

From MySQL 5.7.5, multiple simultaneous locks can be acquired

# File lib/mysql_getlock.rb, line 137
def multiple_lockable?
  return @multiple_lockable unless @multiple_lockable.nil?
  major, minor, patch = mysql_version
  @multiple_lockable = (major > 5) || (major == 5 && minor > 7) || (major == 5 && minor == 7 && patch >= 5)
end
mysql_version() click to toggle source
# File lib/mysql_getlock.rb, line 150
def mysql_version
  return @mysql_version if @mysql_version
  results = mysql2.query('select version()', as: :array)
  version = results.to_a.first.first
  major, minor, patch = version.split('.').map(&:to_i)
  @mysql_version = [major, minor, patch]
end
release_current_session_key() click to toggle source
# File lib/mysql_getlock.rb, line 173
def release_current_session_key
  MysqlGetlock.session_keys.delete(mysql2.object_id)
end
set_current_session_key(key) click to toggle source
# File lib/mysql_getlock.rb, line 169
def set_current_session_key(key)
  MysqlGetlock.session_keys[mysql2.object_id] = key
end
set_key(key) click to toggle source
# File lib/mysql_getlock.rb, line 124
def set_key(key)
  @key = Mysql2::Client.escape(key)
end
set_logger(logger) click to toggle source
# File lib/mysql_getlock.rb, line 128
def set_logger(logger)
  @logger = logger
end
set_timeout(timeout) click to toggle source
# File lib/mysql_getlock.rb, line 115
def set_timeout(timeout)
  if infinite_timeoutable?
    @timeout = timeout
  else
    # To support MySQL < 5.5.8, put large number of seconds to express infinite timeout spuriously
    @timeout = (timeout < 0 ? PSEUDO_INFINITE_TIMEOUT : timeout)
  end
end