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.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -5
  3. data/README.md +8 -0
  4. data/example/non_rails_app/Gemfile.lock +4 -6
  5. data/example/non_rails_app/lib/non_rails_app.rb +3 -0
  6. data/example/non_rails_app/spec/spec_helper.rb +1 -0
  7. data/example/rails_app/Gemfile.lock +3 -5
  8. data/example/rails_app/config/initializers/rabbit_feed.rb +3 -0
  9. data/example/rails_app/spec/spec_helper.rb +6 -0
  10. data/lib/rabbit_feed/client.rb +3 -2
  11. data/lib/rabbit_feed/configuration.rb +5 -6
  12. data/lib/rabbit_feed/connection.rb +28 -0
  13. data/lib/rabbit_feed/consumer.rb +3 -1
  14. data/lib/rabbit_feed/consumer_connection.rb +29 -29
  15. data/lib/rabbit_feed/json_log_formatter.rb +12 -0
  16. data/lib/rabbit_feed/producer.rb +3 -1
  17. data/lib/rabbit_feed/producer_connection.rb +12 -30
  18. data/lib/rabbit_feed/testing_support.rb +1 -1
  19. data/lib/rabbit_feed/version.rb +1 -1
  20. data/lib/rabbit_feed.rb +2 -2
  21. data/rabbit_feed.gemspec +1 -3
  22. data/spec/features/connectivity.feature +1 -3
  23. data/spec/features/step_definitions/connectivity_steps.rb +3 -9
  24. data/spec/fixtures/configuration.yml +7 -1
  25. data/spec/lib/rabbit_feed/configuration_spec.rb +2 -6
  26. data/spec/lib/rabbit_feed/consumer_connection_spec.rb +17 -13
  27. data/spec/lib/rabbit_feed/producer_connection_spec.rb +5 -21
  28. data/spec/lib/rabbit_feed/producer_spec.rb +1 -1
  29. data/spec/spec_helper.rb +8 -24
  30. metadata +8 -31
  31. data/lib/rabbit_feed/connection_concern.rb +0 -86
  32. data/spec/lib/rabbit_feed/connection_concern_spec.rb +0 -127
  33. 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: 58c69d09c8fed13331610e7015a5dcbe19e282cd
4
- data.tar.gz: fd29cc489c1bb251ab1e16bf35eda04728cc839e
3
+ metadata.gz: 573abf58e4399420f0328ebfa8d58213ebacd7f2
4
+ data.tar.gz: 4aa80a039f9849ea8058502b96887c46d967b651
5
5
  SHA512:
6
- metadata.gz: 5389a17aa4bffe0bacbf21f170a86fad67dc23349082502e11fff40b5bb404c405d92c6cfad9e2039bf1d1e00f83c0ccac7d12bf72bd02c2b102520199468511
7
- data.tar.gz: 458a801d1631ee4be89aa3a5c59f2fd861e810a93117d536b64b2a1d725f0cc5c4162b5c4648601b81a70c03a1ae341b2926715d4865f1e1dc639ed46ac8831f
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.1.5)
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 (>= 1.1.9, < 1.8.0)
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 (1.7.0)
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.1.5)
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 (>= 1.1.9, < 1.8.0)
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 (1.7.0)
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.1)
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|
@@ -19,5 +19,6 @@ RSpec.configure do |config|
19
19
  end
20
20
 
21
21
  RabbitFeed.log = Logger.new('log/rabbit_feed.log')
22
+ RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
22
23
  RabbitFeed.environment = 'test'
23
24
  RabbitFeed.configuration_file_path = 'config/rabbit_feed.yml'
@@ -1,12 +1,11 @@
1
1
  PATH
2
2
  remote: ../../
3
3
  specs:
4
- rabbit_feed (2.1.5)
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 (>= 1.1.9, < 1.8.0)
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 (1.7.0)
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
+ RabbitFeed.log = Logger.new('log/rabbit_feed.log')
2
+ RabbitFeed.log.formatter = RabbitFeed::JsonLogFormatter
3
+
1
4
  EventDefinitions do
