agis 0.2.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/agis.rb +287 -0
  3. metadata +73 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6c2efcb1eba59940bd4c9fa4c63baa465d2324a7
4
+ data.tar.gz: 936b33de7cfcdcb35eba8d0cf8400472968383fc
5
+ SHA512:
6
+ metadata.gz: b598596dc39f627f3efdc55ef62001a885cf59fcb930710f30523289dc1bb198558ad4641aeeec3b3fd5e81a3c307d2b890b31256e7844fcdea5d6370736c77e
7
+ data.tar.gz: 47ff87901ca0673f24085843de435f136283d5b8f234ccc1fcef7d0957ff4ee4392c4e7b2b77c48ca75b1110c8850fb9a12dfc35bde28dbf3247a83a6d603775
@@ -0,0 +1,287 @@
1
+ module Agis
2
+ require 'redis'
3
+ require 'redis-lock'
4
+ require 'json'
5
+
6
+ # called whenever a parameter in the queue is of type method
7
+ # this is unusual behavior
8
+ class MethodCallInParameters < StandardError
9
+ end
10
+
11
+ class AgisRetryAttemptsExceeded < StandardError
12
+ end
13
+
14
+ class NoAgisIDAvailable < StandardError
15
+ end
16
+
17
+ class RedisLockExpired < StandardError
18
+ end
19
+
20
+ class MessageBoxEmpty < StandardError
21
+ end
22
+
23
+ # the name of the key used for the Agis message box in Redis
24
+ # the lock is this string followed by ".LOCK"
25
+ def agis_mailbox
26
+ begin
27
+ mid = self.agis_id
28
+ rescue NoMethodError
29
+ end
30
+ begin
31
+ mid ||= self.id
32
+ rescue NoMethodError
33
+ end
34
+ raise NoAgisIDAvailable unless mid
35
+ a = "AGIS TERMINAL : " + self.class.to_s + " : " + mid.to_s
36
+ end
37
+
38
+ def agis_aconv(v)
39
+ a = ""
40
+ case v
41
+ when String
42
+ a = "s:" + v
43
+ when Symbol
44
+ a = "s:" + v.to_s
45
+ when Integer
46
+ a = "i:" + v.to_s
47
+ when Hash
48
+ a = "h:" + v.to_json.to_s
49
+ when Array
50
+ a = "a:" + v.to_json.to_s
51
+ when Float
52
+ a = "d:" + v.to_s
53
+ when TrueClass
54
+ a = "t:"
55
+ when FalseClass
56
+ a = "f:"
57
+ when NilClass
58
+ a = "n:"
59
+ else
60
+ a = "h:" + v.to_json.to_s
61
+ end
62
+ return a
63
+ end
64
+
65
+ def agis_fconv(v)
66
+ case v[0..1]
67
+ when "s:"
68
+ v[2..-1]
69
+ when "i:"
70
+ v[2..-1].to_i
71
+ when "h:"
72
+ JSON.parse!(v[2..-1], symbolize_names: false)
73
+ when "a:"
74
+ JSON.parse!(v[2..-1], symbolize_names: false)
75
+ when "d:"
76
+ v[2..-1].to_f
77
+ when "t:"
78
+ true
79
+ when "f:"
80
+ false
81
+ when "n:"
82
+ nil
83
+ when "m:"
84
+ raise MethodCallInParameters
85
+ end
86
+ end
87
+
88
+ # create a method with no parameters
89
+ def agis_defm0(name, timeout=nil, &b)
90
+ @agis_methods ||= Hash.new
91
+ @agis_methods[name] = [0, b, timeout]
92
+ end
93
+
94
+ # create a method with one parameter
95
+ def agis_defm1(name, timeout=nil, &b)
96
+ @agis_methods ||= Hash.new
97
+ @agis_methods[name] = [1, b, timeout]
98
+ end
99
+
100
+ # create a method with two parameters
101
+ def agis_defm2(name, timeout=nil, &b)
102
+ @agis_methods ||= Hash.new
103
+ @agis_methods[name] = [2, b, timeout]
104
+ end
105
+
106
+ # create a method with three parameters
107
+ def agis_defm3(name, timeout=nil, &b)
108
+ @agis_methods ||= Hash.new
109
+ @agis_methods[name] = [3, b, timeout]
110
+ end
111
+
112
+ # alias for agis_defm3
113
+ def agis_def(name, timeout=nil, &b)
114
+ agis_defm3(name, timeout, b)
115
+ end
116
+
117
+ def pretty_exception(args, e)
118
+ ret = []
119
+ ret << "Agis method call failed: " + args.to_s
120
+ ret << " " + e.class.to_s
121
+ e.backtrace.each do |v|
122
+ ret << v.to_s
123
+ end
124
+ ret
125
+ end
126
+
127
+ def popfive(redis)
128
+ redis.multi do
129
+ redis.lpop self.agis_mailbox
130
+ redis.lpop self.agis_mailbox
131
+ redis.lpop self.agis_mailbox
132
+ redis.lpop self.agis_mailbox
133
+ redis.lpop self.agis_mailbox
134
+ end
135
+ end
136
+
137
+ def agis_boxlock
138
+ self.agis_mailbox + ".LOCK"
139
+ end
140
+
141
+ def agis_returnbox
142
+ self.agis_mailbox + ".RETN"
143
+ end
144
+
145
+ def agis_chew(redis, lock)
146
+ args = redis.lrange(self.agis_mailbox, 0, 4)
147
+ mni = args[0]
148
+ if(mni and mni[0..1] == "m:")
149
+ # don't do any signatures twice ever
150
+ lusig = args[4][2..-1]
151
+ #puts lusig
152
+ if redis.hget self.agis_returnbox, lusig
153
+ popfive redis
154
+ return nil
155
+ end
156
+ mn = mni[2..-1]
157
+ mc = @agis_methods[mn.to_sym][0]
158
+ meti = @agis_methods[mn.to_sym][1]
159
+ case meti
160
+ when Proc
161
+ met = meti
162
+ when Symbol
163
+ met = self.method(meti)
164
+ when NilClass
165
+ met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
166
+ end
167
+
168
+ begin
169
+ #raise Agis::RedisLockExpired if lock.stale_key?
170
+ #begin
171
+ # lock.extend_life (@agis_methods[mn.to_sym][2] or 5)
172
+ #rescue Redis::Lock::LockNotAcquired
173
+ # raise Agis::RedisLockExpired
174
+ #end
175
+ case mc
176
+ when 0
177
+ ret = agis_aconv(met.call())
178
+ when 1
179
+ ret = agis_aconv(met.call(agis_fconv(args[1])))
180
+ when 2
181
+ ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2])))
182
+ when 3
183
+ ret = agis_aconv(met.call(agis_fconv(args[1]), agis_fconv(args[2]), agis_fconv(args[3])))
184
+ end
185
+ redis.multi do
186
+ redis.hset self.agis_returnbox, lusig, ret
187
+ popfive redis
188
+ end
189
+ return :next
190
+ rescue Agis::RedisLockExpired => e
191
+ puts "Agis lock expired for " + args.to_s if (@agis_debugmode == true)
192
+ # popfive redis
193
+ return :relock
194
+ rescue => e
195
+ #puts "feck"
196
+ lock.unlock
197
+ raise e
198
+ end
199
+ elsif not mni
200
+ return :empty
201
+ else
202
+ puts "AGIS error: Unrecognized line!" + mni.to_s
203
+ end
204
+ end
205
+
206
+ def agis_try_usig(redis, usig)
207
+ mayb = redis.hget self.agis_returnbox, usig
208
+ if mayb
209
+ redis.hdel self.agis_returnbox, usig
210
+ return mayb
211
+ else
212
+ return nil
213
+ end
214
+ end
215
+
216
+ def _agis_crunch(redis, usig)
217
+ loop do
218
+ redis.lock(self.agis_boxlock, life: 10) do |lock|
219
+ a = agis_chew(redis, lock)
220
+ next if lock.stale_key?
221
+ u = agis_try_usig(redis, usig)
222
+ if a == :empty
223
+ raise Agis::MessageBoxEmpty unless u
224
+ end
225
+ return agis_fconv(u) if u
226
+ end
227
+ end
228
+ end
229
+
230
+ # Get method in the format
231
+ # [arity, method body]
232
+ def agis_method(name)
233
+ @agis_methods[name]
234
+ end
235
+
236
+ # Push a call and ncrunch immediately
237
+ # this returns the last return value from the queue
238
+ def agis_call(redis, name, arg1=nil, arg2=nil, arg3=nil)
239
+ until_sig = Time.now.to_s + ":" + Process.pid.to_s + Random.new.rand(4000000000).to_s + Random.new.rand(4000000000).to_s
240
+ loop do
241
+ begin
242
+ redis.multi do
243
+ redis.rpush self.agis_mailbox, "m:" + name.to_s
244
+ redis.rpush self.agis_mailbox, agis_aconv(arg1)
245
+ redis.rpush self.agis_mailbox, agis_aconv(arg2)
246
+ redis.rpush self.agis_mailbox, agis_aconv(arg3)
247
+ redis.rpush self.agis_mailbox, "r:" + until_sig
248
+ end
249
+ return _agis_crunch(redis, until_sig)
250
+ rescue Agis::MessageBoxEmpty
251
+ end
252
+ end
253
+ end
254
+
255
+ # Alias for agis_call
256
+ def acall(redis, name, arg1=nil, arg2=nil, arg3=nil)
257
+ agis_call(redis, name, arg1, arg2, arg3)
258
+ end
259
+
260
+ # Method for calling another Agis method, or retrying.
261
+ # this doesn't touch the message box because it should
262
+ # only be called inside an Agis method, where the box
263
+ # is already guaranteed to be locked
264
+ def agis_recall(mn, arg1=nil, arg2=nil, arg3=nil)
265
+ meti = @agis_methods[mn.to_sym][1]
266
+ case meti
267
+ when Proc
268
+ met = meti
269
+ when Symbol
270
+ met = self.method(meti)
271
+ when NilClass
272
+ met = self.method(mn.to_sym) # when proc is Nil, call the class methods all the same
273
+ end
274
+
275
+ case @agis_methods[mn.to_sym][0]
276
+ when 0
277
+ return met.call()
278
+ when 1
279
+ return met.call(arg1)
280
+ when 2
281
+ return met.call(arg1, arg2)
282
+ when 3
283
+ return met.call(arg1, arg2, arg3)
284
+ end
285
+ end
286
+ end
287
+
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.7
5
+ platform: ruby
6
+ authors:
7
+ - Gert Oja
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: redis
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ requirement: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: '0'
25
+ prerelease: false
26
+ type: :development
27
+ - !ruby/object:Gem::Dependency
28
+ name: mlanett-redis-lock
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ prerelease: false
40
+ type: :development
41
+ description: Messagebox Redis Actors for Ruby and ActiveRecord
42
+ email: gertoja1@gmail.com
43
+ executables: []
44
+ extensions: []
45
+ extra_rdoc_files: []
46
+ files:
47
+ - lib/agis.rb
48
+ homepage: http://rubygems.org/gems/agis
49
+ licenses:
50
+ - MIT
51
+ metadata: {}
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 2.4.6
69
+ signing_key:
70
+ specification_version: 4
71
+ summary: Messagebox Redis Actors for Ruby
72
+ test_files: []
73
+ has_rdoc: