Class: DistributedMutex
- Inherits:
-
Object
- Object
- DistributedMutex
- Defined in:
- lib/distributed_mutex.rb
Overview
Cross-process locking using Redis. Expiration happens when the current time is greater than the expire time
Constant Summary collapse
- DEFAULT_VALIDITY =
60
- CHECK_READONLY_ATTEMPTS =
5
- LOCK_SCRIPT =
DiscourseRedis::EvalHelper.new <<~LUA local now = redis.call("time")[1] local expire_time = now + ARGV[1] local current_expire_time = redis.call("get", KEYS[1]) if current_expire_time and tonumber(now) <= tonumber(current_expire_time) then return nil else local result = redis.call("setex", KEYS[1], ARGV[1] + 1, tostring(expire_time)) return expire_time end LUA
- UNLOCK_SCRIPT =
DiscourseRedis::EvalHelper.new <<~LUA local current_expire_time = redis.call("get", KEYS[1]) if current_expire_time == ARGV[1] then local result = redis.call("del", KEYS[1]) return result ~= nil else return false end LUA
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(key, redis: nil, validity: DEFAULT_VALIDITY) ⇒ DistributedMutex
constructor
A new instance of DistributedMutex.
-
#synchronize ⇒ Object
NOTE wrapped in mutex to maintain its semantics.
Constructor Details
#initialize(key, redis: nil, validity: DEFAULT_VALIDITY) ⇒ DistributedMutex
Returns a new instance of DistributedMutex.
37 38 39 40 41 42 43 |
# File 'lib/distributed_mutex.rb', line 37 def initialize(key, redis: nil, validity: DEFAULT_VALIDITY) @key = key @using_global_redis = true if !redis @redis = redis || Discourse.redis @mutex = Mutex.new @validity = validity end |
Class Method Details
.synchronize(key, redis: nil, validity: DEFAULT_VALIDITY, &blk) ⇒ Object
33 34 35 |
# File 'lib/distributed_mutex.rb', line 33 def self.synchronize(key, redis: nil, validity: DEFAULT_VALIDITY, &blk) self.new(key, redis: redis, validity: validity).synchronize(&blk) end |
Instance Method Details
#synchronize ⇒ Object
NOTE wrapped in mutex to maintain its semantics
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/distributed_mutex.rb', line 46 def synchronize result = nil @mutex.synchronize do expire_time = get_lock begin result = yield ensure current_time = redis.time[0] if current_time > expire_time warn( "held for too long, expected max: #{@validity} secs, took an extra #{current_time - expire_time} secs", ) end unlocked = UNLOCK_SCRIPT.eval(redis, [prefixed_key], [expire_time.to_s]) if !unlocked && current_time <= expire_time warn("the redis key appears to have been tampered with before expiration") end end end result end |