message-driver 0.2.1 → 0.2.2

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: 2f5ef960a3a18031fa4f37ed941eae929da2237d
4
- data.tar.gz: 699dee982c1d805fde348b8af5354df212cabc03
3
+ metadata.gz: 2f6ca18a14ba62a965bc18eac1072208821266e6
4
+ data.tar.gz: dbed32cf2ad5170579f1e013c85c79a94f0264e0
5
5
  SHA512:
6
- metadata.gz: 362dffea89638f6366ecf26e673c510aa40981e77f590470e377d51b87e07271caa151bcc8d04b948e1dbe7d1a9268b7cba57fa646328645e8ab58b318ea31cc
7
- data.tar.gz: b044a83acfc3be5a1a6760eb1543cca63f6cf4b08f07a9a3a0e697bdd152e1aa546580a628351986fcbc2156c6c1d2724d101632c18366cecefd6b85750e80e1
6
+ metadata.gz: 9aae643f4f2396e55fbb848770530832ad4729e7fe566219f2c178f22e199adda8d19e70f766b248043f9c2a496909f4b343c575c303ab87ed105162c7138688
7
+ data.tar.gz: 85430c090e5667abe21e58207bcfaafc7e1d9122c260b2c2b50fc0956d8fa645c730db0ef2e678d0f1dc7d23e2a10630b978717f7051ea11dcfab5f3be9e0c93
data/.travis.yml CHANGED
@@ -13,12 +13,13 @@ env:
13
13
  - ADAPTER=stomp
14
14
  - ADAPTER=bunny:1.0.0
15
15
  rvm:
16
+ - 2.1.0
16
17
  - 2.0.0
17
18
  - 1.9.3
18
19
  - 1.9.2
19
20
  - jruby-19mode
20
21
  - jruby-head
21
- - rbx-19mode
22
+ - rbx
22
23
  matrix:
23
24
  exclude:
24
25
  - rvm: jruby-19mode
@@ -26,8 +27,7 @@ matrix:
26
27
  - rvm: jruby-head
27
28
  env: ADAPTER=bunny:0.9.0
28
29
  allow_failures:
29
- - env: ADAPTER=bunny:1.0.0.rc
30
30
  - rvm: jruby-head
31
- - rvm: rbx-19mode
31
+ - rvm: rbx
32
32
  - rvm: jruby-19mode
33
33
  env: ADAPTER=stomp
data/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## 0.3.0.dev - master
4
4
 
5
+ ## 0.2.2 - 2014-02-21
6
+
7
+ * Lots of work on reconnection handling for bunny. Still a work in
8
+ progress.
9
+
5
10
  ## 0.2.1 - 2013-11-13
6
11
 
7
12
  * Correct an issue in handling Bunny::ConnectionLevelErrors.
data/Gemfile CHANGED
@@ -3,15 +3,22 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in message-driver.gemspec
4
4
  gemspec
5
5
 
6
+ platform :rbx do
7
+ gem 'rubysl'
8
+ end
9
+
6
10
  group :tools do
7
11
  gem 'guard'
8
12
  gem 'guard-bundler'
9
13
  gem 'guard-rspec'
10
14
  gem 'guard-cucumber'
11
15
  gem 'pry'
12
- platform :ruby do
16
+ platform :ruby_19 do
13
17
  gem 'pry-debugger'
14
18
  end
19
+ platform :ruby_20 do
20
+ gem 'pry-byebug'
21
+ end
15
22
  group :darwin do
16
23
  gem 'ruby_gntp'
17
24
  gem 'rb-fsevent'
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## 0.3.0.dev - master
4
4
 
5
+ ## 0.2.2 - 2014-02-21
6
+
7
+ * Lots of work on reconnection handling for bunny. Still a work in
8
+ progress.
9
+
5
10
  ## 0.2.1 - 2013-11-13
6
11
 
7
12
  * Correct an issue in handling Bunny::ConnectionLevelErrors.
