action_subscriber 1.3.0.rc0-java → 1.4.0-java
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/bin/action_subscriber +2 -0
- 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/publisher.rb +6 -4
- 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: 54f1bedbe81ed4f5f7be1e55396e635804caee90
|
4
|
+
data.tar.gz: 98653c65724ab0410128c74fa7d7423fea0d3353
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34c87c741309e62954644936130e301c3ded6b007190c7ebf6ad9559f43b6bde2fafd3a02918308816ff6b35082442c1b5e1be145263328c2553511c9ac151bc
|
7
|
+
data.tar.gz: 294933dfbf85d177f2c14a7ba29c811c8ed8e17a955ce2e638804d8b5097ed983dfd4032d9eaa00cafcaab13889240d1aa39a640a16ff6d0c615786436580e89
|
data/bin/action_subscriber
CHANGED
@@ -36,6 +36,8 @@ module ActionSubscriber
|
|
36
36
|
::ActionSubscriber::Babou.auto_pop!
|
37
37
|
when /subscribe/i then
|
38
38
|
::ActionSubscriber::Babou.start_subscribers
|
39
|
+
else
|
40
|
+
fail "ActionSubscriber.configuration.mode must be 'pop' or 'subscribe'. Currently set to '#{::ActionSubscriber.configuration.mode}'"
|
39
41
|
end
|
40
42
|
end
|
41
43
|
end
|
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
|
@@ -25,10 +25,12 @@ module ActionSubscriber
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def self.publishing_options(route,
|
29
|
-
options
|
30
|
-
|
31
|
-
|
28
|
+
def self.publishing_options(route, in_options = {})
|
29
|
+
options = {
|
30
|
+
:mandatory => false,
|
31
|
+
:persistent => false,
|
32
|
+
:routing_key => route,
|
33
|
+
}.merge(in_options)
|
32
34
|
|
33
35
|
if ::RUBY_PLATFORM == "java"
|
34
36
|
java_options = {}
|
@@ -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
|
5
5
|
platform: java
|
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-28 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
@@ -202,6 +202,7 @@ files:
|
|
202
202
|
- lib/action_subscriber/default_routing.rb
|
203
203
|
- lib/action_subscriber/dsl.rb
|
204
204
|
- lib/action_subscriber/march_hare/subscriber.rb
|
205
|
+
- lib/action_subscriber/message_retry.rb
|
205
206
|
- lib/action_subscriber/middleware.rb
|
206
207
|
- lib/action_subscriber/middleware/active_record/connection_management.rb
|
207
208
|
- lib/action_subscriber/middleware/active_record/query_cache.rb
|
@@ -222,6 +223,7 @@ files:
|
|
222
223
|
- spec/integration/at_most_once_spec.rb
|
223
224
|
- spec/integration/automatic_reconnect_spec.rb
|
224
225
|
- spec/integration/basic_subscriber_spec.rb
|
226
|
+
- spec/integration/custom_headers_spec.rb
|
225
227
|
- spec/integration/decoding_payloads_spec.rb
|
226
228
|
- spec/integration/manual_acknowledgement_spec.rb
|
227
229
|
- spec/lib/action_subscriber/base_spec.rb
|
@@ -254,9 +256,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
254
256
|
version: '0'
|
255
257
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
256
258
|
requirements:
|
257
|
-
- - "
|
259
|
+
- - ">="
|
258
260
|
- !ruby/object:Gem::Version
|
259
|
-
version:
|
261
|
+
version: '0'
|
260
262
|
requirements: []
|
261
263
|
rubyforge_project:
|
262
264
|
rubygems_version: 2.4.8
|
@@ -269,6 +271,7 @@ test_files:
|
|
269
271
|
- spec/integration/at_most_once_spec.rb
|
270
272
|
- spec/integration/automatic_reconnect_spec.rb
|
271
273
|
- spec/integration/basic_subscriber_spec.rb
|
274
|
+
- spec/integration/custom_headers_spec.rb
|
272
275
|
- spec/integration/decoding_payloads_spec.rb
|
273
276
|
- spec/integration/manual_acknowledgement_spec.rb
|
274
277
|
- spec/lib/action_subscriber/base_spec.rb
|