action_subscriber 1.3.0 → 1.4.0.pre0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/action_subscriber.rb +1 -0
- data/lib/action_subscriber/base.rb +2 -1
- data/lib/action_subscriber/bunny/subscriber.rb +4 -0
- data/lib/action_subscriber/march_hare/subscriber.rb +23 -12
- data/lib/action_subscriber/message_retry.rb +60 -0
- data/lib/action_subscriber/middleware/env.rb +5 -1
- data/lib/action_subscriber/rspec.rb +4 -0
- data/lib/action_subscriber/version.rb +1 -1
- data/spec/integration/at_least_once_spec.rb +1 -1
- data/spec/integration/custom_headers_spec.rb +27 -0
- data/spec/lib/action_subscriber/middleware/env_spec.rb +4 -0
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: de37da8295015a4541ae74344a94d9d0bd8e17a0
|
4
|
+
data.tar.gz: 1e76a445b345492f5fe2b323ee4d30e93e6242d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e29df8d1d6c5eaad8da7109064a5cd51b8439e895e8e078fcb5f178b6c98765c8eb0a88575ec33898d54ca10aea008890c3f84fb38ef9533e51add3d63f39682
|
7
|
+
data.tar.gz: fc1681979af3ae3f52be362704560c1da355c1b62e52755078fd3cd749c1e95f9feafe9117b1cf32108174b9fa65da0a0c7fcbc92ec59c8e21b9189fe626a5b2
|
data/lib/action_subscriber.rb
CHANGED
@@ -14,6 +14,7 @@ require "action_subscriber/version"
|
|
14
14
|
require "action_subscriber/default_routing"
|
15
15
|
require "action_subscriber/dsl"
|
16
16
|
require "action_subscriber/configuration"
|
17
|
+
require "action_subscriber/message_retry"
|
17
18
|
require "action_subscriber/middleware"
|
18
19
|
require "action_subscriber/rabbit_connection"
|
19
20
|
require "action_subscriber/subscribable"
|
@@ -24,8 +24,10 @@ module ActionSubscriber
|
|
24
24
|
:content_type => properties[:content_type],
|
25
25
|
:delivery_tag => delivery_info.delivery_tag,
|
26
26
|
:exchange => delivery_info.exchange,
|
27
|
+
:headers => properties.headers,
|
27
28
|
:message_id => nil,
|
28
29
|
:routing_key => delivery_info.routing_key,
|
30
|
+
:queue => queue.name,
|
29
31
|
}
|
30
32
|
env = ::ActionSubscriber::Middleware::Env.new(self, encoded_payload, properties)
|
31
33
|
enqueue_env(env)
|
@@ -45,8 +47,10 @@ module ActionSubscriber
|
|
45
47
|
:content_type => properties.content_type,
|
46
48
|
:delivery_tag => delivery_info.delivery_tag,
|
47
49
|
:exchange => delivery_info.exchange,
|
50
|
+
:headers => properties.headers,
|
48
51
|
:message_id => properties.message_id,
|
49
52
|
:routing_key => delivery_info.routing_key,
|
53
|
+
:queue => queue.name,
|
50
54
|
}
|
51
55
|
env = ::ActionSubscriber::Middleware::Env.new(self, encoded_payload, properties)
|
52
56
|
enqueue_env(env)
|
@@ -12,16 +12,18 @@ module ActionSubscriber
|
|
12
12
|
times_to_pop = [::ActionSubscriber::Threadpool.ready_size, ::ActionSubscriber.config.times_to_pop].min
|
13
13
|
times_to_pop.times do
|
14
14
|
queues.each do |queue|
|
15
|
-
|
15
|
+
metadata, encoded_payload = queue.pop(queue_subscription_options)
|
16
16
|
next unless encoded_payload
|
17
17
|
::ActiveSupport::Notifications.instrument "popped_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
|
18
18
|
properties = {
|
19
19
|
:channel => queue.channel,
|
20
|
-
:content_type =>
|
21
|
-
:delivery_tag =>
|
22
|
-
:exchange =>
|
23
|
-
:
|
24
|
-
:
|
20
|
+
:content_type => metadata.content_type,
|
21
|
+
:delivery_tag => metadata.delivery_tag,
|
22
|
+
:exchange => metadata.exchange,
|
23
|
+
:headers => _normalized_headers(metadata),
|
24
|
+
:message_id => metadata.message_id,
|
25
|
+
:routing_key => metadata.routing_key,
|
26
|
+
:queue => queue.name,
|
25
27
|
}
|
26
28
|
env = ::ActionSubscriber::Middleware::Env.new(self, encoded_payload, properties)
|
27
29
|
enqueue_env(env)
|
@@ -35,15 +37,17 @@ module ActionSubscriber
|
|
35
37
|
def auto_subscribe!
|
36
38
|
queues.each do |queue|
|
37
39
|
queue.channel.prefetch = ::ActionSubscriber.config.prefetch if acknowledge_messages?
|
38
|
-
consumer = queue.subscribe(queue_subscription_options) do |
|
40
|
+
consumer = queue.subscribe(queue_subscription_options) do |metadata, encoded_payload|
|
39
41
|
::ActiveSupport::Notifications.instrument "received_event.action_subscriber", :payload_size => encoded_payload.bytesize, :queue => queue.name
|
40
42
|
properties = {
|
41
43
|
:channel => queue.channel,
|
42
|
-
:content_type =>
|
43
|
-
:delivery_tag =>
|
44
|
-
:exchange =>
|
45
|
-
:
|
46
|
-
:
|
44
|
+
:content_type => metadata.content_type,
|
45
|
+
:delivery_tag => metadata.delivery_tag,
|
46
|
+
:exchange => metadata.exchange,
|
47
|
+
:headers => _normalized_headers(metadata),
|
48
|
+
:message_id => metadata.message_id,
|
49
|
+
:routing_key => metadata.routing_key,
|
50
|
+
:queue => queue.name,
|
47
51
|
}
|
48
52
|
env = ::ActionSubscriber::Middleware::Env.new(self, encoded_payload, properties)
|
49
53
|
enqueue_env(env)
|
@@ -66,6 +70,13 @@ module ActionSubscriber
|
|
66
70
|
end
|
67
71
|
end
|
68
72
|
end
|
73
|
+
|
74
|
+
def _normalized_headers(metadata)
|
75
|
+
return {} unless metadata.headers
|
76
|
+
metadata.headers.each_with_object({}) do |(header,value), hash|
|
77
|
+
hash[header] = value.to_s
|
78
|
+
end
|
79
|
+
end
|
69
80
|
end
|
70
81
|
end
|
71
82
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ActionSubscriber
|
2
|
+
module MessageRetry
|
3
|
+
SCHEDULE = {
|
4
|
+
2 => 100,
|
5
|
+
3 => 500,
|
6
|
+
4 => 2_500,
|
7
|
+
5 => 12_500,
|
8
|
+
6 => 62_500,
|
9
|
+
7 => 312_500,
|
10
|
+
8 => 1_562_500,
|
11
|
+
9 => 7_812_500,
|
12
|
+
10 => 39_062_500,
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
def self.redeliver_message_with_backoff(env)
|
16
|
+
next_attempt = get_last_attempt_number(env) + 1
|
17
|
+
ttl = SCHEDULE[next_attempt]
|
18
|
+
return unless ttl
|
19
|
+
with_exchange(env, ttl) do |exchange|
|
20
|
+
exchange.publish(env.encoded_payload, retry_options(env, next_attempt))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Private Implementation
|
25
|
+
def self.get_last_attempt_number(env)
|
26
|
+
attempt_header = env.headers.fetch("as-attempt", "1")
|
27
|
+
attempt_header.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.retry_headers(env, attempt)
|
31
|
+
env.headers.reject do |key, val|
|
32
|
+
key == "x-death"
|
33
|
+
end.merge({"as-attempt" => attempt.to_s})
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.retry_options(env, attempt)
|
37
|
+
{
|
38
|
+
:content_type => env.content_type,
|
39
|
+
:routing_key => env.routing_key,
|
40
|
+
:headers => retry_headers(env, attempt),
|
41
|
+
}
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.with_exchange(env, ttl)
|
45
|
+
exchange_retry_name = "#{env.exchange}_retry_#{ttl}"
|
46
|
+
queue_retry_name = "#{env.queue}_retry_#{ttl}"
|
47
|
+
channel = RabbitConnection.subscriber_connection.create_channel
|
48
|
+
begin
|
49
|
+
channel.confirm_select
|
50
|
+
exchange = channel.topic(exchange_retry_name)
|
51
|
+
queue = channel.queue(queue_retry_name, :arguments => {"x-dead-letter-exchange" => env.exchange, "x-message-ttl" => ttl})
|
52
|
+
queue.bind(exchange, :routing_key => env.routing_key)
|
53
|
+
yield(exchange)
|
54
|
+
channel.wait_for_confirms
|
55
|
+
ensure
|
56
|
+
channel.close rescue nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -6,8 +6,10 @@ module ActionSubscriber
|
|
6
6
|
attr_reader :content_type,
|
7
7
|
:encoded_payload,
|
8
8
|
:exchange,
|
9
|
+
:headers,
|
9
10
|
:message_id,
|
10
11
|
:routing_key,
|
12
|
+
:queue,
|
11
13
|
:subscriber
|
12
14
|
|
13
15
|
##
|
@@ -18,16 +20,18 @@ module ActionSubscriber
|
|
18
20
|
# :content_type => String
|
19
21
|
# :delivery_tag => String (the message identifier to send back to rabbitmq for acknowledgement)
|
20
22
|
# :exchange => String
|
23
|
+
# :headers => Hash[ String => String ]
|
21
24
|
# :message_id => String
|
22
25
|
# :routing_key => String
|
23
|
-
|
24
26
|
def initialize(subscriber, encoded_payload, properties)
|
25
27
|
@channel = properties.fetch(:channel)
|
26
28
|
@content_type = properties.fetch(:content_type)
|
27
29
|
@delivery_tag = properties.fetch(:delivery_tag)
|
28
30
|
@encoded_payload = encoded_payload
|
29
31
|
@exchange = properties.fetch(:exchange)
|
32
|
+
@headers = properties.fetch(:headers) || {}
|
30
33
|
@message_id = properties.fetch(:message_id)
|
34
|
+
@queue = properties.fetch(:queue)
|
31
35
|
@routing_key = properties.fetch(:routing_key)
|
32
36
|
@subscriber = subscriber
|
33
37
|
end
|
@@ -17,8 +17,10 @@ module ActionSubscriber
|
|
17
17
|
:content_type => "text/plain",
|
18
18
|
:delivery_tag => "XYZ",
|
19
19
|
:exchange => "events",
|
20
|
+
:headers => {},
|
20
21
|
:message_id => "MSG-123",
|
21
22
|
:routing_key => "amigo.user.created",
|
23
|
+
:queue => "test.amigo.user.created",
|
22
24
|
}.freeze
|
23
25
|
|
24
26
|
# Create a new subscriber instance. Available options are:
|
@@ -73,8 +75,10 @@ end
|
|
73
75
|
:content_type => "text/plain",
|
74
76
|
:delivery_tag => "XYZ",
|
75
77
|
:exchange => "events",
|
78
|
+
:headers => {},
|
76
79
|
:message_id => "MSG-123",
|
77
80
|
:routing_key => "amigo.user.created",
|
81
|
+
:queue => "test.amigo.user.created",
|
78
82
|
}}
|
79
83
|
end
|
80
84
|
|
@@ -17,7 +17,7 @@ describe "at_least_once! mode", :integration => true do
|
|
17
17
|
exchange = channel.topic("events")
|
18
18
|
exchange.publish("GrumpFace", :routing_key => "gorby_puff.grumpy")
|
19
19
|
|
20
|
-
verify_expectation_within(
|
20
|
+
verify_expectation_within(2.0) do
|
21
21
|
expect($messages).to eq Set.new(["GrumpFace::0","GrumpFace::1","GrumpFace::2"])
|
22
22
|
end
|
23
23
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class PrankSubscriber < ActionSubscriber::Base
|
2
|
+
publisher :pikitis
|
3
|
+
|
4
|
+
def pulled
|
5
|
+
$messages << env.headers
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "Custom Headers Are Published and Received", :integration => true do
|
10
|
+
let(:headers) { { "Custom" => "content/header" } }
|
11
|
+
|
12
|
+
it "works for auto_pop!" do
|
13
|
+
::ActionSubscriber::Publisher.publish("pikitis.prank.pulled", "Yo Knope!", "events", :headers => headers)
|
14
|
+
verify_expectation_within(2.0) do
|
15
|
+
::ActionSubscriber.auto_pop!
|
16
|
+
expect($messages).to eq(Set.new([headers]))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "works for auto_subscriber!" do
|
21
|
+
::ActionSubscriber.auto_subscribe!
|
22
|
+
::ActionSubscriber::Publisher.publish("pikitis.prank.pulled", "Yo Knope!", "events", :headers => headers)
|
23
|
+
verify_expectation_within(2.0) do
|
24
|
+
expect($messages).to eq(Set.new([headers]))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -7,8 +7,10 @@ describe ActionSubscriber::Middleware::Env do
|
|
7
7
|
:delivery_tag => "XYZ",
|
8
8
|
:encoded_payload => encoded_payload,
|
9
9
|
:exchange => "events",
|
10
|
+
:headers => {},
|
10
11
|
:message_id => "MSG-1234",
|
11
12
|
:routing_key => "amigo.user.created",
|
13
|
+
:queue => "test.amigo.user.created",
|
12
14
|
} }
|
13
15
|
let(:subscriber) { UserSubscriber }
|
14
16
|
|
@@ -17,8 +19,10 @@ describe ActionSubscriber::Middleware::Env do
|
|
17
19
|
specify { expect(subject.action).to eq("created") }
|
18
20
|
specify { expect(subject.content_type).to eq(properties[:content_type]) }
|
19
21
|
specify { expect(subject.exchange).to eq(properties[:exchange]) }
|
22
|
+
specify { expect(subject.headers).to eq(properties[:headers]) }
|
20
23
|
specify { expect(subject.message_id).to eq(properties[:message_id]) }
|
21
24
|
specify { expect(subject.routing_key).to eq(properties[:routing_key]) }
|
25
|
+
specify { expect(subject.queue).to eq(properties[:queue]) }
|
22
26
|
|
23
27
|
describe "#acknowledge" do
|
24
28
|
it "sends an acknowledgement to rabbitmq" do
|
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: 1.
|
4
|
+
version: 1.4.0.pre0
|
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: 2015-10-
|
15
|
+
date: 2015-10-22 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: activesupport
|
@@ -203,6 +203,7 @@ files:
|
|
203
203
|
- lib/action_subscriber/default_routing.rb
|
204
204
|
- lib/action_subscriber/dsl.rb
|
205
205
|
- lib/action_subscriber/march_hare/subscriber.rb
|
206
|
+
- lib/action_subscriber/message_retry.rb
|
206
207
|
- lib/action_subscriber/middleware.rb
|
207
208
|
- lib/action_subscriber/middleware/active_record/connection_management.rb
|
208
209
|
- lib/action_subscriber/middleware/active_record/query_cache.rb
|
@@ -223,6 +224,7 @@ files:
|
|
223
224
|
- spec/integration/at_most_once_spec.rb
|
224
225
|
- spec/integration/automatic_reconnect_spec.rb
|
225
226
|
- spec/integration/basic_subscriber_spec.rb
|
227
|
+
- spec/integration/custom_headers_spec.rb
|
226
228
|
- spec/integration/decoding_payloads_spec.rb
|
227
229
|
- spec/integration/manual_acknowledgement_spec.rb
|
228
230
|
- spec/lib/action_subscriber/base_spec.rb
|
@@ -255,9 +257,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
255
257
|
version: '0'
|
256
258
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
257
259
|
requirements:
|
258
|
-
- - "
|
260
|
+
- - ">"
|
259
261
|
- !ruby/object:Gem::Version
|
260
|
-
version:
|
262
|
+
version: 1.3.1
|
261
263
|
requirements: []
|
262
264
|
rubyforge_project:
|
263
265
|
rubygems_version: 2.4.8
|
@@ -271,6 +273,7 @@ test_files:
|
|
271
273
|
- spec/integration/at_most_once_spec.rb
|
272
274
|
- spec/integration/automatic_reconnect_spec.rb
|
273
275
|
- spec/integration/basic_subscriber_spec.rb
|
276
|
+
- spec/integration/custom_headers_spec.rb
|
274
277
|
- spec/integration/decoding_payloads_spec.rb
|
275
278
|
- spec/integration/manual_acknowledgement_spec.rb
|
276
279
|
- spec/lib/action_subscriber/base_spec.rb
|