Class: Kaal::Backend::MySQLAdapter
- Includes:
- DispatchLogging
- Defined in:
- lib/kaal/backend/mysql_adapter.rb
Overview
Distributed backend adapter using MySQL named locks (GET_LOCK/RELEASE_LOCK).
This adapter uses MySQL’s GET_LOCK and RELEASE_LOCK functions for distributed locking across multiple nodes. Locks are connection-based and automatically released when the database connection is closed.
**IMPORTANT LIMITATIONS:**
-
Locks are connection-scoped: if a process crashes, the lock persists until the database connection timeout occurs (typically 28,800 seconds or 8 hours). For critical systems, consider monitoring stale locks or using a time-based fallback mechanism.
-
MySQL named locks have a maximum length of 64 characters. Lock keys longer than 64 characters use a deterministic hash-based shortening scheme (prefix + SHA256 digest) to avoid collisions while respecting the limit.
-
Uses non-blocking acquisition: GET_LOCK is called with timeout=0 for immediate return (does not block waiting for the lock).
-
Ensure connection pooling is properly configured to release connections promptly when processes terminate.
Optionally logs all dispatch attempts to the database when enable_log_dispatch_registry is enabled in configuration.
Constant Summary collapse
- MAX_LOCK_NAME_LENGTH =
MySQL named locks have a maximum length of 64 characters
64
Instance Method Summary collapse
-
#acquire(key, _ttl) ⇒ Boolean
Attempt to acquire a distributed lock using MySQL GET_LOCK.
-
#definition_registry ⇒ Kaal::Definition::DatabaseEngine
Get the definition registry for database-backed definition persistence.
-
#dispatch_registry ⇒ Kaal::Dispatch::DatabaseEngine
Get the dispatch registry for database logging.
-
#initialize ⇒ MySQLAdapter
constructor
A new instance of MySQLAdapter.
-
#release(key) ⇒ Boolean
Release a distributed lock held by MySQL GET_LOCK.
Methods included from DispatchLogging
#log_dispatch_attempt, #parse_lock_key, parse_lock_key
Methods inherited from Adapter
Constructor Details
#initialize ⇒ MySQLAdapter
Returns a new instance of MySQLAdapter.
49 50 51 52 53 |
# File 'lib/kaal/backend/mysql_adapter.rb', line 49 def initialize super @lock_name_length_limit = MAX_LOCK_NAME_LENGTH @false_value_pattern = /\A(0|f|false|)\z/i end |
Instance Method Details
#acquire(key, _ttl) ⇒ Boolean
Attempt to acquire a distributed lock using MySQL GET_LOCK.
Uses MySQL’s GET_LOCK(name, timeout) function with a timeout of 0 seconds to perform non-blocking acquisition. If successful, logs the dispatch attempt when enable_log_dispatch_registry is enabled.
Note: The ttl parameter is ignored. MySQL named locks are connection-based and do not have automatic expiration. The lock will be held until explicitly released or the database connection is closed. See class documentation for limitations.
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'lib/kaal/backend/mysql_adapter.rb', line 89 def acquire(key, _ttl) lock_name = normalize_lock_name(key) # GET_LOCK returns 1 on success, 0 on timeout, NULL on error sql = ActiveRecord::Base.sanitize_sql_array(['SELECT GET_LOCK(?, 0) as lock_result', lock_name]) result_set = ActiveRecord::Base.connection.execute(sql) # Convert result to array and get first row, then first column value result_row = result_set.to_a.first result_value = result_row.is_a?(Hash) ? result_row['lock_result'] : result_row&.first acquired = cast_to_boolean(result_value) log_dispatch_attempt(key) if acquired acquired rescue StandardError => e raise LockAdapterError, "MySQL acquire failed for #{key}: #{e.message}" end |
#definition_registry ⇒ Kaal::Definition::DatabaseEngine
Get the definition registry for database-backed definition persistence.
70 71 72 |
# File 'lib/kaal/backend/mysql_adapter.rb', line 70 def definition_registry @definition_registry ||= Kaal::Definition::DatabaseEngine.new end |
#dispatch_registry ⇒ Kaal::Dispatch::DatabaseEngine
Get the dispatch registry for database logging.
62 63 64 |
# File 'lib/kaal/backend/mysql_adapter.rb', line 62 def dispatch_registry @dispatch_registry ||= Kaal::Dispatch::DatabaseEngine.new end |
#release(key) ⇒ Boolean
Release a distributed lock held by MySQL GET_LOCK.
112 113 114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/kaal/backend/mysql_adapter.rb', line 112 def release(key) lock_name = normalize_lock_name(key) # RELEASE_LOCK returns 1 if held and released, 0 if not held, NULL on error sql = ActiveRecord::Base.sanitize_sql_array(['SELECT RELEASE_LOCK(?) as lock_result', lock_name]) result_set = ActiveRecord::Base.connection.execute(sql) # Convert result to array and get first row, then first column value result_row = result_set.to_a.first result_value = result_row.is_a?(Hash) ? result_row['lock_result'] : result_row&.first cast_to_boolean(result_value) rescue StandardError => e raise LockAdapterError, "MySQL release failed for #{key}: #{e.message}" end |