hutch 0.18.0 → 1.1.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.
Files changed (73) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +3 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +20 -8
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +466 -2
  7. data/Gemfile +18 -4
  8. data/Guardfile +13 -4
  9. data/LICENSE +2 -1
  10. data/README.md +397 -32
  11. data/Rakefile +8 -1
  12. data/bin/ci/before_build.sh +20 -0
  13. data/bin/ci/install_on_debian.sh +46 -0
  14. data/hutch.gemspec +6 -7
  15. data/lib/hutch/acknowledgements/base.rb +16 -0
  16. data/lib/hutch/acknowledgements/nack_on_all_failures.rb +19 -0
  17. data/lib/hutch/adapters/march_hare.rb +1 -1
  18. data/lib/hutch/broker.rb +127 -103
  19. data/lib/hutch/cli.rb +66 -25
  20. data/lib/hutch/config.rb +230 -55
  21. data/lib/hutch/consumer.rb +42 -3
  22. data/lib/hutch/error_handlers/airbrake.rb +44 -16
  23. data/lib/hutch/error_handlers/base.rb +15 -0
  24. data/lib/hutch/error_handlers/bugsnag.rb +30 -0
  25. data/lib/hutch/error_handlers/honeybadger.rb +33 -18
  26. data/lib/hutch/error_handlers/logger.rb +12 -6
  27. data/lib/hutch/error_handlers/rollbar.rb +28 -0
  28. data/lib/hutch/error_handlers/sentry.rb +15 -12
  29. data/lib/hutch/error_handlers/sentry_raven.rb +31 -0
  30. data/lib/hutch/error_handlers.rb +3 -0
  31. data/lib/hutch/exceptions.rb +8 -1
  32. data/lib/hutch/logging.rb +5 -5
  33. data/lib/hutch/message.rb +2 -4
  34. data/lib/hutch/publisher.rb +75 -0
  35. data/lib/hutch/serializers/identity.rb +19 -0
  36. data/lib/hutch/serializers/json.rb +22 -0
  37. data/lib/hutch/tracers/datadog.rb +17 -0
  38. data/lib/hutch/tracers.rb +1 -0
  39. data/lib/hutch/version.rb +1 -2
  40. data/lib/hutch/waiter.rb +104 -0
  41. data/lib/hutch/worker.rb +81 -75
  42. data/lib/hutch.rb +15 -6
  43. data/lib/yard-settings/handler.rb +38 -0
  44. data/lib/yard-settings/yard-settings.rb +2 -0
  45. data/spec/hutch/broker_spec.rb +162 -77
  46. data/spec/hutch/cli_spec.rb +16 -3
  47. data/spec/hutch/config_spec.rb +121 -22
  48. data/spec/hutch/consumer_spec.rb +82 -4
  49. data/spec/hutch/error_handlers/airbrake_spec.rb +25 -10
  50. data/spec/hutch/error_handlers/bugsnag_spec.rb +55 -0
  51. data/spec/hutch/error_handlers/honeybadger_spec.rb +24 -2
  52. data/spec/hutch/error_handlers/logger_spec.rb +14 -1
  53. data/spec/hutch/error_handlers/rollbar_spec.rb +45 -0
  54. data/spec/hutch/error_handlers/sentry_raven_spec.rb +37 -0
  55. data/spec/hutch/error_handlers/sentry_spec.rb +21 -2
  56. data/spec/hutch/logger_spec.rb +12 -6
  57. data/spec/hutch/message_spec.rb +2 -2
  58. data/spec/hutch/serializers/json_spec.rb +17 -0
  59. data/spec/hutch/tracers/datadog_spec.rb +44 -0
  60. data/spec/hutch/waiter_spec.rb +51 -0
  61. data/spec/hutch/worker_spec.rb +89 -5
  62. data/spec/spec_helper.rb +7 -5
  63. data/templates/default/class/html/settings.erb +0 -0
  64. data/templates/default/class/setup.rb +4 -0
  65. data/templates/default/fulldoc/html/css/hutch.css +13 -0
  66. data/templates/default/layout/html/setup.rb +7 -0
  67. data/templates/default/method_details/html/settings.erb +5 -0
  68. data/templates/default/method_details/setup.rb +4 -0
  69. data/templates/default/method_details/text/settings.erb +0 -0
  70. data/templates/default/module/html/settings.erb +40 -0
  71. data/templates/default/module/setup.rb +4 -0
  72. metadata +62 -43
  73. data/circle.yml +0 -3
