Module: Agis

Defined in:
lib/agis.rb

Defined Under Namespace

Classes: AgisRetryAttemptsExceeded, MessageBoxEmpty, MethodCallInParameters, NoAgisIDAvailable, RedisLockExpired

Instance Method Summary collapse

Instance Method Details

#_agis_cleanup_when_empty(redis) ⇒ Object



230
231
232
233
234
235
236
237
238
# File 'lib/agis.rb', line 230

def _agis_cleanup_when_empty(redis)
  redis.del(self.agis_mailbox)
  redis.srem("AGIS_MAILBOX_GLOBAL_LIST", self.agis_mailbox)
  redis.srem("AGIS_MAILBOX_CLASS:" + self.class.to_s, self.agis_id_prelim)
  if(redis.scard("AGIS_MAILBOX_CLASS:" + self.class.to_s) == 0)
    redis.del("AGIS_MAILBOX_CLASS:" + self.class.to_s)
    redis.srem("AGIS_MAILBOX_CLASSES", self.class.to_s)
  end
end

#_agis_crunch(redis, usig) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/agis.rb', line 240

def _agis_crunch(redis, usig)
  loop do
    redis.lock(self.agis_boxlock, life: 10) do |lock|
      a = agis_chew(redis, lock)
      next if lock.stale_key?
      u = agis_try_usig(redis, usig)
      if a == :empty
        _agis_cleanup_when_empty(redis)
        raise Agis::MessageBoxEmpty unless u
      end
      return agis_fconv(u) if u
    end
  end
end

#_agis_find_global_mailboxes(redis) ⇒ Object

Find all mailboxes total



256
257
258
# File 'lib/agis.rb', line 256

def _agis_find_global_mailboxes(redis)
  redis.smembers("AGIS_MAILBOX_GLOBAL_LIST")
end

#acall(redis, name = nil, arg1 = nil, arg2 = nil, arg3 = nil) ⇒ Object

Alias for agis_call



315
316
317
# File 'lib/agis.rb', line 315

def acall(redis, name=nil, arg1=nil, arg2=nil, arg3=nil)
  agis_call(redis, name, arg1, arg2, arg3)
end

#agis_aconv(v) ⇒ Object



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
# File 'lib/agis.rb', line 43

def agis_aconv(v)
  a = ""
  case v
  when String
    a = "s:" + v
  when Symbol
    a = "s:" + v.to_s
  when Integer
    a = "i:" + v.to_s
  when Hash
    a = "h:" + v.to_json.to_s
  when Array
    a = "a:" + v.to_json.to_s
  when Float
    a = "d:" + v.to_s
  when TrueClass
    a = "t:"
  when FalseClass
    a = "f:"
  when NilClass
    a = "n:"
  else
    a = "h:" + v.to_json.to_s
  end
  return a
end

#agis_boxlockObject



142
143
144
# File 'lib/agis.rb', line 142

def agis_boxlock
  self.agis_mailbox + ".LOCK"
end

#agis_call(redis, name = nil, arg1 = nil, arg2 = nil, arg3 = nil) ⇒ Object

Push a call and ncrunch immediately this returns the last return value from the queue



293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/agis.rb', line 293

def agis_call(redis, name=nil, arg1=nil, arg2=nil, arg3=nil)
  name ||= "AGIS_NOOP"
  until_sig = Time.now.to_s + ":" + Process.pid.to_s + Random.new.rand(4000000000).to_s + Random.new.rand(4000000000).to_s
  loop do
    begin
      redis.sadd("AGIS_MAILBOX_CLASSES", self.class.to_s)
      redis.sadd("AGIS_MAILBOX_CLASS:" + self.class.to_s, self.agis_id_prelim)
      redis.sadd("AGIS_MAILBOX_GLOBAL_LIST", self.agis_mailbox)
      redis.multi do
        redis.rpush self.agis_mailbox, "m:" + name.to_s
        redis.rpush self.agis_mailbox, agis_aconv(arg1)
        redis.rpush self.agis_mailbox, agis_aconv(arg2)
        redis.rpush self.agis_mailbox, agis_aconv(arg3)
        redis.rpush self.agis_mailbox, "r:" + until_sig
      end
      return _agis_crunch(redis, until_sig)
    rescue Agis::MessageBoxEmpty
    end
  end
