songkick_queue 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75d2f489f41e935f1c16ef27861a7d9044472eb5
4
- data.tar.gz: d154004c62f76f7a99cff3841fbccaf863930c8e
3
+ metadata.gz: 8b51761dcb720a861e59cd35370dd964fbb0393e
4
+ data.tar.gz: fdcf57c99fa12a9e9e3dbfc9a4c83b8a765486ce
5
5
  SHA512:
6
- metadata.gz: 815d1add90cd8a2d5adc0922babfb3de115d098ee0d86f45d77fc6c92b1e996da766c8ecb244cd22939976581dcb70490d493cec53b433b18dbd6d115334570d
7
- data.tar.gz: 19f0a0f9f4a7dc0f145df5b21f6a96e0fa5ddf1c8e1194fb6810a40878f7e990847fcd3220e5d097d74d6fb3ca7ce37ca4e45281ca9bdd67ef76f12b592e32a6
6
+ metadata.gz: 78d96fe086dfe7afc88548bdfcae4237ecc8fccc95d6d28d24e643b21c058cfd3eb99b8bf49c4e20bff89770763257ace02c56dc672c7d8c6c900f3ea519fabc
7
+ data.tar.gz: feb0f64206f09160396154611dd732c8327b86f146d82afae897719ae90e097cb9634d008999b17dc3985eef57be9596c0190d7bc979a9467c8d169e9dcd7251
data/README.md CHANGED
@@ -40,6 +40,8 @@ SongkickQueue.configure do |config|
40
40
  config.username = 'guest'
41
41
  config.password = 'guest'
42
42
  config.vhost = '/'
43
+ config.max_reconnect_attempts = 10
44
+ config.network_recovery_interval = 1.0
43
45
  end
