hutch 0.7.0 → 0.8.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: 2432ba126d871fbe57468e88a39c6d589a01aa2f
4
- data.tar.gz: 865526ad5d702eaaea80ee000b6e8ef879b5ac18
3
+ metadata.gz: 5a5e2b2315f71a38d34043446034950e26c63612
4
+ data.tar.gz: 24f650b6681c6c7c18e5a9a067666155edae4d2d
5
5
  SHA512:
6
- metadata.gz: 87d496756e3df55679391977995de5eb836a949cd80ca0ecafb7d99966512c736be01307e72976cad95fb122fcd048c51154a7456c9cfec7b1e4d2080ef9c20f
7
- data.tar.gz: 5b8f3e3fd23af0451da805d64f90ff8afc5b8ee0be93c04a4fec7be45598c32fc2ad96d59442d660974f8b3d18e47f2e507aa6988b369207721173e5e1812877
6
+ metadata.gz: ffdefefec637a9ad46fb00207a74c78210fcc167c0dbfe6c34a91da32ec5499ea67c07f12a68af1ac0ea7e9d23a9df7b33690a9c68d9a4b765897533fcc37d18
7
+ data.tar.gz: ac34de5ef0600b31a6f3614de06508cca2c47406371a9137f43458a2533996cac906a47825c45230b5bb887633dbc15b2c859fb1bae477b77149c221e5b6f198
@@ -1,6 +1,85 @@
1
- ## 0.7.0 — unreleased
1
+ ## 0.8.0 — unreleased
2
2
 
