Class: Redlock::Client
- Inherits:
-
Object
- Object
- Redlock::Client
- Defined in:
- lib/redlock/client.rb,
lib/redlock/testing.rb
Defined Under Namespace
Classes: RedisInstance
Constant Summary collapse
- DEFAULT_REDIS_HOST =
ENV["DEFAULT_REDIS_HOST"] || "localhost"
- DEFAULT_REDIS_PORT =
ENV["DEFAULT_REDIS_PORT"] || "6379"
- DEFAULT_REDIS_URLS =
["redis://#{DEFAULT_REDIS_HOST}:#{DEFAULT_REDIS_PORT}"]
- DEFAULT_REDIS_TIMEOUT =
0.1
- DEFAULT_RETRY_COUNT =
3
- DEFAULT_RETRY_DELAY =
200
- DEFAULT_RETRY_JITTER =
50
- CLOCK_DRIFT_FACTOR =
0.01
Instance Attribute Summary collapse
-
#testing_mode ⇒ Object
writeonly
Sets the attribute testing_mode.
Class Method Summary collapse
-
.default_time_source ⇒ Object
Returns default time source function depending on CLOCK_MONOTONIC availability.
Instance Method Summary collapse
-
#initialize(servers = DEFAULT_REDIS_URLS, options = {}) ⇒ Client
constructor
Create a distributed lock manager implementing redlock algorithm.
-
#lock(resource, ttl, options = {}, &block) ⇒ Object
Locks a resource for a given time.
-
#lock!(*args) ⇒ Object
Locks a resource, executing the received block only after successfully acquiring the lock, and returning its return value as a result.
- #try_lock_instances(resource, ttl, options) ⇒ Object
- #try_lock_instances_without_testing ⇒ Object
-
#unlock(lock_info) ⇒ Object
Unlocks a resource.
-
#unlock_without_testing ⇒ Object
Unlocks a resource.
Constructor Details
#initialize(servers = DEFAULT_REDIS_URLS, options = {}) ⇒ Client
Create a distributed lock manager implementing redlock algorithm. Params:
servers
-
The array of redis connection URLs or Redis connection instances. Or a mix of both.
options
-
‘retry_count` being how many times it’ll try to lock a resource (default: 3)
-
‘retry_delay` being how many ms to sleep before try to lock again (default: 200)
-
‘retry_jitter` being how many ms to jitter retry delay (default: 50)
-
‘redis_timeout` being how the Redis timeout will be set in seconds (default: 0.1)
-
‘time_source` being a callable object returning a monotonic time in milliseconds
(default: see #default_time_source)
-
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'lib/redlock/client.rb', line 36 def initialize(servers = DEFAULT_REDIS_URLS, = {}) redis_timeout = [:redis_timeout] || DEFAULT_REDIS_TIMEOUT @servers = servers.map do |server| if server.is_a?(String) RedisInstance.new(url: server, timeout: redis_timeout) else RedisInstance.new(server) end end @quorum = (servers.length / 2).to_i + 1 @retry_count = [:retry_count] || DEFAULT_RETRY_COUNT @retry_delay = [:retry_delay] || DEFAULT_RETRY_DELAY @retry_jitter = [:retry_jitter] || DEFAULT_RETRY_JITTER @time_source = [:time_source] || self.class.default_time_source end |
Instance Attribute Details
#testing_mode=(value) ⇒ Object (writeonly)
Sets the attribute testing_mode
3 4 5 |
# File 'lib/redlock/testing.rb', line 3 def testing_mode=(value) @testing_mode = value end |
Class Method Details
.default_time_source ⇒ Object
Returns default time source function depending on CLOCK_MONOTONIC availability.
18 19 20 21 22 23 24 |
# File 'lib/redlock/client.rb', line 18 def self.default_time_source if defined?(Process::CLOCK_MONOTONIC) proc { (Process.clock_gettime(Process::CLOCK_MONOTONIC) * 1000).to_i } else proc { (Time.now.to_f * 1000).to_i } end end |
Instance Method Details
#lock(resource, ttl, options = {}, &block) ⇒ Object
Locks a resource for a given time. Params:
resource
-
the resource (or key) string to be locked.
ttl
-
The time-to-live in ms for the lock.
options
-
Hash of optional parameters
* +extend+: A lock ("lock_info") to extend.
* +extend_only_if_life+: If +extend+ is given, only acquire lock if currently held
block
-
an optional block to be executed; after its execution, the lock (if successfully
acquired) is automatically unlocked.
61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/redlock/client.rb', line 61 def lock(resource, ttl, = {}, &block) lock_info = try_lock_instances(resource, ttl, ) if block_given? begin yield lock_info !!lock_info ensure unlock(lock_info) if lock_info end else lock_info end end |
#lock!(*args) ⇒ Object
Locks a resource, executing the received block only after successfully acquiring the lock, and returning its return value as a result. See Redlock::Client#lock for parameters.
86 87 88 89 90 91 92 93 |
# File 'lib/redlock/client.rb', line 86 def lock!(*args) fail 'No block passed' unless block_given? lock(*args) do |lock_info| raise LockError, 'failed to acquire lock' unless lock_info return yield end end |
#try_lock_instances(resource, ttl, options) ⇒ Object
165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/redlock/client.rb', line 165 def try_lock_instances(resource, ttl, ) tries = [:extend] ? 1 : (@retry_count + 1) tries.times do |attempt_number| # Wait a random delay before retrying. sleep((@retry_delay + rand(@retry_jitter)).to_f / 1000) if attempt_number > 0 lock_info = lock_instances(resource, ttl, ) return lock_info if lock_info end false end |
#try_lock_instances_without_testing ⇒ Object
5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/redlock/testing.rb', line 5 def try_lock_instances(resource, ttl, ) tries = [:extend] ? 1 : (@retry_count + 1) tries.times do |attempt_number| # Wait a random delay before retrying. sleep((@retry_delay + rand(@retry_jitter)).to_f / 1000) if attempt_number > 0 lock_info = lock_instances(resource, ttl, ) return lock_info if lock_info end false end |
#unlock(lock_info) ⇒ Object
Unlocks a resource. Params:
lock_info
-
the lock that has been acquired when you locked the resource.
79 80 81 |
# File 'lib/redlock/client.rb', line 79 def unlock(lock_info) @servers.each { |s| s.unlock(lock_info[:resource], lock_info[:value]) } end |
#unlock_without_testing ⇒ Object
Unlocks a resource. Params:
lock_info
-
the lock that has been acquired when you locked the resource.
21 22 23 |
# File 'lib/redlock/testing.rb', line 21 def unlock(lock_info) @servers.each { |s| s.unlock(lock_info[:resource], lock_info[:value]) } end |