action_subscriber 5.1.5 → 5.2.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
  SHA256:
3
- metadata.gz: a596e1dcbba93eacacb9d650f0853ffdb1837a3dc36daf5255409d85a9dd52a4
4
- data.tar.gz: f85747ecbb7868d6d0ec0ff01afe9dda45f4fb8ea0a1d8a5f7781cd482e04714
3
+ metadata.gz: eb97087f5a6958b61a4d441245e224c601dc9cb1424251f86aacca5700e327a8
4
+ data.tar.gz: de4d85f2ba2e8aab277d07a51966ef60b6a639d0ff0dc65e2bac2adda2cfd389
5
5
  SHA512:
6
- metadata.gz: 86a8e02b695d8b9a9e8a0cf561b66f2ddcd1a4e8d8471f83143f833db1109b845f0fe42baa20b620628109721d9f1ad059e9ea3e1ac4abfe34548010fd7e3b7c
7
- data.tar.gz: 334ca4005d7f67ddf6579b97488fd7220f8b695f7668098f303ae62ea0a68b188f41da60ea093c1c97a5da39cf7962ab74165a61058ff3e3df308151976e742a
6
+ metadata.gz: 3c1890c8861acdc67a4f6e1d3865980ef497820dd2b02038321bebab9248fee398176097c7f8d4d92332144719ae610d867ddbef93862ecc0e66781992b42036
7
+ data.tar.gz: d8149dbee4e94533b2bc758addf25f66ef8b0c872d3d919dc685486e482ab5127f54fedccd130dd1532809e4bc847c6e381dc786266edbcf102fea5bb84ed22d
data/.travis.yml CHANGED
@@ -4,11 +4,10 @@ os:
4
4
  dist:
5
5
  trusty
6
6
  rvm:
7
- - 2.2.6
8
- - 2.3.3
9
- - jruby-9.0.5.0
10
- - jruby-9.1.7.0
7
+ - 2.3.8
8
+ - 2.5.7
11
9
  - jruby-9.1.12.0
10
+ - jruby-9.2.7.0
12
11
  - jruby-head
13
12
  services:
14
13
  - rabbitmq
data/README.md CHANGED
@@ -135,6 +135,7 @@ Other configuration options include :
135
135
  * config.network_recovery_interval - reconnection interval for TCP connection failures (default 1)
136
136
  * config.password - RabbitMQ password (default "guest")
137
137
  * config.prefetch - number of messages to hold in the local queue in subscriber mode
138
+ * config.resubscribe_on_consumer_cancellation - resubscribe when the consumer is cancelled (queue deleted or cluster fails, default true)
138
139
  * config.seconds_to_wait_for_graceful_shutdown - time to wait before force stopping server after shutdown signal
139
140
  * config.threadpool_size - set the number of threads available to action_subscriber
140
141
  * config.timeout - how many seconds to allow rabbit to respond before timing out
@@ -26,35 +26,52 @@ module ActionSubscriber
26
26
 
27
27
  def start_subscribers!
28
28
  subscriptions.each do |subscription|
29
- route = subscription[:route]
30
- queue = subscription[:queue]
31
- channel = queue.channel
32
- threadpool = ::ActionSubscriber::ThreadPools.threadpools.fetch(route.threadpool_name)
33
- channel.prefetch(route.prefetch) if route.acknowledgements?
34
- consumer = ::Bunny::Consumer.new(channel, queue, channel.generate_consumer_tag, !route.acknowledgements?)
35
- consumer.on_delivery do |delivery_info, properties, encoded_payload|
36
- ::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
37
- properties = {
38
- :action => route.action,
39
- :channel => queue.channel,
40
- :content_type => properties.content_type,
41
- :delivery_tag => delivery_info.delivery_tag,
42
- :exchange => delivery_info.exchange,
43
- :headers => properties.headers,
44
- :message_id => properties.message_id,
45
- :routing_key => delivery_info.routing_key,
46
- :queue => queue.name,
47
- :uses_acknowledgements => route.acknowledgements?,
48
- }
49
- env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
50
- run_env(env, threadpool)
51
- end
52
- bunny_consumers << consumer
53
- queue.subscribe_with(consumer)
29
+ start_subscriber_for_subscription(subscription)
54
30
  end
