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
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
-
#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 |
# File 'lib/active_record/database_mutex/implementation.rb', line 22 def initialize(opts = {}) @name = opts[:name] or raise ArgumentError, "mutex requires a :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.
28 29 30 |
# File 'lib/active_record/database_mutex/implementation.rb', line 28 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
The internal_name method generates an encoded name for this mutex instance based on its class and #name attributes and memoizes it.
34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/active_record/database_mutex/implementation.rb', line 34 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.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/active_record/database_mutex/implementation.rb', line 109 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 |
#locked? ⇒ true, false
The locked? method returns true if this mutex is currently locked by any database connection, the opposite of #unlocked?.
191 192 193 |
# File 'lib/active_record/database_mutex/implementation.rb', line 191 def locked? not unlocked? end |
#not_owned? ⇒ Boolean
Returns true if this mutex was not acquired on this database connection, the opposite of #owned?.
202 203 204 |
# File 'lib/active_record/database_mutex/implementation.rb', line 202 def not_owned? not owned? end |
#owned? ⇒ Boolean
Returns true if the mutex is was acquired on this database connection.
196 197 198 |
# File 'lib/active_record/database_mutex/implementation.rb', line 196 def owned? query("SELECT CONNECTION_ID() = IS_USED_LOCK(#{quote(internal_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.
78 79 80 81 82 83 84 85 |
# File 'lib/active_record/database_mutex/implementation.rb', line 78 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.
210 211 212 |
# File 'lib/active_record/database_mutex/implementation.rb', line 210 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.
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
# File 'lib/active_record/database_mutex/implementation.rb', line 141 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(internal_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.
174 175 176 177 |
# File 'lib/active_record/database_mutex/implementation.rb', line 174 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.
183 184 185 |
# File 'lib/active_record/database_mutex/implementation.rb', line 183 def unlocked? query("SELECT IS_FREE_LOCK(#{quote(internal_name)})") == 1 end |