rabbit_feed 2.1.5 → 2.3.0
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/Gemfile.lock +3 -5
- data/README.md +8 -0
- data/example/non_rails_app/Gemfile.lock +4 -6
- data/example/non_rails_app/lib/non_rails_app.rb +3 -0
- data/example/non_rails_app/spec/spec_helper.rb +1 -0
- data/example/rails_app/Gemfile.lock +3 -5
- data/example/rails_app/config/initializers/rabbit_feed.rb +3 -0
- data/example/rails_app/spec/spec_helper.rb +6 -0
- data/lib/rabbit_feed/client.rb +3 -2
- data/lib/rabbit_feed/configuration.rb +5 -6
- data/lib/rabbit_feed/connection.rb +28 -0
- data/lib/rabbit_feed/consumer.rb +3 -1
- data/lib/rabbit_feed/consumer_connection.rb +29 -29
- data/lib/rabbit_feed/json_log_formatter.rb +12 -0
- data/lib/rabbit_feed/producer.rb +3 -1
- data/lib/rabbit_feed/producer_connection.rb +12 -30
- data/lib/rabbit_feed/testing_support.rb +1 -1
- data/lib/rabbit_feed/version.rb +1 -1
- data/lib/rabbit_feed.rb +2 -2
- data/rabbit_feed.gemspec +1 -3
- data/spec/features/connectivity.feature +1 -3
- data/spec/features/step_definitions/connectivity_steps.rb +3 -9
- data/spec/fixtures/configuration.yml +7 -1
- data/spec/lib/rabbit_feed/configuration_spec.rb +2 -6
- data/spec/lib/rabbit_feed/consumer_connection_spec.rb +17 -13
- data/spec/lib/rabbit_feed/producer_connection_spec.rb +5 -21
- data/spec/lib/rabbit_feed/producer_spec.rb +1 -1
- data/spec/spec_helper.rb +8 -24
- metadata +8 -31
- data/lib/rabbit_feed/connection_concern.rb +0 -86
- data/spec/lib/rabbit_feed/connection_concern_spec.rb +0 -127
- data/spec/support/shared_examples_for_connections.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 573abf58e4399420f0328ebfa8d58213ebacd7f2
|
4
|
+
data.tar.gz: 4aa80a039f9849ea8058502b96887c46d967b651
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 942ab068d1c1b6b5c4a0006aeeb8eaf41e19f0d39f2702113701c92bc3fe2e74e23d1172ead1e3085cb41c1c9938b355aec3938d1b9ff3e6d60efecdabb3c917
|
7
|
+
data.tar.gz: 74108418645bb5e8aaf5e0a4ca9364834ee2f8b625d0c8cf6237105e3920aa5b65c927e8c14b22590f151e4aa4d1cc6a4f345d23d768b9d7fab681d60a5cab17
|
data/Gemfile.lock
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rabbit_feed (2.
|
4
|
+
rabbit_feed (2.3.0)
|
5
5
|
activemodel (>= 3.2.0, < 5.0.0)
|
6
6
|
activesupport (>= 3.2.0, < 5.0.0)
|
7
7
|
avro (>= 1.5.4, < 1.8.0)
|
8
|
-
bunny (
|
9
|
-
connection_pool (< 2.2.0)
|
8
|
+
bunny (= 2.0.0.rc2)
|
10
9
|
pidfile
|
11
10
|
|
12
11
|
GEM
|
@@ -28,13 +27,12 @@ GEM
|
|
28
27
|
debug_inspector (>= 0.0.1)
|
29
28
|
bond (0.5.1)
|
30
29
|
builder (3.2.2)
|
31
|
-
bunny (
|
30
|
+
bunny (2.0.0.rc2)
|
32
31
|
amq-protocol (>= 1.9.2)
|
33
32
|
codeclimate-test-reporter (0.4.6)
|
34
33
|
simplecov (>= 0.7.1, < 1.0.0)
|
35
34
|
coderay (1.1.0)
|
36
35
|
columnize (0.9.0)
|
37
|
-
connection_pool (2.1.3)
|
38
36
|
debug_inspector (0.0.2)
|
39
37
|
debugger (1.6.8)
|
40
38
|
columnize (>= 0.3.1)
|
data/README.md
CHANGED
@@ -90,6 +90,14 @@ RabbitFeed.instance_eval do
|
|
90
90
|
end
|
91
91
|
```
|
92
92
|
|
93
|
+
#### Logging in JSON
|
94
|
+
|
95
|
+
RabbitFeed log messages are constructed in such a way that they are friendly to JSON log formats. This is done to simplify log aggregation with tools like [Kibana](https://www.elastic.co/products/kibana). To log in JSON format, set the RabbitFeed log to use the JSON formatter, e.g.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
|
99
|
+
```
|
100
|
+
|
93
101
|
## Producing events
|
94
102
|
|
95
103
|
The producer defines the events and their payloads using the [Event Definitions DSL](https://github.com/simplybusiness/rabbit_feed#event-definitions-dsl). In a rails app, this can be defined in the [initialiser](https://github.com/simplybusiness/rabbit_feed#initialisation).
|
@@ -1,12 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../../
|
3
3
|
specs:
|
4
|
-
rabbit_feed (2.
|
4
|
+
rabbit_feed (2.3.0)
|
5
5
|
activemodel (>= 3.2.0, < 5.0.0)
|
6
6
|
activesupport (>= 3.2.0, < 5.0.0)
|
7
7
|
avro (>= 1.5.4, < 1.8.0)
|
8
|
-
bunny (
|
9
|
-
connection_pool (< 2.2.0)
|
8
|
+
bunny (= 2.0.0.rc2)
|
10
9
|
pidfile
|
11
10
|
|
12
11
|
GEM
|
@@ -25,14 +24,13 @@ GEM
|
|
25
24
|
avro (1.7.7)
|
26
25
|
multi_json
|
27
26
|
builder (3.2.2)
|
28
|
-
bunny (
|
27
|
+
bunny (2.0.0.rc2)
|
29
28
|
amq-protocol (>= 1.9.2)
|
30
|
-
connection_pool (2.1.3)
|
31
29
|
diff-lcs (1.2.5)
|
32
30
|
i18n (0.7.0)
|
33
31
|
json (1.8.3)
|
34
32
|
minitest (5.7.0)
|
35
|
-
multi_json (1.11.
|
33
|
+
multi_json (1.11.2)
|
36
34
|
pidfile (0.3.0)
|
37
35
|
rake (10.4.2)
|
38
36
|
rspec (3.2.0)
|
@@ -1,6 +1,9 @@
|
|
1
1
|
require 'rabbit_feed'
|
2
2
|
require_relative 'non_rails_app/event_handler'
|
3
3
|
|
4
|
+
RabbitFeed.log = Logger.new('log/rabbit_feed.log')
|
5
|
+
RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
|
6
|
+
|
4
7
|
EventRouting do
|
5
8
|
accept_from('rails_app') do
|
6
9
|
event('user_creates_beaver') do |event|
|
@@ -1,12 +1,11 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../../
|
3
3
|
specs:
|
4
|
-
rabbit_feed (2.
|
4
|
+
rabbit_feed (2.3.0)
|
5
5
|
activemodel (>= 3.2.0, < 5.0.0)
|
6
6
|
activesupport (>= 3.2.0, < 5.0.0)
|
7
7
|
avro (>= 1.5.4, < 1.8.0)
|
8
|
-
bunny (
|
9
|
-
connection_pool (< 2.2.0)
|
8
|
+
bunny (= 2.0.0.rc2)
|
10
9
|
pidfile
|
11
10
|
|
12
11
|
GEM
|
@@ -52,7 +51,7 @@ GEM
|
|
52
51
|
avro (1.7.7)
|
53
52
|
multi_json
|
54
53
|
builder (3.2.2)
|
55
|
-
bunny (
|
54
|
+
bunny (2.0.0.rc2)
|
56
55
|
amq-protocol (>= 1.9.2)
|
57
56
|
coffee-rails (4.0.1)
|
58
57
|
coffee-script (>= 2.2.0)
|
@@ -61,7 +60,6 @@ GEM
|
|
61
60
|
coffee-script-source
|
62
61
|
execjs
|
63
62
|
coffee-script-source (1.9.1)
|
64
|
-
connection_pool (2.1.3)
|
65
63
|
diff-lcs (1.2.5)
|
66
64
|
erubis (2.7.0)
|
67
65
|
execjs (2.3.0)
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'rabbit_feed'
|
2
|
+
require 'logger'
|
3
|
+
|
1
4
|
RSpec.configure do |config|
|
2
5
|
|
3
6
|
# Run specs in random order to surface order dependencies. If you find an
|
@@ -6,3 +9,6 @@ RSpec.configure do |config|
|
|
6
9
|
# --seed 1234
|
7
10
|
config.order = 'random'
|
8
11
|
end
|
12
|
+
|
13
|
+
RabbitFeed.log = Logger.new('log/rabbit_feed.log')
|
14
|
+
RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
|
data/lib/rabbit_feed/client.rb
CHANGED
@@ -86,8 +86,9 @@ module RabbitFeed
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def set_logging
|
89
|
-
RabbitFeed.log
|
90
|
-
RabbitFeed.log.level
|
89
|
+
RabbitFeed.log = Logger.new options[:logfile]
|
90
|
+
RabbitFeed.log.level = verbose? ? Logger::DEBUG : Logger::INFO
|
91
|
+
RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
|
91
92
|
end
|
92
93
|
|
93
94
|
def set_configuration
|
@@ -2,19 +2,18 @@ module RabbitFeed
|
|
2
2
|
class Configuration
|
3
3
|
include ActiveModel::Validations
|
4
4
|
|
5
|
-
attr_reader :host, :hosts, :port, :user, :password, :application, :environment, :exchange, :
|
6
|
-
validates_presence_of :application, :environment, :exchange
|
5
|
+
attr_reader :host, :hosts, :port, :user, :password, :application, :environment, :exchange, :heartbeat, :connect_timeout, :network_recovery_interval, :auto_delete_queue, :auto_delete_exchange
|
6
|
+
validates_presence_of :application, :environment, :exchange
|
7
7
|
|
8
8
|
def initialize options
|
9
|
-
RabbitFeed.log.
|
9
|
+
RabbitFeed.log.info {{ event: :initialize_configuration, options: options.merge({password: :redacted}) }}
|
10
10
|
|
11
11
|
@host = options[:host]
|
12
12
|
@hosts = options[:hosts]
|
13
13
|
@port = options[:port]
|
14
14
|
@user = options[:user]
|
15
15
|
@password = options[:password]
|
16
|
-
@exchange = options[:exchange]
|
17
|
-
@pool_timeout = options[:pool_timeout] || 120
|
16
|
+
@exchange = options[:exchange] || 'amq.topic'
|
18
17
|
@heartbeat = options[:heartbeat]
|
19
18
|
@connect_timeout = options[:connect_timeout]
|
20
19
|
@network_recovery_interval = options[:network_recovery_interval]
|
@@ -26,7 +25,7 @@ module RabbitFeed
|
|
26
25
|
end
|
27
26
|
|
28
27
|
def self.load file_path, environment, application
|
29
|
-
RabbitFeed.log.
|
28
|
+
RabbitFeed.log.info {{ event: :load_configuration_file, file_path: file_path, environment: environment, application: application }}
|
30
29
|
|
31
30
|
raise ConfigurationError.new "The RabbitFeed configuration file path specified does not exist: #{file_path}" unless (File.exist? file_path)
|
32
31
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module RabbitFeed
|
2
|
+
class Connection
|
3
|
+
include Singleton
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
RabbitFeed.log.info {{ event: :connecting_to_rabbitmq, options: RabbitFeed.configuration.connection_options.merge({password: :redacted, logger: :redacted}) }}
|
7
|
+
@connection = Bunny.new RabbitFeed.configuration.connection_options
|
8
|
+
@connection.start
|
9
|
+
RabbitFeed.log.info {{ event: :connected_to_rabbitmq }}
|
10
|
+
@channel = @connection.create_channel
|
11
|
+
@mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :channel, :mutex
|
17
|
+
|
18
|
+
def synchronized &block
|
19
|
+
mutex.synchronize do
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def connection_in_use?
|
25
|
+
mutex.locked?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/rabbit_feed/consumer.rb
CHANGED
@@ -5,9 +5,11 @@ module RabbitFeed
|
|
5
5
|
attr_accessor :event_routing
|
6
6
|
|
7
7
|
def run
|
8
|
-
ConsumerConnection.consume do |raw_event|
|
8
|
+
ConsumerConnection.instance.consume do |raw_event|
|
9
9
|
event = Event.deserialize raw_event
|
10
|
+
RabbitFeed.log.info {{ event: :message_received, metadata: event.metadata }}
|
10
11
|
event_routing.handle_event event
|
12
|
+
RabbitFeed.log.info {{ event: :message_processed, metadata: event.metadata }}
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module RabbitFeed
|
2
|
-
class ConsumerConnection
|
3
|
-
include ConnectionConcern
|
2
|
+
class ConsumerConnection < RabbitFeed::Connection
|
4
3
|
|
5
4
|
SUBSCRIPTION_OPTIONS = {
|
6
5
|
consumer_tag: Socket.gethostname, # Use the host name of the server
|
@@ -19,37 +18,37 @@ module RabbitFeed
|
|
19
18
|
},
|
20
19
|
}.freeze
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
channel.prefetch(1) # Fetch one message at a time to preserve order
|
26
|
-
RabbitFeed.log.debug "Declaring queue on #{self.to_s} (channel #{channel.id}) named: #{RabbitFeed.configuration.queue} with options: #{queue_options}..."
|
21
|
+
def initialize
|
22
|
+
super
|
23
|
+
channel.prefetch(1)
|
27
24
|
@queue = channel.queue RabbitFeed.configuration.queue, queue_options
|
25
|
+
RabbitFeed.log.info {{ event: :queue_declared, queue: RabbitFeed.configuration.queue, options: queue_options }}
|
28
26
|
bind_on_accepted_routes
|
29
27
|
end
|
30
28
|
|
31
|
-
def self.consume &block
|
32
|
-
with_connection do |consumer_connection|
|
33
|
-
consumer_connection.consume(&block)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
29
|
def consume &block
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
30
|
+
raise 'This connection already has a consumer subscribed' if connection_in_use?
|
31
|
+
synchronized do
|
32
|
+
begin
|
33
|
+
RabbitFeed.log.info {{ event: :subscribe_to_queue, queue: RabbitFeed.configuration.queue }}
|
34
|
+
|
35
|
+
consumer = queue.subscribe(SUBSCRIPTION_OPTIONS) do |delivery_info, properties, payload|
|
36
|
+
handle_message delivery_info, payload, &block
|
37
|
+
end
|
38
|
+
|
39
|
+
sleep # Sleep indefinitely, as the consumer runs in its own thread
|
40
|
+
rescue SystemExit, Interrupt
|
41
|
+
RabbitFeed.log.info {{ event: :unsubscribe_from_queue, queue: RabbitFeed.configuration.queue }}
|
42
|
+
ensure
|
43
|
+
(cancel_consumer consumer) if consumer.present?
|
44
|
+
end
|
42
45
|
end
|
43
|
-
|
44
|
-
sleep # Sleep indefinitely, as the consumer runs in its own thread
|
45
|
-
rescue SystemExit, Interrupt
|
46
|
-
RabbitFeed.log.info "Consumer #{self.to_s} received exit request, exiting..."
|
47
|
-
ensure
|
48
|
-
(cancel_consumer consumer) if consumer.present?
|
49
46
|
end
|
50
47
|
|
51
48
|
private
|
52
49
|
|
50
|
+
attr_reader :queue
|
51
|
+
|
53
52
|
def queue_options
|
54
53
|
{
|
55
54
|
auto_delete: RabbitFeed.configuration.auto_delete_queue,
|
@@ -60,20 +59,21 @@ module RabbitFeed
|
|
60
59
|
if RabbitFeed::Consumer.event_routing.present?
|
61
60
|
RabbitFeed::Consumer.event_routing.accepted_routes.each do |accepted_route|
|
62
61
|
queue.bind(RabbitFeed.configuration.exchange, { routing_key: accepted_route })
|
62
|
+
RabbitFeed.log.info {{ event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange, routing_key: accepted_route }}
|
63
63
|
end
|
64
64
|
else
|
65
65
|
queue.bind(RabbitFeed.configuration.exchange)
|
66
|
+
RabbitFeed.log.info {{ event: :queue_bound, queue: RabbitFeed.configuration.queue, exchange: RabbitFeed.configuration.exchange }}
|
66
67
|
end
|
67
68
|
end
|
68
69
|
|
69
70
|
def acknowledge delivery_info
|
70
71
|
queue.channel.ack(delivery_info.delivery_tag)
|
71
|
-
RabbitFeed.log.debug
|
72
|
+
RabbitFeed.log.debug {{ event: :acknowledge, delivery_tag: delivery_info.delivery_tag }}
|
72
73
|
end
|
73
74
|
|
74
75
|
def handle_message delivery_info, payload, &block
|
75
|
-
RabbitFeed.log.debug
|
76
|
-
|
76
|
+
RabbitFeed.log.debug {{ event: :handling_message, delivery_tag: delivery_info.delivery_tag }}
|
77
77
|
begin
|
78
78
|
yield payload
|
79
79
|
acknowledge delivery_info
|
@@ -84,19 +84,19 @@ module RabbitFeed
|
|
84
84
|
|
85
85
|
def cancel_consumer consumer
|
86
86
|
cancel_ok = consumer.cancel
|
87
|
-
RabbitFeed.log.debug
|
87
|
+
RabbitFeed.log.debug {{ event: :consumer_cancelled, status: cancel_ok, queue: RabbitFeed.configuration.queue }}
|
88
88
|
end
|
89
89
|
|
90
90
|
def negative_acknowledge delivery_info
|
91
91
|
# Tell rabbit that we were unable to process the message
|
92
92
|
# This will re-queue the message
|
93
93
|
queue.channel.nack(delivery_info.delivery_tag, false, true)
|
94
|
-
RabbitFeed.log.debug
|
94
|
+
RabbitFeed.log.debug {{ event: :negative_acknowledge, delivery_tag: delivery_info.delivery_tag }}
|
95
95
|
end
|
96
96
|
|
97
97
|
def handle_processing_exception delivery_info, exception
|
98
98
|
negative_acknowledge delivery_info
|
99
|
-
RabbitFeed.log.error
|
99
|
+
RabbitFeed.log.error {{ event: :processing_exception, delivery_tag: delivery_info.delivery_tag, message: exception.message, backtrace: exception.backtrace.join(',') }}
|
100
100
|
RabbitFeed.exception_notify exception
|
101
101
|
end
|
102
102
|
end
|
data/lib/rabbit_feed/producer.rb
CHANGED
@@ -10,7 +10,9 @@ module RabbitFeed
|
|
10
10
|
timestamp = Time.now.utc
|
11
11
|
metadata = (metadata event_definition.version, name, timestamp)
|
12
12
|
event = Event.new metadata, payload, event_definition.schema, event_definition.sensitive_fields
|
13
|
-
|
13
|
+
RabbitFeed.log.info {{ event: :publish_start, metadata: event.metadata }}
|
14
|
+
ProducerConnection.instance.publish event.serialize, (options name, timestamp)
|
15
|
+
RabbitFeed.log.info {{ event: :publish_end, metadata: event.metadata }}
|
14
16
|
event
|
15
17
|
end
|
16
18
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module RabbitFeed
|
2
|
-
class ProducerConnection
|
3
|
-
include ConnectionConcern
|
2
|
+
class ProducerConnection < RabbitFeed::Connection
|
4
3
|
|
5
4
|
PUBLISH_OPTIONS = {
|
6
5
|
persistent: true, # Persist the message to disk
|
@@ -13,53 +12,36 @@ module RabbitFeed
|
|
13
12
|
no_declare: false, # Create the exchange if it does not exist
|
14
13
|
}.freeze
|
15
14
|
|
16
|
-
attr_reader :exchange
|
17
|
-
|
18
15
|
def self.handle_returned_message return_info, content
|
19
|
-
RabbitFeed.log.error
|
16
|
+
RabbitFeed.log.error {{ event: :returned_message, return_info: return_info }}
|
20
17
|
RabbitFeed.exception_notify (ReturnedMessageError.new return_info)
|
21
18
|
end
|
22
19
|
|
23
|
-
def initialize
|
24
|
-
|
20
|
+
def initialize
|
21
|
+
super
|
25
22
|
@exchange = channel.exchange RabbitFeed.configuration.exchange, exchange_options
|
26
|
-
|
23
|
+
RabbitFeed.log.info {{ event: :exchange_declared, exchange: RabbitFeed.configuration.exchange, options: exchange_options }}
|
27
24
|
exchange.on_return do |return_info, properties, content|
|
28
25
|
RabbitFeed::ProducerConnection.handle_returned_message return_info, content
|
29
26
|
end
|
30
27
|
end
|
31
28
|
|
32
|
-
def self.publish message, options
|
33
|
-
retry_on_closed_connection do
|
34
|
-
with_connection do |producer_connection|
|
35
|
-
retry_on_exception do
|
36
|
-
producer_connection.publish message, options
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
29
|
def publish message, options
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
exchange.publish message, bunny_options
|
30
|
+
synchronized do
|
31
|
+
bunny_options = (options.merge PUBLISH_OPTIONS)
|
32
|
+
RabbitFeed.log.debug {{ event: :publish, options: options, exchange: RabbitFeed.configuration.exchange }}
|
33
|
+
exchange.publish message, bunny_options
|
34
|
+
end
|
49
35
|
end
|
50
36
|
|
51
37
|
private
|
52
38
|
|
39
|
+
attr_reader :exchange
|
40
|
+
|
53
41
|
def exchange_options
|
54
42
|
{
|
55
43
|
auto_delete: RabbitFeed.configuration.auto_delete_exchange,
|
56
44
|
}.merge EXCHANGE_OPTIONS
|
57
45
|
end
|
58
|
-
|
59
|
-
def self.connection_options
|
60
|
-
super.merge({
|
61
|
-
threaded: false, # With threading enabled, there is a chance of losing an event during connection recovery
|
62
|
-
})
|
63
|
-
end
|
64
46
|
end
|
65
47
|
end
|
@@ -18,7 +18,7 @@ module RabbitFeed
|
|
18
18
|
|
19
19
|
TestingSupport.published_events = []
|
20
20
|
|
21
|
-
allow(RabbitFeed::ProducerConnection).to receive(:publish) do |serialized_event, routing_key|
|
21
|
+
allow(RabbitFeed::ProducerConnection.instance).to receive(:publish) do |serialized_event, routing_key|
|
22
22
|
TestingSupport.published_events << (Event.deserialize serialized_event)
|
23
23
|
end
|
24
24
|
end
|
data/lib/rabbit_feed/version.rb
CHANGED
data/lib/rabbit_feed.rb
CHANGED
@@ -2,14 +2,13 @@ require 'active_support/all'
|
|
2
2
|
require 'active_model'
|
3
3
|
require 'avro'
|
4
4
|
require 'bunny'
|
5
|
-
require 'connection_pool'
|
6
5
|
require 'yaml'
|
7
6
|
require_relative './dsl'
|
8
7
|
require 'rabbit_feed/version'
|
9
8
|
require 'rabbit_feed/client'
|
10
9
|
require 'rabbit_feed/configuration'
|
11
|
-
require 'rabbit_feed/connection_concern'
|
12
10
|
require 'rabbit_feed/event'
|
11
|
+
require 'rabbit_feed/connection'
|
13
12
|
require 'rabbit_feed/consumer_connection'
|
14
13
|
require 'rabbit_feed/consumer'
|
15
14
|
require 'rabbit_feed/event_routing'
|
@@ -18,6 +17,7 @@ require 'rabbit_feed/producer'
|
|
18
17
|
require 'rabbit_feed/event_definitions'
|
19
18
|
require 'rabbit_feed/testing_support'
|
20
19
|
require 'rabbit_feed/version'
|
20
|
+
require 'rabbit_feed/json_log_formatter'
|
21
21
|
|
22
22
|
module RabbitFeed
|
23
23
|
extend self
|
data/rabbit_feed.gemspec
CHANGED
@@ -19,13 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
# Gem for interfacing with RabbitMq
|
22
|
-
spec.add_dependency 'bunny', '
|
22
|
+
spec.add_dependency 'bunny', '2.0.0.rc2'
|
23
23
|
# We use some helpers from ActiveSupport
|
24
24
|
spec.add_dependency 'activesupport', '>= 3.2.0', '< 5.0.0'
|
25
25
|
# We use validations from ActiveModel
|
26
26
|
spec.add_dependency 'activemodel', '>= 3.2.0', '< 5.0.0'
|
27
|
-
# Provides connection pooling for the producer connections
|
28
|
-
spec.add_dependency 'connection_pool', '< 2.2.0'
|
29
27
|
# Manages process pidfile
|
30
28
|
spec.add_dependency 'pidfile'
|
31
29
|
# Schema definitions and serialization for events
|
@@ -1,3 +1,4 @@
|
|
1
|
+
@connectivity
|
1
2
|
Feature:
|
2
3
|
As a developer
|
3
4
|
I want to be able to publish and consume events
|
@@ -6,8 +7,5 @@ Scenario: I can publish and consume events
|
|
6
7
|
Given I am consuming
|
7
8
|
When I publish an event
|
8
9
|
Then I receive that event
|
9
|
-
|
10
|
-
Scenario: When an event cannot be consumed it remains on the queue
|
11
|
-
Given I am consuming
|
12
10
|
When I publish an event that cannot be processed by the consumer
|
13
11
|
Then the event remains on the queue
|
@@ -2,12 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
step 'I am consuming' do
|
4
4
|
set_event_routing
|
5
|
-
|
5
|
+
set_event_definitions
|
6
|
+
RabbitFeed::ConsumerConnection.instance # Bind the queue
|
6
7
|
@consumer_thread = Thread.new{ RabbitFeed::Consumer.run }
|
7
8
|
end
|
8
9
|
|
9
10
|
step 'I publish an event' do
|
10
|
-
set_event_definitions
|
11
11
|
publish 'test'
|
12
12
|
end
|
13
13
|
|
@@ -17,7 +17,6 @@ step 'I receive that event' do
|
|
17
17
|
end
|
18
18
|
|
19
19
|
step 'I publish an event that cannot be processed by the consumer' do
|
20
|
-
set_event_definitions
|
21
20
|
publish 'test_failure'
|
22
21
|
end
|
23
22
|
|
@@ -29,11 +28,6 @@ end
|
|
29
28
|
|
30
29
|
module Turnip::Steps
|
31
30
|
|
32
|
-
def initialize_queue
|
33
|
-
RabbitFeed::ProducerConnection.with_connection{|connection|}
|
34
|
-
RabbitFeed::ConsumerConnection.with_connection{|connection|}
|
35
|
-
end
|
36
|
-
|
37
31
|
def publish event_name
|
38
32
|
@event_text = "#{event_name}_#{Time.now.iso8601(6)}"
|
39
33
|
RabbitFeed::Producer.publish_event event_name, { 'field' => @event_text }
|
@@ -46,7 +40,7 @@ module Turnip::Steps
|
|
46
40
|
|
47
41
|
def wait_for_event
|
48
42
|
begin
|
49
|
-
Timeout::timeout(
|
43
|
+
Timeout::timeout(2.0) do
|
50
44
|
until @consumed_events.any? do
|
51
45
|
sleep 0.1
|
52
46
|
end
|
@@ -1,11 +1,17 @@
|
|
1
1
|
test:
|
2
|
+
host: localhost
|
3
|
+
port: 5672
|
4
|
+
user: guest
|
5
|
+
password: guest
|
6
|
+
application: rabbit_feed
|
7
|
+
auto_delete_queue: true
|
8
|
+
test_config:
|
2
9
|
host: localhost
|
3
10
|
port: 5672
|
4
11
|
user: guest
|
5
12
|
password: guest
|
6
13
|
application: rabbit_feed
|
7
14
|
exchange: rabbit_feed_exchange
|
8
|
-
pool_timeout: 1
|
9
15
|
heartbeat: 60
|
10
16
|
connect_timeout: 1
|
11
17
|
network_recovery_interval: 0.1
|
@@ -68,7 +68,7 @@ module RabbitFeed
|
|
68
68
|
|
69
69
|
describe '.load' do
|
70
70
|
let(:file_path) { 'spec/fixtures/configuration.yml' }
|
71
|
-
let(:environment) { '
|
71
|
+
let(:environment) { 'test_config' }
|
72
72
|
let(:application) { }
|
73
73
|
subject { described_class.load file_path, environment, application }
|
74
74
|
|
@@ -95,9 +95,8 @@ module RabbitFeed
|
|
95
95
|
its(:user) { should eq 'guest' }
|
96
96
|
its(:password) { should eq 'guest' }
|
97
97
|
its(:application) { should eq 'rabbit_feed' }
|
98
|
-
its(:environment) { should eq '
|
98
|
+
its(:environment) { should eq 'test_config' }
|
99
99
|
its(:exchange) { should eq 'rabbit_feed_exchange' }
|
100
|
-
its(:pool_timeout) { should eq 1 }
|
101
100
|
its(:heartbeat) { should eq 60 }
|
102
101
|
its(:connect_timeout) { should eq 1 }
|
103
102
|
its(:network_recovery_interval) { should eq 0.1 }
|
@@ -142,7 +141,6 @@ module RabbitFeed
|
|
142
141
|
its(:heartbeat) { should be_nil }
|
143
142
|
its(:network_recovery_interval) { should be_nil }
|
144
143
|
its(:exchange) { should eq 'amq.topic' }
|
145
|
-
its(:pool_timeout) { should eq 120 }
|
146
144
|
its(:connect_timeout) { should be_nil }
|
147
145
|
its(:auto_delete_queue) { should be_falsey }
|
148
146
|
its(:auto_delete_exchange) { should be_falsey }
|
@@ -159,7 +157,6 @@ module RabbitFeed
|
|
159
157
|
application: 'rabbit_feed',
|
160
158
|
environment: 'test',
|
161
159
|
exchange: 'exchange_name',
|
162
|
-
pool_timeout: 6,
|
163
160
|
heartbeat: 3,
|
164
161
|
connect_timeout: 4,
|
165
162
|
network_recovery_interval: 2,
|
@@ -176,7 +173,6 @@ module RabbitFeed
|
|
176
173
|
its(:application) { should eq 'rabbit_feed' }
|
177
174
|
its(:environment) { should eq 'test' }
|
178
175
|
its(:exchange) { should eq 'exchange_name' }
|
179
|
-
its(:pool_timeout) { should eq 6 }
|
180
176
|
its(:heartbeat) { should eq 3 }
|
181
177
|
its(:connect_timeout) { should eq 4 }
|
182
178
|
its(:network_recovery_interval) { should eq 2 }
|
@@ -9,7 +9,9 @@ module RabbitFeed
|
|
9
9
|
allow(Bunny).to receive(:new).and_return(bunny_connection)
|
10
10
|
allow(bunny_queue).to receive(:channel).and_return(bunny_channel)
|
11
11
|
end
|
12
|
-
subject
|
12
|
+
subject do
|
13
|
+
Class.new(described_class).instance
|
14
|
+
end
|
13
15
|
|
14
16
|
describe '#new' do
|
15
17
|
before do
|
@@ -21,27 +23,16 @@ module RabbitFeed
|
|
21
23
|
end
|
22
24
|
|
23
25
|
it 'binds the queue to the exchange' do
|
24
|
-
expect(bunny_queue).to receive(:bind).with('
|
26
|
+
expect(bunny_queue).to receive(:bind).with('amq.topic', { routing_key: 'test.rabbit_feed.test'})
|
25
27
|
subject
|
26
28
|
end
|
27
29
|
|
28
|
-
it 'assigns the queue' do
|
29
|
-
expect(subject.queue).to eq bunny_queue
|
30
|
-
end
|
31
|
-
|
32
30
|
it 'preserves message order' do
|
33
31
|
expect(bunny_channel).to receive(:prefetch).with(1)
|
34
32
|
subject
|
35
33
|
end
|
36
34
|
end
|
37
35
|
|
38
|
-
describe '#connection_options' do
|
39
|
-
|
40
|
-
it 'uses a threaded connection' do
|
41
|
-
expect(described_class.connection_options).to include(threaded: true)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
36
|
describe '#consume' do
|
46
37
|
before do
|
47
38
|
allow(bunny_queue).to receive(:subscribe).and_yield(double(:delivery_info, delivery_tag: :tag), 'properties', 'payload')
|
@@ -58,11 +49,24 @@ module RabbitFeed
|
|
58
49
|
subject.consume { }
|
59
50
|
end
|
60
51
|
|
52
|
+
it 'is synchronized' do
|
53
|
+
expect(subject).to receive(:synchronized).and_call_original
|
54
|
+
subject.consume { }
|
55
|
+
end
|
56
|
+
|
61
57
|
it 'cancels the consumer' do
|
62
58
|
expect_any_instance_of(described_class).to receive(:cancel_consumer)
|
63
59
|
subject.consume { }
|
64
60
|
end
|
65
61
|
|
62
|
+
context 'when consuming' do
|
63
|
+
before { allow(subject.send(:mutex)).to receive(:locked?).and_return(true) }
|
64
|
+
|
65
|
+
it 'raises when attempting to consume in parallel' do
|
66
|
+
expect{ subject.consume { } }.to raise_error 'This connection already has a consumer subscribed'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
66
70
|
context 'when an exception is raised' do
|
67
71
|
|
68
72
|
context 'when Airbrake is defined' do
|
@@ -9,7 +9,7 @@ module RabbitFeed
|
|
9
9
|
allow(Bunny).to receive(:new).and_return(bunny_connection)
|
10
10
|
end
|
11
11
|
subject do
|
12
|
-
described_class.
|
12
|
+
Class.new(described_class).instance
|
13
13
|
end
|
14
14
|
|
15
15
|
describe '#new' do
|
@@ -18,10 +18,6 @@ module RabbitFeed
|
|
18
18
|
expect(bunny_exchange).to receive(:on_return).and_yield('return_info', 'properties', 'content')
|
19
19
|
subject
|
20
20
|
end
|
21
|
-
|
22
|
-
it 'assigns the exchange' do
|
23
|
-
expect(subject.exchange).to eq bunny_exchange
|
24
|
-
end
|
25
21
|
end
|
26
22
|
|
27
23
|
describe '#handle_returned_message' do
|
@@ -42,30 +38,18 @@ module RabbitFeed
|
|
42
38
|
end
|
43
39
|
end
|
44
40
|
|
45
|
-
describe '#connection_options' do
|
46
|
-
|
47
|
-
it 'does not use a threaded connection' do
|
48
|
-
expect(described_class.connection_options).to include(threaded: false)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
41
|
describe '#publish' do
|
53
42
|
let(:message) { 'the message' }
|
54
43
|
let(:options) { {routing_key: 'routing_key'} }
|
55
44
|
|
56
45
|
it 'publishes the message as mandatory and persistent' do
|
57
46
|
expect(bunny_exchange).to receive(:publish).with(message, { persistent: true, mandatory: true, routing_key: 'routing_key' })
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
it 'retries on closed connections' do
|
62
|
-
expect(described_class).to receive(:retry_on_closed_connection).and_call_original
|
63
|
-
described_class.publish message, options
|
47
|
+
subject.publish message, options
|
64
48
|
end
|
65
49
|
|
66
|
-
it '
|
67
|
-
expect(
|
68
|
-
|
50
|
+
it 'is synchronized' do
|
51
|
+
expect(subject).to receive(:synchronized).and_call_original
|
52
|
+
subject.publish message, options
|
69
53
|
end
|
70
54
|
end
|
71
55
|
end
|
@@ -59,7 +59,7 @@ module RabbitFeed
|
|
59
59
|
|
60
60
|
it 'serializes the event and provides message metadata' do
|
61
61
|
Timecop.freeze do
|
62
|
-
expect(ProducerConnection).to receive(:publish).with(
|
62
|
+
expect(ProducerConnection.instance).to receive(:publish).with(
|
63
63
|
an_instance_of(String),
|
64
64
|
{
|
65
65
|
routing_key: 'test.rabbit_feed.event_name',
|
data/spec/spec_helper.rb
CHANGED
@@ -30,34 +30,18 @@ RSpec.configure do |config|
|
|
30
30
|
reset_environment
|
31
31
|
end
|
32
32
|
|
33
|
-
config.after do
|
34
|
-
|
35
|
-
# Ensure the consumer thread exists between tests
|
36
|
-
kill_consumer_thread
|
37
|
-
# Ensure that connections don't persist between tests
|
38
|
-
close_connections
|
39
|
-
# Clear event routing
|
40
|
-
RabbitFeed::Consumer.event_routing = nil
|
41
|
-
# Clear event definitions
|
42
|
-
RabbitFeed::Producer.event_definitions = nil
|
33
|
+
config.after(connectivity: true) do
|
34
|
+
Thread.kill @consumer_thread if @consumer_thread.present?
|
43
35
|
end
|
44
36
|
|
45
37
|
RabbitFeed::TestingSupport.include_support config
|
46
38
|
end
|
47
39
|
|
48
|
-
def kill_consumer_thread
|
49
|
-
if @consumer_thread.present?
|
50
|
-
Thread.kill @consumer_thread
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def close_connections
|
55
|
-
RabbitFeed::ProducerConnection.close
|
56
|
-
RabbitFeed::ConsumerConnection.close
|
57
|
-
end
|
58
|
-
|
59
40
|
def reset_environment
|
60
|
-
RabbitFeed.log
|
61
|
-
RabbitFeed.
|
62
|
-
RabbitFeed.
|
41
|
+
RabbitFeed.log = Logger.new('test.log')
|
42
|
+
RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
|
43
|
+
RabbitFeed.environment = 'test'
|
44
|
+
RabbitFeed.configuration_file_path = 'spec/fixtures/configuration.yml'
|
45
|
+
RabbitFeed::Consumer.event_routing = nil
|
46
|
+
RabbitFeed::Producer.event_definitions = nil
|
63
47
|
end
|
metadata
CHANGED
@@ -1,35 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rabbit_feed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Simply Business
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-07-
|
11
|
+
date: 2015-07-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bunny
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.1.9
|
20
|
-
- - "<"
|
17
|
+
- - '='
|
21
18
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
19
|
+
version: 2.0.0.rc2
|
23
20
|
type: :runtime
|
24
21
|
prerelease: false
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '='
|
28
25
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: 1.8.0
|
26
|
+
version: 2.0.0.rc2
|
33
27
|
- !ruby/object:Gem::Dependency
|
34
28
|
name: activesupport
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,20 +64,6 @@ dependencies:
|
|
70
64
|
- - "<"
|
71
65
|
- !ruby/object:Gem::Version
|
72
66
|
version: 5.0.0
|
73
|
-
- !ruby/object:Gem::Dependency
|
74
|
-
name: connection_pool
|
75
|
-
requirement: !ruby/object:Gem::Requirement
|
76
|
-
requirements:
|
77
|
-
- - "<"
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
version: 2.2.0
|
80
|
-
type: :runtime
|
81
|
-
prerelease: false
|
82
|
-
version_requirements: !ruby/object:Gem::Requirement
|
83
|
-
requirements:
|
84
|
-
- - "<"
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: 2.2.0
|
87
67
|
- !ruby/object:Gem::Dependency
|
88
68
|
name: pidfile
|
89
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -258,12 +238,13 @@ files:
|
|
258
238
|
- lib/rabbit_feed.rb
|
259
239
|
- lib/rabbit_feed/client.rb
|
260
240
|
- lib/rabbit_feed/configuration.rb
|
261
|
-
- lib/rabbit_feed/
|
241
|
+
- lib/rabbit_feed/connection.rb
|
262
242
|
- lib/rabbit_feed/consumer.rb
|
263
243
|
- lib/rabbit_feed/consumer_connection.rb
|
264
244
|
- lib/rabbit_feed/event.rb
|
265
245
|
- lib/rabbit_feed/event_definitions.rb
|
266
246
|
- lib/rabbit_feed/event_routing.rb
|
247
|
+
- lib/rabbit_feed/json_log_formatter.rb
|
267
248
|
- lib/rabbit_feed/producer.rb
|
268
249
|
- lib/rabbit_feed/producer_connection.rb
|
269
250
|
- lib/rabbit_feed/testing_support.rb
|
@@ -281,7 +262,6 @@ files:
|
|
281
262
|
- spec/fixtures/configuration.yml
|
282
263
|
- spec/lib/rabbit_feed/client_spec.rb
|
283
264
|
- spec/lib/rabbit_feed/configuration_spec.rb
|
284
|
-
- spec/lib/rabbit_feed/connection_concern_spec.rb
|
285
265
|
- spec/lib/rabbit_feed/consumer_connection_spec.rb
|
286
266
|
- spec/lib/rabbit_feed/event_definitions_spec.rb
|
287
267
|
- spec/lib/rabbit_feed/event_routing_spec.rb
|
@@ -291,7 +271,6 @@ files:
|
|
291
271
|
- spec/lib/rabbit_feed/testing_support/rspec_matchers/publish_event_spec.rb
|
292
272
|
- spec/lib/rabbit_feed/testing_support/testing_helper_spec.rb
|
293
273
|
- spec/spec_helper.rb
|
294
|
-
- spec/support/shared_examples_for_connections.rb
|
295
274
|
homepage: https://github.com/simplybusiness/rabbit_feed
|
296
275
|
licenses:
|
297
276
|
- MIT
|
@@ -323,7 +302,6 @@ test_files:
|
|
323
302
|
- spec/fixtures/configuration.yml
|
324
303
|
- spec/lib/rabbit_feed/client_spec.rb
|
325
304
|
- spec/lib/rabbit_feed/configuration_spec.rb
|
326
|
-
- spec/lib/rabbit_feed/connection_concern_spec.rb
|
327
305
|
- spec/lib/rabbit_feed/consumer_connection_spec.rb
|
328
306
|
- spec/lib/rabbit_feed/event_definitions_spec.rb
|
329
307
|
- spec/lib/rabbit_feed/event_routing_spec.rb
|
@@ -333,5 +311,4 @@ test_files:
|
|
333
311
|
- spec/lib/rabbit_feed/testing_support/rspec_matchers/publish_event_spec.rb
|
334
312
|
- spec/lib/rabbit_feed/testing_support/testing_helper_spec.rb
|
335
313
|
- spec/spec_helper.rb
|
336
|
-
- spec/support/shared_examples_for_connections.rb
|
337
314
|
has_rdoc:
|
@@ -1,86 +0,0 @@
|
|
1
|
-
module RabbitFeed
|
2
|
-
module ConnectionConcern
|
3
|
-
extend ActiveSupport::Concern
|
4
|
-
|
5
|
-
module ClassMethods
|
6
|
-
|
7
|
-
def connection_options
|
8
|
-
RabbitFeed.configuration.connection_options
|
9
|
-
end
|
10
|
-
|
11
|
-
def with_connection &block
|
12
|
-
connection_pool.with do |connection|
|
13
|
-
yield connection
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def retry_on_exception tries=3, &block
|
18
|
-
yield
|
19
|
-
rescue Bunny::ConnectionClosedError
|
20
|
-
raise # There is no point in retrying if the connection is closed
|
21
|
-
rescue => e
|
22
|
-
RabbitFeed.log.warn "Exception encountered; #{tries - 1} tries remaining. #{self.to_s}: #{e.message} #{e.backtrace}"
|
23
|
-
unless (tries -= 1).zero?
|
24
|
-
retry
|
25
|
-
end
|
26
|
-
raise
|
27
|
-
end
|
28
|
-
|
29
|
-
def retry_on_closed_connection tries=3, &block
|
30
|
-
yield
|
31
|
-
rescue Bunny::ConnectionClosedError => e
|
32
|
-
RabbitFeed.log.warn "Closed connection exception encountered; #{tries - 1} tries remaining. #{self.to_s}: #{e.message} #{e.backtrace}"
|
33
|
-
unless (tries -= 1).zero?
|
34
|
-
unset_connection
|
35
|
-
sleep 1
|
36
|
-
retry
|
37
|
-
end
|
38
|
-
raise
|
39
|
-
end
|
40
|
-
|
41
|
-
def close
|
42
|
-
RabbitFeed.log.debug "Closing connection: #{self.to_s}..."
|
43
|
-
@bunny_connection.close if @bunny_connection.present? && !closed?
|
44
|
-
unset_connection
|
45
|
-
rescue => e
|
46
|
-
RabbitFeed.log.warn "Exception encountered whilst closing #{self.to_s}: #{e.message} #{e.backtrace}"
|
47
|
-
end
|
48
|
-
|
49
|
-
def bunny_connection
|
50
|
-
if @bunny_connection.nil?
|
51
|
-
retry_on_exception do
|
52
|
-
RabbitFeed.log.debug "Opening connection: #{self.to_s}..."
|
53
|
-
@bunny_connection = Bunny.new connection_options
|
54
|
-
@bunny_connection.start
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
@bunny_connection
|
59
|
-
end
|
60
|
-
private :bunny_connection
|
61
|
-
|
62
|
-
def connection_pool
|
63
|
-
@connection_pool ||= ConnectionPool.new(
|
64
|
-
size: 1,
|
65
|
-
timeout: RabbitFeed.configuration.pool_timeout
|
66
|
-
) do
|
67
|
-
new bunny_connection.create_channel
|
68
|
-
end
|
69
|
-
end
|
70
|
-
private :connection_pool
|
71
|
-
|
72
|
-
def closed?
|
73
|
-
@bunny_connection.present? && @bunny_connection.closed?
|
74
|
-
end
|
75
|
-
private :closed?
|
76
|
-
|
77
|
-
def unset_connection
|
78
|
-
RabbitFeed.log.debug "Unsetting connection: #{self.to_s}..."
|
79
|
-
@connection_pool = nil
|
80
|
-
@bunny_connection = nil
|
81
|
-
end
|
82
|
-
private :unset_connection
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module RabbitFeed
|
4
|
-
describe ConnectionConcern do
|
5
|
-
let(:bunny_exchange) { double(:bunny_exchange, on_return: nil) }
|
6
|
-
let(:connection_closed) { false }
|
7
|
-
let(:bunny_channel) { double(:bunny_channel, exchange: bunny_exchange, id: 1) }
|
8
|
-
let(:bunny_connection) { double(:bunny_connection, start: nil, closed?: connection_closed, close: nil, create_channel: bunny_channel) }
|
9
|
-
before { allow(Bunny).to receive(:new).and_return(bunny_connection) }
|
10
|
-
after do
|
11
|
-
subject.instance_variable_set(:@bunny_connection, nil)
|
12
|
-
subject.instance_variable_set(:@connection_pool, nil)
|
13
|
-
end
|
14
|
-
subject { RabbitFeed::ProducerConnection }
|
15
|
-
|
16
|
-
describe '.with_connection' do
|
17
|
-
|
18
|
-
it 'retries on exception' do
|
19
|
-
expect(subject).to receive(:retry_on_exception).and_call_original
|
20
|
-
subject.with_connection{|connection| connection }
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'assigns the connection' do
|
24
|
-
subject.with_connection{|connection| connection }
|
25
|
-
expect(subject.instance_variable_get(:@bunny_connection)).to eq bunny_connection
|
26
|
-
end
|
27
|
-
|
28
|
-
it 'assigns the connection pool' do
|
29
|
-
subject.with_connection{|connection| connection }
|
30
|
-
expect(subject.instance_variable_get(:@connection_pool)).to be_a ConnectionPool
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'creates a connection pool of one connection' do
|
34
|
-
expect(ConnectionPool).to receive(:new).with(hash_including({size: 1})).and_call_original
|
35
|
-
subject.with_connection{|connection| connection }
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'provides an instance of the class' do
|
39
|
-
actual = subject.with_connection{|connection| connection }
|
40
|
-
expect(actual).to be_a subject
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe '.close' do
|
45
|
-
|
46
|
-
context 'when the connection is nil' do
|
47
|
-
|
48
|
-
it 'does not close the connection' do
|
49
|
-
expect(bunny_connection).not_to receive(:close)
|
50
|
-
subject.close
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'when the connection is not nil' do
|
55
|
-
before do
|
56
|
-
subject.with_connection{|connection| connection }
|
57
|
-
end
|
58
|
-
|
59
|
-
context 'when the connection is closed' do
|
60
|
-
let(:connection_closed) { true }
|
61
|
-
|
62
|
-
it 'does not close the connection' do
|
63
|
-
expect(bunny_connection).not_to receive(:close)
|
64
|
-
subject.close
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
context 'when the connection is not closed' do
|
69
|
-
let(:connection_closed) { false }
|
70
|
-
|
71
|
-
it 'closes the connection' do
|
72
|
-
expect(bunny_connection).to receive(:close)
|
73
|
-
subject.close
|
74
|
-
end
|
75
|
-
|
76
|
-
context 'when closing raises an exception' do
|
77
|
-
|
78
|
-
it 'does not propogate the exception' do
|
79
|
-
allow(bunny_connection).to receive(:close).and_raise 'error'
|
80
|
-
expect{ subject.close }.not_to raise_error
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'unsets the connection' do
|
86
|
-
subject.close
|
87
|
-
expect(subject.instance_variable_get(:@bunny_connection)).to be_nil
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'unsets the connection pool' do
|
91
|
-
subject.close
|
92
|
-
expect(subject.instance_variable_get(:@connection_pool)).to be_nil
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
describe '.retry_on_exception' do
|
98
|
-
it_behaves_like 'an operation that retries on exception', :retry_on_exception, RuntimeError
|
99
|
-
it_behaves_like 'an operation that does not retry on exception', :retry_on_exception, Bunny::ConnectionClosedError
|
100
|
-
end
|
101
|
-
|
102
|
-
describe '.retry_on_closed_connection' do
|
103
|
-
before do
|
104
|
-
subject.with_connection{|connection| connection }
|
105
|
-
allow(subject).to receive(:sleep).at_least(:once)
|
106
|
-
end
|
107
|
-
|
108
|
-
it_behaves_like 'an operation that retries on exception', :retry_on_closed_connection, Bunny::ConnectionClosedError
|
109
|
-
it_behaves_like 'an operation that does not retry on exception', :retry_on_closed_connection, RuntimeError
|
110
|
-
|
111
|
-
it 'unsets the connection' do
|
112
|
-
expect { subject.retry_on_closed_connection { raise Bunny::ConnectionClosedError.new 'blah' } }.to raise_error
|
113
|
-
expect(subject.instance_variable_get(:@bunny_connection)).to be_nil
|
114
|
-
end
|
115
|
-
|
116
|
-
it 'unsets the connection pool' do
|
117
|
-
expect { subject.retry_on_closed_connection { raise Bunny::ConnectionClosedError.new 'blah' } }.to raise_error
|
118
|
-
expect(subject.instance_variable_get(:@connection_pool)).to be_nil
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'waits between retries' do
|
122
|
-
expect(subject).to receive(:sleep).with(1).twice
|
123
|
-
begin; subject.retry_on_closed_connection { raise Bunny::ConnectionClosedError.new 'blah' }; rescue; end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
RSpec.shared_examples 'an operation that retries on exception' do |operation, exception_class|
|
2
|
-
|
3
|
-
context 'less than three times' do
|
4
|
-
|
5
|
-
it 'traps the exception' do
|
6
|
-
tries = 0
|
7
|
-
expect do
|
8
|
-
subject.send(operation) do
|
9
|
-
(tries += 1) < 3 ? (raise exception_class.new 'blah') : nil
|
10
|
-
end
|
11
|
-
end.to_not raise_error
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
context 'three or more times' do
|
16
|
-
|
17
|
-
it 'raises the exception' do
|
18
|
-
tries = 0
|
19
|
-
expect do
|
20
|
-
subject.send(operation) do
|
21
|
-
tries += 1
|
22
|
-
raise exception_class.new 'blah'
|
23
|
-
end
|
24
|
-
end.to raise_error exception_class
|
25
|
-
expect(tries).to eq 3
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
RSpec.shared_examples 'an operation that does not retry on exception' do |operation, exception_class|
|
31
|
-
|
32
|
-
it 'does not retry when the error is received' do
|
33
|
-
tries = 0
|
34
|
-
expect do
|
35
|
-
subject.send(operation) do
|
36
|
-
(tries += 1) < 3 ? (raise exception_class.new 'blah') : nil
|
37
|
-
end
|
38
|
-
end.to raise_error
|
39
|
-
end
|
40
|
-
end
|