@@ -0,0 +1,19 @@
1
+ require 'bunny'
2
+
3
+ module Bunny
4
+ class Session
5
+ def cleanup_threads
6
+ log_errors { self.maybe_shutdown_heartbeat_sender }
7
+ log_errors { self.maybe_shutdown_reader_loop }
8
+ log_errors { self.close_transport }
9
+ end
10
+
11
+ def log_errors
12
+ begin
13
+ yield
14
+ rescue => e
15
+ @logger.info "#{e.class}: #{e.to_s}"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -3,14 +3,15 @@ module MessageDriver
3
3
  class Base
4
4
  include Logging
5
5
 
6
- attr_accessor :contexts
6
+ def contexts
7
+ @contexts ||= []
8
+ end
7
9
 
8
10
  def initialize(configuration)
9
11
  raise "Must be implemented in subclass"
10
12
  end
11
13
 
12
14
  def new_context
13
- @contexts ||= []
14
15
  ctx = build_context
15
16
  contexts << ctx
16
17
  ctx
@@ -21,7 +22,11 @@ module MessageDriver
21
22
  end
22
23
 
23
24
  def stop
24
- contexts.each { |ctx| ctx.invalidate } if contexts
25
+ if @contexts
26
+ ctxs = @contexts
27
+ @contexts = []
28
+ ctxs.each { |ctx| ctx.invalidate }
29
+ end
25
30
  end
26
31
  end
27
32
 
@@ -1,4 +1,5 @@
1
1
  require 'bunny'
2
+ require 'bunny/session_patch'
2
3
 
3
4
  module MessageDriver
4
5
  class Broker
@@ -116,66 +117,75 @@ module MessageDriver
116
117
  raise MessageDriver::Error, "subscriptions are only supported with QueueDestinations" unless destination.is_a? QueueDestination
117
118
  @sub_ctx = adapter.new_subscription_context(self)
118
119
  @error_handler = options[:error_handler]
120
+ @ack_mode = case options[:ack]
121
+ when :auto, nil
122
+ :auto
123
+ when :manual
124
+ :manual
125
+ when :transactional
126
+ :transactional
127
+ else
128
+ raise MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
129
+ end
130
+ start_subscription
131
+ end
132
+
133
+ def unsubscribe
134
+ unless @bunny_consumer.nil?
135
+ @bunny_consumer.cancel
136
+ @bunny_consumer = nil
137
+ end
138
+ unless @sub_ctx.nil?
139
+ @sub_ctx.invalidate(true)
140
+ @sub_ctx = nil
141
+ end
142
+ end
143
+
144
+ private
145
+ def start_subscription
119
146
  @sub_ctx.with_channel do |ch|
120
147
  queue = destination.bunny_queue(@sub_ctx.channel)
121
148
  if options.has_key? :prefetch_size
122
149
  ch.prefetch(options[:prefetch_size])
123
150
  end
124
- ack_mode = case options[:ack]
125
- when :auto, nil
126
- :auto
127
- when :manual
128
- :manual
129
- when :transactional
130
- :transactional
131
- else
132
- raise MessageDriver::Error, "unrecognized :ack option #{options[:ack]}"
133
- end
134
151
  @bunny_consumer = queue.subscribe(options.merge(manual_ack: true)) do |delivery_info, properties, payload|
135
- message = @sub_ctx.args_to_message(delivery_info, properties, payload)
136
152
  Client.with_adapter_context(@sub_ctx) do
137
- begin
138
- case ack_mode
139
- when :auto
140
- consumer.call(message)
141
- @sub_ctx.ack_message(message)
142
- when :manual
143
- consumer.call(message)
144
- when :transactional
145
- Client.with_message_transaction do
146
- consumer.call(message)
147
- @sub_ctx.ack_message(message)
148
- end
149
- end
150
- rescue => e
151
- if [:auto, :transactional].include? ack_mode
152
- requeue = true
153
- if e.is_a?(DontRequeue) || (options[:retry_redelivered] == false && message.redelivered?)
154
- requeue = false
155
- end
156
- if @sub_ctx.valid?
157
- begin
158
- @sub_ctx.nack_message(message, requeue: requeue)
159
- rescue => e
160
- logger.error exception_to_str(e)
161
- end
162
- end
163
- end
164
- @error_handler.call(e, message) unless @error_handler.nil?
165
- end
153
+ message = @sub_ctx.args_to_message(delivery_info, properties, payload)
154
+ handle_message(message)
166
155
  end