55
31
  end
56
32
 
57
- private
33
+ private
34
+
35
+ def start_subscriber_for_subscription(subscription)
36
+ route = subscription[:route]
37
+ queue = subscription[:queue]
38
+ channel = queue.channel
39
+ threadpool = ::ActionSubscriber::ThreadPools.threadpools.fetch(route.threadpool_name)
40
+ channel.prefetch(route.prefetch) if route.acknowledgements?
41
+ consumer = ::Bunny::Consumer.new(channel, queue, channel.generate_consumer_tag, !route.acknowledgements?)
42
+
43
+ if ::ActionSubscriber.configuration.resubscribe_on_consumer_cancellation
44
+ # Add cancellation callback to rebuild subscriber on cancel.
45
+ consumer.on_cancellation do
46
+ ::ActionSubscriber.logger.warn "Cancelation received for queue consumer: #{queue.name}, rebuilding subscription..."
47
+ bunny_consumers.delete(consumer)
48
+ channel.close
49
+ queue = subscription[:queue] = setup_queue(route)
50
+ start_subscriber_for_subscription(subscription)
51
+ end
52
+ end
53
+
54
+ consumer.on_delivery do |delivery_info, properties, encoded_payload|
55
+ ::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
56
+ properties = {
57
+ :action => route.action,
58
+ :channel => queue.channel,
59
+ :content_type => properties.content_type,
60
+ :delivery_tag => delivery_info.delivery_tag,
61
+ :exchange => delivery_info.exchange,
62
+ :headers => properties.headers,
63
+ :message_id => properties.message_id,
64
+ :routing_key => delivery_info.routing_key,
65
+ :queue => queue.name,
66
+ :uses_acknowledgements => route.acknowledgements?,
67
+ }
68
+ env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
69
+ run_env(env, threadpool)
70
+ end
71
+
72
+ bunny_consumers << consumer
73
+ queue.subscribe_with(consumer)
74
+ end
58
75
 
59
76
  def setup_queue(route)
60
77
  channel = ::ActionSubscriber::RabbitConnection.with_connection{|connection| connection.create_channel(nil, 1) }
@@ -16,6 +16,7 @@ module ActionSubscriber
16
16
  :password,
17
17
  :port,
18
18
  :prefetch,
19
+ :resubscribe_on_consumer_cancellation,
19
20
  :seconds_to_wait_for_graceful_shutdown,
20
21
  :threadpool_size,
21
22
  :timeout,
@@ -42,6 +43,7 @@ module ActionSubscriber
42
43
  :password => "guest",
43
44
  :port => 5672,
44
45
  :prefetch => 2,
46
+ :resubscribe_on_consumer_cancellation => true,
45
47
  :seconds_to_wait_for_graceful_shutdown => 30,
46
48
  :threadpool_size => 8,
47
49
  :timeout => 1,
@@ -4,7 +4,8 @@ module ActionSubscriber
4
4
  include ::ActionSubscriber::Logging
5
5
 
6
6
  def cancel_consumers!
7
- march_hare_consumers.each(&:cancel)
7
+ # Cancel any non-cancelled consumers.
8
+ march_hare_consumers.reject(&:cancelled?).each(&:cancel)
8
9
  ::ActionSubscriber::ThreadPools.threadpools.each do |name, threadpool|
9
10
  threadpool.shutdown
10
11
  end
@@ -26,33 +27,50 @@ module ActionSubscriber
26
27
 
27
28
  def start_subscribers!
28
29
  subscriptions.each do |subscription|
