Class: UncomplicatedMutex
- Inherits:
-
Object
- Object
- UncomplicatedMutex
- Defined in:
- lib/uncomplicated_mutex.rb
Constant Summary collapse
- MutexTimeout =
Class.new(StandardError)
- LUA_ACQUIRE =
"return redis.call('SET', KEYS[1], ARGV[2], 'NX', 'EX', ARGV[1]) and redis.call('expire', KEYS[1], ARGV[1]) and 1 or 0"
- LUA_RELEASE =
"if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end"
Instance Attribute Summary collapse
-
#lock_name ⇒ Object
readonly
Returns the value of attribute lock_name.
Instance Method Summary collapse
- #acquire_mutex ⇒ Object
- #destroy_mutex ⇒ Object
-
#initialize(obj, opts = {}) ⇒ UncomplicatedMutex
constructor
A new instance of UncomplicatedMutex.
- #lock(&block) ⇒ Object
- #overwrite_mutex ⇒ Object
- #recurse_until_ready(depth = 1) ⇒ Object
- #release_mutex ⇒ Object
- #wait_a_tick ⇒ Object
- #wait_for_mutex ⇒ Object
Constructor Details
#initialize(obj, opts = {}) ⇒ UncomplicatedMutex
Returns a new instance of UncomplicatedMutex.
12 13 14 15 16 17 18 19 20 21 |
# File 'lib/uncomplicated_mutex.rb', line 12 def initialize(obj, opts = {}) @verbose = opts[:verbose] @timeout = opts[:timeout] || 300 @fail_on_timeout = opts[:fail_on_timeout] @ticks = opts[:ticks] || 100 @wait_tick = @timeout.to_f / @ticks.to_f @redis = opts[:redis] || Redis.new @lock_name = "lock:#{obj.class.name}:#{obj.id}".squeeze(":") @token = Digest::MD5.new.hexdigest("#{@lock_name}_#{Time.now.to_f}") end |
Instance Attribute Details
#lock_name ⇒ Object (readonly)
Returns the value of attribute lock_name.
5 6 7 |
# File 'lib/uncomplicated_mutex.rb', line 5 def lock_name @lock_name end |
Instance Method Details
#acquire_mutex ⇒ Object
23 24 25 26 |
# File 'lib/uncomplicated_mutex.rb', line 23 def acquire_mutex puts("Running transaction to acquire the lock #{@lock_name}") if @verbose @redis.eval(LUA_ACQUIRE, [ @lock_name ], [ @timeout, @token ]) == 1 end |
#destroy_mutex ⇒ Object
28 29 30 31 |
# File 'lib/uncomplicated_mutex.rb', line 28 def destroy_mutex puts("Destroying the lock #{@lock_name}") if @verbose @redis.del(@lock_name) end |
#lock(&block) ⇒ Object
33 34 35 36 37 38 39 40 |
# File 'lib/uncomplicated_mutex.rb', line 33 def lock(&block) begin wait_for_mutex yield block ensure release_mutex end end |
#overwrite_mutex ⇒ Object
42 43 44 45 |
# File 'lib/uncomplicated_mutex.rb', line 42 def overwrite_mutex puts("Replacing the lock #{@lock_name} with #{@token}") if @verbose @redis.set(@lock_name, @token) end |
#recurse_until_ready(depth = 1) ⇒ Object
47 48 49 50 51 |
# File 'lib/uncomplicated_mutex.rb', line 47 def recurse_until_ready(depth = 1) return false if depth == @ticks wait_a_tick if depth > 1 acquire_mutex || recurse_until_ready(depth + 1) end |
#release_mutex ⇒ Object
53 54 55 56 |
# File 'lib/uncomplicated_mutex.rb', line 53 def release_mutex puts("Releasing the lock #{@lock_name} if it still holds the value '#{@token}'") if @verbose @redis.eval(LUA_RELEASE, [ @lock_name ], [ @token ]) end |
#wait_a_tick ⇒ Object
58 59 60 61 |
# File 'lib/uncomplicated_mutex.rb', line 58 def wait_a_tick puts("Sleeping #{@wait_tick} for the lock #{@lock_name} to become available") if @verbose sleep(@wait_tick) end |
#wait_for_mutex ⇒ Object
63 64 65 66 67 68 69 70 71 |
# File 'lib/uncomplicated_mutex.rb', line 63 def wait_for_mutex if recurse_until_ready puts("Acquired lock #{@lock_name}") if @verbose else puts("Failed to acquire the lock") if @verbose raise MutexTimeout.new("Failed to acquire the lock") if @fail_on_timeout overwrite_mutex end end |