eventq 2.0.0.rc1
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 +7 -0
- data/README.md +336 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/eventq/aws.rb +38 -0
- data/lib/eventq/eventq_aws/README.md +53 -0
- data/lib/eventq/eventq_aws/aws_eventq_client.rb +120 -0
- data/lib/eventq/eventq_aws/aws_queue_client.rb +64 -0
- data/lib/eventq/eventq_aws/aws_queue_manager.rb +68 -0
- data/lib/eventq/eventq_aws/aws_queue_worker.rb +168 -0
- data/lib/eventq/eventq_aws/aws_status_checker.rb +25 -0
- data/lib/eventq/eventq_aws/aws_subscription_manager.rb +65 -0
- data/lib/eventq/eventq_aws/jruby/aws_queue_worker.rb +370 -0
- data/lib/eventq/eventq_aws/sns.rb +64 -0
- data/lib/eventq/eventq_aws/sqs.rb +112 -0
- data/lib/eventq/eventq_base/configuration.rb +33 -0
- data/lib/eventq/eventq_base/event_raised_exchange.rb +7 -0
- data/lib/eventq/eventq_base/event_raised_queue.rb +7 -0
- data/lib/eventq/eventq_base/eventq_client_contract.rb +9 -0
- data/lib/eventq/eventq_base/eventq_logger.rb +28 -0
- data/lib/eventq/eventq_base/exceptions/invalid_signature_exception.rb +9 -0
- data/lib/eventq/eventq_base/exceptions/worker_thread_error.rb +10 -0
- data/lib/eventq/eventq_base/exceptions.rb +2 -0
- data/lib/eventq/eventq_base/exchange.rb +5 -0
- data/lib/eventq/eventq_base/message_args.rb +23 -0
- data/lib/eventq/eventq_base/nonce_manager.rb +57 -0
- data/lib/eventq/eventq_base/queue.rb +27 -0
- data/lib/eventq/eventq_base/queue_message.rb +31 -0
- data/lib/eventq/eventq_base/queue_worker_contract.rb +23 -0
- data/lib/eventq/eventq_base/serialization_providers/binary_serialization_provider.rb +15 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/array_writer.rb +20 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/attribute_writer.rb +24 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/class_writer.rb +20 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/date_time_writer.rb +33 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/date_writer.rb +22 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/hash_writer.rb +18 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/rational_writer.rb +20 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/serializer.rb +17 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/time_writer.rb +18 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj/value_writer.rb +16 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj.rb +10 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby/oj_serialization_provider.rb +25 -0
- data/lib/eventq/eventq_base/serialization_providers/jruby.rb +2 -0
- data/lib/eventq/eventq_base/serialization_providers/json_serialization_provider.rb +28 -0
- data/lib/eventq/eventq_base/serialization_providers/oj_serialization_provider.rb +24 -0
- data/lib/eventq/eventq_base/serialization_providers.rb +36 -0
- data/lib/eventq/eventq_base/signature_providers/sha256_signature_provider.rb +31 -0
- data/lib/eventq/eventq_base/signature_providers.rb +44 -0
- data/lib/eventq/eventq_base/subscription_manager_contract.rb +13 -0
- data/lib/eventq/eventq_base/version.rb +3 -0
- data/lib/eventq/eventq_base/worker_id.rb +20 -0
- data/lib/eventq/eventq_rabbitmq/README.md +36 -0
- data/lib/eventq/eventq_rabbitmq/default_queue.rb +12 -0
- data/lib/eventq/eventq_rabbitmq/jruby/rabbitmq_queue_worker.rb +367 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_eventq_client.rb +140 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_queue_client.rb +54 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_queue_manager.rb +104 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_queue_worker.rb +168 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_status_checker.rb +62 -0
- data/lib/eventq/eventq_rabbitmq/rabbitmq_subscription_manager.rb +54 -0
- data/lib/eventq/queue_worker.rb +241 -0
- data/lib/eventq/rabbitmq.rb +49 -0
- data/lib/eventq/worker_status.rb +64 -0
- data/lib/eventq.rb +25 -0
- metadata +289 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
module EventQ
|
2
|
+
module SerializationProviders
|
3
|
+
module JRuby
|
4
|
+
module Oj
|
5
|
+
class ValueWriter < AttributeWriter
|
6
|
+
def valid?(obj)
|
7
|
+
obj.is_a?(String) || obj.is_a?(Integer) || obj.is_a?(Float)
|
8
|
+
end
|
9
|
+
def exec(obj)
|
10
|
+
obj
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require_relative 'oj/attribute_writer'
|
2
|
+
require_relative 'oj/class_writer'
|
3
|
+
require_relative 'oj/rational_writer'
|
4
|
+
require_relative 'oj/date_time_writer'
|
5
|
+
require_relative 'oj/date_writer'
|
6
|
+
require_relative 'oj/time_writer'
|
7
|
+
require_relative 'oj/array_writer'
|
8
|
+
require_relative 'oj/hash_writer'
|
9
|
+
require_relative 'oj/value_writer'
|
10
|
+
require_relative 'oj/serializer'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module EventQ
|
2
|
+
module SerializationProviders
|
3
|
+
module JRuby
|
4
|
+
class OjSerializationProvider
|
5
|
+
def initialize
|
6
|
+
@json_serializer = EventQ::SerializationProviders::JsonSerializationProvider.new
|
7
|
+
@oj_serializer = Oj::Serializer.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def serialize(object)
|
11
|
+
@oj_serializer.dump(object)
|
12
|
+
end
|
13
|
+
|
14
|
+
def deserialize(json)
|
15
|
+
begin
|
16
|
+
return @oj_serializer.load(json)
|
17
|
+
rescue
|
18
|
+
EventQ.log(:debug, "[#{self.class}] - Failed to deserialize using Oj, falling back to JsonSerializationProvider.")
|
19
|
+
return @json_serializer.deserialize(json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module EventQ
|
2
|
+
module SerializationProviders
|
3
|
+
class JsonSerializationProvider
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
require 'class_kit'
|
7
|
+
require 'hash_kit'
|
8
|
+
@class_kit_helper = ClassKit::Helper.new
|
9
|
+
@hash_helper = HashKit::Helper.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def serialize(object)
|
13
|
+
JSON.dump(object_to_hash(object))
|
14
|
+
end
|
15
|
+
|
16
|
+
def deserialize(json)
|
17
|
+
return @class_kit_helper.from_json(json: json, klass: EventQ::QueueMessage)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def object_to_hash(object)
|
23
|
+
return object if object.is_a?(Hash)
|
24
|
+
@hash_helper.to_hash(object)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module EventQ
|
2
|
+
module SerializationProviders
|
3
|
+
class OjSerializationProvider
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
require 'oj'
|
7
|
+
@json_serializer = EventQ::SerializationProviders::JsonSerializationProvider.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def serialize(object)
|
11
|
+
Oj.dump(object, mode: :object)
|
12
|
+
end
|
13
|
+
|
14
|
+
def deserialize(json)
|
15
|
+
begin
|
16
|
+
Oj.load(json)
|
17
|
+
rescue Oj::ParseError, ArgumentError
|
18
|
+
EventQ.log(:debug, "[#{self.class}] - Failed to deserialize using Oj, falling back to JsonSerializationProvider.")
|
19
|
+
@json_serializer.deserialize(json)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative 'serialization_providers/json_serialization_provider'
|
2
|
+
unless RUBY_PLATFORM =~ /java/
|
3
|
+
require_relative 'serialization_providers/oj_serialization_provider'
|
4
|
+
end
|
5
|
+
require_relative 'serialization_providers/jruby'
|
6
|
+
require_relative 'serialization_providers/binary_serialization_provider'
|
7
|
+
|
8
|
+
module EventQ
|
9
|
+
module SerializationProviders
|
10
|
+
|
11
|
+
OJ_PROVIDER = 'oj'.freeze
|
12
|
+
JSON_PROVIDER = 'json'.freeze
|
13
|
+
BINARY_PROVIDER = 'binary'.freeze
|
14
|
+
|
15
|
+
class Manager
|
16
|
+
def initialize
|
17
|
+
@providers = {}
|
18
|
+
if RUBY_PLATFORM =~ /java/
|
19
|
+
@providers[OJ_PROVIDER] = EventQ::SerializationProviders::JRuby::OjSerializationProvider
|
20
|
+
else
|
21
|
+
@providers[OJ_PROVIDER] = EventQ::SerializationProviders::OjSerializationProvider
|
22
|
+
end
|
23
|
+
@providers[JSON_PROVIDER] = EventQ::SerializationProviders::JsonSerializationProvider
|
24
|
+
@providers[BINARY_PROVIDER] = EventQ::SerializationProviders::BinarySerializationProvider
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_provider(provider_type)
|
28
|
+
provider = @providers[provider_type]
|
29
|
+
if provider.nil?
|
30
|
+
raise "Invalid provider type specified: #{provider_type}"
|
31
|
+
end
|
32
|
+
return provider.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module EventQ
|
2
|
+
module SignatureProviders
|
3
|
+
class Sha256SignatureProvider
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
require 'openssl'
|
7
|
+
require 'base64'
|
8
|
+
@serializer = serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
|
9
|
+
end
|
10
|
+
|
11
|
+
#This method is called to create a signature for a message
|
12
|
+
def write(message:, secret:)
|
13
|
+
json = @serializer.serialize(message.content)
|
14
|
+
hash = OpenSSL::HMAC.digest('sha256', secret, json)
|
15
|
+
Base64.encode64(hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
#This method is called to validate a message signature
|
19
|
+
def valid?(message:, secret:)
|
20
|
+
signature = write(message: message, secret: secret)
|
21
|
+
message.signature == signature
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def serialization_provider_manager
|
27
|
+
EventQ::SerializationProviders::Manager.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require_relative 'signature_providers/sha256_signature_provider'
|
2
|
+
|
3
|
+
module EventQ
|
4
|
+
module SignatureProviders
|
5
|
+
|
6
|
+
SHA256 = 'sha256'.freeze
|
7
|
+
|
8
|
+
class Manager
|
9
|
+
def initialize
|
10
|
+
@providers = {}
|
11
|
+
@providers[SHA256] = EventQ::SignatureProviders::Sha256SignatureProvider
|
12
|
+
end
|
13
|
+
|
14
|
+
#This method is called to get a signature provider
|
15
|
+
def get_provider(provider_type)
|
16
|
+
provider = @providers[provider_type]
|
17
|
+
if provider == nil
|
18
|
+
raise "Invalid provider type specified: #{provider_type}"
|
19
|
+
end
|
20
|
+
return provider.new
|
21
|
+
end
|
22
|
+
|
23
|
+
#This method is called to validate a queue message's signature
|
24
|
+
def validate_signature(message:, queue:)
|
25
|
+
valid = true
|
26
|
+
|
27
|
+
if queue.require_signature == true && message.signature == nil
|
28
|
+
valid = false
|
29
|
+
elsif message.signature != nil
|
30
|
+
provider = get_provider(EventQ::Configuration.signature_provider)
|
31
|
+
valid = provider.valid?(message: message, secret: EventQ::Configuration.signature_secret)
|
32
|
+
end
|
33
|
+
|
34
|
+
if !valid
|
35
|
+
raise EventQ::Exceptions::InvalidSignatureException.new
|
36
|
+
end
|
37
|
+
|
38
|
+
true
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EventQ
|
2
|
+
# Module to be used by concrete worker classes to tag each thread working on a message
|
3
|
+
# Allows to be used in custom logging to track group of log messages per queue message processing.
|
4
|
+
module WorkerId
|
5
|
+
def tag_processing_thread
|
6
|
+
Thread.current[key_name] = SecureRandom.uuid
|
7
|
+
end
|
8
|
+
|
9
|
+
def untag_processing_thread
|
10
|
+
Thread.current[key_name] = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def key_name
|
16
|
+
'worker_id'.freeze
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# EventQ [RabbitMq]
|
2
|
+
|
3
|
+
Welcome to EventQ. This gem contains the RabbitMq implementations of the EventQ framework components.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'eventq_rabbitmq'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install eventq_rabbitmq
|
20
|
+
|
21
|
+
|
22
|
+
## Development
|
23
|
+
|
24
|
+
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
25
|
+
|
26
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
27
|
+
|
28
|
+
## Contributing
|
29
|
+
|
30
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/sage/eventq. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
31
|
+
|
32
|
+
|
33
|
+
## License
|
34
|
+
|
35
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
36
|
+
|
@@ -0,0 +1,367 @@
|
|
1
|
+
require 'java'
|
2
|
+
java_import java.util.concurrent.Executors
|
3
|
+
module EventQ
|
4
|
+
module RabbitMq
|
5
|
+
class QueueWorker
|
6
|
+
include EventQ::WorkerId
|
7
|
+
|
8
|
+
attr_accessor :is_running
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@is_running = false
|
12
|
+
|
13
|
+
@retry_exceeded_block = nil
|
14
|
+
@on_retry_block = nil
|
15
|
+
@on_error_block = nil
|
16
|
+
@hash_helper = HashKit::Helper.new
|
17
|
+
@serialization_provider_manager = EventQ::SerializationProviders::Manager.new
|
18
|
+
@signature_provider_manager = EventQ::SignatureProviders::Manager.new
|
19
|
+
@last_gc_flush = Time.now
|
20
|
+
@gc_flush_interval = 10
|
21
|
+
end
|
22
|
+
|
23
|
+
def start(queue, options = {}, &block)
|
24
|
+
|
25
|
+
EventQ.logger.info("[#{self.class}] - Preparing to start listening for messages.")
|
26
|
+
|
27
|
+
configure(queue, options)
|
28
|
+
|
29
|
+
raise "[#{self.class}] - Worker is already running." if running?
|
30
|
+
|
31
|
+
if options[:client] == nil
|
32
|
+
raise "[#{self.class}] - :client (QueueClient) must be specified."
|
33
|
+
end
|
34
|
+
|
35
|
+
EventQ.logger.info("[#{self.class}] - Listening for messages.")
|
36
|
+
EventQ.logger.debug do
|
37
|
+
"[#{self.class} #start] - Listening for messages on queue: #{EventQ.create_queue_name(queue.name)}"
|
38
|
+
end
|
39
|
+
|
40
|
+
start_process(options, queue, block)
|
41
|
+
|
42
|
+
return true
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_process(options, queue, block)
|
46
|
+
|
47
|
+
@is_running = true
|
48
|
+
|
49
|
+
%w'INT TERM'.each do |sig|
|
50
|
+
Signal.trap(sig) {
|
51
|
+
stop
|
52
|
+
exit
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
if !options.key?(:durable)
|
57
|
+
options[:durable] = true
|
58
|
+
end
|
59
|
+
|
60
|
+
client = options[:client]
|
61
|
+
manager = EventQ::RabbitMq::QueueManager.new
|
62
|
+
manager.durable = options[:durable]
|
63
|
+
@connection = client.get_connection
|
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
|
+
#begin the queue loop for this thread
|
73
|
+
while true do
|
74
|
+
|
75
|
+
#check if the worker is still allowed to run and break out of thread loop if not
|
76
|
+
unless running?
|
77
|
+
break
|
78
|
+
end
|
79
|
+
|
80
|
+
if @executor.is_shutdown
|
81
|
+
break
|
82
|
+
end
|
83
|
+
|
84
|
+
has_received_message = false
|
85
|
+
|
86
|
+
begin
|
87
|
+
|
88
|
+
channel = @connection.create_channel
|
89
|
+
|
90
|
+
has_received_message = thread_process_iteration(channel, manager, queue, block)
|
91
|
+
|
92
|
+
rescue => e
|
93
|
+
EventQ.logger.error("An unhandled error occurred. Error: #{e} | Backtrace: #{e.backtrace}")
|
94
|
+
call_on_error_block(error: e)
|
95
|
+
end
|
96
|
+
|
97
|
+
if channel != nil && channel.open?
|
98
|
+
channel.close
|
99
|
+
end
|
100
|
+
|
101
|
+
gc_flush
|
102
|
+
|
103
|
+
if !has_received_message
|
104
|
+
EventQ.logger.debug { "[#{self.class}] - No message received." }
|
105
|
+
if @sleep > 0
|
106
|
+
EventQ.logger.debug { "[#{self.class}] - Sleeping for #{@sleep} seconds" }
|
107
|
+
sleep(@sleep)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
if options.key?(:wait) && options[:wait] == true
|
118
|
+
while running? do end
|
119
|
+
@connection.close if @connection.open?
|
120
|
+
end
|
121
|
+
|
122
|
+
return true
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
def call_on_error_block(error:, message: nil)
|
127
|
+
if @on_error_block
|
128
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_error block." }
|
129
|
+
begin
|
130
|
+
@on_error_block.call(error, message)
|
131
|
+
rescue => e
|
132
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_error block. Error: #{e}")
|
133
|
+
end
|
134
|
+
else
|
135
|
+
EventQ.logger.debug { "[#{self.class}] - No on_error block specified to execute." }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def gc_flush
|
140
|
+
if Time.now - last_gc_flush > @gc_flush_interval
|
141
|
+
GC.start
|
142
|
+
@last_gc_flush = Time.now
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def last_gc_flush
|
147
|
+
@last_gc_flush
|
148
|
+
end
|
149
|
+
|
150
|
+
def thread_process_iteration(channel, manager, queue, block)
|
151
|
+
|
152
|
+
#get the queue
|
153
|
+
q = manager.get_queue(channel, queue)
|
154
|
+
retry_exchange = manager.get_retry_exchange(channel, queue)
|
155
|
+
|
156
|
+
received = false
|
157
|
+
|
158
|
+
begin
|
159
|
+
delivery_info, payload = manager.pop_message(queue: q)
|
160
|
+
|
161
|
+
#check that message was received
|
162
|
+
if payload != nil
|
163
|
+
received = true
|
164
|
+
begin
|
165
|
+
tag_processing_thread
|
166
|
+
process_message(payload, queue, channel, retry_exchange, delivery_info, block)
|
167
|
+
ensure
|
168
|
+
untag_processing_thread
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
rescue => e
|
174
|
+
EventQ.logger.error("[#{self.class}] - An error occurred attempting to process a message. Error: #{e} | Backtrace: #{e.backtrace}")
|
175
|
+
call_on_error_block(error: e)
|
176
|
+
end
|
177
|
+
|
178
|
+
return received
|
179
|
+
end
|
180
|
+
|
181
|
+
def stop
|
182
|
+
EventQ.logger.info { "[#{self.class}] - Stopping..." }
|
183
|
+
@is_running = false
|
184
|
+
@executor.shutdown
|
185
|
+
if @connection != nil
|
186
|
+
@connection.close if @connection.open?
|
187
|
+
end
|
188
|
+
return true
|
189
|
+
end
|
190
|
+
|
191
|
+
def on_retry_exceeded(&block)
|
192
|
+
@retry_exceeded_block = block
|
193
|
+
return nil
|
194
|
+
end
|
195
|
+
|
196
|
+
def on_retry(&block)
|
197
|
+
@on_retry_block = block
|
198
|
+
return nil
|
199
|
+
end
|
200
|
+
|
201
|
+
def on_error(&block)
|
202
|
+
@on_error_block = block
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
|
206
|
+
def running?
|
207
|
+
return @is_running
|
208
|
+
end
|
209
|
+
|
210
|
+
def deserialize_message(payload)
|
211
|
+
provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
|
212
|
+
return provider.deserialize(payload)
|
213
|
+
end
|
214
|
+
|
215
|
+
def serialize_message(msg)
|
216
|
+
provider = @serialization_provider_manager.get_provider(EventQ::Configuration.serialization_provider)
|
217
|
+
return provider.serialize(msg)
|
218
|
+
end
|
219
|
+
|
220
|
+
def call_on_retry_exceeded_block(message)
|
221
|
+
if @retry_exceeded_block != nil
|
222
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_retry_exceeded block." }
|
223
|
+
begin
|
224
|
+
@retry_exceeded_block.call(message)
|
225
|
+
rescue => e
|
226
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry_exceeded block. Error: #{e}")
|
227
|
+
end
|
228
|
+
else
|
229
|
+
EventQ.logger.debug { "[#{self.class}] - No on_retry_exceeded block specified." }
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def call_on_retry_block(message)
|
234
|
+
if @on_retry_block
|
235
|
+
EventQ.logger.debug { "[#{self.class}] - Executing on_retry block." }
|
236
|
+
begin
|
237
|
+
@on_retry_block.call(message, abort)
|
238
|
+
rescue => e
|
239
|
+
EventQ.logger.error("[#{self.class}] - An error occurred executing the on_retry block. Error: #{e}")
|
240
|
+
end
|
241
|
+
else
|
242
|
+
EventQ.logger.debug { "[#{self.class}] - No on_retry block specified." }
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
|
247
|
+
|
248
|
+
EventQ.logger.info("[#{self.class}] - Message rejected removing from queue.")
|
249
|
+
#reject the message to remove from queue
|
250
|
+
channel.reject(delivery_tag, false)
|
251
|
+
|
252
|
+
#check if the message retry limit has been exceeded
|
253
|
+
if message.retry_attempts >= queue.max_retry_attempts
|
254
|
+
|
255
|
+
EventQ.logger.info("[#{self.class}] - Message retry attempt limit exceeded. Msg: #{serialize_message(message)}")
|
256
|
+
|
257
|
+
call_on_retry_exceeded_block(message)
|
258
|
+
|
259
|
+
#check if the message is allowed to be retried
|
260
|
+
elsif queue.allow_retry
|
261
|
+
|
262
|
+
EventQ.logger.debug { "[#{self.class}] - Incrementing retry attempts count." }
|
263
|
+
message.retry_attempts += 1
|
264
|
+
|
265
|
+
if queue.allow_retry_back_off == true
|
266
|
+
EventQ.logger.debug { "[#{self.class}] - Calculating message back off retry delay. Attempts: #{message.retry_attempts} * Retry Delay: #{queue.retry_delay}" }
|
267
|
+
message_ttl = message.retry_attempts * queue.retry_delay
|
268
|
+
if (message.retry_attempts * queue.retry_delay) > queue.max_retry_delay
|
269
|
+
EventQ.logger.debug { "[#{self.class}] - Max message back off retry delay reached." }
|
270
|
+
message_ttl = queue.max_retry_delay
|
271
|
+
end
|
272
|
+
else
|
273
|
+
EventQ.logger.debug { "[#{self.class}] - Setting fixed retry delay for message." }
|
274
|
+
message_ttl = queue.retry_delay
|
275
|
+
end
|
276
|
+
|
277
|
+
EventQ.logger.debug { "[#{self.class}] - Sending message for retry. Message TTL: #{message_ttl}" }
|
278
|
+
retry_exchange.publish(serialize_message(message), :expiration => message_ttl)
|
279
|
+
EventQ.logger.debug { "[#{self.class}] - Published message to retry exchange." }
|
280
|
+
|
281
|
+
call_on_retry_block(message)
|
282
|
+
|
283
|
+
end
|
284
|
+
|
285
|
+
return true
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
def configure(queue, options = {})
|
290
|
+
|
291
|
+
@queue = queue
|
292
|
+
|
293
|
+
#default thread count
|
294
|
+
@thread_count = 4
|
295
|
+
if options.key?(:thread_count)
|
296
|
+
@thread_count = options[:thread_count]
|
297
|
+
end
|
298
|
+
|
299
|
+
#default sleep time in seconds
|
300
|
+
@sleep = 15
|
301
|
+
if options.key?(:sleep)
|
302
|
+
@sleep = options[:sleep]
|
303
|
+
end
|
304
|
+
|
305
|
+
@gc_flush_interval = 10
|
306
|
+
if options.key?(:gc_flush_interval)
|
307
|
+
@gc_flush_interval = options[:gc_flush_interval]
|
308
|
+
end
|
309
|
+
|
310
|
+
EventQ.logger.info("[#{self.class}] - Configuring. Thread Count: #{@thread_count} | Interval Sleep: #{@sleep}.")
|
311
|
+
|
312
|
+
return true
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
private
|
317
|
+
|
318
|
+
def process_message(payload, queue, channel, retry_exchange, delivery_tag, block)
|
319
|
+
abort = false
|
320
|
+
error = false
|
321
|
+
message = deserialize_message(payload)
|
322
|
+
|
323
|
+
EventQ.logger.info("[#{self.class}] - Message received. Retry Attempts: #{message.retry_attempts}")
|
324
|
+
|
325
|
+
@signature_provider_manager.validate_signature(message: message, queue: queue)
|
326
|
+
|
327
|
+
message_args = EventQ::MessageArgs.new(type: message.type,
|
328
|
+
retry_attempts: message.retry_attempts,
|
329
|
+
context: message.context,
|
330
|
+
content_type: message.content_type)
|
331
|
+
|
332
|
+
if(!EventQ::NonceManager.is_allowed?(message.id))
|
333
|
+
EventQ.logger.info("[#{self.class}] - Duplicate Message received. Dropping message.")
|
334
|
+
channel.acknowledge(delivery_tag, false)
|
335
|
+
return false
|
336
|
+
end
|
337
|
+
|
338
|
+
#begin worker block for queue message
|
339
|
+
begin
|
340
|
+
block.call(message.content, message_args)
|
341
|
+
|
342
|
+
if message_args.abort == true
|
343
|
+
abort = true
|
344
|
+
EventQ.logger.info("[#{self.class}] - Message aborted.")
|
345
|
+
else
|
346
|
+
#accept the message as processed
|
347
|
+
channel.acknowledge(delivery_tag, false)
|
348
|
+
EventQ.logger.info("[#{self.class}] - Message acknowledged.")
|
349
|
+
end
|
350
|
+
|
351
|
+
rescue => e
|
352
|
+
EventQ.logger.error("[#{self.class}] - An unhandled error happened attempting to process a queue message. Error: #{e} | Backtrace: #{e.backtrace}")
|
353
|
+
error = true
|
354
|
+
call_on_error_block(error: e, message: message)
|
355
|
+
end
|
356
|
+
|
357
|
+
if error || abort
|
358
|
+
EventQ::NonceManager.failed(message.id)
|
359
|
+
reject_message(channel, message, delivery_tag, retry_exchange, queue, abort)
|
360
|
+
else
|
361
|
+
EventQ::NonceManager.complete(message.id)
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
366
|
+
end
|
367
|
+
|