167
156
  end
168
157
  end
169
158
  end
170
159
 
171
- def unsubscribe
172
- unless @bunny_consumer.nil?
173
- @bunny_consumer.cancel
174
- @bunny_consumer = nil
175
- end
176
- unless @sub_ctx.nil?
177
- @sub_ctx.invalidate(true)
178
- @sub_ctx = nil
160
+ def handle_message(message)
161
+ begin
162
+ case @ack_mode
163
+ when :auto
164
+ consumer.call(message)
165
+ @sub_ctx.ack_message(message)
166
+ when :manual
167
+ consumer.call(message)
168
+ when :transactional
169
+ Client.with_message_transaction do
170
+ consumer.call(message)
171
+ @sub_ctx.ack_message(message)
172
+ end
173
+ end
174
+ rescue => e
175
+ if [:auto, :transactional].include? @ack_mode
176
+ requeue = true
177
+ if e.is_a?(DontRequeue) || (options[:retry_redelivered] == false && message.redelivered?)
178
+ requeue = false
179
+ end
180
+ if @sub_ctx.valid?
181
+ begin
182
+ @sub_ctx.nack_message(message, requeue: requeue)
183
+ rescue => e
184
+ logger.error exception_to_str(e)
185
+ end
186
+ end
187
+ end
188
+ @error_handler.call(e, message) unless @error_handler.nil?
179
189
  end
180
190
  end
181
191
  end
@@ -183,12 +193,13 @@ module MessageDriver
183
193
  def initialize(config)
184
194
  validate_bunny_version
185
195
  @config = config
186
- start_connection_thread
196
+ @handle_connection_errors = config.fetch(:handle_connection_errors, true)
197
+ initialize_connection
187
198
  end
188
199
 
189
200
  def connection(ensure_started=true)
190
- start_connection_thread
191
- if ensure_started && !@connection.open?
201
+ initialize_connection
202
+ if ensure_started
192
203
  begin
193
204
  @connection.start
194
205
  rescue *NETWORK_ERRORS => e
@@ -204,6 +215,10 @@ module MessageDriver
204
215
  @connection.close if !@connection.nil? && @connection.open?
205
216
  rescue *NETWORK_ERRORS => e
206
217
  logger.error "error while attempting connection close\n#{exception_to_str(e)}"
218
+ ensure
219
+ conn = @connection
220
+ @connection = nil
221
+ conn.cleanup_threads unless conn.nil?
207
222
  end
208
223
  end
209
224
 
@@ -340,10 +355,20 @@ module MessageDriver
340
355
  def invalidate(in_unsubscribe=false)
341
356
  super()
342
357
  unless @subscription.nil? || in_unsubscribe
343
- @subscription.unsubscribe
358
+ begin
359
+ @subscription.unsubscribe if adapter.connection.open?
360
+ rescue => e
361
+ logger.debug "error trying to end subscription\n#{exception_to_str(e)}"
362
+ end
344
363
  end
345
364
  unless @channel.nil?
346
- @channel.close if @channel.open?
365
+ begin
366
+ @channel.close if @channel.open? && adapter.connection.open?
367
+ rescue => e
368
+ logger.debug "error trying to close channel\n#{exception_to_str(e)}"
369
+ ensure
370
+ begin @channel.maybe_kill_consumer_work_pool! rescue nil; end
371
+ end
347
372
  end
348
373
  end
349
374
 
@@ -389,7 +414,7 @@ module MessageDriver
389
414
 
390
415
  def reset_channel
391
416
  unless @channel.open?
392
- @channel.open
417
+ @channel = adapter.connection.create_channel
393
418
  @is_transactional = false
394
419
  @rollback_only = true if in_transaction?
395
420
  end
@@ -399,27 +424,44 @@ module MessageDriver
399
424
 
400
425
  private
401
426
 