@@ -1,22 +1,25 @@
1
1
  require 'hutch/logging'
2
- require 'raven'
2
+ require 'sentry-ruby'
3
+ require 'hutch/error_handlers/base'
3
4
 
4
5
  module Hutch
5
6
  module ErrorHandlers
6
- class Sentry
7
- include Logging
8
-
9
- def initialize
10
- unless Raven.respond_to?(:capture_exception)
11
- raise "The Hutch Sentry error handler requires Raven >= 0.4.0"
7
+ class Sentry < Base
8
+ def handle(properties, payload, consumer, ex)
9
+ message_id = properties.message_id
10
+ prefix = "message(#{message_id || '-'}):"
11
+ logger.error "#{prefix} Logging event to Sentry"
12
+ logger.error "#{prefix} #{ex.class} - #{ex.message}"
13
+ ::Sentry.configure_scope do |scope|
14
+ scope.set_context("payload", payload)
12
15
  end
16
+ ::Sentry.capture_exception(ex)
13
17
  end
14
18
 
15
- def handle(message_id, payload, consumer, ex)
16
- prefix = "message(#{message_id || '-'}): "
17
- logger.error prefix + "Logging event to Sentry"
18
- logger.error prefix + "#{ex.class} - #{ex.message}"
19
- Raven.capture_exception(ex)
19
+ def handle_setup_exception(ex)
20
+ logger.error "Logging setup exception to Sentry"
21
+ logger.error "#{ex.class} - #{ex.message}"
22
+ ::Sentry.capture_exception(ex)
20
23
  end
21
24
  end
22
25
  end
@@ -0,0 +1,31 @@
1
+ require 'hutch/logging'
2
+ require 'raven'
3
+ require 'hutch/error_handlers/base'
4
+
5
+ module Hutch
6
+ module ErrorHandlers
7
+ class SentryRaven < Base
8
+
9
+ def initialize
10
+ unless Raven.respond_to?(:capture_exception)
11
+ raise "The Hutch Sentry error handler requires Raven >= 0.4.0"
12
+ end
13
+ end
14
+
15
+ def handle(properties, payload, consumer, ex)
16
+ message_id = properties.message_id
17
+ prefix = "message(#{message_id || '-'}):"
18
+ logger.error "#{prefix} Logging event to Sentry"
19
+ logger.error "#{prefix} #{ex.class} - #{ex.message}"
20
+ Raven.capture_exception(ex, extra: { payload: payload })
21
+ end
22
+
23
+ def handle_setup_exception(ex)
24
+ logger.error "Logging setup exception to Sentry"
25
+ logger.error "#{ex.class} - #{ex.message}"
26
+ Raven.capture_exception(ex)
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -2,7 +2,10 @@ module Hutch
2
2
  module ErrorHandlers
3
3
  autoload :Logger, 'hutch/error_handlers/logger'
4
4
  autoload :Sentry, 'hutch/error_handlers/sentry'
5
+ autoload :SentryRaven, 'hutch/error_handlers/sentry_raven'
5
6
  autoload :Honeybadger, 'hutch/error_handlers/honeybadger'
6
7
  autoload :Airbrake, 'hutch/error_handlers/airbrake'
8
+ autoload :Rollbar, 'hutch/error_handlers/rollbar'
9
+ autoload :Bugsnag, 'hutch/error_handlers/bugsnag'
7
10
  end
8
11
  end
@@ -1,5 +1,12 @@
1
1
  module Hutch
