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 +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
|