402
- def start_connection_thread
403
- @connection_thread ||= Thread.new do
404
- begin
405
- @connection = Bunny.new(@config)
406
- sleep
407
- rescue *NETWORK_ERRORS => e
408
- logger.error "error on connection\n#{exception_to_str(e)}"
409
- begin
410
- while true
411
- @connection.start
427
+ def log_errors
428
+ begin
429
+ yield
430
+ rescue => e
431
+ logger.error exception_to_str(e)
432
+ end
433
+ end
434
+
435
+ def initialize_connection
436
+ if @handle_connection_errors
437
+ if @connection_thread.nil?
438
+ #hi mom!
439
+ @connection_thread = Thread.new do
440
+ @connection = Bunny.new(@config)
441
+ begin
412
442
  sleep
443
+ rescue *NETWORK_ERRORS => e
444
+ logger.error "error on connection\n#{exception_to_str(e)}"
445
+ if @connection.automatically_recover?
446
+ sleep 0.1
447
+ unless @connection.recovering_from_network_failure?
448
+ stop
449
+ end
450
+ else
451
+ stop
452
+ end
453
+ retry
454
+ rescue => e
455
+ logger.error "unhandled error in connection thread! #{exception_to_str(e)}"
413
456
  end
414
- rescue *NETWORK_ERRORS => e
415
- logger.error "error trying to restart connection\n#{exception_to_str(e)}"
416
- sleep 1
417
- retry
418
457
  end
458
+ @connection_thread.abort_on_exception = true
459
+ sleep 0.1
419
460
  end
420
- @connection_thread = nil
461
+ sleep 0.1 while @connection_thread.status != 'sleep'
462
+ else
463
+ @connection ||= Bunny.new(@config)
421
464
  end
422
- sleep 0.1 while @connection_thread.status != 'sleep'
423
465
  end
424
466
 
425
467
  def validate_bunny_version
@@ -7,8 +7,6 @@ module MessageDriver
7
7
 
8
8
  attr_reader :adapter, :configuration, :destinations, :consumers, :logger
9
9
 
10
- def_delegators :@adapter, :stop
11
-
12
10
  class << self
13
11
  def configure(options)
14
12
  @instance = new(options)
@@ -29,6 +27,7 @@ module MessageDriver
29
27
 
30
28
  def initialize(options)
31
29
  @adapter = resolve_adapter(options[:adapter], options)
30
+ @stopped = false
32
31
  @configuration = options
33
32
  @destinations = {}
34
33
  @consumers = {}
@@ -36,6 +35,23 @@ module MessageDriver
36
35
  logger.debug "MessageDriver configured successfully!"
37
36
  end
38
37
 
38
+ def stop
39
+ @adapter.stop
40
+ @stopped = true
41
+ end
42
+
43
+ def stopped?
44
+ @stopped
45
+ end
46
+
47
+ def restart
48
+ unless stopped?
49
+ @adapter.stop
50
+ end
51
+ @adapter = resolve_adapter(@configuration[:adapter], @configuration)
52
+ @stopped = false
53
+ end
54
+
39
55
  def dynamic_destination(dest_name, dest_options={}, message_props={})
40
56
  Client.dynamic_destination(dest_name, dest_options, message_props)
41
57
  end
@@ -5,7 +5,7 @@ module MessageDriver
5
5
  end
6
6
 
7
7
  def exception_to_str(e)
8
- ([e.to_s] + e.backtrace).join(" \n")
8
+ (["#{e.class}: #{e.to_s}"] + e.backtrace).join("\n ")
9
9
  end
10
10
  end
11
11
  end
@@ -1,6 +1,7 @@
1
1
  module MessageDriver
2
2
  module Subscription
3
3
  class Base
4
+ include Logging
4
5
  attr_reader :adapter, :destination, :consumer, :options
5
6
 
6
7
  def initialize(adapter, destination, consumer, options={})
@@ -1,5 +1,5 @@
1
1
  module Message
2
2
  module Driver
3
- VERSION = "0.2.1"
3
+ VERSION = "0.2.2"
4
4
  end
5
5
  end