2
- class Exception < StandardError; end
2
+ if defined?(JRUBY_VERSION)
3
+ require 'march_hare/exceptions'
4
+ class Exception < MarchHare::Exception; end
5
+ else
6
+ require "bunny/exceptions"
7
+ # Bunny::Exception inherits from StandardError
8
+ class Exception < Bunny::Exception; end
9
+ end
3
10
  class ConnectionError < Exception; end
4
11
  class AuthenticationError < Exception; end
5
12
  class WorkerSetupError < Exception; end
data/lib/hutch/logging.rb CHANGED
@@ -9,12 +9,12 @@ module Hutch
9
9
  end
10
10
  end
11
11
 
12
- def self.setup_logger(target = $stdout)
12
+ def self.setup_logger
13
13
  require 'hutch/config'
14
- @logger = Logger.new(target)
15
- @logger.level = Hutch::Config.log_level
16
- @logger.formatter = HutchFormatter.new
17
- @logger
14
+ @logger = Logger.new($stdout).tap do |l|
15
+ l.level = Hutch::Config.log_level
16
+ l.formatter = HutchFormatter.new
17
+ end
18
18
  end
19
19
 
20
20
  def self.logger
data/lib/hutch/message.rb CHANGED
@@ -1,6 +1,4 @@
1
- require 'multi_json'
2
1
  require 'forwardable'
3
- require 'active_support/core_ext/hash/indifferent_access'
4
2
 
5
3
  module Hutch
6
4
  class Message
@@ -8,11 +6,11 @@ module Hutch
8
6
 
9
7
  attr_reader :delivery_info, :properties, :payload
10
8
 
11
- def initialize(delivery_info, properties, payload)
9
+ def initialize(delivery_info, properties, payload, serializer)
12
10
  @delivery_info = delivery_info
13
11
  @properties = properties
14
12
  @payload = payload
15
- @body = MultiJson.load(payload).with_indifferent_access
13
+ @body = serializer.decode(payload)
16
14
  end
17
15
 
18
16
  def_delegator :@body, :[]
@@ -0,0 +1,75 @@
1
+ require 'securerandom'
2
+ require 'hutch/logging'
3
+ require 'hutch/exceptions'
4
+
5
+ module Hutch
6
+ class Publisher
7
+ include Logging
8
+ attr_reader :connection, :channel, :exchange, :config
9
+
10
+ def initialize(connection, channel, exchange, config = Hutch::Config)
11
+ @connection = connection
12
+ @channel = channel
13
+ @exchange = exchange
14
+ @config = config
15
+ end
16
+
17
+ def publish(routing_key, message, properties = {}, options = {})
18
+ ensure_connection!(routing_key, message)
19
+
20
+ serializer = options[:serializer] || config[:serializer]
21
+
22
+ non_overridable_properties = {
23
+ routing_key: routing_key,
24
+ timestamp: connection.current_timestamp,
25
+ content_type: serializer.content_type,
26
+ }
27
+ properties[:message_id] ||= generate_id
28
+
29
+ payload = serializer.encode(message)
30
+
31
+ log_publication(serializer, payload, routing_key)
32
+
33
+ response = exchange.publish(payload, {persistent: true}.
34
+ merge(properties).
35
+ merge(global_properties).
36
+ merge(non_overridable_properties))
37
+
38
+ channel.wait_for_confirms if config[:force_publisher_confirms]
39
+ response
40
+ end
41
+
42
+ private
43
+
44
+ def log_publication(serializer, payload, routing_key)
45
+ logger.debug {
46
+ spec =
47
+ if serializer.binary?
48
+ "#{payload.bytesize} bytes message"
49
+ else
50
+ "message '#{payload}'"
51
+ end
52
+ "publishing #{spec} to #{routing_key}"
53
+ }
54
+ end
55
+
56
+ def raise_publish_error(reason, routing_key, message)
57
+ msg = "unable to publish - #{reason}. Message: #{JSON.dump(message)}, Routing key: #{routing_key}."
58
+ logger.error(msg)
59
+ raise PublishError, msg
60
+ end
61
+
62
+ def ensure_connection!(routing_key, message)
63
+ raise_publish_error('no connection to broker', routing_key, message) unless connection
64
+ raise_publish_error('connection is closed', routing_key, message) unless connection.open?
65
+ end
66
+
67
+ def generate_id
68
+ SecureRandom.uuid
69
+ end
70
+
71
+ def global_properties
72
+ Hutch.global_properties.respond_to?(:call) ? Hutch.global_properties.call : Hutch.global_properties
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,19 @@
1
+ module Hutch
2
+ module Serializers
3
+ class Identity
4
+
5
+ def self.encode(payload)
6
+ payload
7
+ end
8
+
9
+ def self.decode(payload)
10
+ payload
11
+ end
12
+
13
+ def self.binary? ; false ; end
14
+
15
+ def self.content_type ; nil ; end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require 'multi_json'
2
+ require 'active_support/core_ext/hash/indifferent_access'
3
+
4
+ module Hutch
5
+ module Serializers
6
+ class JSON
7
+
8
+ def self.encode(payload)
9
+ ::MultiJson.dump(payload)
10
+ end
11
+
12
+ def self.decode(payload)
13
+ ::MultiJson.load(payload).with_indifferent_access
14
+ end
15
+
16
+ def self.binary? ; false ; end
17
+
18
+ def self.content_type ; 'application/json' ; end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ require 'ddtrace'
2
+
3
+ module Hutch
4
+ module Tracers
5
+ class Datadog
6
+ def initialize(klass)
7
+ @klass = klass
8
+ end
9
+
10
+ def handle(message)
11
+ ::Datadog.tracer.trace(@klass.class.name, service: 'hutch', span_type: 'rabbitmq') do
12
+ @klass.process(message)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
data/lib/hutch/tracers.rb CHANGED
@@ -2,5 +2,6 @@ module Hutch
2
2
  module Tracers