29
- route = subscription[:route]
30
- queue = subscription[:queue]
31
- queue.channel.prefetch = route.prefetch if route.acknowledgements?
32
- threadpool = ::ActionSubscriber::ThreadPools.threadpools.fetch(route.threadpool_name)
33
- consumer = queue.subscribe(route.queue_subscription_options) do |metadata, encoded_payload|
34
- ::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
35
- properties = {
36
- :action => route.action,
37
- :channel => queue.channel,
38
- :content_type => metadata.content_type,
39
- :delivery_tag => metadata.delivery_tag,
40
- :exchange => metadata.exchange,
41
- :headers => _normalized_headers(metadata),
42
- :message_id => metadata.message_id,
43
- :routing_key => metadata.routing_key,
44
- :queue => queue.name,
45
- :uses_acknowledgements => route.acknowledgements?,
46
- }
47
- env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
48
- run_env(env, threadpool)
30
+ start_subscriber_for_subscription(subscription)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def start_subscriber_for_subscription(subscription)
37
+ route = subscription[:route]
38
+ queue = subscription[:queue]
39
+ queue.channel.prefetch = route.prefetch if route.acknowledgements?
40
+ threadpool = ::ActionSubscriber::ThreadPools.threadpools.fetch(route.threadpool_name)
41
+ opts = route.queue_subscription_options
42
+
43
+ if ::ActionSubscriber.configuration.resubscribe_on_consumer_cancellation
44
+ # Add cancellation callback to rebuild subscriber on cancel.
45
+ opts[:on_cancellation] = lambda do |the_consumer|
46
+ ::ActionSubscriber.logger.warn "Cancelation received for queue consumer: #{queue.name}, rebuilding subscription..."
47
+ march_hare_consumers.delete(the_consumer)
48
+ queue.channel.close
49
+ queue = subscription[:queue] = setup_queue(route)
50
+ start_subscriber_for_subscription(subscription)
49
51
  end
52
+ end
50
53
 
51
- march_hare_consumers << consumer
54
+ consumer = queue.subscribe(opts) do |metadata, encoded_payload|
55
+ ::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
56
+ properties = {
57
+ :action => route.action,
58
+ :channel => queue.channel,
59
+ :content_type => metadata.content_type,
60
+ :delivery_tag => metadata.delivery_tag,
61
+ :exchange => metadata.exchange,
62
+ :headers => _normalized_headers(metadata),
63
+ :message_id => metadata.message_id,
64
+ :routing_key => metadata.routing_key,
65
+ :queue => queue.name,
66
+ :uses_acknowledgements => route.acknowledgements?,
67
+ }
68
+ env = ::ActionSubscriber::Middleware::Env.new(route.subscriber, encoded_payload, properties)
69
+ run_env(env, threadpool)
52
70
  end
53
- end
54
71
 
55
- private
72
+ march_hare_consumers << consumer
73
+ end
56
74
 
57
75
  def setup_queue(route)
58
76
  channel = ::ActionSubscriber::RabbitConnection.with_connection{|connection| connection.create_channel }
@@ -1,3 +1,3 @@
1
1
  module ActionSubscriber
2
- VERSION = "5.1.5"
2
+ VERSION = "5.2.0"
3
3
  end
