Class: Promiscuous::Redis::Mutex
- Inherits:
-
Object
- Object
- Promiscuous::Redis::Mutex
- Defined in:
- lib/promiscuous/redis.rb
Instance Attribute Summary collapse
-
#token ⇒ Object
readonly
Returns the value of attribute token.
Instance Method Summary collapse
- #extend ⇒ Object
-
#initialize(key, options = {}) ⇒ Mutex
constructor
A new instance of Mutex.
- #key ⇒ Object
- #lock ⇒ Object
- #node ⇒ Object
- #still_locked? ⇒ Boolean
- #try_lock ⇒ Object
- #unlock ⇒ Object
Constructor Details
#initialize(key, options = {}) ⇒ Mutex
Returns a new instance of Mutex.
105 106 107 108 109 110 111 112 113 114 115 |
# File 'lib/promiscuous/redis.rb', line 105 def initialize(key, ={}) # TODO remove old code with orig_key @orig_key = key.to_s @key = "#{key}:lock" @timeout = [:timeout].to_i @sleep = [:sleep].to_f @expire = [:expire].to_i @lock_set = [:lock_set] @node = [:node] raise "Which node?" unless @node end |
Instance Attribute Details
#token ⇒ Object (readonly)
Returns the value of attribute token.
103 104 105 |
# File 'lib/promiscuous/redis.rb', line 103 def token @token end |
Instance Method Details
#extend ⇒ Object
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
# File 'lib/promiscuous/redis.rb', line 174 def extend now = Time.now.to_i @@extend_script ||= Promiscuous::Redis::Script.new <<-SCRIPT local key = KEYS[1] local expires_at = tonumber(ARGV[1]) local token = ARGV[2] if redis.call('hget', key, 'token') == token then redis.call('hset', key, 'expires_at', expires_at) return true else return false end SCRIPT !!@@extend_script.eval(@node, :keys => [@key].compact, :argv => [now + @expire, @token]) end |
#key ⇒ Object
117 118 119 |
# File 'lib/promiscuous/redis.rb', line 117 def key @orig_key end |
#lock ⇒ Object
125 126 127 128 129 130 131 132 133 |
# File 'lib/promiscuous/redis.rb', line 125 def lock result = false start_at = Time.now while Time.now - start_at < @timeout break if result = try_lock sleep @sleep end result end |
#node ⇒ Object
121 122 123 |
# File 'lib/promiscuous/redis.rb', line 121 def node @node end |
#still_locked? ⇒ Boolean
218 219 220 221 |
# File 'lib/promiscuous/redis.rb', line 218 def still_locked? raise "You never locked that mutex" unless @token @node.hget(@key, 'token').to_i == @token end |
#try_lock ⇒ Object
135 136 137 138 139 140 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 169 170 171 172 |
# File 'lib/promiscuous/redis.rb', line 135 def try_lock raise "You are trying to lock an already locked mutex" if @token now = Time.now.to_i # This script loading is not thread safe (touching a class variable), but # that's okay, because the race is harmless. @@lock_script ||= Promiscuous::Redis::Script.new <<-SCRIPT local key = KEYS[1] local token_key = KEYS[2] local lock_set = KEYS[3] local now = tonumber(ARGV[1]) local expires_at = tonumber(ARGV[2]) local orig_key = ARGV[3] local prev_expires_at = tonumber(redis.call('hget', key, 'expires_at')) if prev_expires_at and prev_expires_at > now then return {false, nil} end local next_token = redis.call('incr', 'promiscuous:next_token') redis.call('hmset', key, 'expires_at', expires_at, 'token', next_token) if lock_set then redis.call('zadd', lock_set, now, orig_key) end if prev_expires_at then return {'recovered', next_token} else return {true, next_token} end SCRIPT result, @token = @@lock_script.eval(@node, :keys => [@key, 'promiscuous:next_token', @lock_set].compact, :argv => [now, now + @expire, @orig_key]) result == 'recovered' ? :recovered : !!result end |
#unlock ⇒ Object
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/promiscuous/redis.rb', line 191 def unlock raise "You are trying to unlock a non locked mutex" unless @token # Since it's possible that the operations in the critical section took a long time, # we can't just simply release the lock. The unlock method checks if the unique @token # remains the same, and do not release if the lock token was overwritten. @@unlock_script ||= Script.new <<-LUA local key = KEYS[1] local lock_set = KEYS[2] local token = ARGV[1] local orig_key = ARGV[2] if redis.call('hget', key, 'token') == token then redis.call('del', key) if lock_set then redis.call('zrem', lock_set, orig_key) end return true else return false end LUA result = @@unlock_script.eval(@node, :keys => [@key, @lock_set].compact, :argv => [@token, @orig_key]) @token = nil !!result end |