3
3
  autoload :NullTracer, 'hutch/tracers/null_tracer'
4
4
  autoload :NewRelic, 'hutch/tracers/newrelic'
5
+ autoload :Datadog, 'hutch/tracers/datadog'
5
6
  end
6
7
  end
data/lib/hutch/version.rb CHANGED
@@ -1,4 +1,3 @@
1
1
  module Hutch
2
- VERSION = '0.18.0'.freeze
2
+ VERSION = '1.1.0'.freeze
3
3
  end
4
-
@@ -0,0 +1,104 @@
1
+ require 'hutch/logging'
2
+
3
+ module Hutch
4
+ # Signal-handling class.
5
+ #
6
+ # Currently, the signal USR2 performs a thread dump,
7
+ # while QUIT, TERM and INT all perform a graceful shutdown.
8
+ class Waiter
9
+ include Logging
10
+
11
+ class ContinueProcessingSignals < RuntimeError
12
+ end
13
+
14
+ def self.supported_signals_of(list)
15
+ list.keep_if { |s| Signal.list.keys.include?(s) }.tap do |result|
16
+ result.delete('QUIT') if defined?(JRUBY_VERSION)
17
+ end
18
+ end
19
+
20
+ SHUTDOWN_SIGNALS = supported_signals_of(%w(QUIT TERM INT)).freeze
21
+ # We have chosen a JRuby-supported signal
22
+ USER_SIGNALS = supported_signals_of(%w(USR2)).freeze
23
+ REGISTERED_SIGNALS = (SHUTDOWN_SIGNALS + USER_SIGNALS).freeze
24
+
25
+ def self.wait_until_signaled
26
+ new.wait_until_signaled
27
+ end
28
+
29
+ def wait_until_signaled
30
+ self.sig_read, self.sig_write = IO.pipe
31
+
32
+ register_signal_handlers
33
+
34
+ begin
35
+ wait_for_signal
36
+
37
+ sig = sig_read.gets.strip
38
+ handle_signal(sig)
39
+ rescue ContinueProcessingSignals
40
+ retry
41
+ end
42
+ end
43
+
44
+ def handle_signal(sig)
45
+ raise ContinueProcessingSignals unless REGISTERED_SIGNALS.include?(sig)
46
+ if user_signal?(sig)
47
+ handle_user_signal(sig)
48
+ else
49
+ handle_shutdown_signal(sig)
50
+ end
51
+ end
52
+
53
+ # @raise ContinueProcessingSignals
54
+ def handle_user_signal(sig)
55
+ case sig
56
+ when 'USR2' then log_thread_backtraces
57
+ else raise "Assertion failed - unhandled signal: #{sig.inspect}"
58
+ end
59
+ raise ContinueProcessingSignals
60
+ end
61
+
62
+ def handle_shutdown_signal(sig)
63
+ logger.info "caught SIG#{sig}, stopping hutch..."
64
+ end
65
+
66
+ private
67
+
68
+ def log_thread_backtraces
69
+ logger.info 'Requested a VM-wide thread stack trace dump...'
70
+ Thread.list.each do |thread|
71
+ logger.info "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
72
+ logger.info backtrace_for(thread)
73
+ end
74
+ end
75
+
76
+ def backtrace_for(thread)
77
+ if thread.backtrace
78
+ thread.backtrace.join("\n")
79
+ else
80
+ '<no backtrace available>'
81
+ end
82
+ end
83
+
84
+ attr_accessor :sig_read, :sig_write
85
+
86
+ def wait_for_signal
87
+ IO.select([sig_read])
88
+ end
89
+
90
+ def register_signal_handlers
91
+ REGISTERED_SIGNALS.each do |sig|
92
+ # This needs to be reentrant, so we queue up signals to be handled
93
+ # in the run loop, rather than acting on signals here
94
+ trap(sig) do
95
+ sig_write.puts(sig)
96
+ end
97
+ end
98
+ end
99
+
100
+ def user_signal?(sig)
101
+ USER_SIGNALS.include?(sig)
102
+ end
103
+ end
104
+ end
data/lib/hutch/worker.rb CHANGED
@@ -1,15 +1,19 @@
1
1
  require 'hutch/message'
