Module: SimpleLock
- Extended by:
- With
- Defined in:
- lib/simple_lock.rb,
lib/initializers/with.rb,
lib/simple_lock/redis.rb,
lib/simple_lock/config.rb,
lib/simple_lock/scripts.rb,
lib/simple_lock/version.rb,
lib/initializers/delegation.rb
Defined Under Namespace
Modules: Delegation, With
Classes: Config, Error, Redis, Script
Constant Summary
collapse
- NOSCRIPT_MAX_RETRIES =
1
- LOCK_VALUE =
"1"
- SCRIPTS =
{
lock: Script.new("return redis.call('set', KEYS[1], #{LOCK_VALUE}, 'NX', 'PX', ARGV[1])"),
unlock: Script.new("redis.call('del', KEYS[1])")
}.freeze
- VERSION =
"1.0.0"
Class Method Summary
collapse
Methods included from With
with
Class Method Details
.backoff_for_attempt(attempt) ⇒ Object
83
84
85
86
87
|
# File 'lib/simple_lock.rb', line 83
def self.backoff_for_attempt(attempt)
delay = config.retry_proc.respond_to?(:call) ? config.retry_proc.call(attempt) : config.retry_delay
(delay + rand(config.retry_jitter)).fdiv(1000)
end
|
.client ⇒ Object
15
16
17
|
# File 'lib/simple_lock.rb', line 15
def self.client
@client ||= SimpleLock::Redis.new(url: nil)
end
|
.client=(client_or_url) ⇒ Object
19
20
21
22
23
24
25
26
27
28
|
# File 'lib/simple_lock.rb', line 19
def self.client=(client_or_url)
case client_or_url
when SimpleLock::Redis
@client = client
when String
@client = SimpleLock::Redis.new(url: client_or_url)
else
raise ArgumentError, "client must be an instance of SimpleLock::Redis or a String"
end
end
|
.config ⇒ Object
30
31
32
|
# File 'lib/simple_lock.rb', line 30
def self.config
@config ||= Config.new
end
|
.load_scripts ⇒ Object
62
63
64
65
66
|
# File 'lib/simple_lock.rb', line 62
def self.load_scripts
SCRIPTS.each_value do |script|
client.script("load", script.raw)
end
end
|
.lock(key, ttl) ⇒ Object
rubocop:disable Metrics/MethodLength
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
# File 'lib/simple_lock.rb', line 35
def self.lock(key, ttl)
key = "#{config.key_prefix}#{key}"
locked = (config.retry_count + 1).times.any? do |attempt|
sleep(backoff_for_attempt(attempt)) unless attempt.zero?
safe_exec_script(SCRIPTS[:lock], [key], [ttl]) == "OK"
end
return locked unless block_given?
begin
yield(locked)
ensure
unlock(key) if locked
end
end
|
.safe_exec_script(script) ⇒ Object
68
69
70
71
72
73
74
75
76
77
78
79
80
81
|
# File 'lib/simple_lock.rb', line 68
def self.safe_exec_script(script, ...)
retries = 0
begin
client.evalsha(script.sha, ...)
rescue ::Redis::CommandError => e
if e.message.include?("NOSCRIPT") && (retries += 1) <= NOSCRIPT_MAX_RETRIES
load_scripts
retry
end
raise Error, e.message
end
end
|
.unlock(key) ⇒ Object
rubocop:enable Metrics/MethodLength
54
55
56
57
58
59
60
|
# File 'lib/simple_lock.rb', line 54
def self.unlock(key)
key = "#{config.key_prefix}#{key}"
safe_exec_script(SCRIPTS[:unlock], [key])
rescue StandardError
end
|