songkick_queue 0.5.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b51761dcb720a861e59cd35370dd964fbb0393e
4
- data.tar.gz: fdcf57c99fa12a9e9e3dbfc9a4c83b8a765486ce
3
+ metadata.gz: c907eaed6febf237e49b8ff56171ecdd09976fe1
4
+ data.tar.gz: ac2c3f1207df01dd24623fef5de6492c1b8016ff
5
5
  SHA512:
6
- metadata.gz: 78d96fe086dfe7afc88548bdfcae4237ecc8fccc95d6d28d24e643b21c058cfd3eb99b8bf49c4e20bff89770763257ace02c56dc672c7d8c6c900f3ea519fabc
7
- data.tar.gz: feb0f64206f09160396154611dd732c8327b86f146d82afae897719ae90e097cb9634d008999b17dc3985eef57be9596c0190d7bc979a9467c8d169e9dcd7251
6
+ metadata.gz: 0c1f1c1cd6b38341badd1b4a0065278730ed6caac7193c4d1c35a374076cd0151bad596eb0b00b7abbba312ae67fe5ff6619978b54e176cdee0e372ce8f02702
7
+ data.tar.gz: 3d21e628d13199e1467e8bb483432ec6abfe31fbfde4cda5858693450ec1faea565fe1848eb83ddfa2376c4f843739cb8ca0842cba285a9254629b65d01671b5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0
4
+ ### Added
5
+ - Instrumentation (for tracking metrics using statsd or other tools)
6
+ - Message ID and produced at timestamp to consumer logs
7
+
8
+ ### Changed
9
+ - Bunny gem dependency to `~> 2.2`
10
+ - Reset reconnect attempts back to zero if message is successfully published
11
+
12
+ ## 0.5.0
13
+ ### Added
14
+ - Configuration options: `max_reconnect_attempts` and `network_recovery_interval`
15
+ - Reconnect handling on closed connections in producer
16
+
17
+ ## 0.4.0
18
+ ### Added
19
+ - Configuration option: `heartbeat_interval`
20
+
21
+ ### Changed
22
+ - Configuration values: replaced single AMQP string with individual config options (eg. host, port etc...)
23
+
24
+ ### Removed
25
+ - Configuration option: `queue_namespace` as RabbitMQ native VHOST can be used instead
26
+
27
+ ## 0.3.0
28
+ ### Added
29
+ - Message ID to payload
30
+ - Produced at timestamp to payload
31
+ - Improved documentation
32
+
3
33
  ## 0.2.0
4
34
  ### Added
5
35
  - Configuration option: `queue_namespace` to namespace queue names when
data/README.md CHANGED
@@ -71,8 +71,6 @@ class TweetConsumer
71
71
  TwitterClient.send_tweet(message[:text], message[:user_id])
72
72
  rescue TwitterClient::HttpError => e
73
73
  logger.warn(e)
74
- rescue StandardError => e
75
- logger.error(e)
76
74
  end
77
75
  end
78
76
  ```
@@ -125,6 +123,27 @@ consumer.
125
123
  SongkickQueue.publish('notifications-service.tweets', { text: 'Hello world', user_id: 57237722 })
126
124
  ```
127
125
 
