super_queue 0.1.1 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/super_queue.rb +74 -109
- metadata +4 -4
data/lib/super_queue.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'aws-sdk'
|
2
2
|
require 'base64'
|
3
3
|
require 'socket'
|
4
4
|
require 'digest/md5'
|
@@ -6,20 +6,11 @@ require 'zlib'
|
|
6
6
|
|
7
7
|
class SuperQueue
|
8
8
|
|
9
|
-
def self.mock!
|
10
|
-
@@mock = true
|
11
|
-
Fog.mock!
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.mocking?
|
15
|
-
defined?(@@mock) && @@mock
|
16
|
-
end
|
17
|
-
|
18
9
|
def initialize(opts)
|
10
|
+
AWS.eager_autoload! # for thread safety
|
19
11
|
check_opts(opts)
|
20
|
-
|
12
|
+
@should_poll_sqs = opts[:should_poll_sqs]
|
21
13
|
@buffer_size = opts[:buffer_size] || 100
|
22
|
-
@localize_queue = opts[:localize_queue]
|
23
14
|
@queue_name = generate_queue_name(opts)
|
24
15
|
@request_count = 0
|
25
16
|
initialize_sqs(opts)
|
@@ -31,12 +22,11 @@ class SuperQueue
|
|
31
22
|
@in_buffer = []
|
32
23
|
@out_buffer = []
|
33
24
|
@deletion_queue = []
|
34
|
-
@mock_length = 0 if SuperQueue.mocking?
|
35
25
|
|
36
26
|
@compressor = Zlib::Deflate.new
|
37
27
|
@decompressor = Zlib::Inflate.new
|
38
28
|
|
39
|
-
@sqs_tracker = Thread.new { poll_sqs }
|
29
|
+
@sqs_tracker = Thread.new { poll_sqs } if @should_poll_sqs
|
40
30
|
@gc = Thread.new { collect_garbage }
|
41
31
|
end
|
42
32
|
|
@@ -94,14 +84,14 @@ class SuperQueue
|
|
94
84
|
end
|
95
85
|
|
96
86
|
def shutdown
|
97
|
-
@sqs_tracker.terminate
|
87
|
+
@sqs_tracker.terminate if @should_poll_sqs
|
98
88
|
@mutex.synchronize { clear_in_buffer }
|
99
89
|
@gc.terminate
|
100
90
|
@mutex.synchronize { clear_deletion_queue }
|
101
91
|
end
|
102
92
|
|
103
93
|
def destroy
|
104
|
-
@sqs_tracker.terminate
|
94
|
+
@sqs_tracker.terminate if @should_poll_sqs
|
105
95
|
@gc.terminate
|
106
96
|
delete_queue
|
107
97
|
end
|
@@ -126,10 +116,6 @@ class SuperQueue
|
|
126
116
|
queue_name
|
127
117
|
end
|
128
118
|
|
129
|
-
def localized?
|
130
|
-
!!@localize_queue
|
131
|
-
end
|
132
|
-
|
133
119
|
private
|
134
120
|
|
135
121
|
#
|
@@ -148,11 +134,11 @@ class SuperQueue
|
|
148
134
|
|
149
135
|
def create_sqs_connection(opts)
|
150
136
|
aws_options = {
|
151
|
-
:
|
152
|
-
:
|
137
|
+
:access_key_id => opts[:aws_access_key_id],
|
138
|
+
:secret_access_key => opts[:aws_secret_access_key]
|
153
139
|
}
|
154
140
|
begin
|
155
|
-
@sqs =
|
141
|
+
@sqs = AWS::SQS.new(aws_options)
|
156
142
|
rescue Exception => e
|
157
143
|
raise e
|
158
144
|
end
|
@@ -161,20 +147,29 @@ class SuperQueue
|
|
161
147
|
def create_sqs_queue(opts)
|
162
148
|
retries = 0
|
163
149
|
begin
|
164
|
-
@sqs_queue = new_sqs_queue(opts)
|
150
|
+
@sqs_queue = find_queue_by_name || new_sqs_queue(opts)
|
165
151
|
check_for_queue_creation_success
|
166
152
|
rescue RuntimeError => e
|
167
153
|
retries += 1
|
168
|
-
|
154
|
+
sleep 1
|
155
|
+
(retries >= 20) ? retry : raise(e)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def find_queue_by_name
|
160
|
+
begin
|
161
|
+
@sqs.queues.named(queue_name)
|
162
|
+
rescue AWS::SQS::Errors::NonExistentQueue
|
163
|
+
return nil
|
169
164
|
end
|
170
165
|
end
|
171
166
|
|
172
167
|
def new_sqs_queue(opts)
|
173
168
|
@request_count += 1
|
174
169
|
if opts[:visibility_timeout]
|
175
|
-
@sqs.
|
170
|
+
@sqs.queues.create(queue_name, { :visibility_timeout => opts[:visibility_timeout] })
|
176
171
|
else
|
177
|
-
@sqs.
|
172
|
+
@sqs.queues.create(queue_name)
|
178
173
|
end
|
179
174
|
end
|
180
175
|
|
@@ -187,56 +182,59 @@ class SuperQueue
|
|
187
182
|
raise "Couldn't create queue #{queue_name}, or delete existing queue by this name." if q_url.nil?
|
188
183
|
end
|
189
184
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
185
|
+
def send_messages_to_queue
|
186
|
+
number_of_batches = @in_buffer.size / 10
|
187
|
+
number_of_batches += 1 if @in_buffer.size % 10
|
188
|
+
batches = []
|
189
|
+
number_of_batches.times do
|
190
|
+
batch = []
|
191
|
+
10.times do
|
192
|
+
p = @in_buffer.shift
|
193
|
+
obj = is_a_link?(p) ? p : encode(p)
|
194
|
+
batch << obj
|
195
|
+
end
|
196
|
+
batches << batch
|
197
|
+
end
|
198
|
+
|
199
|
+
#Ugliness! But I'm not sure how else to tackle this at the moment
|
200
|
+
batches.each do |b|
|
195
201
|
@request_count += 1
|
196
|
-
@
|
197
|
-
rescue Excon::Errors::BadRequest => e
|
198
|
-
retries += 1
|
199
|
-
(retries >= 10) ? retry : raise(e)
|
202
|
+
@sqs_queue.batch_send(b)
|
200
203
|
end
|
201
|
-
@mock_length += 1 if SuperQueue.mocking?
|
202
204
|
end
|
203
205
|
|
204
|
-
def
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
206
|
+
def get_messages_from_queue(number_of_messages_to_receive)
|
207
|
+
messages = []
|
208
|
+
number_of_batches = number_of_messages_to_receive / 10
|
209
|
+
number_of_batches += 1 if number_of_messages_to_receive % 10
|
210
|
+
number_of_batches.times do |c|
|
211
|
+
batch = @sqs_queue.receive_messages(:limit => 10)
|
212
|
+
batch.each do |m|
|
213
|
+
if is_a_link?(m.body)
|
214
|
+
obj = {:message => m, :payload => m.body}
|
215
|
+
else
|
216
|
+
obj = { :message => m, :payload => decode(m.body) }
|
217
|
+
end
|
218
|
+
messages << obj
|
219
|
+
end
|
220
|
+
@request_count += 1
|
221
|
+
end
|
222
|
+
messages
|
214
223
|
end
|
215
224
|
|
216
225
|
def sqs_length
|
217
|
-
|
218
|
-
|
219
|
-
@request_count += 1
|
220
|
-
begin
|
221
|
-
retval = 0
|
222
|
-
if body
|
223
|
-
attrs = body["Attributes"]
|
224
|
-
if attrs
|
225
|
-
retval = attrs["ApproximateNumberOfMessages"]
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end until !retval.nil?
|
229
|
-
retval
|
226
|
+
n = @sqs_queue.approximate_number_of_messages
|
227
|
+
return n.is_a?(Integer) ? n : 0
|
230
228
|
end
|
231
229
|
|
232
230
|
def delete_queue
|
233
231
|
@request_count += 1
|
234
|
-
@
|
232
|
+
@sqs_queue.delete
|
235
233
|
end
|
236
234
|
|
237
235
|
def clear_deletion_queue
|
238
236
|
while !@deletion_queue.empty?
|
239
|
-
@
|
237
|
+
@sqs_queue.batch_delete(@deletion_queue[0..9])
|
240
238
|
@request_count += 1
|
241
239
|
end
|
242
240
|
end
|
@@ -247,15 +245,9 @@ class SuperQueue
|
|
247
245
|
def fill_out_buffer_from_sqs_queue
|
248
246
|
return false if sqs_length == 0
|
249
247
|
@gc.wakeup if @gc.stop? # This is the best time to do GC, because there are no pops happening.
|
250
|
-
|
251
|
-
|
252
|
-
m
|
253
|
-
if m.nil?
|
254
|
-
nil_count += 1
|
255
|
-
else
|
256
|
-
@out_buffer.push m
|
257
|
-
nil_count = 0
|
258
|
-
end
|
248
|
+
while (@out_buffer.size < @buffer_size)
|
249
|
+
messages = get_messages_from_queue(@buffer_size - @out_buffer.size)
|
250
|
+
messages.each { |m| @out_buffer.push m }
|
259
251
|
end
|
260
252
|
!@out_buffer.empty?
|
261
253
|
end
|
@@ -270,13 +262,13 @@ class SuperQueue
|
|
270
262
|
|
271
263
|
def pop_out_buffer
|
272
264
|
m = @out_buffer.shift
|
273
|
-
@deletion_queue << m[:
|
265
|
+
@deletion_queue << m[:message] if m[:message]
|
274
266
|
m[:payload]
|
275
267
|
end
|
276
268
|
|
277
269
|
def clear_in_buffer
|
278
270
|
while !@in_buffer.empty? do
|
279
|
-
|
271
|
+
send_messages_to_queue
|
280
272
|
end
|
281
273
|
end
|
282
274
|
|
@@ -291,24 +283,16 @@ class SuperQueue
|
|
291
283
|
# Misc helper methods
|
292
284
|
#
|
293
285
|
def encode(p)
|
294
|
-
|
295
|
-
retval = nil
|
296
|
-
retries = 0
|
297
|
-
begin
|
298
|
-
retval = @compressor.deflate(text)
|
299
|
-
retries += 1
|
300
|
-
end until !(retval.nil? || retval.empty?) || (retries > 5)
|
301
|
-
retval
|
286
|
+
Base64.urlsafe_encode64(Marshal.dump(p))
|
302
287
|
end
|
303
288
|
|
304
289
|
def decode(ser_obj)
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
Marshal.load(Base64.decode64(text))
|
290
|
+
#puts "Decode: Decoding message #{ser_obj} from base64..."
|
291
|
+
obj = Base64.urlsafe_decode64(ser_obj)
|
292
|
+
#puts "Decode: Object decoded as #{obj}. Doing Marshal load..."
|
293
|
+
ret = Marshal.load(obj)
|
294
|
+
#puts "Decode: Marshal loaded as #{ret}"
|
295
|
+
ret
|
312
296
|
end
|
313
297
|
|
314
298
|
def is_a_link?(s)
|
@@ -318,15 +302,7 @@ class SuperQueue
|
|
318
302
|
|
319
303
|
def generate_queue_name(opts)
|
320
304
|
q_name = opts[:name] || random_name
|
321
|
-
|
322
|
-
"#{@namespace}-#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
323
|
-
elsif opts[:namespace]
|
324
|
-
"#{@namespace}-#{q_name}"
|
325
|
-
elsif opts[:localize_queue]
|
326
|
-
"#{Digest::MD5.hexdigest(local_ip)}-#{q_name}"
|
327
|
-
else
|
328
|
-
q_name
|
329
|
-
end
|
305
|
+
return opts[:namespace] ? "#{@namespace}-#{q_name}" : q_name
|
330
306
|
end
|
331
307
|
|
332
308
|
#
|
@@ -334,7 +310,7 @@ class SuperQueue
|
|
334
310
|
#
|
335
311
|
def q_url
|
336
312
|
return @q_url if @q_url
|
337
|
-
@q_url = @sqs_queue.
|
313
|
+
@q_url = @sqs_queue.url
|
338
314
|
@q_url
|
339
315
|
end
|
340
316
|
|
@@ -347,17 +323,6 @@ class SuperQueue
|
|
347
323
|
@queue_name
|
348
324
|
end
|
349
325
|
|
350
|
-
def local_ip
|
351
|
-
orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true # turn off reverse DNS resolution temporarily
|
352
|
-
return "127.0.0.1" if SuperQueue.mocking?
|
353
|
-
UDPSocket.open do |s|
|
354
|
-
s.connect '64.233.187 .99', 1
|
355
|
-
s.addr.last
|
356
|
-
end
|
357
|
-
ensure
|
358
|
-
Socket.do_not_reverse_lookup = orig
|
359
|
-
end
|
360
|
-
|
361
326
|
#
|
362
327
|
# Maintence thread-related methods
|
363
328
|
#
|
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.
|
4
|
+
version: 0.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,18 +12,18 @@ cert_chain: []
|
|
12
12
|
date: 2013-02-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: aws-sdk
|
16
16
|
version_requirements: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 1.
|
20
|
+
version: 1.6.5
|
21
21
|
none: false
|
22
22
|
requirement: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.6.5
|
27
27
|
none: false
|
28
28
|
prerelease: false
|
29
29
|
type: :runtime
|