# Cross-process locking using Redis. class DistributedMutex def self.synchronize(key, redis=nil, &blk) self.new(key, redis).synchronize(&blk) end def initialize(key, redis=nil) @key = key @redis = redis || $redis @mutex = Mutex.new end # NOTE wrapped in mutex to maintain its semantics def synchronize @mutex.lock while !try_to_get_lock sleep 0.001 end yield ensure @redis.del @key @mutex.unlock end private def try_to_get_lock got_lock = false if @redis.setnx @key, Time.now.to_i + 60 @redis.expire @key, 60 got_lock = true else begin @redis.watch @key time = @redis.get @key if time && time.to_i < Time.now.to_i got_lock = @redis.multi do @redis.set @key, Time.now.to_i + 60 end end ensure @redis.unwatch end end got_lock end end