2
5
  define_event('user_creates_beaver', version: '1.0.0') do
3
6
  defined_as do
@@ -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
@@ -86,8 +86,9 @@ module RabbitFeed
86
86
  end
87
87
 
88
88
  def set_logging
89
- RabbitFeed.log = Logger.new(options[:logfile])
90
- RabbitFeed.log.level = verbose? ? Logger::DEBUG : Logger::INFO
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, :pool_size, :pool_timeout, :heartbeat, :connect_timeout, :network_recovery_interval, :auto_delete_queue, :auto_delete_exchange
6
- validates_presence_of :application, :environment, :exchange, :pool_timeout
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.debug "RabbitFeed initialising with options: #{options}..."
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] || 'amq.topic'
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.debug "Reading configurations from #{file_path} in #{environment} for application #{application}..."
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
@@ -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
- attr_reader :queue
23
-
24
- def initialize channel
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
- RabbitFeed.log.info "Consuming messages on #{self.to_s} from queue: #{RabbitFeed.configuration.queue}..."
39
-
40
- consumer = queue.subscribe(SUBSCRIPTION_OPTIONS) do |delivery_info, properties, payload|
41
- handle_message delivery_info, payload, &block
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 "Message acknowledged on #{self.to_s} from queue: #{RabbitFeed.configuration.queue}..."
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 "Message received on #{self.to_s} from queue: #{RabbitFeed.configuration.queue}..."
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 "Consumer: #{cancel_ok.consumer_tag} cancelled on #{self.to_s} from queue: #{RabbitFeed.configuration.queue}..."
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 "Message negatively acknowledged on #{self.to_s} from queue: #{RabbitFeed.configuration.queue}..."
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 "Exception encountered while consuming message on #{self.to_s} from queue #{RabbitFeed.configuration.queue}: #{exception.message} #{exception.backtrace}"
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
@@ -0,0 +1,12 @@
1
+ module RabbitFeed
2
+ class JsonLogFormatter < Logger::Formatter
3
+ def self.call(severity, time, progname, msg)
4
+ {
5
+ severity: severity,
6
+ time: time.utc.iso8601(6),
7
+ progname: progname,
8
+ message: msg,
9
+ }.to_json + "\n"
10
+ end
11
+ end
12
+ end
@@ -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
- ProducerConnection.publish event.serialize, (options name, timestamp)
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 "Handling returned message on #{self.to_s} details: #{return_info}..."
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 channel
24
- RabbitFeed.log.debug "Declaring exchange on #{self.to_s} (channel #{channel.id}) named: #{RabbitFeed.configuration.exchange} with options: #{exchange_options}..."
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
- # It's critical to dup the options for the sake of retries, as bunny modifies this hash
44
- bunny_options = (options.merge PUBLISH_OPTIONS)
45
-
46
- RabbitFeed.log.debug "Publishing message on #{self.to_s} with options: #{options} to exchange: #{RabbitFeed.configuration.exchange}..."
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
@@ -1,3 +1,3 @@
1
1
  module RabbitFeed
2
- VERSION = '2.1.5'
2
+ VERSION = '2.3.0'
3
3
  end
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', '>= 1.1.9', '< 1.8.0'
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
- initialize_queue
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(5.0) do
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) { 'test' }
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 'test' }
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{ described_class.new bunny_channel }
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('rabbit_feed_exchange', { routing_key: 'test.rabbit_feed.test'})
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.new bunny_channel
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
- described_class.publish message, options
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 'retries on exception' do
67
- expect(described_class).to receive(:retry_on_exception).twice.and_call_original
68
- described_class.publish message, options
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
- reset_environment
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 = Logger.new('test.log')
61
- RabbitFeed.environment = 'test'
62
- RabbitFeed.configuration_file_path = 'spec/fixtures/configuration.yml'
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.1.5
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-09 00:00:00.000000000 Z
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: 1.8.0
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: 1.1.9
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/connection_concern.rb
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