eventq_aws 1.14.0 → 1.14.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a65199a6edaa2640fbfb5232fa6f4d3941eec0d7
4
- data.tar.gz: 389bfb8a4b098c4e74a6f17012cb9b3820563ac3
3
+ metadata.gz: 01ef419cf1b91a14f8743834a579d5455371d553
4
+ data.tar.gz: 758ea58e432046f75d5193379dad37b10aa96b36
5
5
  SHA512:
6
- metadata.gz: 08c08291ccb63f216ec6760373c77633653603311f27b6365a9d1fda00ff56d0a8fd305e94dfc3cd90d4729f9e3701ef4d290407ab4f8900dc30fcbf042e3e38
7
- data.tar.gz: 999898344d7c50de7b9946e19955e1b78b07bf600e5e3ab7f3f5f3bef5296cb6c22d6ea0e7fb84d0f32d59b0e3edca482d5f8c1c6254a68da3eed4f1496d968d
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
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module EventQ
2
4
  module Amazon
3
- VERSION = "1.14.0"
5
+ VERSION = "1.14.1"
4
6
  end
5
7
  end
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
- # if RUBY_PLATFORM =~ /java/
13
- # EventQ::Configuration.serialization_provider = EventQ::SerializationProviders::JSON_PROVIDER
14
- # end
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.0
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-06-27 00:00:00.000000000 Z
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.5.1
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