Class: Redlock
- Inherits:
-
Object
- Object
- Redlock
- Defined in:
- lib/redlock.rb,
lib/redlock/version.rb
Constant Summary collapse
- DEFAULT_RETRY_COUNT =
3- DEFAULT_RETRY_DELAY =
200- CLOCK_DRIFT_FACTOR =
0.01- UNLOCK_SCRIPT =
' if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end'- VERSION =
'0.0.2'
Instance Method Summary collapse
- #get_unique_lock_id ⇒ Object
-
#initialize(*server_urls) ⇒ Redlock
constructor
A new instance of Redlock.
- #load_script ⇒ Object
- #lock(resource, ttl, val = nil) ⇒ Object
- #lock_instance(redis, resource, val, ttl) ⇒ Object
- #set_retry(count, delay) ⇒ Object
- #unlock(lock) ⇒ Object
- #unlock_instance(redis, resource, val) ⇒ Object
Constructor Details
#initialize(*server_urls) ⇒ Redlock
Returns a new instance of Redlock.
15 16 17 18 19 20 21 22 23 |
# File 'lib/redlock.rb', line 15 def initialize(*server_urls) @servers = [] server_urls.each{|url| @servers << Redis.new(:url => url) } @quorum = server_urls.length / 2 + 1 @retry_count = DEFAULT_RETRY_COUNT @retry_delay = DEFAULT_RETRY_DELAY end |
Instance Method Details
#get_unique_lock_id ⇒ Object
52 53 54 55 56 57 58 59 |
# File 'lib/redlock.rb', line 52 def get_unique_lock_id val = "" bytes = SecureRandom.random_bytes(20) bytes.each_byte{|b| val << b.to_s(32) } val end |
#load_script ⇒ Object
25 26 27 28 29 |
# File 'lib/redlock.rb', line 25 def load_script @servers.each do |server| @unlock_sha = server.script(:load, UNLOCK_SCRIPT) end end |
#lock(resource, ttl, val = nil) ⇒ Object
61 62 63 64 65 66 67 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.rb', line 61 def lock(resource,ttl,val=nil) val = get_unique_lock_id if val.nil? @retry_count.times { n = 0 start_time = (Time.now.to_f*1000).to_i @servers.each{|s| n += 1 if lock_instance(s,resource,val,ttl) } # Add 2 milliseconds to the drift to account for Redis expires # precision, which is 1 milliescond, plus 1 millisecond min drift # for small TTLs. drift = (ttl*CLOCK_DRIFT_FACTOR).to_i + 2 validity_time = ttl-((Time.now.to_f*1000).to_i - start_time)-drift if n >= @quorum && validity_time > 0 return { :validity => validity_time, :resource => resource, :val => val } else @servers.each{|s| unlock_instance(s,resource,val) } end # Wait a random delay before to retry sleep(rand(@retry_delay).to_f/1000) } return false end |
#lock_instance(redis, resource, val, ttl) ⇒ Object
36 37 38 39 40 41 42 |
# File 'lib/redlock.rb', line 36 def lock_instance(redis,resource,val,ttl) begin return redis.set(resource, val, nx: true, px: ttl) rescue return false end end |
#set_retry(count, delay) ⇒ Object
31 32 33 34 |
# File 'lib/redlock.rb', line 31 def set_retry(count,delay) @retry_count = count @retry_delay = delay end |
#unlock(lock) ⇒ Object
91 92 93 94 95 |
# File 'lib/redlock.rb', line 91 def unlock(lock) @servers.each{|s| unlock_instance(s,lock[:resource],lock[:val]) } end |
#unlock_instance(redis, resource, val) ⇒ Object
44 45 46 47 48 49 50 |
# File 'lib/redlock.rb', line 44 def unlock_instance(redis,resource,val) begin redis.evalsha(@unlock_sha, keys: [resource], argv: [val]) rescue # Nothing to do, unlocking is just a best-effort attempt. end end |