action_subscriber 5.1.5 → 5.2.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
  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