126
+ ## Instrumentation
127
+
128
+ Hooks are provided to instrument producing and consuming of messages using [ActiveSupport's Notifications](http://api.rubyonrails.org/classes/ActiveSupport/Notifications.html) API.
129
+
130
+ You can subscribe to the following events:
131
+
132
+ ```
133
+ consume_message.songkick_queue
134
+ produce_message.songkick_queue
135
+ ```
136
+
137
+ For both events, the payload includes the message id, produced at timestamp and queue name. The `consume_message` event also includes the consumer class.
138
+
139
+ For example:
140
+
141
+ ```ruby
142
+ ActiveSupport::Notifications.subscribe('consume_message.songkick_queue') do |name, start, finish, id, payload|
143
+ # Log info to statsd or something similar
144
+ end
145
+ ```
146
+
128
147
  ## Tests
129
148
 
130
149
  See the current build status on Travis CI: https://travis-ci.org/songkick/queue
data/examples/consumer.rb CHANGED
@@ -10,6 +10,11 @@ SongkickQueue.configure do |config|
10
10
  config.logger = Logger.new(STDOUT)
11
11
  end
12
12
 
13
+ ActiveSupport::Notifications.subscribe('consume_message.songkick_queue') do |*args|
14
+ event = ActiveSupport::Notifications::Event.new(*args)
15
+ SongkickQueue.configuration.logger.info "name: #{event.name}, duration: #{event.duration}, payload: #{event.payload}"
16
+ end
17
+
13
18
  class TweetConsumer
14
19
  include SongkickQueue::Consumer
15
20
 
data/examples/producer.rb CHANGED
@@ -10,6 +10,11 @@ SongkickQueue.configure do |config|
10
10
  config.logger = Logger.new(STDOUT)
11
11
  end
12
12
 
13
+ ActiveSupport::Notifications.subscribe('produce_message.songkick_queue') do |*args|
14
+ event = ActiveSupport::Notifications::Event.new(*args)
15
+ SongkickQueue.configuration.logger.info "name: #{event.name}, duration: #{event.duration}, payload: #{event.payload}"
16
+ end
17
+
13
18
  3.times do
14
19
  SongkickQueue.publish('notifications-service.tweets', { text: 'Hello world', user_id: 57237722 })
15
20
  end
@@ -2,6 +2,8 @@ require 'json'
2
2
  require 'securerandom'
3
3
  require 'logger'
4
4
  require 'bunny'
5
+ require 'time'
6
+ require 'active_support/notifications'
5
7
 
6
8
  require 'songkick_queue/version'
7
9
  require 'songkick_queue/client'
@@ -30,9 +30,18 @@ module SongkickQueue
30
30
 
31
31
  message = JSON.generate(message)
32
32
 
33
- exchange = client
34
- .default_exchange
35
- .publish(message, routing_key: String(queue_name))
33
+ exchange = client.default_exchange
34
+
35
+ instrumentation_options = {
36
+ queue_name: String(queue_name),
37
+ message_id: message_id,
38
+ produced_at: produced_at,
39
+ }
40
+ ActiveSupport::Notifications.instrument('produce_message.songkick_queue', instrumentation_options) do
41
+ exchange.publish(message, routing_key: String(queue_name))
42
+ end
43
+
44
+ self.reconnect_attempts = 0
36
45
 
37
46
  logger.info "Published message #{message_id} to '#{queue_name}' at #{produced_at}"
38
47
 
@@ -1,3 +1,3 @@
1
1
  module SongkickQueue
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -83,15 +83,24 @@ module SongkickQueue
83
83
  def process_message(consumer_class, delivery_info, properties, message)
84
84
  message = JSON.parse(message, symbolize_names: true)
85
85
 
86
- # Handle both old and new format of messages
87
- # TODO: Tidy this up once messages always have a payload
88
- payload = message.fetch(:payload, message)
86
+ message_id = message.fetch(:message_id)
87
+ produced_at = message.fetch(:produced_at)
88
+ payload = message.fetch(:payload)
89
89
 
90
- logger.info "Processing message via #{consumer_class}..."
91
- set_process_name(consumer_class)
90
+ logger.info "Processing message #{message_id} via #{consumer_class}, produced at #{produced_at}"
91
+ set_process_name(consumer_class, message_id)
92
92
 
93
93
  consumer = consumer_class.new(delivery_info, logger)
94
- consumer.process(payload)
94
+
95
+ instrumentation_options = {
96
+ consumer_class: consumer_class.to_s,
97
+ queue_name: consumer_class.queue_name,
98
+ message_id: message_id,
99
+ produced_at: produced_at,
100
+ }
101
+ ActiveSupport::Notifications.instrument('consume_message.songkick_queue', instrumentation_options) do
102
+ consumer.process(payload)
103
+ end
95
104
  rescue Object => exception
96
105
  logger.error(exception)
97
106
  ensure
@@ -116,14 +125,19 @@ module SongkickQueue
116
125
  # @example idle
117
126
  # set_process_name #=> "songkick_queue[idle]"
118
127
  # @example consumer running, namespace is removed
119
- # set_process_name(Foo::TweetConsumer) #=> "songkick_queue[TweetConsumer]"
128
+ # set_process_name(Foo::TweetConsumer, 'a729bcd8') #=> "songkick_queue[TweetConsumer#a729bcd8]"
120
129
  # @param status [String] of the program
121
- def set_process_name(status = 'idle')
130
+ # @param message_id [String] identifying the message currently being consumed
131
+ def set_process_name(status = 'idle', message_id = nil)
122
132
  formatted_status = String(status)
123
133
  .split('::')
124
134
  .last
125
135
 
126
- $PROGRAM_NAME = "#{process_name}[#{formatted_status}]"
136
+ ident = [formatted_status, message_id]
137
+ .compact
138
+ .join('#')
139
+
140
+ $PROGRAM_NAME = "#{process_name}[#{ident}]"
127
141
  end
128
142
  end
129
143
  end
@@ -25,5 +25,6 @@ Gem::Specification.new do |spec|
25
25
  # Used by yardoc for processing README.md code snippets
26
26
  spec.add_development_dependency "redcarpet"
27
27
 
28
- spec.add_dependency "bunny", ">= 1.7.0"
28
+ spec.add_dependency "bunny", "~> 2.2"
29
+ spec.add_dependency "activesupport", ">= 3.0.0"
29
30
  end
@@ -66,36 +66,11 @@ module SongkickQueue
66
66
 
67
67
  describe "#process_message" do
68
68
  it "should instantiate the consumer and call #process" do
69
- ::FooConsumer = Class.new
70
- worker = Worker.new(:process_name, FooConsumer)
71
-
72
- logger = double(:logger, info: :null)
73
- allow(worker).to receive(:logger) { logger }
74
-
75
- channel = double(:channel, ack: :null)
76
- allow(worker).to receive(:channel) { channel }
77
-
78
- delivery_info = double(:delivery_info, delivery_tag: 'tag')
79
-
80
- consumer = double(FooConsumer, process: :null)
81
-
82
- expect(FooConsumer).to receive(:new)
83
- .with(delivery_info, logger) { consumer }
84
-
85
- expect(consumer).to receive(:process)
86
- .with({ example: 'message', value: true})
87
-
88
- worker.send(:process_message, FooConsumer, delivery_info,
89
- :properties, '{"example":"message","value":true}')
90
-
91
- expect(logger).to have_received(:info)
92
- .with('Processing message via FooConsumer...')
93
-
94
- expect(channel).to have_received(:ack).with('tag', false)
95
- end
96
-
97
- it "should handle new message format with nested payload" do
98
- ::BarConsumer = Class.new
69
+ ::BarConsumer = Struct.new(:delivery_info, :logger) do
70
+ def self.queue_name
71
+ "bar-queue"
72
+ end
73
+ end
99
74
  worker = Worker.new(:process_name, BarConsumer)
100
75
 
101
76
  logger = double(:logger, info: :null)
@@ -119,7 +94,7 @@ module SongkickQueue
119
94
  '"payload":{"example":"message","value":true}}')
