Module: WithAdvisoryLock::CoreAdvisory

Extended by:
ActiveSupport::Concern
Defined in:
lib/with_advisory_lock/core_advisory.rb

Constant Summary collapse

LOCK_PREFIX_ENV =
'WITH_ADVISORY_LOCK_PREFIX'

Instance Method Summary collapse

Instance Method Details

#advisory_lock_stackObject

Thread-local lock stack management



12
13
14
# File 'lib/with_advisory_lock/core_advisory.rb', line 12

def advisory_lock_stack
  Thread.current[:with_advisory_lock_stack] ||= []
end

#with_advisory_lock_if_needed(lock_name, options = {}, &block) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/with_advisory_lock/core_advisory.rb', line 16

def with_advisory_lock_if_needed(lock_name, options = {}, &block)
  options = { timeout_seconds: options } unless options.respond_to?(:fetch)
  options.assert_valid_keys :timeout_seconds, :shared, :transaction, :disable_query_cache

  # Validate transaction-level locks are used within a transaction
  if options.fetch(:transaction, false) && !transaction_open?
    raise ArgumentError, 'transaction-level advisory locks require an active transaction'
  end

  lock_str = "#{ENV.fetch(LOCK_PREFIX_ENV, nil)}#{lock_name}"
  lock_stack_item = LockStackItem.new(lock_str, options.fetch(:shared, false))

  if advisory_lock_stack.include?(lock_stack_item)
    # Already have this exact lock (same name and type), just yield
    return Result.new(lock_was_acquired: true, result: yield)
  end

  # Check if we have a lock with the same name but different type (for upgrade/downgrade)
  same_name_different_type = advisory_lock_stack.any? do |item|
    item.name == lock_str && item.shared != options.fetch(:shared, false)
  end
  if same_name_different_type && options.fetch(:transaction, false)
    # PostgreSQL doesn't support upgrading/downgrading transaction-level locks
    return Result.new(lock_was_acquired: false)
  end

  disable_query_cache = options.fetch(:disable_query_cache, false)

  if disable_query_cache
    uncached do
      advisory_lock_and_yield(lock_name, lock_str, lock_stack_item, options, &block)
    end
  else
    advisory_lock_and_yield(lock_name, lock_str, lock_stack_item, options, &block)
  end
end