action_subscriber 5.2.2 → 5.2.3
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/action_subscriber.gemspec +1 -1
- data/lib/action_subscriber/bunny/subscriber.rb +3 -3
- data/lib/action_subscriber/march_hare/subscriber.rb +3 -3
- data/lib/action_subscriber/subscriber.rb +18 -0
- data/lib/action_subscriber/version.rb +1 -1
- data/lib/action_subscriber.rb +1 -0
- data/spec/integration/automatic_reconnect_spec.rb +28 -28
- data/spec/integration/consumer_cancellation_spec.rb +112 -72
- data/spec/spec_helper.rb +8 -0
- metadata +18 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8ceaeb4042785bd8ea3ada6665612eb2e744234ef258211ef26a87dd2e78630
|
4
|
+
data.tar.gz: '008e7aaae3c7d0ad58fdee940486eaf6a4f43fa55628edd8106da725c0158348'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7588e706731a7f2e1e8dbfb077f429ccc6a483dad80c93845063b01e13ea6a8d4c2ef667e96cec49de70062f3b7072cd36546e613be8be790e4afb401b2239c0
|
7
|
+
data.tar.gz: e2906c4a77e5a2afadf697f8640d8cefb1e9bdcc04886073be044063212cb35eb956c39038106b199633b0defcfbd69fe43e2e72452f16b9667cceddce817666
|
data/action_subscriber.gemspec
CHANGED
@@ -33,9 +33,9 @@ Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency "active_publisher", "~> 0.1.5"
|
34
34
|
spec.add_development_dependency "activerecord", ">= 3.2"
|
35
35
|
spec.add_development_dependency "bundler", ">= 1.6"
|
36
|
-
spec.add_development_dependency "pry-coolline"
|
37
36
|
spec.add_development_dependency "pry-nav"
|
38
37
|
spec.add_development_dependency "rabbitmq_http_api_client", "~> 1.2.0"
|
39
38
|
spec.add_development_dependency "rspec", "~> 3.0"
|
40
39
|
spec.add_development_dependency "rake"
|
40
|
+
spec.add_development_dependency "simplecov"
|
41
41
|
end
|
@@ -2,6 +2,7 @@ module ActionSubscriber
|
|
2
2
|
module Bunny
|
3
3
|
module Subscriber
|
4
4
|
include ::ActionSubscriber::Logging
|
5
|
+
include ::ActionSubscriber::Subscriber
|
5
6
|
|
6
7
|
def bunny_consumers
|
7
8
|
@bunny_consumers ||= []
|
@@ -43,11 +44,10 @@ module ActionSubscriber
|
|
43
44
|
if ::ActionSubscriber.configuration.resubscribe_on_consumer_cancellation
|
44
45
|
# Add cancellation callback to rebuild subscriber on cancel.
|
45
46
|
consumer.on_cancellation do
|
46
|
-
::ActionSubscriber.logger.warn "
|
47
|
+
::ActionSubscriber.logger.warn "Cancellation received for queue consumer: #{queue.name}, rebuilding subscription..."
|
47
48
|
bunny_consumers.delete(consumer)
|
48
49
|
channel.close
|
49
|
-
|
50
|
-
start_subscriber_for_subscription(subscription)
|
50
|
+
safely_restart_subscriber(subscription)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -2,6 +2,7 @@ module ActionSubscriber
|
|
2
2
|
module MarchHare
|
3
3
|
module Subscriber
|
4
4
|
include ::ActionSubscriber::Logging
|
5
|
+
include ::ActionSubscriber::Subscriber
|
5
6
|
|
6
7
|
def cancel_consumers!
|
7
8
|
# Cancel any non-cancelled consumers.
|
@@ -43,11 +44,10 @@ module ActionSubscriber
|
|
43
44
|
if ::ActionSubscriber.configuration.resubscribe_on_consumer_cancellation
|
44
45
|
# Add cancellation callback to rebuild subscriber on cancel.
|
45
46
|
opts[:on_cancellation] = lambda do |the_consumer|
|
46
|
-
::ActionSubscriber.logger.warn "
|
47
|
+
::ActionSubscriber.logger.warn "Cancellation received for queue consumer: #{queue.name}, rebuilding subscription..."
|
47
48
|
march_hare_consumers.delete(the_consumer)
|
48
49
|
queue.channel.close
|
49
|
-
|
50
|
-
start_subscriber_for_subscription(subscription)
|
50
|
+
safely_restart_subscriber(subscription)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module ActionSubscriber
|
2
|
+
module Subscriber
|
3
|
+
# resubscribes to queue, continuously retrying to subscribe in the event of a potentially recoverable error while
|
4
|
+
# also calling the error handler to surface that a subscription failure happened
|
5
|
+
def safely_restart_subscriber(subscription)
|
6
|
+
subscription[:queue] = setup_queue(subscription[:route])
|
7
|
+
start_subscriber_for_subscription(subscription)
|
8
|
+
rescue StandardError => e
|
9
|
+
::ActionSubscriber.configuration.error_handler.call(e)
|
10
|
+
raise e unless e.message =~ /queue .* process is stopped by supervisor/
|
11
|
+
|
12
|
+
nap_time = rand(2.0..5.0)
|
13
|
+
::ActionSubscriber.logger.error("Failed to resubscribe to #{subscription[:queue].name}, retrying again in #{nap_time} seconds...")
|
14
|
+
sleep(nap_time)
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/action_subscriber.rb
CHANGED
@@ -21,6 +21,7 @@ require "action_subscriber/message_retry"
|
|
21
21
|
require "action_subscriber/middleware"
|
22
22
|
require "action_subscriber/rabbit_connection"
|
23
23
|
require "action_subscriber/subscribable"
|
24
|
+
require "action_subscriber/subscriber"
|
24
25
|
require "action_subscriber/thread_pools"
|
25
26
|
require "action_subscriber/bunny/subscriber"
|
26
27
|
require "action_subscriber/march_hare/subscriber"
|
@@ -7,36 +7,36 @@ class GusSubscriber < ActionSubscriber::Base
|
|
7
7
|
end
|
8
8
|
|
9
9
|
describe "Automatically reconnect on connection failure", :integration => true, :slow => true do
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
10
|
+
let(:draw_routes) do
|
11
|
+
::ActionSubscriber.draw_routes do
|
12
|
+
default_routes_for GusSubscriber
|
13
|
+
end
|
14
|
+
end
|
15
|
+
let(:http_client) { RabbitMQ::HTTP::Client.new("http://127.0.0.1:15672") }
|
16
|
+
let(:subscriber) { GusSubscriber }
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
it "reconnects when a connection drops" do
|
19
|
+
::ActionSubscriber::start_subscribers!
|
20
|
+
::ActivePublisher.publish("gus.spoke", "First", "events")
|
21
|
+
verify_expectation_within(5.0) do
|
22
|
+
expect($messages).to eq(Set.new(["First"]))
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
close_all_connections!
|
26
|
+
sleep 5.0
|
27
|
+
verify_expectation_within(5.0) do
|
28
|
+
expect(::ActionSubscriber::RabbitConnection.with_connection{|connection| connection.open?}).to eq(true)
|
29
|
+
end
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
31
|
+
::ActivePublisher.publish("gus.spoke", "Second", "events")
|
32
|
+
verify_expectation_within(5.0) do
|
33
|
+
expect($messages).to eq(Set.new(["First", "Second"]))
|
34
|
+
end
|
35
|
+
end
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
def close_all_connections!
|
38
|
+
http_client.list_connections.each do |conn_info|
|
39
|
+
http_client.close_connection(conn_info.name)
|
40
|
+
end
|
41
|
+
end
|
42
42
|
end
|
@@ -8,76 +8,116 @@ class YoloSubscriber < ActionSubscriber::Base
|
|
8
8
|
end
|
9
9
|
|
10
10
|
describe "Automatically handles consumer cancellation", :integration => true, :slow => true do
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
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
|
+
describe "resubscription logic" do
|
74
|
+
let(:subscription) { subject.send(:subscriptions).first }
|
75
|
+
subject { ::ActionSubscriber.send(:route_set) }
|
76
|
+
|
77
|
+
it "sets up consumers" do
|
78
|
+
if ::RUBY_PLATFORM == "java"
|
79
|
+
expect { subject.safely_restart_subscriber(subscription) }.to change { subject.march_hare_consumers.count }.from(0).to(1)
|
80
|
+
else
|
81
|
+
expect { subject.safely_restart_subscriber(subscription) }.to change { subject.bunny_consumers.count }.from(0).to(1)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context "when error is raised during resubscription process" do
|
86
|
+
context "and error is one that can be retried" do
|
87
|
+
let(:error) { ::RuntimeError.new("queue 're.created' in vhost '/' process is stopped by supervisor") }
|
88
|
+
|
89
|
+
it "retries resubscription process" do
|
90
|
+
expect(subject).to receive(:setup_queue).and_raise(error).ordered
|
91
|
+
expect(subject).to receive(:setup_queue).and_raise(error).ordered
|
92
|
+
expect(subject).to receive(:setup_queue).and_call_original.ordered
|
93
|
+
expect(::ActionSubscriber.config.error_handler).to receive(:call).with(error).twice
|
94
|
+
expect(::ActionSubscriber.logger).to receive(:error).twice
|
95
|
+
expect(subject).to receive(:sleep).twice # mostly to skip the delay
|
96
|
+
subject.safely_restart_subscriber(subscription)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context "and error is one that can't be retried" do
|
101
|
+
let(:error) { ::RuntimeError.new("kaBOOM") }
|
102
|
+
|
103
|
+
it "calls error handler and raises" do
|
104
|
+
expect(subject).to receive(:setup_queue).and_raise(error).ordered
|
105
|
+
expect(::ActionSubscriber.config.error_handler).to receive(:call).with(error).once
|
106
|
+
expect(subject).to_not receive(:sleep)
|
107
|
+
expect { subject.safely_restart_subscriber(subscription) }.to raise_error(error)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def rabbit_consumers
|
114
|
+
route_set = ::ActionSubscriber.send(:route_set)
|
115
|
+
route_set.try(:bunny_consumers) || route_set.try(:march_hare_consumers)
|
116
|
+
end
|
117
|
+
|
118
|
+
def delete_all_queues!
|
119
|
+
http_client.list_queues.each do |queue|
|
120
|
+
http_client.delete_queue(queue.vhost, queue.name)
|
121
|
+
end
|
122
|
+
end
|
83
123
|
end
|
data/spec/spec_helper.rb
CHANGED
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.2.
|
4
|
+
version: 5.2.3
|
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:
|
15
|
+
date: 2021-10-05 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: activesupport
|
@@ -126,20 +126,6 @@ dependencies:
|
|
126
126
|
- - ">="
|
127
127
|
- !ruby/object:Gem::Version
|
128
128
|
version: '1.6'
|
129
|
-
- !ruby/object:Gem::Dependency
|
130
|
-
name: pry-coolline
|
131
|
-
requirement: !ruby/object:Gem::Requirement
|
132
|
-
requirements:
|
133
|
-
- - ">="
|
134
|
-
- !ruby/object:Gem::Version
|
135
|
-
version: '0'
|
136
|
-
type: :development
|
137
|
-
prerelease: false
|
138
|
-
version_requirements: !ruby/object:Gem::Requirement
|
139
|
-
requirements:
|
140
|
-
- - ">="
|
141
|
-
- !ruby/object:Gem::Version
|
142
|
-
version: '0'
|
143
129
|
- !ruby/object:Gem::Dependency
|
144
130
|
name: pry-nav
|
145
131
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,6 +182,20 @@ dependencies:
|
|
196
182
|
- - ">="
|
197
183
|
- !ruby/object:Gem::Version
|
198
184
|
version: '0'
|
185
|
+
- !ruby/object:Gem::Dependency
|
186
|
+
name: simplecov
|
187
|
+
requirement: !ruby/object:Gem::Requirement
|
188
|
+
requirements:
|
189
|
+
- - ">="
|
190
|
+
- !ruby/object:Gem::Version
|
191
|
+
version: '0'
|
192
|
+
type: :development
|
193
|
+
prerelease: false
|
194
|
+
version_requirements: !ruby/object:Gem::Requirement
|
195
|
+
requirements:
|
196
|
+
- - ">="
|
197
|
+
- !ruby/object:Gem::Version
|
198
|
+
version: '0'
|
199
199
|
description: ActionSubscriber is a DSL that allows a rails app to consume messages
|
200
200
|
from a RabbitMQ broker.
|
201
201
|
email:
|
@@ -249,6 +249,7 @@ files:
|
|
249
249
|
- lib/action_subscriber/router.rb
|
250
250
|
- lib/action_subscriber/rspec.rb
|
251
251
|
- lib/action_subscriber/subscribable.rb
|
252
|
+
- lib/action_subscriber/subscriber.rb
|
252
253
|
- lib/action_subscriber/thread_pools.rb
|
253
254
|
- lib/action_subscriber/uri.rb
|
254
255
|
- lib/action_subscriber/version.rb
|
@@ -298,7 +299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
298
299
|
- !ruby/object:Gem::Version
|
299
300
|
version: '0'
|
300
301
|
requirements: []
|
301
|
-
rubygems_version: 3.1.
|
302
|
+
rubygems_version: 3.1.6
|
302
303
|
signing_key:
|
303
304
|
specification_version: 4
|
304
305
|
summary: ActionSubscriber is a DSL that allows a rails app to consume messages from
|