3
- - [Global properties can now be specified](https://github.com/gocardless/hutch/pull/62) for publishing
3
+ ### Uncaught Exceptions Result in Rejected Messages
4
+
5
+ Uncaught exceptions in consumers now result in Hutch rejecting
6
+ messages (deliveries) using `basic.nack`. This way they are [dead lettered](http://www.rabbitmq.com/dlx.html).
7
+
8
+ Contributed by Garrett Johnson.
9
+
10
+ ### Missing Require
11
+
12
+ `hutch/consumer.rb` no longer fails to load with the
13
+ apps that do not `require "set"`.
14
+
15
+ ### Relaxed Queue Namespace Validation
16
+
17
+ Namespaces now can include any characters that are valid in RabbitMQ
18
+ queue names.
19
+
20
+ Contributed by Garrett Johnson.
21
+
22
+ ### basic.qos Configuration
23
+
24
+ It is now possible to configure `basic.qos` (aka channel prefetch) setting
25
+ used by Hutch using the `:channel_prefetch` config key.
26
+
27
+ ### Passwords No Longer Logged
28
+
29
+ Hutch now elides passwords from logs.
30
+
31
+
32
+ ## 0.7.0 — January 14, 2014
33
+
34
+ ### Optional HTTP API Use
35
+
36
+ It is now possible to make Hutch [not use RabbitMQ HTTP
37
+ API](https://github.com/gocardless/hutch/pull/69) (e.g. when the
38
+ RabbitMQ management plugin that provides it is not available).
39
+
40
+
41
+ ### Extra Arguments for Hutch::Broker#publish
42
+
43
+ Extra options [passed to `Hutch::Broker#publish` will now be propagated](https://github.com/gocardless/hutch/pull/61).
44
+
45
+
46
+ ### Content-Type for Messages
47
+
48
+ Messages published with Hutch now have content type set
49
+ to `application/json`.
50
+
51
+
52
+ ### Greater Heartbeat Interval
53
+
54
+ Hutch now uses heartbeat interval of 30, so heartbeats won't interfere with transfers
55
+ of large messages over high latency networks (e.g. between AWS availability regions).
56
+
57
+
58
+ ### Custom Queue Names
59
+
60
+ It is now possible to [specify an optional queue name](https://github.com/gocardless/hutch/pull/49):
61
+
62
+ ``` ruby
63
+ class FailedPaymentConsumer
64
+ include Hutch::Consumer
65
+ consume 'gc.ps.payment.failed'
66
+ queue_name 'failed_payments'
67
+
68
+ def process(message)
69
+ mark_payment_as_failed(message[:id])
70
+ end
71
+ end
72
+ ```
73
+
74
+ ### Global Properties for Publishers
75
+
76
+ [Global properties can now be specified](https://github.com/gocardless/hutch/pull/62) for publishing:
77
+
78
+ ``` ruby
79
+ Hutch.global_properties = proc {
80
+ { app_id: 'api', headers: { request_id: RequestId.request_id } }
81
+ }
82
+ ```
4
83
 
5
84
  ## 0.6.0 - November 4, 2013
6
85
 
@@ -1,7 +1,7 @@
1
1
  require File.expand_path('../lib/hutch/version', __FILE__)
2
2
 
3
3
  Gem::Specification.new do |gem|
4
- gem.add_runtime_dependency 'bunny', '~> 1.1.0'
4
+ gem.add_runtime_dependency 'bunny', '~> 1.1.2'
5
5
  gem.add_runtime_dependency 'carrot-top', '~> 0.0.7'
6
6
  gem.add_runtime_dependency 'multi_json', '~> 1.5'
7
7
  gem.add_development_dependency 'rspec', '~> 2.12.0'
@@ -55,9 +55,8 @@ module Hutch
55
55
  tls_key = @config[:mq_tls_cert]
56
56
  tls_cert = @config[:mq_tls_key]
57
57
  protocol = tls ? "amqps://" : "amqp://"
58
- uri = "#{username}:#{password}@#{host}:#{port}/#{vhost.sub(/^\//, '')}"
59
- sanitized_uri = "#{protocol}#{host}:#{port}"
60
- logger.info "connecting to rabbitmq (#{protocol}#{uri})"
58
+ sanitized_uri = "#{protocol}#{username}@#{host}:#{port}/#{vhost.sub(/^\//, '')}"
59
+ logger.info "connecting to rabbitmq (#{sanitized_uri})"
61
60
 
62
61
  @connection = Bunny.new(host: host, port: port, vhost: vhost,
63
62
  tls: tls, tls_key: tls_key, tls_cert: tls_cert,
@@ -74,14 +73,16 @@ module Hutch
74
73
 
75
74
  def open_channel!
76
75
  logger.info 'opening rabbitmq channel'
77
- @channel = connection.create_channel
76
+ @channel = connection.create_channel.tap do |ch|
77
+ ch.prefetch(@config[:channel_prefetch]) if @config[:channel_prefetch]
78
+ end
78
79
  end
79
80
 
80
81
  # Set up the connection to the RabbitMQ management API. Unfortunately, this
81
82
  # is necessary to do a few things that are impossible over AMQP. E.g.
82
83
  # listing queues and bindings.
83
84
  def set_up_api_connection
84
- logger.info "connecting to rabbitmq management api (#{api_config.management_uri})"
85
+ logger.info "connecting to rabbitmq HTTP API (#{api_config.sanitized_uri})"
85
86
 
86
87
  with_authentication_error_handler do
87
88
  with_connection_error_handler do
@@ -96,7 +97,7 @@ module Hutch
96
97
  # Create / get a durable queue and apply namespace if it exists.
97
98
  def queue(name)
98
99
  with_bunny_precondition_handler('queue') do
99
- namespace = @config[:namespace].to_s.downcase.gsub(/\W|:/, "")
100
+ namespace = @config[:namespace].to_s.downcase.gsub(/[^-_:\.\w]/, "")
100
101
  name = name.prepend(namespace + ":") unless namespace.empty?
101
102
  channel.queue(name, durable: true)
102
103
  end
@@ -151,6 +152,10 @@ module Hutch
151
152
  @channel.ack(delivery_tag, false)
152
153
  end
153
154
 
155
+ def nack(delivery_tag)
156
+ @channel.nack(delivery_tag, false, false)
157
+ end
158
+
154
159
  def publish(routing_key, message, properties = {})
155
160
  ensure_connection!(routing_key, message)
156
161
 
@@ -189,16 +194,16 @@ module Hutch
189
194
  config.password = @config[:mq_password]
190
195
  config.ssl = @config[:mq_api_ssl]
191
196
  config.protocol = config.ssl ? "https://" : "http://"
192
- config.management_uri = "#{config.protocol}#{config.username}:#{config.password}@#{config.host}:#{config.port}/"
197
+ config.sanitized_uri = "#{config.protocol}#{config.username}@#{config.host}:#{config.port}/"
193
198
  end
194
199
  end
195
200
 
196
201
  def with_authentication_error_handler
197
202
  yield
198
203
  rescue Net::HTTPServerException => ex
199
- logger.error "api connection error: #{ex.message.downcase}"
204
+ logger.error "HTTP API connection error: #{ex.message.downcase}"
200
205
  if ex.response.code == '401'
201
- raise AuthenticationError.new('invalid api credentials')
206
+ raise AuthenticationError.new('invalid HTTP API credentials')
202
207
  else
203
208
  raise
204
209
  end
@@ -207,15 +212,15 @@ module Hutch
207
212
  def with_connection_error_handler
208
213
  yield
209
214
  rescue Errno::ECONNREFUSED => ex
210
- logger.error "api connection error: #{ex.message.downcase}"
211
- raise ConnectionError.new("couldn't connect to api at #{api_config.management_uri}")
215
+ logger.error "HTTP API connection error: #{ex.message.downcase}"
216
+ raise ConnectionError.new("couldn't connect to HTTP API at #{api_config.sanitized_uri}")
212
217
  end
213
218
 
214
219
  def with_bunny_precondition_handler(item)
215
220
  yield
216
221
  rescue Bunny::PreconditionFailed => ex
217
222
  logger.error ex.message
218
- s = "RabbitMQ responded with Precondition Failed when creating this #{item}. " +
223
+ s = "RabbitMQ responded with 406 Precondition Failed when creating this #{item}. " +
219
224
  "Perhaps it is being redeclared with non-matching attributes"
220
225
  raise WorkerSetupError.new(s)
221
226
  end
@@ -25,7 +25,8 @@ module Hutch
25
25
  require_paths: [],
26
26
  autoload_rails: true,
27
27
  error_handlers: [Hutch::ErrorHandlers::Logger.new],
28
- namespace: nil
28
+ namespace: nil,
29
+ channel_prefetch: 0
29
30
  }
30
31
  end
31
32
 
@@ -1,3 +1,5 @@
1
+ require 'set'
2
+
1
3
  module Hutch
2
4
  # Include this module in a class to register it as a consumer. Consumers
3
5
  # gain a class method called `consume`, which should be used to register
@@ -1,4 +1,4 @@
1
1
  module Hutch
2
- VERSION = '0.7.0'.freeze
2
+ VERSION = '0.8.0'.freeze
3
3
  end
4
4
 
@@ -85,7 +85,7 @@ module Hutch
85
85
  broker.ack(delivery_info.delivery_tag)
86
86
  rescue StandardError => ex
87
87
  handle_error(properties.message_id, consumer, ex)
88
- broker.ack(delivery_info.delivery_tag)
88
+ broker.nack(delivery_info.delivery_tag)
89
89
  end
90
90
  end
91
91
 
@@ -97,8 +97,7 @@ module Hutch
97
97
 
98
98
  def consumers=(val)
99
99
  if val.empty?
100
- logger.warn 'no consumer loaded, ensure there\'s' +
101
- 'no configuration issue'
100
+ logger.warn "no consumer loaded, ensure there's no configuration issue"
102
101
  end
103
102
  @consumers = val
104
103
  end
@@ -58,6 +58,17 @@ describe Hutch::Broker do
58
58
 
59
59
  specify { set_up_amqp_connection.should raise_error }
60
60
  end
61
+
62
+ context 'with channel_prefetch set' do
63
+ let(:prefetch_value) { 1 }
64
+ before { config[:channel_prefetch] = prefetch_value }
65
+ after { broker.disconnect }
66
+
67
+ it "set's channel's prefetch" do
68
+ Bunny::Channel.any_instance.should_receive(:prefetch).with(prefetch_value)
69
+ broker.set_up_amqp_connection
70
+ end
71
+ end
61
72
  end
62
73
 
63
74
  describe '#set_up_api_connection', rabbitmq: true do
@@ -82,8 +93,8 @@ describe Hutch::Broker do
82
93
  before { broker.stub(:channel) { channel } }
83
94
 
84
95
  it 'applies a global namespace' do
85
- config[:namespace] = 'service'
86
- broker.channel.should_receive(:queue).with { |*args| args.first == 'service:test' }
96
+ config[:namespace] = 'mirror-all.service'
97
+ broker.channel.should_receive(:queue).with { |*args| args.first == 'mirror-all.service:test' }
87
98
  broker.queue('test')
88
99
  end
89
100
  end
@@ -108,7 +119,7 @@ describe Hutch::Broker do
108
119
  end
109
120
 
110
121
  describe '#bind_queue' do
111
-
122
+
112
123
  around { |example| broker.connect { example.run } }
113
124
 
114
125
  let(:routing_keys) { %w( a b c ) }
@@ -47,6 +47,7 @@ describe Hutch::Worker do
47
47
  let(:properties) { double('Properties', message_id: nil) }
48
48
  before { consumer.stub(new: consumer_instance) }
49
49
  before { broker.stub(:ack) }
50
+ before { broker.stub(:nack) }
50
51
 
51
52
  it 'passes the message to the consumer' do
52
53
  consumer_instance.should_receive(:process).
@@ -70,8 +71,8 @@ describe Hutch::Worker do
70
71
  worker.handle_message(consumer, delivery_info, properties, payload)
71
72
  end
72
73
 
73
- it 'acknowledges the message' do
74
- broker.should_receive(:ack).with(delivery_info.delivery_tag)
74
+ it 'rejects the message' do
75
+ broker.should_receive(:nack).with(delivery_info.delivery_tag)
75
76
  worker.handle_message(consumer, delivery_info, properties, payload)
76
77
  end
77
78
  end
@@ -86,8 +87,8 @@ describe Hutch::Worker do
86
87
  worker.handle_message(consumer, delivery_info, properties, payload)
87
88
  end
88
89
 
89
- it 'acknowledges the message' do
90
- broker.should_receive(:ack).with(delivery_info.delivery_tag)
90
+ it 'rejects the message' do
91
+ broker.should_receive(:nack).with(delivery_info.delivery_tag)
91
92
  worker.handle_message(consumer, delivery_info, properties, payload)
92
93
  end
93
94
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hutch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Harry Marr
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-01-14 00:00:00.000000000 Z
11
+ date: 2014-02-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bunny
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: 1.1.0
19
+ version: 1.1.2
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: 1.1.0
26
+ version: 1.1.2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: carrot-top
29
29
  requirement: !ruby/object:Gem::Requirement