Class: WithAdvisoryLock::Base
- Inherits:
-
Object
- Object
- WithAdvisoryLock::Base
- Defined in:
- lib/with_advisory_lock/base.rb
Direct Known Subclasses
Instance Attribute Summary collapse
-
#connection ⇒ Object
readonly
Returns the value of attribute connection.
-
#disable_query_cache ⇒ Object
readonly
Returns the value of attribute disable_query_cache.
-
#lock_name ⇒ Object
readonly
Returns the value of attribute lock_name.
-
#shared ⇒ Object
readonly
Returns the value of attribute shared.
-
#timeout_seconds ⇒ Object
readonly
Returns the value of attribute timeout_seconds.
-
#transaction ⇒ Object
readonly
Returns the value of attribute transaction.
Class Method Summary collapse
Instance Method Summary collapse
- #already_locked? ⇒ Boolean
-
#initialize(connection, lock_name, options) ⇒ Base
constructor
A new instance of Base.
- #lock_and_yield(&block) ⇒ Object
- #lock_stack_item ⇒ Object
- #lock_str ⇒ Object
- #stable_hashcode(input) ⇒ Object
-
#unique_column_name ⇒ Object
Prevent AR from caching results improperly.
- #with_advisory_lock_if_needed(&block) ⇒ Object
- #yield_with_lock ⇒ Object
- #yield_with_lock_and_timeout(&block) ⇒ Object
Constructor Details
#initialize(connection, lock_name, options) ⇒ Base
Returns a new instance of Base.
26 27 28 29 30 31 32 33 34 35 36 |
# File 'lib/with_advisory_lock/base.rb', line 26 def initialize(connection, lock_name, ) = { timeout_seconds: } unless .respond_to?(:fetch) .assert_valid_keys :timeout_seconds, :shared, :transaction, :disable_query_cache @connection = connection @lock_name = lock_name @timeout_seconds = .fetch(:timeout_seconds, nil) @shared = .fetch(:shared, false) @transaction = .fetch(:transaction, false) @disable_query_cache = .fetch(:disable_query_cache, false) end |
Instance Attribute Details
#connection ⇒ Object (readonly)
Returns the value of attribute connection.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def connection @connection end |
#disable_query_cache ⇒ Object (readonly)
Returns the value of attribute disable_query_cache.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def disable_query_cache @disable_query_cache end |
#lock_name ⇒ Object (readonly)
Returns the value of attribute lock_name.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def lock_name @lock_name end |
#shared ⇒ Object (readonly)
Returns the value of attribute shared.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def shared @shared end |
#timeout_seconds ⇒ Object (readonly)
Returns the value of attribute timeout_seconds.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def timeout_seconds @timeout_seconds end |
#transaction ⇒ Object (readonly)
Returns the value of attribute transaction.
24 25 26 |
# File 'lib/with_advisory_lock/base.rb', line 24 def transaction @transaction end |
Class Method Details
.lock_stack ⇒ Object
46 47 48 49 |
# File 'lib/with_advisory_lock/base.rb', line 46 def self.lock_stack # access doesn't need to be synchronized as it is only accessed by the current thread. Thread.current[:with_advisory_lock_stack] ||= [] end |
Instance Method Details
#already_locked? ⇒ Boolean
52 53 54 |
# File 'lib/with_advisory_lock/base.rb', line 52 def already_locked? lock_stack.include? lock_stack_item end |
#lock_and_yield(&block) ⇒ Object
66 67 68 69 70 71 72 73 74 |
# File 'lib/with_advisory_lock/base.rb', line 66 def lock_and_yield(&block) if already_locked? Result.new(true, yield) elsif timeout_seconds == 0 yield_with_lock(&block) else yield_with_lock_and_timeout(&block) end end |
#lock_stack_item ⇒ Object
42 43 44 |
# File 'lib/with_advisory_lock/base.rb', line 42 def lock_stack_item @lock_stack_item ||= LockStackItem.new(lock_str, shared) end |
#lock_str ⇒ Object
38 39 40 |
# File 'lib/with_advisory_lock/base.rb', line 38 def lock_str @lock_str ||= "#{ENV['WITH_ADVISORY_LOCK_PREFIX']}#{lock_name}" end |
#stable_hashcode(input) ⇒ Object
76 77 78 79 80 81 82 83 84 |
# File 'lib/with_advisory_lock/base.rb', line 76 def stable_hashcode(input) if input.is_a? Numeric input.to_i else # Ruby MRI's String#hash is randomly seeded as of Ruby 1.9 so # make sure we use a deterministic hash. Zlib.crc32(input.to_s, 0) end end |
#unique_column_name ⇒ Object
Prevent AR from caching results improperly
114 115 116 |
# File 'lib/with_advisory_lock/base.rb', line 114 def unique_column_name "t#{SecureRandom.hex}" end |
#with_advisory_lock_if_needed(&block) ⇒ Object
56 57 58 59 60 61 62 63 64 |
# File 'lib/with_advisory_lock/base.rb', line 56 def with_advisory_lock_if_needed(&block) if disable_query_cache return lock_and_yield do ActiveRecord::Base.uncached(&block) end end lock_and_yield(&block) end |
#yield_with_lock ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/with_advisory_lock/base.rb', line 98 def yield_with_lock if try_lock begin lock_stack.push(lock_stack_item) result = block_given? ? yield : nil Result.new(true, result) ensure lock_stack.pop release_lock end else FAILED_TO_LOCK end end |
#yield_with_lock_and_timeout(&block) ⇒ Object
86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/with_advisory_lock/base.rb', line 86 def yield_with_lock_and_timeout(&block) give_up_at = Time.now + @timeout_seconds if @timeout_seconds while @timeout_seconds.nil? || Time.now < give_up_at r = yield_with_lock(&block) return r if r.lock_was_acquired? # Randomizing sleep time may help reduce contention. sleep(rand(0.05..0.15)) end FAILED_TO_LOCK end |