2
2
  require 'hutch/logging'
3
3
  require 'hutch/broker'
4
+ require 'hutch/acknowledgements/nack_on_all_failures'
5
+ require 'hutch/waiter'
4
6
  require 'carrot-top'
7
+ require 'securerandom'
5
8
 
6
9
  module Hutch
7
10
  class Worker
8
11
  include Logging
9
12
 
10
- def initialize(broker, consumers)
13
+ def initialize(broker, consumers, setup_procs)
11
14
  @broker = broker
12
15
  self.consumers = consumers
16
+ self.setup_procs = setup_procs
13
17
  end
14
18
 
15
19
  # Run the main event loop. The consumers will be set up with queues, and
@@ -17,46 +21,11 @@ module Hutch
17
21
  # never returns.
18
22
  def run
19
23
  setup_queues
24
+ setup_procs.each(&:call)
20
25
 
21
- # Set up signal handlers for graceful shutdown
22
- register_signal_handlers
26
+ Waiter.wait_until_signaled
23
27
 
24
- main_loop
25
- end
26
-
27
- def main_loop
28
- if defined?(JRUBY_VERSION)
29
- # Binds shutdown listener to notify main thread if channel was closed
30
- bind_shutdown_handler
31
-
32
- handle_signals until shutdown_not_called?(0.1)
33
- else
34
- # Take a break from Thread#join every 0.1 seconds to check if we've
35
- # been sent any signals
36
- handle_signals until @broker.wait_on_threads(0.1)
37
- end
38
- end
39
-
40
- # Register handlers for SIG{QUIT,TERM,INT} to shut down the worker
41
- # gracefully. Forceful shutdowns are very bad!
42
- def register_signal_handlers
43
- Thread.main[:signal_queue] = []
44
- %w(QUIT TERM INT).keep_if { |s| Signal.list.keys.include? s }.map(&:to_sym).each do |sig|
45
- # This needs to be reentrant, so we queue up signals to be handled
46
- # in the run loop, rather than acting on signals here
47
- trap(sig) do
48
- Thread.main[:signal_queue] << sig
49
- end
50
- end
51
- end
52
-
53
- # Handle any pending signals
54
- def handle_signals
55
- signal = Thread.main[:signal_queue].shift
56
- if signal
57
- logger.info "caught sig#{signal.downcase}, stopping hutch..."
58
- stop
59
- end
28
+ stop
60
29
  end