44
46
  ```
45
47
 
@@ -1,7 +1,7 @@
1
- # Example environment file
1
+ # Example consumer
2
2
  # Require this file when running `songkick_queue` like so:
3
3
  #
4
- # $ bin/songkick_queue --require ./examples/environment.rb --consumer TweetConsumer
4
+ # $ bin/songkick_queue --require ./examples/consumer.rb --consumer TweetConsumer
5
5
  #
6
6
  require_relative '../lib/songkick_queue'
7
7
 
@@ -1,3 +1,8 @@
1
+ # Example producer
2
+ # Run this file like so:
3
+ #
4
+ # $ bundle exec ruby examples/producer.rb
5
+ #
1
6
  require_relative '../lib/songkick_queue'
2
7
 
3
8
  SongkickQueue.configure do |config|
@@ -18,18 +18,22 @@ module SongkickQueue
18
18
  :username,
19
19
  :password,
20
20
  :vhost,
21
+ :max_reconnect_attempts,
22
+ :network_recovery_interval,
21
23
  )
22
24
 
23
- ConfigurationError = Class.new(StandardError)
25
+ TooManyReconnectAttemptsError = Class.new(StandardError)
24
26
 
25
27
  # Retrieve configuration for SongkickQueue
26
28
  #
27
29
  # @return [Configuration]
28
30
  def self.configuration
29
- @configuration ||= Configuration.new(
30
- logger: Logger.new(STDOUT),
31
- port: 5672,
32
- )
31
+ @configuration ||= Configuration.new.tap do |config|
32
+ config.logger = Logger.new(STDOUT)
33
+ config.port = 5672
34
+ config.max_reconnect_attempts = 10
35
+ config.network_recovery_interval = 1.0
36
+ end
33
37
  end
34
38
 
35
39
  # Yields a block, passing the memoized configuration instance
@@ -8,36 +8,43 @@ module SongkickQueue
8
8
  #
9
9
  # @return [Bunny::Channel]
10
10
  def channel
11
- @channel ||= begin
12
- channel = connection.create_channel
13
- channel.prefetch(1)
14
-
15
- channel
16
- end
11
+ @channel ||= build_channel
17
12
  end
18
13
 
19
14
  # Creates a memoized connection to RabbitMQ
20
15
  #
21
16
  # @return [Bunny::Session]
22
17
  def connection
23
- @connection ||= begin
24
- connection = Bunny.new(
25
- host: config.host,
26
- port: config.port,
27
- username: config.username,
28
- password: config.password,
29
- vhost: config.vhost,
30
- heartbeat_interval: 60,
31
- )
32
-
33
- connection.start
34
-
35
- connection
36
- end
18
+ @connection ||= build_connection
37
19
  end
38
20
 
39
21
  private
40
22
 
23
+ def build_channel
24
+ channel = connection.create_channel
25
+ channel.prefetch(1)
26
+
27
+ channel
28
+ end
29
+
30
+ def build_connection
31
+ connection = Bunny.new(
32
+ host: config.host,
33
+ port: config.port,
34
+ username: config.username,
35
+ password: config.password,
36
+ vhost: config.vhost,
37
+ heartbeat_interval: 10,
38
+ automatically_recover: true,
39
+ network_recovery_interval: config.network_recovery_interval,
40
+ recover_from_connection_close: true,
41
+ )
42
+
43
+ connection.start
44
+
45
+ connection
46
+ end
47
+
41
48
  def config
42
49
  SongkickQueue.configuration
43
50
  end
@@ -1,16 +1,23 @@
1
1
  module SongkickQueue
2
2
  class Producer
3
+ attr_accessor :reconnect_attempts
4
+
3
5
  def initialize
4
6
  @client = Client.new
7
+ @reconnect_attempts = 0
8
+ @publish_reconnect_delay = 5.0
5
9
  end
6
10
 
7
- # Serializes the given message and publishes it to the default RabbitMQ
8
- # exchange
11
+ # Serializes the given message and publishes it to the default RabbitMQ exchange
9
12
  #
10
13
  # @param queue_name [String] to publish to
11
14
  # @param message [#to_json] to serialize and enqueue
12
15
  # @option options [String] :message_id to pass through to the consumer (will be logged)
13
16
  # @option options [String] :produced_at time when the message was created, ISO8601 formatted
17
+ #
18
+ # @raise [TooManyReconnectAttemptsError] if max reconnect attempts is exceeded
19
+ #
20
+ # @return [Bunny::Exchange]
14
21
  def publish(queue_name, payload, options = {})
15
22
  message_id = options.fetch(:message_id) { SecureRandom.hex(6) }
16
23
  produced_at = options.fetch(:produced_at) { Time.now.utc.iso8601 }
@@ -23,26 +30,55 @@ module SongkickQueue
23
30
 
24
31
  message = JSON.generate(message)
25
32
 
26
- client
33
+ exchange = client
27
34
  .default_exchange
28
35
  .publish(message, routing_key: String(queue_name))
29
36
 
30
37
  logger.info "Published message #{message_id} to '#{queue_name}' at #{produced_at}"
38
+
39
+ exchange
40
+ rescue Bunny::ConnectionClosedError
41
+ self.reconnect_attempts += 1
42
+
43
+ if (reconnect_attempts > config.max_reconnect_attempts)
44
+ fail TooManyReconnectAttemptsError, "Attempted to reconnect more than " +
45
+ "#{config.max_reconnect_attempts} times"
46
+ end
47
+
48
+ logger.info "Attempting to reconnect to RabbitMQ, attempt #{reconnect_attempts} " +
49
+ "of #{config.max_reconnect_attempts}"
50
+
51
+ wait_for_bunny_session_to_reconnect
52
+
53
+ retry
31
54
  end
32
55
 
33
56
  private
34
57
 
35
- # Retrieve the logger defined in the configuration
58
+ # When retrying publishing of a message after a ConnectionClosedError, we must first wait for
59
+ # the defined network_recovery_interval and then a bit longer for it to reopen connections and
60
+ # channels etc...
61
+ #
62
+ # If we attempt to publish again before the connection has been reopened we'll catch the
63
+ # Bunny::ConnectionClosedError exception again and just use another attempt to try and connect.
36
64
  #
37
- # @raise [ConfigurationError] if not defined
65
+ # @todo Optimize this to know when the connection is open again, rather than picking an
66
+ # arbitary time period.
67
+ #
68
+ # @return [void]
69
+ def wait_for_bunny_session_to_reconnect
70
+ wait_time = config.network_recovery_interval + publish_reconnect_delay
71
+ sleep wait_time
72
+ end
73
+
38
74
  def logger
39
- config.logger || fail(ConfigurationError, 'No logger configured, see README for more details')
75
+ config.logger
40
76
  end
41
77
 
42
78
  def config
43
79
  SongkickQueue.configuration
44
80
  end
45
81
 
46
- attr_reader :client
82
+ attr_reader :client, :publish_reconnect_delay
47
83
  end
48
84
  end
@@ -1,3 +1,3 @@
1
1
  module SongkickQueue
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
@@ -103,11 +103,8 @@ module SongkickQueue
103
103
  client.channel
104
104
  end
105
105
 
106
- # Retrieve the logger defined in the configuration
107
- #
108
- # @raise [ConfigurationError] if not defined
109
106
  def logger
110
- config.logger || fail(ConfigurationError, 'No logger configured, see README for more details')
107
+ config.logger
111
108
  end
112
109
 
113
110
  def config
@@ -1,3 +1,4 @@
1
+ require 'songkick_queue'
1
2
  require 'songkick_queue/producer'
2
3
 
3
4
  module SongkickQueue
@@ -23,6 +24,28 @@ module SongkickQueue
23
24
  producer.publish(:queue_name, { example: 'message', value: true },
24
25
  message_id: '92c583bdc248', produced_at: '2015-03-30T15:41:55Z')
25
26
  end
27
+
28
+ it "should retry when publishing fails" do
29
+ producer = Producer.new
30
+
31
+ exchange = double(:exchange, publish: :published)
32
+ allow(exchange).to receive(:publish) { raise Bunny::ConnectionClosedError, 'test' }
33
+ client = instance_double(Client, default_exchange: exchange)
34
+ allow(producer).to receive(:client) { client }
35
+
36
+ logger = double(:logger, info: true)
37
+ allow(producer).to receive(:logger) { logger }
38
+
39
+ allow(producer).to receive(:wait_for_bunny_session_to_reconnect) { true }
40
+
41
+ config = double(:config, max_reconnect_attempts: 2)
42
+ allow(producer).to receive(:config) { config }
43
+
44
+ expect { producer.publish(:queue_name, { example: 'message', value: true }) }.to(
45
+ raise_error(TooManyReconnectAttemptsError, 'Attempted to reconnect more than 2 times'))
46
+
47
+ expect(producer.reconnect_attempts).to eq 3
48
+ end
26
49
  end
27
50
  end
28
51
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: songkick_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Lucraft
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-05-15 00:00:00.000000000 Z
12
+ date: 2015-05-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -100,7 +100,7 @@ files:
100
100
  - README.md
101
101
  - Rakefile
102
102
  - bin/songkick_queue
103
- - examples/environment.rb
103
+ - examples/consumer.rb
104
104
  - examples/producer.rb
105
105
  - lib/songkick_queue.rb
106
106
  - lib/songkick_queue/cli.rb