end

#agis_chew(redis, lock) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/agis.rb', line 150

def agis_chew(redis, lock)
  args = redis.lrange(self.agis_mailbox, 0, 4)
  mni  = args[0]
  if(mni and mni[0..1] == "m:")
    # don't do any signatures twice ever
    lusig = args[4][2..-1]
    #puts lusig
    if redis.hget self.agis_returnbox, lusig
      popfive redis
      return nil
    end
    mn        = mni[2..-1]
    mns       = mn.to_sym
    if mn == "AGIS_NOOP" # NOOP
      redis.hset self.agis_returnbox, lusig, "done"
      popfive(redis)
      return :next
    else
      mc        = @agis_methods[mns][:arity]
      mrm       = @agis_methods[mns][:mode]
      meti      = @agis_methods[mns][:method]
      case meti
      when Proc
        met = meti
      when Symbol
        met = self.method(meti)
      when NilClass
        met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
      end
    end
    
    begin
      #raise Agis::RedisLockExpired if lock.stale_key?
      #begin
      #  lock.extend_life (@agis_methods[mn.to_sym][2] or 5)
      #rescue Redis::Lock::LockNotAcquired
      #  raise Agis::RedisLockExpired
      #end
      popfive(redis) if mrm == :once
      case mc
      when 0
        ret = agis_aconv(met.call())
      when 1
        ret = agis_aconv(met.call(agis_fconv(args[1])))
      when 2
        ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2])))
      when 3
        ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2]), agis_fconv(args[3])))
      end
      redis.multi do
        redis.hset self.agis_returnbox, lusig, ret
        popfive(redis) if mrm == :retry
      end
      return :next
    rescue Agis::RedisLockExpired => e
      puts "Agis lock expired for " + args.to_s if (@agis_debugmode == true)
      # popfive redis
      return :relock
    rescue => e
      #puts "feck"
      lock.unlock
      raise e
    end
  elsif not mni
    return :empty
  else
    puts "AGIS error: Unrecognized line!" + mni.to_s
  end
end

#agis_crunch_all_records(redis) ⇒ Object

Crunch all agis calls on a single model found using the #find method with the agis_id or id. Must be numeric



268
269
270
271
272
273
# File 'lib/agis.rb', line 268

def agis_crunch_all_records(redis)
  all = agis_find_all_mailboxes(redis)
  all.each do |id|
    self.class.find(id).agis_call(redis)
  end
end

#agis_crunch_all_records_new(redis) ⇒ Object

Crunch all agis calls on a single model found using the #new method with id set to agis_id or id



278
279
280
281
282
283
# File 'lib/agis.rb', line 278

def agis_crunch_all_records_new(redis)
  all = agis_find_all_mailboxes(redis)
  all.each do |id|
    self.class.new(id).agis_call(redis)
  end
end

#agis_def(name, mode = :retry, timeout = nil, &b) ⇒ Object

alias for agis_defm3



118
119
120
# File 'lib/agis.rb', line 118

def agis_def(name, mode=:retry, timeout=nil, &b)
  agis_defm3(name, timeout, b)
end

#agis_defm0(name, mode = :retry, timeout = nil, &b) ⇒ Object

create a method with no parameters



94
95
96
97
# File 'lib/agis.rb', line 94

def agis_defm0(name, mode=:retry, timeout=nil, &b)
  @agis_methods ||= Hash.new
  @agis_methods[name] = {arity: 0, method: b, mode: mode, timeout: timeout}
end

#agis_defm1(name, mode = :retry, timeout = nil, &b) ⇒ Object

create a method with one parameter



100
101
102
103
# File 'lib/agis.rb', line 100

def agis_defm1(name, mode=:retry, timeout=nil, &b)
  @agis_methods ||= Hash.new
  @agis_methods[name] = {arity: 1, method: b, mode: mode, timeout: timeout}
end

#agis_defm2(name, mode = :retry, timeout = nil, &b) ⇒ Object

create a method with two parameters



106
107
108
109
# File 'lib/agis.rb', line 106

def agis_defm2(name, mode=:retry, timeout=nil, &b)
  @agis_methods ||= Hash.new
  @agis_methods[name] = {arity: 2, method: b, mode: mode, timeout: timeout}
