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
-
#get_remaining_ttl_for_lock(lock_info) ⇒ Object
Gets remaining ttl of a resource.
-
#get_remaining_ttl_for_resource(resource) ⇒ Object
Gets remaining ttl of a resource.
-
#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.
-
#locked?(resource) ⇒ Boolean
- Checks if a resource is locked Params:
lock_info -
the lock that has been acquired when you locked the resource.
- Checks if a resource is locked Params:
- #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.
-
#valid_lock?(lock_info) ⇒ Boolean
- Checks if a lock is still valid Params:
lock_info -
the lock that has been acquired when you locked the resource.
- Checks if a lock is still valid Params:
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)
-
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/redlock/client.rb', line 38 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.
6 7 8 |
# File 'lib/redlock/testing.rb', line 6 def testing_mode @testing_mode end |
Class Method Details
.default_time_source ⇒ Object
Returns default time source function depending on CLOCK_MONOTONIC availability.
20 21 22 23 24 25 26 |
# File 'lib/redlock/client.rb', line 20 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
#get_remaining_ttl_for_lock(lock_info) ⇒ Object
Gets remaining ttl of a resource. The ttl is returned if the holder currently holds the lock and it has not expired, otherwise the method returns nil. Params:
lock_info-
the lock that has been acquired when you locked the resource
115 116 117 118 119 |
# File 'lib/redlock/client.rb', line 115 def get_remaining_ttl_for_lock(lock_info) ttl_info = try_get_remaining_ttl(lock_info[:resource]) return nil if ttl_info.nil? || ttl_info[:value] != lock_info[:value] ttl_info[:ttl] end |
#get_remaining_ttl_for_resource(resource) ⇒ Object
Gets remaining ttl of a resource. If there is no valid lock, the method returns nil. Params:
resource-
the name of the resource (string) for which to check the ttl
125 126 127 128 129 |
# File 'lib/redlock/client.rb', line 125 def get_remaining_ttl_for_resource(resource) ttl_info = try_get_remaining_ttl(resource) return nil if ttl_info.nil? ttl_info[:ttl] end |
#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
* +retry_count+: see +initialize+
* +retry_delay+: see +initialize+
* +retry_jitter+: see +initialize+
* +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.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/redlock/client.rb', line 68 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.
101 102 103 104 105 106 107 108 |
# File 'lib/redlock/client.rb', line 101 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 |
#locked?(resource) ⇒ Boolean
Checks if a resource is locked Params:
lock_info-
the lock that has been acquired when you locked the resource
134 135 136 137 |
# File 'lib/redlock/client.rb', line 134 def locked?(resource) ttl = get_remaining_ttl_for_resource(resource) !(ttl.nil? || ttl.zero?) end |
#testing_mode=(mode) ⇒ Object
9 10 11 12 13 14 15 |
# File 'lib/redlock/testing.rb', line 9 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
224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/redlock/client.rb', line 224 def try_lock_instances(resource, ttl, ) retry_count = [:retry_count] || @retry_count 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
17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/redlock/testing.rb', line 17 def try_lock_instances(resource, ttl, ) retry_count = [:retry_count] || @retry_count 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.
94 95 96 |
# File 'lib/redlock/client.rb', line 94 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.
33 34 35 |
# File 'lib/redlock/testing.rb', line 33 def unlock(lock_info) @servers.each { |s| s.unlock(lock_info[:resource], lock_info[:value]) } end |
#valid_lock?(lock_info) ⇒ Boolean
Checks if a lock is still valid Params:
lock_info-
the lock that has been acquired when you locked the resource
142 143 144 145 |
# File 'lib/redlock/client.rb', line 142 def valid_lock?(lock_info) ttl = get_remaining_ttl_for_lock(lock_info) !(ttl.nil? || ttl.zero?) end |