songkick_queue 0.5.0 → 0.6.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: 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.