message-driver 0.2.1 → 0.2.2

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: 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