120
95
 
121
96
  expect(logger).to have_received(:info)
122
- .with('Processing message via BarConsumer...')
97
+ .with('Processing message 92c583bdc248 via BarConsumer, produced at 2015-03-30T15:41:55Z')
123
98
 
124
99
  expect(channel).to have_received(:ack).with('tag', false)
125
100
  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.5.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Lucraft
@@ -9,78 +9,92 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2015-05-18 00:00:00.000000000 Z
12
+ date: 2015-10-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - ~>
18
+ - - "~>"
19
19
  - !ruby/object:Gem::Version
20
20
  version: '10.0'
21
21
  type: :development
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - ~>
25
+ - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '10.0'
28
28
  - !ruby/object:Gem::Dependency
29
29
  name: rspec
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ~>
32
+ - - "~>"
33
33
  - !ruby/object:Gem::Version
34
34
  version: '3.2'
35
35
  type: :development
36
36
  prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - ~>
39
+ - - "~>"
40
40
  - !ruby/object:Gem::Version
41
41
  version: '3.2'
42
42
  - !ruby/object:Gem::Dependency
43
43
  name: yard
44
44
  requirement: !ruby/object:Gem::Requirement
45
45
  requirements:
46
- - - '>='
46
+ - - ">="
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  type: :development
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: redcarpet
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - '>='
60
+ - - ">="
61
61
  - !ruby/object:Gem::Version
62
62
  version: '0'
63
63
  type: :development
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - '>='
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: bunny
72
72
  requirement: !ruby/object:Gem::Requirement
73
73
  requirements:
74
- - - '>='
74
+ - - "~>"
75
75
  - !ruby/object:Gem::Version
76
- version: 1.7.0
76
+ version: '2.2'
77
77
  type: :runtime
78
78
  prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - '>='
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
- version: 1.7.0
83
+ version: '2.2'
84
+ - !ruby/object:Gem::Dependency
85
+ name: activesupport
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 3.0.0
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 3.0.0
84
98
  description: A gem for processing tasks asynchronously, powered by RabbitMQ.
85
99
  email:
86
100
  - dan.lucraft@songkick.com
@@ -90,10 +104,10 @@ executables:
90
104
  extensions: []
91
105
  extra_rdoc_files: []
92
106
  files:
93
- - .gitignore
94
- - .rspec
95
- - .travis.yml
96
- - .yardopts
107
+ - ".gitignore"
108
+ - ".rspec"
109
+ - ".travis.yml"
110
+ - ".yardopts"
97
111
  - CHANGELOG.md
98
112
  - Gemfile
99
113
  - LICENSE.txt
@@ -126,17 +140,17 @@ require_paths:
126
140
  - lib
127
141
  required_ruby_version: !ruby/object:Gem::Requirement
128
142
  requirements:
129
- - - '>='
143
+ - - ">="
130
144
  - !ruby/object:Gem::Version
131
145
  version: '0'
132
146
  required_rubygems_version: !ruby/object:Gem::Requirement
133
147
  requirements:
134
- - - '>='
148
+ - - ">="
135
149
  - !ruby/object:Gem::Version
136
150
  version: '0'
137
151
  requirements: []
138
152
  rubyforge_project:
139
- rubygems_version: 2.4.6
153
+ rubygems_version: 2.4.5
140
154
  signing_key:
141
155
  specification_version: 4
142
156
  summary: A gem for processing tasks asynchronously, powered by RabbitMQ.