songkick_queue 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/examples/{environment.rb → consumer.rb} +2 -2
- data/examples/producer.rb +5 -0
- data/lib/songkick_queue.rb +9 -5
- data/lib/songkick_queue/client.rb +27 -20
- data/lib/songkick_queue/producer.rb +43 -7
- data/lib/songkick_queue/version.rb +1 -1
- data/lib/songkick_queue/worker.rb +1 -4
- data/spec/songkick_queue/producer_spec.rb +23 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b51761dcb720a861e59cd35370dd964fbb0393e
|
4
|
+
data.tar.gz: fdcf57c99fa12a9e9e3dbfc9a4c83b8a765486ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78d96fe086dfe7afc88548bdfcae4237ecc8fccc95d6d28d24e643b21c058cfd3eb99b8bf49c4e20bff89770763257ace02c56dc672c7d8c6c900f3ea519fabc
|
7
|
+
data.tar.gz: feb0f64206f09160396154611dd732c8327b86f146d82afae897719ae90e097cb9634d008999b17dc3985eef57be9596c0190d7bc979a9467c8d169e9dcd7251
|
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
# Example
|
1
|
+
# Example consumer
|
2
2
|
# Require this file when running `songkick_queue` like so:
|
3
3
|
#
|
4
|
-
# $ bin/songkick_queue --require ./examples/
|
4
|
+
# $ bin/songkick_queue --require ./examples/consumer.rb --consumer TweetConsumer
|
5
5
|
#
|
6
6
|
require_relative '../lib/songkick_queue'
|
7
7
|
|
data/examples/producer.rb
CHANGED
data/lib/songkick_queue.rb
CHANGED
@@ -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
|
-
|
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
|
31
|
-
port
|
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 ||=
|
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 ||=
|
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
|
-
#
|
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
|
-
# @
|
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
|
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
|
@@ -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
|
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
|
+
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-
|
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/
|
103
|
+
- examples/consumer.rb
|
104
104
|
- examples/producer.rb
|
105
105
|
- lib/songkick_queue.rb
|
106
106
|
- lib/songkick_queue/cli.rb
|