Class: ActiveRecord::DatabaseMutex::Implementation
- Inherits:
-
Object
- Object
- ActiveRecord::DatabaseMutex::Implementation
- Defined in:
- lib/active_record/database_mutex/implementation.rb
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the name of this mutex as given via the constructor argument.
Class Method Summary collapse
-
.db ⇒ Object
The db method returns an instance of ActiveRecord::Base.connection.
Instance Method Summary collapse
-
#initialize(opts = {}) ⇒ Implementation
constructor
The initialize method initializes an instance of the DatabaseMutex class by setting its name and internal_name attributes.
-
#internal_name ⇒ String
(also: #counter_name)
The internal_name method generates an encoded name for this mutex instance based on its class and #name attributes and memoizes it.
- #lock(opts = {}) ⇒ true, false
-
#lock_name ⇒ String
The lock_name method generates the name for the mutex’s internal lock variable based on its class and #name attributes, prefixing it with a truncated version of the name that only includes printable characters.
-
#locked? ⇒ true, false
The locked? method returns true if this mutex is currently locked by any database connection, the opposite of #unlocked?.
-
#not_owned? ⇒ Boolean
Returns true if this mutex was not acquired on this database connection, the opposite of #owned?.
-
#owned? ⇒ Boolean
Returns true if the mutex is was acquired on this database connection.
-
#synchronize(opts = {}) {|Result| ... } ⇒ Nil or result of yielded block
The synchronize method attempts to acquire a mutex lock for the given name and executes the block passed to it.
-
#to_s ⇒ String
(also: #inspect)
The to_s method returns a string representation of this DatabaseMutex instance.
-
#unlock(opts = {}) ⇒ true, false
The unlock method releases the mutex lock for the given name and returns true if successful.
-
#unlock?(opts = {}) ⇒ self?
The unlock? method returns self if the mutex could successfully unlocked, otherwise it returns nil.
-
#unlocked? ⇒ true, false
The unlocked? method checks whether the mutex is currently free and not locked by any database connection.
Constructor Details
#initialize(opts = {}) ⇒ Implementation
The initialize method initializes an instance of the DatabaseMutex class by setting its name and internal_name attributes.
22 23 24 25 26 |
# File 'lib/active_record/database_mutex/implementation.rb', line 22 def initialize(opts = {}) @name = opts[:name].to_s @name.size != 0 or raise ArgumentError, "mutex requires a nonempty :name argument" internal_name # create/check internal_name end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the name of this mutex as given via the constructor argument.
29 30 31 |
# File 'lib/active_record/database_mutex/implementation.rb', line 29 def name @name end |
Class Method Details
.db ⇒ Object
The db method returns an instance of ActiveRecord::Base.connection
9 10 11 |
# File 'lib/active_record/database_mutex/implementation.rb', line 9 def db ActiveRecord::Base.connection end |
Instance Method Details
#internal_name ⇒ String Also known as: counter_name
The internal_name method generates an encoded name for this mutex instance based on its class and #name attributes and memoizes it.
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/active_record/database_mutex/implementation.rb', line 35 def internal_name @internal_name and return @internal_name encoded_name = ?$ + Digest::MD5.base64digest([ self.class.name, name ] * ?#). delete('^A-Za-z0-9+/').gsub(/[+\/]/, ?+ => ?_, ?/ => ?.) if encoded_name.size <= 64 @internal_name = encoded_name else # This should never happen: raise MutexInvalidState, "internal_name #{encoded_name} too long: >64 characters" end end |
#lock(opts = {}) ⇒ true, false
The lock method attempts to acquire the mutex lock for the configured name and returns true if successful, that means ##locked? and ##owned? will be true. Note that you can lock the mutex n-times, but it has to be unlocked n-times to be released as well.
If the block option was given as false, it returns false instead of raising MutexLocked exception when unable to acquire lock without blocking.
If a timeout option with the (nonnegative) timeout in seconds was given, a MutexLocked exception is raised after this time, otherwise the method blocks forever.
If the raise option is given as false, no MutexLocked exception is raised, but false is returned.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/active_record/database_mutex/implementation.rb', line 120 def lock(opts = {}) opts = { block: true, raise: true }.merge(opts) if opts[:block] timeout = opts[:timeout] || -1 lock_with_timeout timeout: else begin lock_with_timeout timeout: 0 rescue MutexLocked false # If non-blocking and unable to acquire lock, return false. end end rescue MutexLocked if opts[:raise] raise else return false end end |
#lock_name ⇒ String
The lock_name method generates the name for the mutex’s internal lock variable based on its class and #name attributes, prefixing it with a truncated version of the name that only includes printable characters.
52 53 54 55 |
# File 'lib/active_record/database_mutex/implementation.rb', line 52 def lock_name prefix_name = name.gsub(/[^[:print:]]/, '')[0, 32] prefix_name + ?= + internal_name end |
#locked? ⇒ true, false
The locked? method returns true if this mutex is currently locked by any database connection, the opposite of #unlocked?.
202 203 204 |
# File 'lib/active_record/database_mutex/implementation.rb', line 202 def locked? not unlocked? end |
#not_owned? ⇒ Boolean
Returns true if this mutex was not acquired on this database connection, the opposite of #owned?.
213 214 215 |
# File 'lib/active_record/database_mutex/implementation.rb', line 213 def not_owned? not owned? end |
#owned? ⇒ Boolean
Returns true if the mutex is was acquired on this database connection.
207 208 209 |
# File 'lib/active_record/database_mutex/implementation.rb', line 207 def owned? query("SELECT CONNECTION_ID() = IS_USED_LOCK(#{quote(lock_name)})") == 1 end |
#synchronize(opts = {}) {|Result| ... } ⇒ Nil or result of yielded block
The synchronize method attempts to acquire a mutex lock for the given name and executes the block passed to it. If the lock is already held by another database connection, this method will return nil instead of raising an exception and not execute the block. #
This method provides a convenient way to ensure that critical sections of code are executed while holding the mutex lock. It attempts to acquire the lock using the underlying locking mechanisms (such as #lock and #unlock) and executes the block passed to it.
The block and timeout options are passed to the #lock method and configure the way the lock is acquired.
The force option is passed to the #unlock method, which will force the lock to open if true.
89 90 91 92 93 94 95 96 |
# File 'lib/active_record/database_mutex/implementation.rb', line 89 def synchronize(opts = {}) locked = lock(opts.slice(:block, :timeout)) or return yield rescue ActiveRecord::DatabaseMutex::MutexLocked return nil ensure locked and unlock opts.slice(:force) end |
#to_s ⇒ String Also known as: inspect
The to_s method returns a string representation of this DatabaseMutex instance.
221 222 223 |
# File 'lib/active_record/database_mutex/implementation.rb', line 221 def to_s "#<#{self.class} #{name}>" end |
#unlock(opts = {}) ⇒ true, false
The unlock method releases the mutex lock for the given name and returns true if successful. If the lock doesn’t belong to this connection raises a MutexUnlockFailed exception.
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/active_record/database_mutex/implementation.rb', line 152 def unlock(opts = {}) opts = { raise: true, force: false }.merge(opts) if owned? if opts[:force] reset_counter else decrement_counter end if counter_zero? case query("SELECT RELEASE_LOCK(#{quote(lock_name)})") when 1 true when 0, nil raise MutexUnlockFailed, "unlocking of mutex '#{name}' failed" end else false end else raise MutexUnlockFailed, "unlocking of mutex '#{name}' failed" end rescue MutexUnlockFailed if opts[:raise] raise else return false end end |
#unlock?(opts = {}) ⇒ self?
The unlock? method returns self if the mutex could successfully unlocked, otherwise it returns nil.
185 186 187 188 |
# File 'lib/active_record/database_mutex/implementation.rb', line 185 def unlock?(opts = {}) opts = { raise: false }.merge(opts) self if unlock(opts) end |
#unlocked? ⇒ true, false
The unlocked? method checks whether the mutex is currently free and not locked by any database connection.
194 195 196 |
# File 'lib/active_record/database_mutex/implementation.rb', line 194 def unlocked? query("SELECT IS_FREE_LOCK(#{quote(lock_name)})") == 1 end |