rabbit_feed 2.1.5 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|