@@ -0,0 +1,83 @@
1
+ require "spec_helper"
2
+ require "rabbitmq/http/client"
3
+
4
+ class YoloSubscriber < ActionSubscriber::Base
5
+ def created
6
+ $messages << payload
7
+ end
8
+ end
9
+
10
+ describe "Automatically handles consumer cancellation", :integration => true, :slow => true do
11
+ let(:draw_routes) do
12
+ ::ActionSubscriber.draw_routes do
13
+ default_routes_for ::YoloSubscriber
14
+ end
15
+ end
16
+ let(:http_client) { ::RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") }
17
+ let(:subscriber) { ::YoloSubscriber }
18
+
19
+ it "resubscribes on cancellation" do
20
+ ::ActionSubscriber::start_subscribers!
21
+ ::ActivePublisher.publish("yolo.created", "First", "events")
22
+ verify_expectation_within(5.0) do
23
+ expect($messages).to eq(::Set.new(["First"]))
24
+ end
25
+
26
+ consumers = rabbit_consumers.dup
27
+
28
+ # Signal a cancellation event to all subscribers.
29
+ delete_all_queues!
30
+
31
+ # Give consumers a chance to restart.
32
+ sleep 2.0
33
+
34
+ expect(rabbit_consumers).to_not eq(consumers)
35
+
36
+ ::ActivePublisher.publish("yolo.created", "Second", "events")
37
+ verify_expectation_within(5.0) do
38
+ expect($messages).to eq(Set.new(["First", "Second"]))
39
+ end
40
+ end
41
+
42
+ context "when resubscribe on consumer cancellation is disabled" do
43
+ before do
44
+ allow(::ActionSubscriber.configuration).to receive(:resubscribe_on_consumer_cancellation).and_return(false)
45
+ end
46
+
47
+ it "does not resubscribe on cancellation" do
48
+ ::ActionSubscriber::start_subscribers!
49
+ ::ActivePublisher.publish("yolo.created", "First", "events")
50
+ verify_expectation_within(5.0) do
51
+ expect($messages).to eq(::Set.new(["First"]))
52
+ end
53
+
54
+ consumers = rabbit_consumers.dup
55
+
56
+ # Signal a cancellation event to all subscribers.
57
+ delete_all_queues!
58
+
59
+ # Give consumers a chance to restart.
60
+ sleep 2.0
61
+
62
+ # Verify the consumers did not change.
63
+ expect(rabbit_consumers).to eq(consumers)
64
+
65
+ ::ActivePublisher.publish("yolo.created", "Second", "events")
66
+
67
+ # Force sleep 2 seconds to ensure a resubscribe did not happen and messages were not processed.
68
+ sleep 2.0
69
+ expect($messages).to eq(Set.new(["First"]))
70
+ end
71
+ end
72
+
73
+ def rabbit_consumers
74
+ route_set = ::ActionSubscriber.send(:route_set)
75
+ route_set.try(:bunny_consumers) || route_set.try(:march_hare_consumers)
76
+ end
77
+
78
+ def delete_all_queues!
79
+ http_client.list_queues.each do |queue|
80
+ http_client.delete_queue(queue.vhost, queue.name)
81
+ end
82
+ end
83
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: action_subscriber
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.5
4
+ version: 5.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Stien
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2020-01-10 00:00:00.000000000 Z
15
+ date: 2020-05-15 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: activesupport
@@ -258,6 +258,7 @@ files:
258
258
  - spec/integration/at_most_once_spec.rb
259
259
  - spec/integration/automatic_reconnect_spec.rb
260
260
  - spec/integration/basic_subscriber_spec.rb
261
+ - spec/integration/consumer_cancellation_spec.rb
261
262
  - spec/integration/custom_actions_spec.rb
262
263
  - spec/integration/custom_headers_spec.rb
263
264
  - spec/integration/decoding_payloads_spec.rb
@@ -296,7 +297,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
296
297
  - !ruby/object:Gem::Version
297
298
  version: '0'
298
299
  requirements: []
299
- rubygems_version: 3.0.3
300
+ rubyforge_project:
301
+ rubygems_version: 2.7.6
300
302
  signing_key:
301
303
  specification_version: 4
302
304
  summary: ActionSubscriber is a DSL that allows a rails app to consume messages from
@@ -307,6 +309,7 @@ test_files:
307
309
  - spec/integration/at_most_once_spec.rb
308
310
  - spec/integration/automatic_reconnect_spec.rb
309
311
  - spec/integration/basic_subscriber_spec.rb
312
+ - spec/integration/consumer_cancellation_spec.rb
310
313
  - spec/integration/custom_actions_spec.rb
311
314
  - spec/integration/custom_headers_spec.rb
312
315
  - spec/integration/decoding_payloads_spec.rb