Class: Locker::Advisory

Inherits:
Object
  • Object
show all
Defined in:
lib/locker/advisory.rb

Defined Under Namespace

Classes: LockConnectionLost

Constant Summary collapse

MAX_LOCK =

The advisory function we use from PostgreSQL needs the arguments to be INT, therefore this are the range of int numbers for PostgreSQL

2147483647
MIN_LOCK =
-2147483648
OVERFLOW_ADJUSTMENT =

Max number that a 32bit computer can hold

2**32

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, options = {}) ⇒ Advisory

Returns a new instance of Advisory.

Raises:

  • (ArgumentError)


17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/locker/advisory.rb', line 17

def initialize(key, options={})
  raise ArgumentError, "key must be a string" unless key.is_a?(String)

  @key             = key
  @crc             = convert_to_crc(key)
  @lockspace       = (options[:lockspace] || 1)
  @blocking        = !!options[:blocking]
  @locked          = false
  @block_timeout   = options[:block_timeout]
  @block_spin_wait = options[:block_spin_wait] || 0.005

  if !@lockspace.is_a?(Integer) || @lockspace < MIN_LOCK || @lockspace > MAX_LOCK
    raise ArgumentError, "The :lockspace option must be an integer between #{MIN_LOCK} and #{MAX_LOCK}"
  end
end

Instance Attribute Details

#blockingObject (readonly)

Returns the value of attribute blocking.



7
8
9
# File 'lib/locker/advisory.rb', line 7

def blocking
  @blocking
end

#crcObject (readonly)

Returns the value of attribute crc.



7
8
9
# File 'lib/locker/advisory.rb', line 7

def crc
  @crc
end

#keyObject (readonly)

Returns the value of attribute key.



7
8
9
# File 'lib/locker/advisory.rb', line 7

def key
  @key
end

#lockedObject (readonly)

Returns the value of attribute locked.



7
8
9
# File 'lib/locker/advisory.rb', line 7

def locked
  @locked
end

#lockspaceObject (readonly)

Returns the value of attribute lockspace.



7
8
9
# File 'lib/locker/advisory.rb', line 7

def lockspace
  @lockspace
end

Class Method Details

.run(key, options = {}, &block) ⇒ Object



33
34
35
36
# File 'lib/locker/advisory.rb', line 33

def self.run(key, options={}, &block)
  advisory = new(key, options)
  advisory.run(&block)
end

Instance Method Details

#run(&block) ⇒ Object



38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
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
# File 'lib/locker/advisory.rb', line 38

def run(&block)
  connection = ActiveRecord::Base.connection_pool.checkout
  connection.transaction :requires_new => true do
    if @blocking && @block_timeout
      break_at = Time.now + @block_timeout
    end

    while !get(connection) && @blocking
      break if break_at && break_at < Time.now
      sleep @block_spin_wait
    end

    if @locked
      begin
        parent_thread = Thread.current

        mutex = Mutex.new

        checker = Thread.new do
          while @locked
            10.times{ sleep 0.5 if @locked }
            mutex.synchronize do
              if @locked
                check(connection, parent_thread)
              end
            end
          end
        end

        block.call
      ensure
        @locked = false
        # Using a mutex to synchronize so that we're sure we're not
        # executing a query when we kill the thread.
        mutex.synchronize do
          if checker.alive?
            checker.exit rescue nil
          end
        end
      end
      true
    else
      false
    end
  end
ensure
  ActiveRecord::Base.connection_pool.checkin(connection) if connection
end