@@ -74,6 +74,65 @@ module MessageDriver
74
74
  described_class.new(adapter: adapter)
75
75
  }.to raise_error(/adapter must be a MessageDriver::Adapters::Base/)
76
76
  end
77
+
78
+ it "starts off with the adapter not stopped" do
79
+ adapter = :in_memory
80
+
81
+ instance = described_class.new(adapter: adapter)
82
+ expect(instance).not_to be_stopped
83
+ end
84
+ end
85
+
86
+ describe "#stop" do
87
+ let(:adapter) { broker.adapter }
88
+ it "calls stop on the adapter" do
89
+ allow(adapter).to receive(:stop).and_call_original
90
+
91
+ subject.stop
92
+
93
+ expect(adapter).to have_received(:stop)
94
+ end
95
+
96
+ it "marks the broker as stopped" do
97
+ expect {
98
+ subject.stop
99
+ }.to change { subject.stopped? }.from(false).to(true)
100
+ end
101
+
102
+ it "invalidates the contexts" do
103
+ my_ctx = double("context", invalidate: nil)
104
+ adapter.contexts << my_ctx
105
+ subject.stop
106
+ expect(adapter.contexts).to be_empty
107
+ expect(my_ctx).to have_received(:invalidate)
108
+ end
109
+ end
110
+
111
+ describe "#restart" do
112
+ let!(:original_adapter) { subject.adapter }
113
+ before do
114
+ allow(original_adapter).to receive(:stop).and_call_original
115
+ end
116
+
117
+ it "reconfigures the adapter" do
118
+ expect {
119
+ subject.restart
120
+ }.to change { subject.adapter }
121
+ end
122
+
123
+ it "stops the adapter if it hasn't already been stopped" do
124
+ subject.restart
125
+ expect(original_adapter).to have_received(:stop).once
126
+ end
127
+
128
+ it "does not stop the adapter again if it has already been stopped" do
129
+ expect(subject.adapter).to be original_adapter
130
+ subject.stop
131
+ expect {
132
+ subject.restart
133
+ }.to change { subject.stopped? }.from(true).to(false)
134
+ expect(original_adapter).to have_received(:stop).once
135
+ end
77
136
  end
78
137
 
79
138
  describe "#configuration" do
metadata CHANGED
@@ -1,69 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: message-driver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Campbell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-13 00:00:00.000000000 Z
11
+ date: 2014-02-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
33
  version: 2.14.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: 2.14.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: cucumber
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: aruba
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - '>='
59
+ - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
69
  description: Easy message queues for ruby using AMQ, STOMP and others
@@ -73,10 +73,10 @@ executables: []
73
73
  extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
- - .gitignore
77
- - .relish
78
- - .rspec
79
- - .travis.yml
76
+ - ".gitignore"
77
+ - ".relish"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
80
  - CHANGELOG.md
81
81
  - Gemfile
82
82
  - Guardfile
@@ -124,6 +124,7 @@ files:
124
124
  - features/support/no_error_matcher.rb
125
125
  - features/support/test_runner.rb
126
126
  - features/support/transforms.rb
127
+ - lib/bunny/session_patch.rb
127
128
  - lib/message-driver.rb
128
129
  - lib/message_driver.rb
129
130
  - lib/message_driver/adapters/base.rb
@@ -172,17 +173,17 @@ require_paths:
172
173
  - lib
173
174
  required_ruby_version: !ruby/object:Gem::Requirement
174
175
  requirements:
175
- - - '>='
176
+ - - ">="
176
177
  - !ruby/object:Gem::Version
177
178
  version: 1.9.2
178
179
  required_rubygems_version: !ruby/object:Gem::Requirement
179
180
  requirements:
180
- - - '>='
181
+ - - ">="
181
182
  - !ruby/object:Gem::Version
182
183
  version: '0'
183
184
  requirements: []
184
185
  rubyforge_project:
185
- rubygems_version: 2.0.3
186
+ rubygems_version: 2.2.0
186
187
  signing_key:
187
188
  specification_version: 4
188
189
  summary: Easy message queues for ruby