61
30
 
62
31
  # Stop a running worker by killing all subscriber threads.
@@ -64,36 +33,24 @@ module Hutch
64
33
  @broker.stop
65
34
  end
66
35
 
67
- # Binds shutdown handler, called if channel is closed or network Failed
68
- def bind_shutdown_handler
69
- @broker.channel.on_shutdown do
70
- Thread.main[:shutdown_received] = true
71
- end
72
- end
73
-
74
- # Checks if shutdown handler was called, then sleeps for interval
75
- def shutdown_not_called?(interval)
76
- if Thread.main[:shutdown_received]
77
- true
78
- else
79
- sleep(interval)
80
- false
81
- end
82
- end
83
-
84
36
  # Set up the queues for each of the worker's consumers.
85
37
  def setup_queues
86
38
  logger.info 'setting up queues'
87
- @consumers.each { |consumer| setup_queue(consumer) }
39
+ vetted = @consumers.reject { |c| group_configured? && group_restricted?(c) }
40
+ vetted.each do |c|
41
+ setup_queue(c)
42
+ end
88
43
  end
89
44
 
90
45
  # Bind a consumer's routing keys to its queue, and set up a subscription to
91
46
  # receive messages sent to the queue.
92
47
  def setup_queue(consumer)
48
+ logger.info "setting up queue: #{consumer.get_queue_name}"
49
+
93
50
  queue = @broker.queue(consumer.get_queue_name, consumer.get_arguments)
94
51
  @broker.bind_queue(queue, consumer.routing_keys)
95
52
 
96
- queue.subscribe(manual_ack: true) do |*args|
53
+ queue.subscribe(consumer_tag: unique_consumer_tag, manual_ack: true) do |*args|
97
54
  delivery_info, properties, payload = Hutch::Adapter.decode_message(*args)
98
55
  handle_message(consumer, delivery_info, properties, payload)
99
56
  end
@@ -102,30 +59,39 @@ module Hutch
102
59
  # Called internally when a new messages comes in from RabbitMQ. Responsible
103
60
  # for wrapping up the message and passing it to the consumer.
104
61
  def handle_message(consumer, delivery_info, properties, payload)
105
- logger.info("message(#{properties.message_id || '-'}): " +
106
- "routing key: #{delivery_info.routing_key}, " +
107
- "consumer: #{consumer}, " +
108
- "payload: #{payload}")
109
-
110
- broker = @broker
111
- begin
112
- message = Message.new(delivery_info, properties, payload)
113
- consumer_instance = consumer.new.tap { |c| c.broker, c.delivery_info = @broker, delivery_info }
114
- with_tracing(consumer_instance).handle(message)
115
- broker.ack(delivery_info.delivery_tag)
116
- rescue StandardError => ex
117
- broker.nack(delivery_info.delivery_tag)
118
- handle_error(properties.message_id, payload, consumer, ex)
119
- end
62
+ serializer = consumer.get_serializer || Hutch::Config[:serializer]
63
+ logger.debug {
64
+ spec = serializer.binary? ? "#{payload.bytesize} bytes" : "#{payload}"
65
+ "message(#{properties.message_id || '-'}): " +
66
+ "routing key: #{delivery_info.routing_key}, " +
67
+ "consumer: #{consumer}, " +
68
+ "payload: #{spec}"
69
+ }
70
+
71
+ message = Message.new(delivery_info, properties, payload, serializer)
72
+ consumer_instance = consumer.new.tap { |c| c.broker, c.delivery_info = @broker, delivery_info }
73
+ with_tracing(consumer_instance).handle(message)
74
+ @broker.ack(delivery_info.delivery_tag)
75
+ rescue => ex
76
+ acknowledge_error(delivery_info, properties, @broker, ex)
77
+ handle_error(properties, payload, consumer, ex)
120
78
  end
121
79
 
122
80
  def with_tracing(klass)
