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 +4 -4
- data/.travis.yml +3 -4
- data/README.md +1 -0
- data/lib/action_subscriber/bunny/subscriber.rb +43 -26
- data/lib/action_subscriber/configuration.rb +2 -0
- data/lib/action_subscriber/march_hare/subscriber.rb +42 -24
- data/lib/action_subscriber/version.rb +1 -1
- data/spec/integration/consumer_cancellation_spec.rb +83 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: eb97087f5a6958b61a4d441245e224c601dc9cb1424251f86aacca5700e327a8
|
4
|
+
data.tar.gz: de4d85f2ba2e8aab277d07a51966ef60b6a639d0ff0dc65e2bac2adda2cfd389
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c1890c8861acdc67a4f6e1d3865980ef497820dd2b02038321bebab9248fee398176097c7f8d4d92332144719ae610d867ddbef93862ecc0e66781992b42036
|
7
|
+
data.tar.gz: d8149dbee4e94533b2bc758addf25f66ef8b0c872d3d919dc685486e482ab5127f54fedccd130dd1532809e4bc847c6e381dc786266edbcf102fea5bb84ed22d
|
data/.travis.yml
CHANGED
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
-
|
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
|
-
|
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 }
|
@@ -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.
|
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-
|
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
|
-
|
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
|