super_queue 0.0.9 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/super_queue.rb +123 -72
- metadata +2 -2
data/lib/super_queue.rb
CHANGED
@@ -2,6 +2,7 @@ require 'fog'
|
|
2
2
|
require 'base64'
|
3
3
|
require 'socket'
|
4
4
|
require 'digest/md5'
|
5
|
+
require 'zlib'
|
5
6
|
|
6
7
|
class SuperQueue
|
7
8
|
|
@@ -20,6 +21,7 @@ class SuperQueue
|
|
20
21
|
@buffer_size = opts[:buffer_size] || 100
|
21
22
|
@localize_queue = opts[:localize_queue]
|
22
23
|
@queue_name = generate_queue_name(opts)
|
24
|
+
@request_count = 0
|
23
25
|
initialize_sqs(opts)
|
24
26
|
|
25
27
|
@waiting = []
|
@@ -31,6 +33,9 @@ class SuperQueue
|
|
31
33
|
@deletion_queue = []
|
32
34
|
@mock_length = 0 if SuperQueue.mocking?
|
33
35
|
|
36
|
+
@compressor = Zlib::Deflate.new
|
37
|
+
@decompressor = Zlib::Inflate.new
|
38
|
+
|
34
39
|
@sqs_tracker = Thread.new { poll_sqs }
|
35
40
|
@gc = Thread.new { collect_garbage }
|
36
41
|
end
|
@@ -101,6 +106,10 @@ class SuperQueue
|
|
101
106
|
delete_queue
|
102
107
|
end
|
103
108
|
|
109
|
+
def sqs_requests
|
110
|
+
@request_count
|
111
|
+
end
|
112
|
+
|
104
113
|
alias enq push
|
105
114
|
alias << push
|
106
115
|
|
@@ -123,34 +132,15 @@ class SuperQueue
|
|
123
132
|
|
124
133
|
private
|
125
134
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
@mutex.synchronize { clear_in_buffer } if !@in_buffer.empty? && (@in_buffer.size > @buffer_size)
|
130
|
-
Thread.pass
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def collect_garbage
|
135
|
-
loop do
|
136
|
-
#This also needs a condition to clear the del queue if there are any handles where the invisibility is about to expire
|
137
|
-
@mutex.synchronize { clear_deletion_queue } if !@deletion_queue.empty? && (@deletion_queue.size >= (@buffer_size / 2))
|
138
|
-
sleep
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def check_opts(opts)
|
143
|
-
raise "Options can't be nil!" if opts.nil?
|
144
|
-
raise "Minimun :buffer_size is 5." if opts[:buffer_size] && (opts[:buffer_size] < 5)
|
145
|
-
raise "AWS credentials :aws_access_key_id and :aws_secret_access_key required!" unless opts[:aws_access_key_id] && opts[:aws_secret_access_key]
|
146
|
-
raise "Visbility timeout must be an integer (in seconds)!" if opts[:visibility_timeout] && !opts[:visibility_timeout].is_a?(Integer)
|
147
|
-
end
|
148
|
-
|
135
|
+
#
|
136
|
+
# Amazon SQS methods
|
137
|
+
#
|
149
138
|
def initialize_sqs(opts)
|
150
139
|
create_sqs_connection(opts)
|
151
140
|
create_sqs_queue(opts)
|
152
141
|
check_for_queue_creation_success
|
153
142
|
@sqs.set_queue_attributes(q_url, "VisibilityTimeout", opts[:visibility_timeout]) if opts[:visibility_timeout]
|
143
|
+
@request_count += 1
|
154
144
|
end
|
155
145
|
|
156
146
|
def create_sqs_connection(opts)
|
@@ -177,6 +167,7 @@ class SuperQueue
|
|
177
167
|
@sqs_queue = @sqs.create_queue(queue_name)
|
178
168
|
end
|
179
169
|
end
|
170
|
+
@request_count += 1
|
180
171
|
end
|
181
172
|
|
182
173
|
def check_for_queue_creation_success
|
@@ -190,9 +181,10 @@ class SuperQueue
|
|
190
181
|
|
191
182
|
def send_message_to_queue
|
192
183
|
p = @in_buffer.shift
|
193
|
-
payload = is_a_link?(p) ? p :
|
184
|
+
payload = is_a_link?(p) ? p : encode(p)
|
194
185
|
@sqs.send_message(q_url, payload)
|
195
186
|
@mock_length += 1 if SuperQueue.mocking?
|
187
|
+
@request_count += 1
|
196
188
|
end
|
197
189
|
|
198
190
|
def get_message_from_queue
|
@@ -202,46 +194,15 @@ class SuperQueue
|
|
202
194
|
ser_obj = message.body['Message'].first['Body']
|
203
195
|
return nil if ser_obj.nil? || ser_obj.empty?
|
204
196
|
@mock_length -= 1 if SuperQueue.mocking?
|
197
|
+
@request_count += 1
|
205
198
|
return {:handle => handle, :payload => ser_obj} if is_a_link?(ser_obj)
|
206
|
-
{ :handle => handle, :payload =>
|
207
|
-
end
|
208
|
-
|
209
|
-
def q_url
|
210
|
-
return @q_url if @q_url
|
211
|
-
@q_url = @sqs_queue.body['QueueUrl']
|
212
|
-
@q_url
|
213
|
-
end
|
214
|
-
|
215
|
-
def is_a_link?(s)
|
216
|
-
return false unless s.is_a? String
|
217
|
-
(s[0..6] == "http://") || (s[0..7] == "https://")
|
218
|
-
end
|
219
|
-
|
220
|
-
def delete_queue
|
221
|
-
@sqs.delete_queue(q_url)
|
222
|
-
end
|
223
|
-
|
224
|
-
def generate_queue_name(opts)
|
225
|
-
q_name = opts[:name] || random_name
|
226
|
-
if opts[:namespace] && opts[:localize_queue]
|
227
|
-
"#{@namespace}-#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
228
|
-
elsif opts[:namespace]
|
229
|
-
"#{@namespace}-#{q_name}"
|
230
|
-
elsif opts[:localize_queue]
|
231
|
-
"#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
232
|
-
else
|
233
|
-
q_name
|
234
|
-
end
|
235
|
-
end
|
236
|
-
|
237
|
-
def random_name
|
238
|
-
o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten
|
239
|
-
(0...15).map{ o[rand(o.length)] }.join
|
199
|
+
{ :handle => handle, :payload => decode(ser_obj) }
|
240
200
|
end
|
241
201
|
|
242
202
|
def sqs_length
|
243
203
|
return @mock_length if SuperQueue.mocking?
|
244
204
|
body = @sqs.get_queue_attributes(q_url, "ApproximateNumberOfMessages").body
|
205
|
+
@request_count += 1
|
245
206
|
begin
|
246
207
|
retval = 0
|
247
208
|
if body
|
@@ -254,17 +215,21 @@ class SuperQueue
|
|
254
215
|
retval
|
255
216
|
end
|
256
217
|
|
257
|
-
def
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
218
|
+
def delete_queue
|
219
|
+
@request_count += 1
|
220
|
+
@sqs.delete_queue(q_url)
|
221
|
+
end
|
222
|
+
|
223
|
+
def clear_deletion_queue
|
224
|
+
while !@deletion_queue.empty?
|
225
|
+
@sqs.delete_message(q_url, @deletion_queue.shift)
|
226
|
+
@request_count += 1
|
263
227
|
end
|
264
|
-
ensure
|
265
|
-
Socket.do_not_reverse_lookup = orig
|
266
228
|
end
|
267
229
|
|
230
|
+
#
|
231
|
+
# Buffer-related methods
|
232
|
+
#
|
268
233
|
def fill_out_buffer_from_sqs_queue
|
269
234
|
return false if sqs_length == 0
|
270
235
|
@gc.wakeup if @gc.stop? # This is the best time to do GC, because there are no pops happening.
|
@@ -295,20 +260,106 @@ class SuperQueue
|
|
295
260
|
m[:payload]
|
296
261
|
end
|
297
262
|
|
263
|
+
def clear_in_buffer
|
264
|
+
while !@in_buffer.empty? do
|
265
|
+
send_message_to_queue
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
def check_opts(opts)
|
270
|
+
raise "Options can't be nil!" if opts.nil?
|
271
|
+
raise "Minimun :buffer_size is 5." if opts[:buffer_size] && (opts[:buffer_size] < 5)
|
272
|
+
raise "AWS credentials :aws_access_key_id and :aws_secret_access_key required!" unless opts[:aws_access_key_id] && opts[:aws_secret_access_key]
|
273
|
+
raise "Visbility timeout must be an integer (in seconds)!" if opts[:visibility_timeout] && !opts[:visibility_timeout].is_a?(Integer)
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Misc helper methods
|
278
|
+
#
|
279
|
+
def encode(p)
|
280
|
+
text = Base64.encode64(Marshal.dump(p))
|
281
|
+
retval = nil
|
282
|
+
retries = 0
|
283
|
+
begin
|
284
|
+
retval = @compressor.deflate(text)
|
285
|
+
retries += 1
|
286
|
+
end until !(retval.nil? || retval.empty?) || (retries > 5)
|
287
|
+
retval
|
288
|
+
end
|
289
|
+
|
290
|
+
def decode(ser_obj)
|
291
|
+
text = nil
|
292
|
+
retries = 0
|
293
|
+
begin
|
294
|
+
text = @decompressor.inflate(ser_obj)
|
295
|
+
retries += 1
|
296
|
+
end until !(text.nil? || text.empty?) || (retries > 5)
|
297
|
+
Marshal.load(Base64.decode64(text))
|
298
|
+
end
|
299
|
+
|
300
|
+
def is_a_link?(s)
|
301
|
+
return false unless s.is_a? String
|
302
|
+
(s[0..6] == "http://") || (s[0..7] == "https://")
|
303
|
+
end
|
304
|
+
|
305
|
+
def generate_queue_name(opts)
|
306
|
+
q_name = opts[:name] || random_name
|
307
|
+
if opts[:namespace] && opts[:localize_queue]
|
308
|
+
"#{@namespace}-#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
309
|
+
elsif opts[:namespace]
|
310
|
+
"#{@namespace}-#{q_name}"
|
311
|
+
elsif opts[:localize_queue]
|
312
|
+
"#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
313
|
+
else
|
314
|
+
q_name
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
#
|
319
|
+
# Virtul attributes and convenience methods
|
320
|
+
#
|
321
|
+
def q_url
|
322
|
+
return @q_url if @q_url
|
323
|
+
@q_url = @sqs_queue.body['QueueUrl']
|
324
|
+
@q_url
|
325
|
+
end
|
326
|
+
|
327
|
+
def random_name
|
328
|
+
o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten
|
329
|
+
(0...15).map{ o[rand(o.length)] }.join
|
330
|
+
end
|
331
|
+
|
298
332
|
def queue_name
|
299
333
|
@queue_name
|
300
334
|
end
|
301
335
|
|
302
|
-
def
|
303
|
-
|
304
|
-
|
336
|
+
def local_ip
|
337
|
+
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
338
|
+
return "127.0.0.1" if SuperQueue.mocking?
|
339
|
+
UDPSocket.open do |s|
|
340
|
+
s.connect '64.233.187 .99', 1
|
341
|
+
s.addr.last
|
305
342
|
end
|
343
|
+
ensure
|
344
|
+
Socket.do_not_reverse_lookup = orig
|
306
345
|
end
|
307
346
|
|
308
|
-
|
309
|
-
|
310
|
-
|
347
|
+
#
|
348
|
+
# Maintence thread-related methods
|
349
|
+
#
|
350
|
+
def poll_sqs
|
351
|
+
loop do
|
352
|
+
@mutex.synchronize { fill_out_buffer_from_sqs_queue || fill_out_buffer_from_in_buffer } if @out_buffer.empty?
|
353
|
+
@mutex.synchronize { clear_in_buffer } if !@in_buffer.empty? && (@in_buffer.size > @buffer_size)
|
354
|
+
Thread.pass
|
311
355
|
end
|
312
356
|
end
|
313
357
|
|
358
|
+
def collect_garbage
|
359
|
+
loop do
|
360
|
+
#This also needs a condition to clear the del queue if there are any handles where the invisibility is about to expire
|
361
|
+
@mutex.synchronize { clear_deletion_queue } if !@deletion_queue.empty? && (@deletion_queue.size >= (@buffer_size / 2))
|
362
|
+
sleep
|
363
|
+
end
|
364
|
+
end
|
314
365
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: super_queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-02-
|
12
|
+
date: 2013-02-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: fog
|