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)


18
19
20
21
22
23
24
# File 'lib/redis_locks/mutex.rb', line 18

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

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

Instance Method Details

#expired?(safety_margin: 0) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
78
79
# File 'lib/redis_locks/mutex.rb', line 75

def expired?(safety_margin: 0)
  return true unless @expires_at

  Time.now.utc.to_i + safety_margin >= @expires_at
end

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



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/redis_locks/mutex.rb', line 26

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

  if expires_at
    expires_at = expires_at.to_i
  else
    expires_at = now + @expires_in
  end

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

  return false unless locked

  return_or_yield(&block)
end

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

Raises:



57
58
59
60
61
# File 'lib/redis_locks/mutex.rb', line 57

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

#not_expired!(safety_margin: 0) ⇒ Object

Raises:



81
82
83
# File 'lib/redis_locks/mutex.rb', line 81

def not_expired!(safety_margin: 0)
  raise MutexExpired.new(@key) if expired?(safety_margin: safety_margin)
end

#unlockObject



63
64
65
66
67
68
69
70
71
72
73
# File 'lib/redis_locks/mutex.rb', line 63

def unlock
  return unless @expires_at

  # To prevent deleting a lock acquired from another process, only delete
  # the key if it's still valid, and will be for another 2 seconds
  if !expired?(safety_margin: 2)
    @redis.with { |conn| conn.del(@key) }
  end

  @expires_at = nil
end