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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ae5b2f882ea1029a22240ed01ebe6c7182413df3
4
- data.tar.gz: 6b231f3f390ec2b9f9f4445a414dbcd8996e4f6c
3
+ metadata.gz: de37da8295015a4541ae74344a94d9d0bd8e17a0
4
+ data.tar.gz: 1e76a445b345492f5fe2b323ee4d30e93e6242d0
5
5
  SHA512:
6
- metadata.gz: c86487cde5cdc57eb02fcc8188916b1e497e225a27fde9e4e22c4265d8542de32253bbb0c2800d9bfd16382fca3bf692e44fcaec8d9991e9d3dca7acde30fa0c
7
- data.tar.gz: c8042ece3e2d8867e4d8ef9ba844cba7057277f9a4018d28e1cb035324ff909856142e19189ecf5b67d4265e3f7b6aba2de736ce2c80750ca446a92488bb57c9
6
+ metadata.gz: e29df8d1d6c5eaad8da7109064a5cd51b8439e895e8e078fcb5f178b6c98765c8eb0a88575ec33898d54ca10aea008890c3f84fb38ef9533e51add3d63f39682
7
+ data.tar.gz: fc1681979af3ae3f52be362704560c1da355c1b62e52755078fd3cd749c1e95f9feafe9117b1cf32108174b9fa65da0a0c7fcbc92ec59c8e21b9189fe626a5b2
@@ -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"
@@ -80,7 +80,8 @@ module ActionSubscriber
80
80
  yield
81
81
  acknowledge
82
82
  rescue => error
83
- reject
83
+ ::ActionSubscriber::MessageRetry.redeliver_message_with_backoff(env)
84
+ acknowledge
84
85
  raise error
85
86
  end
86
87
 
@@ -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
- header, encoded_payload = queue.pop(queue_subscription_options)
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 => header.content_type,
21
- :delivery_tag => header.delivery_tag,
22
- :exchange => header.exchange,
23
- :message_id => header.message_id,
24
- :routing_key => header.routing_key,
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 |header, encoded_payload|
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 => header.content_type,
43
- :delivery_tag => header.delivery_tag,
44
- :exchange => header.exchange,
45
- :message_id => header.message_id,
46
- :routing_key => header.routing_key,
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
 
@@ -1,3 +1,3 @@
1
1
  module ActionSubscriber
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0.pre0"
3
3
  end
@@ -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(1.0) do
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.3.0
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-21 00:00:00.000000000 Z
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: '0'
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