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
Class Attribute Summary collapse
-
.testing_mode ⇒ Object
Returns the value of 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!(resource, *args) ⇒ Object
Locks a resource, executing the received block only after successfully acquiring the lock, and returning its return value as a result.
- #testing_mode=(mode) ⇒ Object
- #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 |
Class Attribute Details
.testing_mode ⇒ Object
Returns the value of attribute testing_mode.
4 5 6 |
# File 'lib/redlock/testing.rb', line 4 def testing_mode @testing_mode 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_locked+: Boolean, if +extend+ is given, only acquire lock if currently held
* +extend_only_if_life+: Deprecated, same as +extend_only_if_locked+
* +extend_life+: Deprecated, same as +extend_only_if_locked+
block-
an optional block to be executed; after its execution, the lock (if successfully
acquired) is automatically unlocked.
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/redlock/client.rb', line 63 def lock(resource, ttl, = {}, &block) lock_info = try_lock_instances(resource, ttl, ) if [:extend_only_if_life] && !Gem::Deprecate.skip warn 'DEPRECATION WARNING: The `extend_only_if_life` option has been renamed `extend_only_if_locked`.' [:extend_only_if_locked] = [:extend_only_if_life] end if [:extend_life] && !Gem::Deprecate.skip warn 'DEPRECATION WARNING: The `extend_life` option has been renamed `extend_only_if_locked`.' [:extend_only_if_locked] = [:extend_life] end if block_given? begin yield lock_info !!lock_info ensure unlock(lock_info) if lock_info end else lock_info end end |
#lock!(resource, *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.
96 97 98 99 100 101 102 103 |
# File 'lib/redlock/client.rb', line 96 def lock!(resource, *args) fail 'No block passed' unless block_given? lock(resource, *args) do |lock_info| raise LockError, resource unless lock_info return yield end end |
#testing_mode=(mode) ⇒ Object
7 8 9 10 11 12 13 |
# File 'lib/redlock/testing.rb', line 7 def testing_mode=(mode) warn 'DEPRECATION WARNING: Instance-level `testing_mode` has been removed, and this ' + 'setter will be removed in the future. Please set the testing mode on the `Redlock::Client` ' + 'instead, e.g. `Redlock::Client.testing_mode = :bypass`.' self.class.testing_mode = mode end |
#try_lock_instances(resource, ttl, options) ⇒ Object
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/redlock/client.rb', line 188 def try_lock_instances(resource, ttl, ) tries = [:extend] ? 1 : (@retry_count + 1) tries.times do |attempt_number| # Wait a random delay before retrying. sleep(attempt_retry_delay(attempt_number)) 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
15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/redlock/testing.rb', line 15 def try_lock_instances(resource, ttl, ) tries = [:extend] ? 1 : (@retry_count + 1) tries.times do |attempt_number| # Wait a random delay before retrying. sleep(attempt_retry_delay(attempt_number)) 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.
89 90 91 |
# File 'lib/redlock/client.rb', line 89 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.
31 32 33 |
# File 'lib/redlock/testing.rb', line 31 def unlock(lock_info) @servers.each { |s| s.unlock(lock_info[:resource], lock_info[:value]) } end |