Class: RedisLocks::Mutex

Inherits:
Object
  • Object
show all
Defined in:
lib/redis_locks/mutex.rb

Constant Summary collapse

NAMESPACE =
"mutex"

Instance Method Summary collapse

Constructor Details

#initialize(key, expires_in: 86400, redis: RedisLocks.redis) ⇒ Mutex

Returns a new instance of Mutex.

Raises:

  • (ArgumentError)


12
13
14
15
16
17
18
# File 'lib/redis_locks/mutex.rb', line 12

def initialize(key, expires_in: 86400, redis: RedisLocks.redis)
  @key = "#{NAMESPACE}:#{key}"
  @redis = redis
  @expires_in = expires_in.to_i

  raise ArgumentError.new("Invalid expires_in: #{expires_in}") unless expires_in > 0
end

Instance Method Details

#lock(expires_at: nil, &block) ⇒ Object



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/redis_locks/mutex.rb', line 20

def lock(expires_at: nil, &block)
  now = Time.now.utc.to_i
  locked = false
  expires_at ||= now + @expires_in

  if @redis.setnx(@key, expires_at)
    @redis.expire(@key, expires_at - now)
    locked = true
  else # it was locked
    if (old_value = @redis.get(@key)).to_i <= now
      # lock has expired
      if @redis.getset(@key, expires_at) == old_value
        locked = true
      end
    end
  end

  return false unless locked

  return_or_yield(&block)
end

#lock!(expires_at: nil, &block) ⇒ Object

Raises:



42
43
44
45
46
# File 'lib/redis_locks/mutex.rb', line 42

def lock!(expires_at: nil, &block)
  locked = lock(expires_at: expires_at)
  raise AlreadyLocked.new(@key) unless locked
  return_or_yield(&block)
end

#unlockObject



48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/redis_locks/mutex.rb', line 48

def unlock
  @redis.watch(@key) do
    # only delete the key if it's still valid, and will be for another 2 seconds
    if @redis.get(@key).to_i > Time.now.utc.to_i + 2
      @redis.multi do |multi|
        multi.del(@key)
      end
    else
      @redis.unwatch
    end
  end
end