Class: Pesto::Lock

Inherits:
Object
  • Object
show all
Defined in:
lib/pesto/lock.rb

Instance Method Summary collapse

Constructor Details

#initialize(ctx = {}, opts = {}) ⇒ Lock

Returns a new instance of Lock.



4
5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/pesto/lock.rb', line 4

def initialize(ctx = {}, opts = {})
  @ctx = ctx

  raise 'ERR_REDIS_NOTFOUND' if @ctx[:pool].nil?

  @conf = {
    :timeout_lock_expire => 5,
    :timeout_lock => 1,
    :interval_check => 0.05
  }.merge(opts)

  load_scripts
end

Instance Method Details

#confObject



39
40
41
# File 'lib/pesto/lock.rb', line 39

def conf
  @conf
end

#cpObject



43
44
45
# File 'lib/pesto/lock.rb', line 43

def cp
  @ctx[:pool]
end

#get_locks(names, opts = {}) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/pesto/lock.rb', line 77

def get_locks names, opts = {}
  locked = 0
  locks = []
  res = []

  timeout_lock_expire = opts[:timeout_lock_expire]

  cp.with do |rc|
    res = rc.multi do
      names.each do |n|
        res << rc.evalsha(@script_sha, {
          :keys => [lock_hash(n)],
          :argv => [timeout_lock_expire]
        })
      end
    end
  end

  names.each_with_index do |n, ix|
    next if res[ix] != 1
    locked += 1
    locks.push n
  end

  return [res, locks, locked == names.size]
end

#load_scriptsObject



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/pesto/lock.rb', line 18

def load_scripts
  cp.with do |rc|
    @script_sha = rc.script(
      :load,
      "local ret = 0 \
      local timeout = tonumber(ARGV[1]) \
      if type(timeout) ~= 'number' then \
        return 0 \
      end \
      local res = redis.call('setnx', KEYS[1], 1) \
      if res == 1 then \
        redis.call('expire', KEYS[1], ARGV[1]) \
        ret = 1 \
      else \
        ret = 0 \
      end \
      return ret"
    )
  end
end

#lock(_names, _opts = {}) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/pesto/lock.rb', line 53

def lock _names, _opts = {}
  opts = merge_options _opts, :timeout_lock_expire, :timeout_lock, :interval_check

  names = (_names.is_a?(String) ? [_names] : _names).uniq
  opts[:timeout_lock_expire] = opts[:timeout_lock_expire].to_i
  opts[:timeout_lock_expire] += (opts[:timeout_lock] * names.size).ceil.to_i

  t_start = Time.now
  stop = false

  while true
    res, locks, stop = get_locks names, {
      :timeout_lock_expire => opts[:timeout_lock_expire]
    }

    break if stop || (Time.now - t_start) > opts[:timeout_lock]

    unlock locks
    sleep opts[:interval_check]
  end

  stop ? 1 : 0
end

#locki(name = 'global', opts = {}) ⇒ Object



104
105
106
# File 'lib/pesto/lock.rb', line 104

def locki name = 'global', opts = {}
  lock name, opts.merge(timeout_lock: 0)
end

#lockx(name = 'global', opts = {}, err = 'ERR_LOCKING') ⇒ Object



108
109
110
111
112
113
# File 'lib/pesto/lock.rb', line 108

def lockx name = 'global', opts = {}, err = 'ERR_LOCKING'
  locked = lock(name, opts)
  return 1 if locked == 1

  raise "#{err} (#{name})"
end

#merge_options(o = {}, *filter) ⇒ Object



47
48
49
50
51
# File 'lib/pesto/lock.rb', line 47

def merge_options o = {}, *filter
  c = conf.merge(o)
  c.delete_if{|k,v| !filter.include?(k) } unless filter.empty?
  c
end

#unlock(_names = []) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/pesto/lock.rb', line 115

def unlock _names = []
  _names = [_names] if _names.is_a?(String)
  names = _names.uniq
  res = []

  cp.with do |rc|
    res = rc.multi do
      names.each do |n|
        rc.del(lock_hash(n))
      end
    end
  end

  val = res.reduce(0){|sum, n| sum + n}

  val > 0 ? 1 : 0
end