123
81
  Hutch::Config[:tracer].new(klass)
124
82
  end
125
83
 
126
- def handle_error(message_id, payload, consumer, ex)
84
+ def handle_error(*args)
127
85
  Hutch::Config[:error_handlers].each do |backend|
128
- backend.handle(message_id, payload, consumer, ex)
86
+ backend.handle(*args)
87
+ end
88
+ end
89
+
90
+ def acknowledge_error(delivery_info, properties, broker, ex)
91
+ acks = error_acknowledgements +
92
+ [Hutch::Acknowledgements::NackOnAllFailures.new]
93
+ acks.find do |backend|
94
+ backend.handle(delivery_info, properties, broker, ex)
129
95
  end
130
96
  end
131
97
 
@@ -135,5 +101,45 @@ module Hutch
135
101
  end
136
102
  @consumers = val
137
103
  end
104
+
105
+ def error_acknowledgements
106
+ Hutch::Config[:error_acknowledgements]
107
+ end
108
+
109
+ private
110
+
111
+ def group_configured?
112
+ if group.present? && consumer_groups.blank?
113
+ logger.info 'Consumer groups are blank'
114
+ end
115
+ group.present?
116
+ end
117
+
118
+ def group_restricted?(consumer)
119
+ consumers_to_load = consumer_groups[group]
120
+ if consumers_to_load
121
+ !consumers_to_load.include?(consumer.name)
122
+ else
123
+ true
124
+ end
125
+ end
126
+
127
+ def group
128
+ Hutch::Config[:group]
129
+ end
130
+
131
+ def consumer_groups
132
+ Hutch::Config[:consumer_groups]
133
+ end
134
+
135
+ attr_accessor :setup_procs
136
+
137
+ def unique_consumer_tag
138
+ prefix = Hutch::Config[:consumer_tag_prefix]
139
+ unique_part = SecureRandom.uuid
140
+ "#{prefix}-#{unique_part}".tap do |tag|
141
+ raise "Tag must be 255 bytes long at most, current one is #{tag.bytesize} ('#{tag}')" if tag.bytesize > 255
142
+ end
143
+ end
138
144
  end
139
145
  end
data/lib/hutch.rb CHANGED
@@ -3,6 +3,8 @@ require 'hutch/consumer'
3
3
  require 'hutch/worker'
4
4
  require 'hutch/broker'
5
5
  require 'hutch/logging'
6
+ require 'hutch/serializers/identity'
7
+ require 'hutch/serializers/json'
6
8
  require 'hutch/config'
7
9
  require 'hutch/message'
8
10
  require 'hutch/cli'
@@ -12,6 +14,7 @@ require 'hutch/exceptions'
12
14
  require 'hutch/tracers'
13
15
 
14
16
  module Hutch
17
+ @@connection_mutex = Mutex.new
15
18
 
16
19
  def self.register_consumer(consumer)
17
20
  self.consumers << consumer
@@ -33,10 +36,17 @@ module Hutch
33
36
  @global_properties ||= {}
34
37
  end
35
38
 
39
+ # Connects to broker, if not yet connected.
40
+ #
41
+ # @param options [Hash] Connection options
42
+ # @param config [Hash] Configuration
43
+ # @option options [Boolean] :enable_http_api_use
36
44
  def self.connect(options = {}, config = Hutch::Config)
37
- unless connected?
38
- @broker = Hutch::Broker.new(config)
39
- @broker.connect(options)
45
+ @@connection_mutex.synchronize do
46
+ unless connected?
47
+ @broker = Hutch::Broker.new(config)
48
+ @broker.connect(options)
49
+ end
40
50
  end
41
51
  end
42
52
 
@@ -48,10 +58,9 @@ module Hutch
48
58
  @broker
49
59
  end
50
60
 
61
+ # @return [Boolean]
51
62
  def self.connected?
52
- return false unless broker
53
- return false unless broker.connection
54
- broker.connection.open?
63
+ broker && broker.connection && broker.connection.open?
55
64
  end
56
65
 
57
66
  def self.publish(*args)