Class: HighSchool::Locker

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

Overview

Defined Under Namespace

Classes: LockNotAcquired

Constant Summary collapse

UNLOCK_LUA_SCRIPT =

unlock is a lua script because this call will run atomically vs having to issue 2 calls independently to delete the lock. Script alo makes sure that the creator of the lock is the one deleting the lock.

"if redis.call('get',KEYS[1])==ARGV[1] then redis.call('del',KEYS[1]) end"

Instance Method Summary collapse

Constructor Details

#initialize(redis) ⇒ Locker

Returns a new instance of Locker.



15
16
17
# File 'lib/high_school.rb', line 15

def initialize(redis)
  @redis = redis
end

Instance Method Details

#lock(lock_name, acquire_timeout: nil, lock_timeout: 10, &block) ⇒ Object

A default timeout of 10 seconds is set on the lock. You probably want to set that to something safe for yourself. But leaving it locked for infinity is probably not what you want.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/high_school.rb', line 24

def lock(lock_name, acquire_timeout: nil, lock_timeout: 10, &block)
  identifier = SecureRandom.uuid

  end_time = Time.now + acquire_timeout if acquire_timeout

  # prefix it to make them easier to find in redis if we need to.
  lock_name = "lock:" + lock_name

  begin
    while end_time.nil? || Time.now < end_time
      # returns false if the lock with this name already exists
      if @redis.set(lock_name, identifier, nx: true, ex: lock_timeout)
        return yield
      else
        # just to vary up how many simulatenous threads are asking to set the lock
        sleep(rand(0.10..0.5))
      end
    end

    raise LockNotAcquired.new("Waited #{acquire_timeout} seconds to get lock for #{lock_name}")
  ensure
    @redis.eval(UNLOCK_LUA_SCRIPT, [lock_name], [identifier])
  end
end