end

#agis_defm3(name, mode = :retry, timeout = nil, &b) ⇒ Object

create a method with three parameters



112
113
114
115
# File 'lib/agis.rb', line 112

def agis_defm3(name, mode=:retry, timeout=nil, &b)
  @agis_methods ||= Hash.new
  @agis_methods[name] = {arity: 3, method: b, mode: mode, timeout: timeout}
end

#agis_fconv(v) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/agis.rb', line 70

def agis_fconv(v)
  case v[0..1]
  when "s:"
    v[2..-1]
  when "i:"
    v[2..-1].to_i
  when "h:"
    JSON.parse!(v[2..-1], symbolize_names: false)
  when "a:"
    JSON.parse!(v[2..-1], symbolize_names: false)
  when "d:"
    v[2..-1].to_f
  when "t:"
    true
  when "f:"
    false
  when "n:"
    nil
  when "m:"
    raise MethodCallInParameters
  end
end

#agis_find_all_mailboxes(redis) ⇒ Object

Find all mailboxes for this class



261
262
263
# File 'lib/agis.rb', line 261

def agis_find_all_mailboxes(redis)
  redis.smembers("AGIS_MAILBOX_CLASS:" + self.class.to_s)
end

#agis_id_prelimObject



23
24
25
26
27
28
29
30
31
32
33
# File 'lib/agis.rb', line 23

def agis_id_prelim
  begin
    mid = self.agis_id
  rescue NoMethodError
  end
  begin
    mid ||= self.id
  rescue NoMethodError
  end
  return mid
end

#agis_mailboxObject

the name of the key used for the Agis message box in Redis the lock is this string followed by “.LOCK”

Raises:



37
38
39
40
41
# File 'lib/agis.rb', line 37

def agis_mailbox
  mid = agis_id_prelim
  raise NoAgisIDAvailable unless mid
  a = "AGIS TERMINAL : " + self.class.to_s + " : " + mid.to_s
end

#agis_method(name) ⇒ Object

Get method in the format

arity, method body


287
288
289
# File 'lib/agis.rb', line 287

def agis_method(name)
  @agis_methods[name]
end

#agis_recall(mn, arg1 = nil, arg2 = nil, arg3 = nil) ⇒ Object

Method for calling another Agis method, or retrying. this doesn’t touch the message box because it should only be called inside an Agis method, where the box is already guaranteed to be locked



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
# File 'lib/agis.rb', line 323

def agis_recall(mn, arg1=nil, arg2=nil, arg3=nil)
  meti = @agis_methods[mn.to_sym][:method]
  case meti
  when Proc
    met = meti
  when Symbol
    met = self.method(meti)
  when NilClass
    met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
  end
  
  case @agis_methods[mn.to_sym][:arity]
  when 0
    return met.call()
  when 1
    return met.call(arg1)
  when 2
    return met.call(arg1, arg2)
  when 3
    return met.call(arg1, arg2, arg3)
  end
end

#agis_returnboxObject



146
147
148
# File 'lib/agis.rb', line 146

def agis_returnbox
  self.agis_mailbox + ".RETN"
end

#agis_try_usig(redis, usig) ⇒ Object



220
221
222
223
224
225
226
227
228
# File 'lib/agis.rb', line 220

def agis_try_usig(redis, usig)
  mayb = redis.hget self.agis_returnbox, usig
  if mayb
    redis.hdel self.agis_returnbox, usig
    return mayb
  else
    return nil
  end
end

#popfive(redis) ⇒ Object



132
133
134
135
136
137
138
139
140
# File 'lib/agis.rb', line 132

def popfive(redis)
  redis.multi do
    redis.lpop self.agis_mailbox
    redis.lpop self.agis_mailbox
    redis.lpop self.agis_mailbox
    redis.lpop self.agis_mailbox
    redis.lpop self.agis_mailbox
  end
end

#pretty_exception(args, e) ⇒ Object



122
123
124
125
126
127
128
129
130
# File 'lib/agis.rb', line 122

def pretty_exception(args, e)
  ret = []
  ret << "Agis method call failed: " + args.to_s
  ret << "  " + e.class.to_s
  e.backtrace.each do |v|
    ret << v.to_s
  end
  ret
end