Module: WithAdvisoryLock::MySQLAdvisory
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/with_advisory_lock/mysql_advisory.rb
Instance Method Summary collapse
-
#advisory_lock_exists_for?(lock_name, shared: false) ⇒ Boolean
Non-blocking check via IS_USED_LOCK, no lock is acquired as a side effect.
- #lock_keys_for(lock_name) ⇒ Object
- #release_advisory_lock(*args, **kwargs) ⇒ Object
- #supports_database_timeout? ⇒ Boolean
- #try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seconds: nil, blocking: false) ⇒ Object
Instance Method Details
#advisory_lock_exists_for?(lock_name, shared: false) ⇒ Boolean
Non-blocking check via IS_USED_LOCK, no lock is acquired as a side effect
60 61 62 63 64 65 66 67 |
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 60 def advisory_lock_exists_for?(lock_name, shared: false) raise ArgumentError, 'shared locks are not supported on MySQL' if shared lock_keys = lock_keys_for(lock_name) !query_value("SELECT IS_USED_LOCK(#{quote(lock_keys.first)})").nil? rescue ActiveRecord::StatementInvalid nil end |
#lock_keys_for(lock_name) ⇒ Object
50 51 52 53 |
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 50 def lock_keys_for(lock_name) lock_str = "#{ENV.fetch(CoreAdvisory::LOCK_PREFIX_ENV, nil)}#{lock_name}" [lock_str] end |
#release_advisory_lock(*args, **kwargs) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 33 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 raise unless connection_lost_error?(e) end |
#supports_database_timeout? ⇒ Boolean
55 56 57 |
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 55 def supports_database_timeout? true end |
#try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seconds: nil, blocking: false) ⇒ Object
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/with_advisory_lock/mysql_advisory.rb', line 9 def try_advisory_lock(lock_keys, lock_name:, shared:, transaction:, timeout_seconds: nil, blocking: false) raise ArgumentError, 'shared locks are not supported on MySQL' if shared raise ArgumentError, 'transaction level locks are not supported on MySQL' if transaction # Note: blocking parameter is accepted for API compatibility but ignored for MySQL # MySQL's GET_LOCK already provides native timeout support, making the blocking # parameter redundant. MySQL doesn't have separate try/blocking functions like PostgreSQL. # MySQL/MariaDB GET_LOCK supports native timeout: # - timeout_seconds = nil: wait indefinitely # - timeout_seconds = 0: try once, no wait (0) # - timeout_seconds > 0: wait up to timeout_seconds # # Note: MySQL accepts -1 for infinite wait, but MariaDB does not. # Using a large value (1 year) for cross-compatibility. mysql_timeout = case timeout_seconds when nil then 31_536_000 # 1 year in seconds when 0 then 0 else timeout_seconds.to_i end execute_successful?("GET_LOCK(#{quote(lock_keys.first)}, #{mysql_timeout})") end |