eventq_aws 1.14.0 → 1.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/eventq_aws/aws_eventq_client.rb +5 -1
- data/lib/eventq_aws/jruby/aws_queue_worker.rb +370 -0
- data/lib/eventq_aws/version.rb +3 -1
- data/lib/eventq_aws.rb +5 -4
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01ef419cf1b91a14f8743834a579d5455371d553
|
4
|
+
data.tar.gz: 758ea58e432046f75d5193379dad37b10aa96b36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c831f9ecbc289341bb2cd2612f2bd32ad9d98aea8c9c24097d7efc37feb7251e2855c66a61b91ce506de6110c805d64931350d4462bf3f53bcd31976b7638db6
|
7
|
+
data.tar.gz: b85c9b279d4243826c60d65f932c0d3f56a1db59f93567c486c7db28aa9aa50f04023ddb1556973fb691030717c455bd6f1cd00eba96c5ee1af3d07b2e5ebc12
|
@@ -63,7 +63,7 @@ module EventQ
|
|
63
63
|
|
64
64
|
response = @client.sqs.send_message(
|
65
65
|
queue_url: queue_url,
|
66
|
-
message_body: message,
|
66
|
+
message_body: sqs_message_body_for(message),
|
67
67
|
delay_seconds: delay
|
68
68
|
)
|
69
69
|
|
@@ -111,6 +111,10 @@ module EventQ
|
|
111
111
|
def topic_arn(event_type)
|
112
112
|
@client.get_topic_arn(event_type)
|
113
113
|
end
|
114
|
+
|
115
|
+
def sqs_message_body_for(payload_message)
|
116
|
+
JSON.dump(EventQ::Amazon::QueueWorker::MESSAGE => payload_message)
|
117
|
+
end
|
114
118
|
end
|
115
119
|
end
|
116
120
|
end
|
@@ -0,0 +1,370 @@
|
|
1
|
+
require 'java'
|
2
|
+
java_import java.util.concurrent.Executors
|
3
|
+
module EventQ
|
4
|
+
module Amazon
|
5
|
+
class QueueWorker
|
6
|
+
include EventQ::WorkerId
|
7
|
+
|
8
|
+
APPROXIMATE_RECEIVE_COUNT = 'ApproximateReceiveCount'.freeze
|
9
|
+
MESSAGE = 'Message'.freeze
|
10
|
+
|
11
|
+
attr_accessor :is_running
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@is_running = false
|
15
|
+
|
16
|
+
@on_retry_exceeded_block = nil
|
17
|
+
@on_retry_block = nil
|
18
|
+
@on_error_block = nil
|
19
|
+
|
20
|
+
@hash_helper = HashKit::Helper.new
|
21
|
+
@serialization_provider_manager = EventQ::SerializationProviders::Manager.new
|
22
|
+
@signature_provider_manager = EventQ::SignatureProviders::Manager.new
|
23
|
+
|
24
|
+
@last_gc_flush = Time.now
|
25
|
+
@gc_flush_interval = 10
|
26
|
+
|
27
|
+
@queue_poll_wait = 10
|
28
|
+
end
|
29
|
+
|
30
|
+
def start(queue, options = {}, &block)
|
31
|
+
|
32
|
+
EventQ.logger.info("[#{self.class}] - Preparing to start listening for messages.")
|
33
|
+
|
34
|
+
configure(queue, options)
|
35
|
+
|
36
|
+
if options[:client] == nil
|
37
|
+
raise "[#{self.class}] - :client (QueueClient) must be specified."
|
38
|
+
end
|
39
|
+
|
40
|
+
raise "[#{self.class}] - Worker is already running." if running?
|
41
|
+
|
42
|
+
client = options[:client]
|
43
|
+
EventQ.logger.debug do
|
44
|
+
"[#{self.class} #start] - Listening for messages on queue: #{queue.name}, Queue Url: #{client.get_queue_url(queue)}, Queue arn: #{client.get_queue_arn(queue)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
EventQ.logger.info("[#{self.class}] - Listening for messages.")
|
48
|
+
|
49
|
+
start_process(options, queue, block)
|
50
|
+
|
51
|
+
return true
|
52
|
+
end
|
53
|
+
|
54
|
+
def start_process(options, queue, block)
|
55
|
+
|
56
|
+
%w'INT TERM'.each do |sig|
|
57
|
+
Signal.trap(sig) {
|
58
|
+
stop
|
59
|
+
exit
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
@is_running = true
|
64
|
+
|
65
|
+
@executor = java.util.concurrent.Executors::newFixedThreadPool @thread_count
|
66
|
+
|
67
|
+
#loop through each thread count
|
68
|
+
@thread_count.times do
|
69
|
+
|
70
|
+
@executor.execute do
|
71
|
+
|
72
|
+
client = options[:client]
|
73
|
+
manager = EventQ::Amazon::QueueManager.new({ client: client })
|
74
|
+
|
75
|
+
#begin the queue loop for this thread
|
76
|
+
while true do
|
77
|
+
|
78
|
+
#check if the worker is still allowed to run and break out of thread loop if not
|
79
|
+
unless running?
|
80
|
+
break
|
81
|
+
end
|
82
|
+
|
83
|
+
if @executor.is_shutdown
|
84
|
+
break
|
85
|
+
end
|
86
|
+
|
87
|
+
has_message_received = thread_process_iteration(client, manager, queue, block)
|
88
|
+
|
89
|
+
gc_flush
|
90
|
+
|
91
|
+
if !has_message_received
|
92
|
+
EventQ.logger.debug { "[#{self.class}] - No message received." }
|
93
|
+
if @sleep > 0
|
94
|
+
EventQ.logger.debug { "[#{self.class}] - Sleeping for #{@sleep} seconds" }
|
95
|
+
sleep(@sleep)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
if options.key?(:wait) && options[:wait] == true
|
106
|
+
while running? do end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
def gc_flush
|
112
|
+
if Time.now - last_gc_flush > @gc_flush_interval
|
113
|
+
GC.start
|
114
|
+
@last_gc_flush = Time.now
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def last_gc_flush
|
119
|
+
@last_gc_flush
|
120
|
+
end
|
121
|
+
|
122
|
+
def thread_process_iteration(client, manager, queue, block)
|
123
|
+
#get the queue
|
124
|
+
q = manager.get_queue(queue)
|
125
|
+
|
126
|
+
received = false
|
127
|
+
|
128
|
+
begin
|
129
|
+
|
130
|
+
#request a message from the queue
|
131
|
+
response = client.sqs.receive_message({
|
132
|
+
queue_url: q,
|
133
|
+
max_number_of_messages: 1,
|
134
|
+
wait_time_seconds: @queue_poll_wait,
|
135
|
+
attribute_names: [APPROXIMATE_RECEIVE_COUNT]
|
136
|
+
})
|
137
|
+
|
138
|
+
#check that a message was received
|
139
|
+
if response.messages.length > 0
|
140
|
+
received = true
|
141
|
+
begin
|
142
|
+
tag_processing_thread
|
143
|
+
process_message(response, client, queue, q, block)
|
144
|
+
ensure
|
145
|
+
untag_processing_thread
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|
149
|
+
|
150
|
+
rescue => e
|
151
|
+
EventQ.log(:error, "[#{self.class}] - An unhandled error occurred. Error: #{e} | Backtrace: #{e.backtrace}")
|
152
|
+
call_on_error_block(error: e)
|
153
|
+
end
|
154
|
+
|
155
|
+
return received
|
156
|
+
end
|
157
|
+
|
158
|
+
def call_on_error_block(error:, message: nil)
|
159
|
+
if @on_error_block
|
160
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_error block." }
|
161
|
+
begin
|
162
|
+
@on_error_block.call(error, message)
|
163
|
+
rescue => e
|
164
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_error block. Error: #{e}")
|
165
|
+
end
|
166
|
+
else
|
167
|
+
EventQ.logger.debug { "[#{self.class}] - No on_error block specified to execute." }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def call_on_retry_exceeded_block(message)
|
172
|
+
if @on_retry_exceeded_block != nil
|
173
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_retry_exceeded block." }
|
174
|
+
begin
|
175
|
+
@on_retry_exceeded_block.call(message)
|
176
|
+
rescue => e
|
177
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry_exceeded block. Error: #{e}")
|
178
|
+
end
|
179
|
+
else
|
180
|
+
EventQ.logger.debug { "[#{self.class}] - No on_retry_exceeded block specified." }
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def call_on_retry_block(message)
|
185
|
+
if @on_retry_block
|
186
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_retry block." }
|
187
|
+
begin
|
188
|
+
@on_retry_block.call(message, abort)
|
189
|
+
rescue => e
|
190
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry block. Error: #{e}")
|
191
|
+
end
|
192
|
+
else
|
193
|
+
EventQ.logger.debug { "[#{self.class}] - No on_retry block specified." }
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def stop
|
198
|
+
EventQ.logger.info("[#{self.class}] - Stopping.")
|
199
|
+
@is_running = false
|
200
|
+
@executor.shutdown
|
201
|
+
return true
|
202
|
+
end
|
203
|
+
|
204
|
+
def on_retry_exceeded(&block)
|
205
|
+
@retry_exceeded_block = block
|
206
|
+
end
|
207
|
+
|
208
|
+
def on_retry(&block)
|
209
|
+
@on_retry_block = block
|
210
|
+
return nil
|
211
|
+
end
|
212
|
+
|
213
|
+
def on_error(&block)
|
214
|
+
@on_error_block = block
|
215
|
+
return nil
|
216
|
+
end
|
217
|
+
|
218
|
+
def running?
|
219
|
+
return @is_running
|
220
|
+
end
|
221
|
+
|
222
|
+
def deserialize_message(payload)
|
223
|
+
provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
|
224
|
+
return provider.deserialize(payload)
|
225
|
+
end
|
226
|
+
|
227
|
+
def serialize_message(msg)
|
228
|
+
provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
|
229
|
+
return provider.serialize(msg)
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def process_message(response, client, queue, q, block)
|
235
|
+
msg = response.messages[0]
|
236
|
+
retry_attempts = msg.attributes[APPROXIMATE_RECEIVE_COUNT].to_i - 1
|
237
|
+
|
238
|
+
#deserialize the message payload
|
239
|
+
payload = JSON.load(msg.body)
|
240
|
+
message = deserialize_message(payload[MESSAGE])
|
241
|
+
|
242
|
+
message_args = EventQ::MessageArgs.new(type: message.type,
|
243
|
+
retry_attempts: retry_attempts,
|
244
|
+
context: message.context,
|
245
|
+
content_type: message.content_type)
|
246
|
+
|
247
|
+
EventQ.logger.info("[#{self.class}] - Message received. Retry Attempts: #{retry_attempts}")
|
248
|
+
|
249
|
+
@signature_provider_manager.validate_signature(message: message, queue: queue)
|
250
|
+
|
251
|
+
if(!EventQ::NonceManager.is_allowed?(message.id))
|
252
|
+
EventQ.logger.info("[#{self.class}] - Duplicate Message received. Dropping message.")
|
253
|
+
client.sqs.delete_message({ queue_url: q, receipt_handle: msg.receipt_handle })
|
254
|
+
return false
|
255
|
+
end
|
256
|
+
|
257
|
+
#begin worker block for queue message
|
258
|
+
begin
|
259
|
+
|
260
|
+
block.call(message.content, message_args)
|
261
|
+
|
262
|
+
if message_args.abort == true
|
263
|
+
EventQ.logger.info("[#{self.class}] - Message aborted.")
|
264
|
+
else
|
265
|
+
#accept the message as processed
|
266
|
+
client.sqs.delete_message({ queue_url: q, receipt_handle: msg.receipt_handle })
|
267
|
+
EventQ.logger.info("[#{self.class}] - Message acknowledged.")
|
268
|
+
end
|
269
|
+
|
270
|
+
rescue => e
|
271
|
+
EventQ.logger.error("[#{self.class}] - An unhandled error happened while attempting to process a queue message. Error: #{e} | Backtrace: #{e.backtrace}")
|
272
|
+
error = true
|
273
|
+
call_on_error_block(error: e, message: message)
|
274
|
+
end
|
275
|
+
|
276
|
+
if message_args.abort || error
|
277
|
+
EventQ::NonceManager.failed(message.id)
|
278
|
+
reject_message(queue, client, msg, q, retry_attempts, message, message_args.abort)
|
279
|
+
else
|
280
|
+
EventQ::NonceManager.complete(message.id)
|
281
|
+
end
|
282
|
+
|
283
|
+
return true
|
284
|
+
end
|
285
|
+
|
286
|
+
def reject_message(queue, client, msg, q, retry_attempts, message, abort)
|
287
|
+
|
288
|
+
if !queue.allow_retry || retry_attempts >= queue.max_retry_attempts
|
289
|
+
|
290
|
+
EventQ.logger.info("[#{self.class}] - Message rejected removing from queue. Message: #{serialize_message(message)}")
|
291
|
+
|
292
|
+
#remove the message from the queue so that it does not get retried again
|
293
|
+
client.sqs.delete_message({ queue_url: q, receipt_handle: msg.receipt_handle })
|
294
|
+
|
295
|
+
if retry_attempts >= queue.max_retry_attempts
|
296
|
+
|
297
|
+
EventQ.logger.info("[#{self.class}] - Message retry attempt limit exceeded.")
|
298
|
+
call_on_retry_exceeded_block(message)
|
299
|
+
|
300
|
+
end
|
301
|
+
|
302
|
+
elsif queue.allow_retry
|
303
|
+
|
304
|
+
retry_attempts += 1
|
305
|
+
|
306
|
+
EventQ.logger.info("[#{self.class}] - Message rejected requesting retry. Attempts: #{retry_attempts}")
|
307
|
+
|
308
|
+
if queue.allow_retry_back_off == true
|
309
|
+
EventQ.logger.debug { "[#{self.class}] - Calculating message back off retry delay. Attempts: #{retry_attempts} * Delay: #{queue.retry_delay}" }
|
310
|
+
visibility_timeout = (queue.retry_delay * retry_attempts) / 1000
|
311
|
+
if visibility_timeout > (queue.max_retry_delay / 1000)
|
312
|
+
EventQ.logger.debug { "[#{self.class}] - Max message back off retry delay reached." }
|
313
|
+
visibility_timeout = queue.max_retry_delay / 1000
|
314
|
+
end
|
315
|
+
else
|
316
|
+
EventQ.logger.debug { "[#{self.class}] - Setting fixed retry delay for message." }
|
317
|
+
visibility_timeout = queue.retry_delay / 1000
|
318
|
+
end
|
319
|
+
|
320
|
+
if visibility_timeout > 43200
|
321
|
+
EventQ.logger.debug { "[#{self.class}] - AWS max visibility timeout of 12 hours has been exceeded. Setting message retry delay to 12 hours." }
|
322
|
+
visibility_timeout = 43200
|
323
|
+
end
|
324
|
+
|
325
|
+
EventQ.logger.debug { "[#{self.class}] - Sending message for retry. Message TTL: #{visibility_timeout}" }
|
326
|
+
client.sqs.change_message_visibility({
|
327
|
+
queue_url: q, # required
|
328
|
+
receipt_handle: msg.receipt_handle, # required
|
329
|
+
visibility_timeout: visibility_timeout.to_s, # required
|
330
|
+
})
|
331
|
+
|
332
|
+
call_on_retry_block(message)
|
333
|
+
|
334
|
+
end
|
335
|
+
|
336
|
+
end
|
337
|
+
|
338
|
+
def configure(queue, options = {})
|
339
|
+
|
340
|
+
@queue = queue
|
341
|
+
|
342
|
+
#default thread count
|
343
|
+
@thread_count = 5
|
344
|
+
if options.key?(:thread_count)
|
345
|
+
@thread_count = options[:thread_count]
|
346
|
+
end
|
347
|
+
|
348
|
+
#default sleep time in seconds
|
349
|
+
@sleep = 5
|
350
|
+
if options.key?(:sleep)
|
351
|
+
@sleep = options[:sleep]
|
352
|
+
end
|
353
|
+
|
354
|
+
if options.key?(:gc_flush_interval)
|
355
|
+
@gc_flush_interval = options[:gc_flush_interval]
|
356
|
+
end
|
357
|
+
|
358
|
+
if options.key?(:queue_poll_wait)
|
359
|
+
@queue_poll_wait = options[:queue_poll_wait]
|
360
|
+
end
|
361
|
+
|
362
|
+
EventQ.logger.info("[#{self.class}] - Configuring. Thread Count: #{@thread_count} | Interval Sleep: #{@sleep} | GC Flush Interval: #{@gc_flush_interval} | Queue Poll Wait: #{@queue_poll_wait}.")
|
363
|
+
|
364
|
+
return true
|
365
|
+
|
366
|
+
end
|
367
|
+
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
data/lib/eventq_aws/version.rb
CHANGED
data/lib/eventq_aws.rb
CHANGED
@@ -5,13 +5,14 @@ require 'eventq_aws/version'
|
|
5
5
|
require 'eventq_aws/aws_eventq_client'
|
6
6
|
require 'eventq_aws/aws_queue_client'
|
7
7
|
require 'eventq_aws/aws_queue_manager'
|
8
|
-
require 'eventq_aws/aws_queue_worker'
|
9
8
|
require 'eventq_aws/aws_subscription_manager'
|
10
9
|
require_relative 'eventq_aws/aws_status_checker'
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
if RUBY_PLATFORM =~ /java/
|
12
|
+
require 'eventq_aws/jruby/aws_queue_worker'
|
13
|
+
else
|
14
|
+
require 'eventq_aws/aws_queue_worker'
|
15
|
+
end
|
15
16
|
|
16
17
|
module EventQ
|
17
18
|
def self.namespace
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eventq_aws
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.14.
|
4
|
+
version: 1.14.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- vaughanbrittonsage
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: aws-sdk-core
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
61
|
+
version: '2.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
68
|
+
version: '2.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: eventq_base
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- lib/eventq_aws/aws_queue_worker.rb
|
97
97
|
- lib/eventq_aws/aws_status_checker.rb
|
98
98
|
- lib/eventq_aws/aws_subscription_manager.rb
|
99
|
+
- lib/eventq_aws/jruby/aws_queue_worker.rb
|
99
100
|
- lib/eventq_aws/version.rb
|
100
101
|
homepage: https://github.com/vaughanbrittonsage/eventq
|
101
102
|
licenses:
|
@@ -117,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
118
|
version: '0'
|
118
119
|
requirements: []
|
119
120
|
rubyforge_project:
|
120
|
-
rubygems_version: 2.
|
121
|
+
rubygems_version: 2.6.13
|
121
122
|
signing_key:
|
122
123
|
specification_version: 4
|
123
124
|
summary: This is the aws implementation for EventQ
|