Module: WithAdvisoryLock::MySQLAdvisory

Extended by:
ActiveSupport::Concern
Defined in:
lib/with_advisory_lock/mysql_advisory.rb

Constant Summary collapse

LOCK_PREFIX_ENV =
'WITH_ADVISORY_LOCK_PREFIX'

Instance Method Summary collapse

Instance Method Details

#lock_keys_for(lock_name) ⇒ Object



56
57
58
59
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 56

def lock_keys_for(lock_name)
  lock_str = "#{ENV.fetch(LOCK_PREFIX_ENV, nil)}#{lock_name}"
  [lock_str]
end

#release_advisory_lock(*args, **kwargs) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 28

def release_advisory_lock(*args, **kwargs)
  # Handle both signatures - ActiveRecord's built-in and ours
  if args.length == 1 && kwargs.empty?
    # ActiveRecord's signature: release_advisory_lock(lock_id)
    # Called by Rails migrations with a single positional argument
    super
  else
    # Our signature: release_advisory_lock(lock_keys, lock_name:, **)
    lock_keys = args.first
    execute_successful?("RELEASE_LOCK(#{quote(lock_keys.first)})")
  end
rescue ActiveRecord::StatementInvalid => e
  # If the connection is broken, the lock is automatically released by MySQL
  # No need to fail the release operation
  connection_lost = case e.cause
                    when defined?(Mysql2::Error::ConnectionError) && Mysql2::Error::ConnectionError
                      true
                    when defined?(Trilogy::ConnectionError) && Trilogy::ConnectionError
                      true
                    else
                      e.message =~ /Lost connection|MySQL server has gone away|Connection refused/i
                    end

  return if connection_lost

  raise
end

#supports_database_timeout?Boolean



61
62
63
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 61

def supports_database_timeout?
  true
end

#try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seconds: nil) ⇒ Object

Raises:

  • (ArgumentError)


11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 11

def try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seconds: nil)
  raise ArgumentError, 'shared locks are not supported on MySQL' if shared
  raise ArgumentError, 'transaction level locks are not supported on MySQL' if transaction

  # MySQL GET_LOCK supports native timeout:
  # - timeout_seconds = nil: wait indefinitely (-1)
  # - timeout_seconds = 0: try once, no wait (0)
  # - timeout_seconds > 0: wait up to timeout_seconds
  mysql_timeout = case timeout_seconds
                  when nil then -1
                  when 0 then 0
                  else timeout_seconds.to_i
                  end

  execute_successful?("GET_LOCK(#{quote(lock_keys.first)}, #{mysql_timeout})")
end