Class: MysqlFramework::Scripts::LockManager

Inherits:
Object
  • Object
show all
Defined in:
lib/mysql_framework/scripts/lock_manager.rb

Instance Method Summary collapse

Constructor Details

#initializeLockManager

Returns a new instance of LockManager.



8
9
10
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 8

def initialize
  @pool = Queue.new
end

Instance Method Details

#fetch_clientObject

This method is called to retrieve a Redlock client from the pool



65
66
67
68
69
70
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 65

def fetch_client
  @pool.pop(true)
rescue StandardError
  # By not letting redlock retry we will rely on the retry that happens in this class
  Redlock::Client.new([redis_url], retry_jitter: retry_jitter, retry_count: 1, retry_delay: 0)
end

#release_lock(key:, lock:) ⇒ Object

This method is called to release a lock



44
45
46
47
48
49
50
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 44

def release_lock(key:, lock:)
  return if lock.nil?

  MysqlFramework.logger.info { "[#{self.class}] - Releasing lock: #{key}." }

  with_client { |client| client.unlock(lock) }
end

#request_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay) ⇒ Object

This method is called to request a lock (Default 5 minutes)



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 13

def request_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay)
  MysqlFramework.logger.info { "[#{self.class}] - Requesting lock: #{key}." }

  lock = false
  count = 0

  loop do
    # request a lock
    lock = with_client { |client| client.lock(key, ttl) }

    # if lock was received break out of the loop
    break if lock

    # lock was not received so increment request count
    count += 1

    MysqlFramework.logger.debug do
      "[#{self.class}] - Key is currently locked, waiting for lock: #{key} | Wait count: #{count}."
    end

    # check if lock requests have exceeded max request attempts
    raise "Resource is already locked. Lock key: #{key}. Max attempt exceeded." if count == max_attempts

    # sleep and try requesting the lock again
    sleep(retry_delay)
  end

  lock
end

#with_clientObject

This method is called to retrieve a Redlock client from the pool and yield it to a block



73
74
75
76
77
78
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 73

def with_client
  client = fetch_client
  yield client
ensure
  @pool.push(client)
end

#with_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay) ⇒ Object

This method is called to request and release a lock around yielding to a user supplied block



53
54
55
56
57
58
59
60
61
62
# File 'lib/mysql_framework/scripts/lock_manager.rb', line 53

def with_lock(key:, ttl: default_ttl, max_attempts: default_max_retries, retry_delay: default_retry_delay)
  raise 'Block must be specified.' unless block_given?

  begin
    lock = request_lock(key: key, ttl: ttl, max_attempts: max_attempts, retry_delay: retry_delay)
    yield
  ensure
    release_lock(key: key, lock: lock)
  end
end