Class: WithAdvisoryLock::Base

Inherits:
Object
  • Object
show all
Defined in:
lib/with_advisory_lock/base.rb

Direct Known Subclasses

Flock, MySQL, PostgreSQL

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(connection, lock_name, timeout_seconds) ⇒ Base

Returns a new instance of Base.



7
8
9
10
11
# File 'lib/with_advisory_lock/base.rb', line 7

def initialize(connection, lock_name, timeout_seconds)
  @connection = connection
  @lock_name = lock_name
  @timeout_seconds = timeout_seconds
end

Instance Attribute Details

#connectionObject (readonly)

Returns the value of attribute connection.



5
6
7
# File 'lib/with_advisory_lock/base.rb', line 5

def connection
  @connection
end

#lock_nameObject (readonly)

Returns the value of attribute lock_name.



5
6
7
# File 'lib/with_advisory_lock/base.rb', line 5

def lock_name
  @lock_name
end

#timeout_secondsObject (readonly)

Returns the value of attribute timeout_seconds.



5
6
7
# File 'lib/with_advisory_lock/base.rb', line 5

def timeout_seconds
  @timeout_seconds
end

Class Method Details

.lock_stackObject



17
18
19
# File 'lib/with_advisory_lock/base.rb', line 17

def self.lock_stack
  Thread.current[:with_advisory_lock_stack] ||= []
end

Instance Method Details

#advisory_lock_exists?Boolean

Returns:

  • (Boolean)


45
46
47
48
49
# File 'lib/with_advisory_lock/base.rb', line 45

def advisory_lock_exists?
  acquired_lock = try_lock
ensure
  release_lock if acquired_lock
end

#already_locked?Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/with_advisory_lock/base.rb', line 23

def already_locked?
  lock_stack.include? lock_str
end

#lock_strObject



13
14
15
# File 'lib/with_advisory_lock/base.rb', line 13

def lock_str
  @lock_str ||= "#{ENV['WITH_ADVISORY_LOCK_PREFIX'].to_s}#{lock_name.to_s}"
end

#query_cache_busterObject

The timestamp prevents AR from caching the result improperly, and is ignored.



72
73
74
# File 'lib/with_advisory_lock/base.rb', line 72

def query_cache_buster
  "AS t#{(Time.now.to_f * 1000).to_i}"
end

#stable_hashcode(input) ⇒ Object



35
36
37
38
39
40
41
42
43
# File 'lib/with_advisory_lock/base.rb', line 35

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)
  end
end

#with_advisory_lock_if_neededObject



27
28
29
30
31
32
33
# File 'lib/with_advisory_lock/base.rb', line 27

def with_advisory_lock_if_needed
  if already_locked?
    yield
  else
    yield_with_lock { yield }
  end
end

#yield_with_lockObject



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/with_advisory_lock/base.rb', line 51

def yield_with_lock
  give_up_at = Time.now + @timeout_seconds if @timeout_seconds
  begin
    if try_lock
      begin
        lock_stack.push(lock_str)
        return yield
      ensure
        lock_stack.pop
        release_lock
      end
    else
      # sleep between 1/20 and ~1/5 of a second.
      # Randomizing sleep time may help reduce contention.
      sleep(rand * 0.15 + 0.05)
    end
  end while @timeout_seconds.nil? || Time.now < give_up_at
  false # failed to get lock in time.
end