Class: Redstruct::Lock
- Inherits:
-
Factory::Object
- Object
- Factory::Object
- Redstruct::Lock
- Includes:
- Utils::Coercion, Utils::Scriptable
- Defined in:
- lib/redstruct/lock.rb
Overview
Implementation of a simple binary lock (locked/not locked), with option to block and wait for the lock. Uses two redis structures: a string for the lease, and a list for blocking operations.
Constant Summary collapse
- DEFAULT_EXPIRY =
The default expiry on the underlying redis keys, in seconds; can be between 0 and 1 as a float for milliseconds
1- DEFAULT_TIMEOUT =
The default timeout when blocking, in seconds
nil
Instance Attribute Summary collapse
-
#expiry ⇒ Float, Integer
readonly
The expiry of the underlying redis structure in seconds.
-
#resource ⇒ String
readonly
The resource name (or ID of the lock).
-
#timeout ⇒ Integer
readonly
If greater than 0, will block until timeout is reached or the lock is acquired.
-
#token ⇒ String
readonly
The current token.
Attributes inherited from Factory::Object
Instance Method Summary collapse
-
#acquire ⇒ Boolean
Attempts to acquire the lock.
-
#blocking? ⇒ Boolean
Whether or not the lock will block when attempting to acquire it.
-
#delete ⇒ Boolean
Deletes all traces of this lock.
-
#initialize(resource, expiry: DEFAULT_EXPIRY, timeout: DEFAULT_TIMEOUT, **options) ⇒ Lock
constructor
A new instance of Lock.
-
#locked { ... } ⇒ Object
Executes the given block if the lock can be acquired.
-
#release ⇒ Boolean
Releases the lock only if the current token is the value of the lease.
Methods included from Utils::Coercion
Methods included from Utils::Scriptable
Methods inherited from Factory::Object
Methods included from Utils::Inspectable
Constructor Details
#initialize(resource, expiry: DEFAULT_EXPIRY, timeout: DEFAULT_TIMEOUT, **options) ⇒ Lock
Returns a new instance of Lock.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/redstruct/lock.rb', line 36 def initialize(resource, expiry: DEFAULT_EXPIRY, timeout: DEFAULT_TIMEOUT, **) super(**) @resource = resource @token = nil @expiry = expiry @timeout = case timeout when nil then nil when Float::INFINITY then 0 else timeout.to_i end factory = @factory.factory(@resource) @lease = factory.string('lease') @tokens = factory.list('tokens') end |
Instance Attribute Details
#expiry ⇒ Float, Integer (readonly)
Returns the expiry of the underlying redis structure in seconds.
28 29 30 |
# File 'lib/redstruct/lock.rb', line 28 def expiry @expiry end |
#resource ⇒ String (readonly)
Returns the resource name (or ID of the lock).
22 23 24 |
# File 'lib/redstruct/lock.rb', line 22 def resource @resource end |
#timeout ⇒ Integer (readonly)
Returns if greater than 0, will block until timeout is reached or the lock is acquired.
31 32 33 |
# File 'lib/redstruct/lock.rb', line 31 def timeout @timeout end |
#token ⇒ String (readonly)
Returns the current token.
25 26 27 |
# File 'lib/redstruct/lock.rb', line 25 def token @token end |
Instance Method Details
#acquire ⇒ Boolean
Attempts to acquire the lock. First attempts to grab the lease (a redis string). If the current token is already the lease token, the lock is considered acquired. If there is no current lease, then sets it to the current token. If there is a current lease that is not the current token, then:
1) If this not a blocking lock (see Lock#blocking?), return false
2) If this is a blocking lock, block and wait for the next token to be pushed on the tokens list
3) If a token was pushed, set it as our token and refresh the expiry
90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/redstruct/lock.rb', line 90 def acquire acquired = false token = non_blocking_acquire token = blocking_acquire if token.nil? && blocking? unless token.nil? @lease.expire(@expiry) @token = token acquired = true end return acquired end |
#blocking? ⇒ Boolean
Whether or not the lock will block when attempting to acquire it
78 79 80 |
# File 'lib/redstruct/lock.rb', line 78 def blocking? return !@timeout.nil? end |
#delete ⇒ Boolean
Deletes all traces of this lock
56 57 58 |
# File 'lib/redstruct/lock.rb', line 56 def delete return coerce_bool(delete_script(keys: [@lease.key, @tokens.key])) end |
#locked { ... } ⇒ Object
Executes the given block if the lock can be acquired
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/redstruct/lock.rb', line 62 def locked Thread.handle_interrupt(Exception => :never) do begin if acquire Thread.handle_interrupt(Exception => :immediate) do yield end end ensure release end end end |
#release ⇒ Boolean
Releases the lock only if the current token is the value of the lease. If the lock is a blocking lock (see Lock#blocking?), push the next token on the tokens list.
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/redstruct/lock.rb', line 108 def release return false if @token.nil? keys = [@lease.key, @tokens.key] argv = [@token, generate_token, (@expiry.to_f * 1000).floor] released = release_script(keys: keys, argv: argv) @token = nil return coerce_bool(released) end |