action_subscriber 2.5.0.pre-java → 3.0.0-java

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -2
  3. data/README.md +15 -23
  4. data/action_subscriber.gemspec +1 -0
  5. data/lib/action_subscriber.rb +11 -23
  6. data/lib/action_subscriber/babou.rb +2 -29
  7. data/lib/action_subscriber/base.rb +0 -4
  8. data/lib/action_subscriber/bunny/subscriber.rb +18 -4
  9. data/lib/action_subscriber/configuration.rb +1 -11
  10. data/lib/action_subscriber/default_routing.rb +6 -4
  11. data/lib/action_subscriber/march_hare/subscriber.rb +20 -4
  12. data/lib/action_subscriber/message_retry.rb +1 -1
  13. data/lib/action_subscriber/middleware/env.rb +3 -1
  14. data/lib/action_subscriber/middleware/error_handler.rb +20 -4
  15. data/lib/action_subscriber/rabbit_connection.rb +24 -33
  16. data/lib/action_subscriber/route.rb +5 -1
  17. data/lib/action_subscriber/route_set.rb +13 -6
  18. data/lib/action_subscriber/router.rb +15 -3
  19. data/lib/action_subscriber/version.rb +1 -1
  20. data/spec/integration/around_filters_spec.rb +1 -1
  21. data/spec/integration/at_least_once_spec.rb +1 -1
  22. data/spec/integration/at_most_once_spec.rb +1 -1
  23. data/spec/integration/automatic_reconnect_spec.rb +3 -4
  24. data/spec/integration/basic_subscriber_spec.rb +2 -2
  25. data/spec/integration/custom_actions_spec.rb +1 -1
  26. data/spec/integration/custom_headers_spec.rb +2 -2
  27. data/spec/integration/decoding_payloads_spec.rb +2 -2
  28. data/spec/integration/manual_acknowledgement_spec.rb +1 -1
  29. data/spec/integration/multiple_connections_spec.rb +36 -0
  30. data/spec/integration/multiple_threadpools_spec.rb +3 -3
  31. data/spec/lib/action_subscriber/configuration_spec.rb +1 -5
  32. data/spec/lib/action_subscriber/middleware/error_handler_spec.rb +15 -0
  33. data/spec/spec_helper.rb +8 -4
  34. metadata +21 -16
  35. data/lib/action_subscriber/publisher.rb +0 -46
  36. data/lib/action_subscriber/publisher/async.rb +0 -31
  37. data/lib/action_subscriber/publisher/async/in_memory_adapter.rb +0 -153
  38. data/spec/integration/inferred_routes_spec.rb +0 -53
  39. data/spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb +0 -135
  40. data/spec/lib/action_subscriber/publisher/async_spec.rb +0 -40
  41. data/spec/lib/action_subscriber/publisher_spec.rb +0 -35
@@ -15,6 +15,7 @@ require 'action_subscriber/rspec'
15
15
  # Silence the Logger
16
16
  $TESTING = true
17
17
  ::ActionSubscriber::Logging.initialize_logger(nil)
18
+ ::ActionSubscriber.setup_default_connection!
18
19
 
19
20
  RSpec.configure do |config|
20
21
  config.mock_with :rspec do |mocks|
@@ -23,14 +24,17 @@ RSpec.configure do |config|
23
24
 
24
25
  config.before(:each, :integration => true) do
25
26
  $messages = Set.new
26
- draw_routes if respond_to?(:draw_routes)
27
- ::ActionSubscriber::RabbitConnection.subscriber_connection
28
- ::ActionSubscriber.setup_queues!
27
+ draw_routes
28
+ ::ActionSubscriber.setup_subscriptions!
29
29
  end
30
30
  config.after(:each, :integration => true) do
31
31
  ::ActionSubscriber.stop_subscribers!
32
- ::ActionSubscriber::RabbitConnection.subscriber_disconnect!
33
32
  ::ActionSubscriber.instance_variable_set("@route_set", nil)
33
+ ::ActionSubscriber.instance_variable_set("@route_set_block", nil)
34
+ end
35
+ config.after(:suite) do
36
+ ::ActionSubscriber.stop_subscribers!
37
+ ::ActionSubscriber::RabbitConnection.subscriber_disconnect!
34
38
  end
35
39
  end
36
40
 
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: 2.5.0.pre
4
+ version: 3.0.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: 2016-09-09 00:00:00.000000000 Z
15
+ date: 2016-09-22 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  requirement: !ruby/object:Gem::Requirement
@@ -84,6 +84,20 @@ dependencies:
84
84
  - - ">="
85
85
  - !ruby/object:Gem::Version
86
86
  version: '0'
87
+ - !ruby/object:Gem::Dependency
88
+ requirement: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - "~>"
91
+ - !ruby/object:Gem::Version
92
+ version: 0.1.5
93
+ name: active_publisher
94
+ prerelease: false
95
+ type: :development
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: 0.1.5
87
101
  - !ruby/object:Gem::Dependency
88
102
  requirement: !ruby/object:Gem::Requirement
89
103
  requirements:
@@ -213,9 +227,6 @@ files:
213
227
  - lib/action_subscriber/middleware/router.rb
214
228
  - lib/action_subscriber/middleware/runner.rb
215
229
  - lib/action_subscriber/preload.rb
216
- - lib/action_subscriber/publisher.rb
217
- - lib/action_subscriber/publisher/async.rb
218
- - lib/action_subscriber/publisher/async/in_memory_adapter.rb
219
230
  - lib/action_subscriber/rabbit_connection.rb
220
231
  - lib/action_subscriber/railtie.rb
221
232
  - lib/action_subscriber/route.rb
@@ -235,8 +246,8 @@ files:
235
246
  - spec/integration/custom_actions_spec.rb
236
247
  - spec/integration/custom_headers_spec.rb
237
248
  - spec/integration/decoding_payloads_spec.rb
238
- - spec/integration/inferred_routes_spec.rb
239
249
  - spec/integration/manual_acknowledgement_spec.rb
250
+ - spec/integration/multiple_connections_spec.rb
240
251
  - spec/integration/multiple_threadpools_spec.rb
241
252
  - spec/lib/action_subscriber/base_spec.rb
242
253
  - spec/lib/action_subscriber/configuration_spec.rb
@@ -248,9 +259,6 @@ files:
248
259
  - spec/lib/action_subscriber/middleware/error_handler_spec.rb
249
260
  - spec/lib/action_subscriber/middleware/router_spec.rb
250
261
  - spec/lib/action_subscriber/middleware/runner_spec.rb
251
- - spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb
252
- - spec/lib/action_subscriber/publisher/async_spec.rb
253
- - spec/lib/action_subscriber/publisher_spec.rb
254
262
  - spec/lib/action_subscriber/router_spec.rb
255
263
  - spec/lib/action_subscriber/subscribable_spec.rb
256
264
  - spec/lib/action_subscriber/threadpool_spec.rb
@@ -271,12 +279,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
271
279
  version: '0'
272
280
  required_rubygems_version: !ruby/object:Gem::Requirement
273
281
  requirements:
274
- - - ">"
282
+ - - ">="
275
283
  - !ruby/object:Gem::Version
276
- version: 1.3.1
284
+ version: '0'
277
285
  requirements: []
278
286
  rubyforge_project:
279
- rubygems_version: 2.6.4
287
+ rubygems_version: 2.6.6
280
288
  signing_key:
281
289
  specification_version: 4
282
290
  summary: ActionSubscriber is a DSL that allows a rails app to consume messages from a RabbitMQ broker.
@@ -289,8 +297,8 @@ test_files:
289
297
  - spec/integration/custom_actions_spec.rb
290
298
  - spec/integration/custom_headers_spec.rb
291
299
  - spec/integration/decoding_payloads_spec.rb
292
- - spec/integration/inferred_routes_spec.rb
293
300
  - spec/integration/manual_acknowledgement_spec.rb
301
+ - spec/integration/multiple_connections_spec.rb
294
302
  - spec/integration/multiple_threadpools_spec.rb
295
303
  - spec/lib/action_subscriber/base_spec.rb
296
304
  - spec/lib/action_subscriber/configuration_spec.rb
@@ -302,9 +310,6 @@ test_files:
302
310
  - spec/lib/action_subscriber/middleware/error_handler_spec.rb
303
311
  - spec/lib/action_subscriber/middleware/router_spec.rb
304
312
  - spec/lib/action_subscriber/middleware/runner_spec.rb
305
- - spec/lib/action_subscriber/publisher/async/in_memory_adapter_spec.rb
306
- - spec/lib/action_subscriber/publisher/async_spec.rb
307
- - spec/lib/action_subscriber/publisher_spec.rb
308
313
  - spec/lib/action_subscriber/router_spec.rb
309
314
  - spec/lib/action_subscriber/subscribable_spec.rb
310
315
  - spec/lib/action_subscriber/threadpool_spec.rb
@@ -1,46 +0,0 @@
1
- module ActionSubscriber
2
- module Publisher
3
- # Publish a message to RabbitMQ
4
- #
5
- # @param [String] route The routing key to use for this message.
6
- # @param [String] payload The message you are sending. Should already be encoded as a string.
7
- # @param [String] exchange The exchange you want to publish to.
8
- # @param [Hash] options hash to set message parameters (e.g. headers)
9
- def self.publish(route, payload, exchange_name, options = {})
10
- with_exchange(exchange_name) do |exchange|
11
- exchange.publish(payload, publishing_options(route, options))
12
- end
13
- end
14
-
15
- def self.with_exchange(exchange_name)
16
- connection = RabbitConnection.publisher_connection
17
- channel = connection.create_channel
18
- begin
19
- channel.confirm_select if ActionSubscriber.configuration.publisher_confirms
20
- exchange = channel.topic(exchange_name)
21
- yield(exchange)
22
- channel.wait_for_confirms if ActionSubscriber.configuration.publisher_confirms
23
- ensure
24
- channel.close rescue nil
25
- end
26
- end
27
-
28
- def self.publishing_options(route, in_options = {})
29
- options = {
30
- :mandatory => false,
31
- :persistent => false,
32
- :routing_key => route,
33
- }.merge(in_options)
34
-
35
- if ::RUBY_PLATFORM == "java"
36
- java_options = {}
37
- java_options[:mandatory] = options.delete(:mandatory)
38
- java_options[:routing_key] = options.delete(:routing_key)
39
- java_options[:properties] = options
40
- java_options
41
- else
42
- options
43
- end
44
- end
45
- end
46
- end
@@ -1,31 +0,0 @@
1
- module ActionSubscriber
2
- module Publisher
3
- # Publish a message asynchronously to RabbitMQ.
4
- #
5
- # Asynchronous is designed to do two things:
6
- # 1. Introduce the idea of a durable retry should the RabbitMQ connection disconnect.
7
- # 2. Provide a higher-level pattern for fire-and-forget publishing.
8
- #
9
- # @param [String] route The routing key to use for this message.
10
- # @param [String] payload The message you are sending. Should already be encoded as a string.
11
- # @param [String] exchange The exchange you want to publish to.
12
- # @param [Hash] options hash to set message parameters (e.g. headers).
13
- def self.publish_async(route, payload, exchange_name, options = {})
14
- Async.publisher_adapter.publish(route, payload, exchange_name, options)
15
- end
16
-
17
- module Async
18
- def self.publisher_adapter
19
- @publisher_adapter ||= case ::ActionSubscriber.configuration.async_publisher
20
- when /memory/i then
21
- require "action_subscriber/publisher/async/in_memory_adapter"
22
- InMemoryAdapter.new
23
- when /redis/i then
24
- fail "Not yet implemented"
25
- else
26
- fail "Unknown adapter '#{::ActionSubscriber.configuration.async_publisher}' provided"
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,153 +0,0 @@
1
- require "thread"
2
-
3
- module ActionSubscriber
4
- module Publisher
5
- module Async
6
- class InMemoryAdapter
7
- include ::ActionSubscriber::Logging
8
-
9
- attr_reader :async_queue
10
-
11
- def initialize
12
- logger.info "Starting in-memory publisher adapter."
13
-
14
- @async_queue = AsyncQueue.new
15
- end
16
-
17
- def publish(route, payload, exchange_name, options = {})
18
- message = Message.new(route, payload, exchange_name, options)
19
- async_queue.push(message)
20
- nil
21
- end
22
-
23
- def shutdown!
24
- max_wait_time = ::ActionSubscriber.configuration.seconds_to_wait_for_graceful_shutdown
25
- started_shutting_down_at = ::Time.now
26
-
27
- logger.info "Draining async publisher in-memory adapter queue before shutdown. Current queue size: #{async_queue.size}."
28
- while async_queue.size > 0
29
- if (::Time.now - started_shutting_down_at) > max_wait_time
30
- logger.info "Forcing async publisher adapter shutdown because graceful shutdown period of #{max_wait_time} seconds was exceeded. Current queue size: #{async_queue.size}."
31
- break
32
- end
33
-
34
- sleep 0.1
35
- end
36
- end
37
-
38
- class Message
39
- attr_reader :route, :payload, :exchange_name, :options
40
-
41
- def initialize(route, payload, exchange_name, options)
42
- @route = route
43
- @payload = payload
44
- @exchange_name = exchange_name
45
- @options = options
46
- end
47
- end
48
-
49
- class UnableToPersistMessageError < ::StandardError
50
- end
51
-
52
- class AsyncQueue
53
- include ::ActionSubscriber::Logging
54
-
55
- attr_reader :consumer, :queue, :supervisor
56
-
57
- if ::RUBY_PLATFORM == "java"
58
- NETWORK_ERRORS = [::MarchHare::Exception, ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
59
- else
60
- NETWORK_ERRORS = [::Bunny::Exception, ::Timeout::Error, ::IOError].freeze
61
- end
62
-
63
- def initialize
64
- @queue = ::Queue.new
65
- create_and_supervise_consumer!
66
- end
67
-
68
- def push(message)
69
- # Default of 1_000_000 messages.
70
- if queue.size > ::ActionSubscriber.configuration.async_publisher_max_queue_size
71
- # Drop Messages if the queue is full and we were configured to do so.
72
- return if ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full
73
-
74
- # By default we will raise an error to push the responsibility onto the caller.
75
- fail UnableToPersistMessageError, "Queue is full, messages will be dropped."
76
- end
77
-
78
- queue.push(message)
79
- end
80
-
81
- def size
82
- queue.size
83
- end
84
-
85
- private
86
-
87
- def await_network_reconnect
88
- sleep ::ActionSubscriber::RabbitConnection::NETWORK_RECOVERY_INTERVAL
89
- end
90
-
91
- def create_and_supervise_consumer!
92
- @consumer = create_consumer
93
- @supervisor = ::Thread.new do
94
- loop do
95
- unless consumer.alive?
96
- # Why might need to requeue the last message.
97
- queue.push(@current_message) if @current_message.present?
98
- consumer.kill
99
- @consumer = create_consumer
100
- end
101
-
102
- # Pause before checking the consumer again.
103
- sleep supervisor_interval
104
- end
105
- end
106
- end
107
-
108
- def create_consumer
109
- ::Thread.new do
110
- loop do
111
- # Write "current_message" so we can requeue should something happen to the consumer. I don't love this, but it's
112
- # better than writing my own `#peek' method.
113
- @current_message = message = queue.pop
114
-
115
- begin
116
- ::ActionSubscriber::Publisher.publish(message.route, message.payload, message.exchange_name, message.options)
117
-
118
- # Reset
119
- @current_message = nil
120
- rescue *NETWORK_ERRORS
121
- # Sleep because the connection is down.
122
- await_network_reconnect
123
-
124
- # Requeue and try again.
125
- queue.push(message)
126
- rescue => unknown_error
127
- # Do not requeue the message because something else horrible happened.
128
- @current_message = nil
129
-
130
- # Log the error.
131
- logger.info unknown_error.class
132
- logger.info unknown_error.message
133
- logger.info unknown_error.backtrace.join("\n")
134
-
135
- # TODO: Find a way to bubble this out of the thread for logging purposes.
136
- # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
137
- raise unknown_error
138
- end
139
- end
140
- end
141
- end
142
-
143
- def supervisor_interval
144
- @supervisor_interval ||= begin
145
- interval_in_milliseconds = ::ActionSubscriber.configuration.async_publisher_supervisor_interval
146
- interval_in_milliseconds / 1000.0
147
- end
148
- end
149
- end
150
- end
151
- end
152
- end
153
- end
@@ -1,53 +0,0 @@
1
- class InferenceSubscriber < ActionSubscriber::Base
2
- publisher :kyle
3
-
4
- def yo
5
- $messages << payload
6
- end
7
-
8
- queue_for :hey, "some_other_queue.hey"
9
- routing_key_for :hey, "other_routing_key.hey"
10
- def hey
11
- $messages << payload
12
- end
13
- end
14
-
15
- describe "A Subscriber With Inferred Routes", :integration => true do
16
- context "explicit routing with default_routes_for helper" do
17
- let(:draw_routes) do
18
- ::ActionSubscriber.draw_routes do
19
- default_routes_for InferenceSubscriber
20
- end
21
- end
22
-
23
- it "registers the routes and sets up the queues" do
24
- ::ActionSubscriber.auto_subscribe!
25
- ::ActionSubscriber::Publisher.publish("kyle.inference.yo", "YO", "events")
26
- ::ActionSubscriber::Publisher.publish("other_routing_key.hey", "HEY", "events")
27
-
28
- verify_expectation_within(2.0) do
29
- expect($messages).to eq(Set.new(["YO","HEY"]))
30
- end
31
- end
32
- end
33
-
34
- # This is the deprecated behavior we want to keep until version 2.0
35
- context "no explicit routes" do
36
- before do
37
- # TEST HACK: Bust any memoized routes.
38
- ::ActionSubscriber.instance_variable_set(:@route_set, nil)
39
- ::ActionSubscriber.instance_variable_set(:@draw_routes_block, nil)
40
- ::ActionSubscriber.setup_queues!
41
- end
42
-
43
- it "registers the routes and sets up the queues" do
44
- ::ActionSubscriber.auto_subscribe!
45
- ::ActionSubscriber::Publisher.publish("kyle.inference.yo", "YO", "events")
46
- ::ActionSubscriber::Publisher.publish("other_routing_key.hey", "HEY", "events")
47
-
48
- verify_expectation_within(2.0) do
49
- expect($messages).to eq(Set.new(["YO","HEY"]))
50
- end
51
- end
52
- end
53
- end
@@ -1,135 +0,0 @@
1
- describe ::ActionSubscriber::Publisher::Async::InMemoryAdapter do
2
- let(:route) { "test" }
3
- let(:payload) { "message" }
4
- let(:exchange_name) { "place" }
5
- let(:options) { { :test => :ok } }
6
- let(:message) { described_class::Message.new(route, payload, exchange_name, options) }
7
- let(:mock_queue) { double(:push => nil, :size => 0) }
8
-
9
- describe "#publish" do
10
- before do
11
- allow(described_class::Message).to receive(:new).with(route, payload, exchange_name, options).and_return(message)
12
- allow(described_class::AsyncQueue).to receive(:new).and_return(mock_queue)
13
- end
14
-
15
- it "can publish a message to the queue" do
16
- expect(mock_queue).to receive(:push).with(message)
17
- subject.publish(route, payload, exchange_name, options)
18
- end
19
- end
20
-
21
- describe "#shutdown!" do
22
- # This is called when the rspec finishes. I'm sure we can make this a better test.
23
- end
24
-
25
- describe "::ActionSubscriber::Publisher::Async::InMemoryAdapter::Message" do
26
- specify { expect(message.route).to eq(route) }
27
- specify { expect(message.payload).to eq(payload) }
28
- specify { expect(message.exchange_name).to eq(exchange_name) }
29
- specify { expect(message.options).to eq(options) }
30
- end
31
-
32
- describe "::ActionSubscriber::Publisher::Async::InMemoryAdapter::AsyncQueue" do
33
- subject { described_class::AsyncQueue.new }
34
-
35
- describe ".initialize" do
36
- it "creates a supervisor" do
37
- expect_any_instance_of(described_class::AsyncQueue).to receive(:create_and_supervise_consumer!)
38
- subject
39
- end
40
- end
41
-
42
- describe "#create_and_supervise_consumer!" do
43
- it "creates a supervisor" do
44
- expect_any_instance_of(described_class::AsyncQueue).to receive(:create_consumer)
45
- subject
46
- end
47
-
48
- it "restarts the consumer when it dies" do
49
- consumer = subject.consumer
50
- consumer.kill
51
-
52
- verify_expectation_within(0.1) do
53
- expect(consumer).to_not be_alive
54
- end
55
-
56
- verify_expectation_within(0.3) do
57
- expect(subject.consumer).to be_alive
58
- end
59
- end
60
- end
61
-
62
- describe "#create_consumer" do
63
- it "can successfully publish a message" do
64
- expect(::ActionSubscriber::Publisher).to receive(:publish).with(route, payload, exchange_name, options)
65
- subject.push(message)
66
- sleep 0.1 # Await results
67
- end
68
-
69
- context "when network error occurs" do
70
- let(:error) { described_class::AsyncQueue::NETWORK_ERRORS.first }
71
- before { allow(::ActionSubscriber::Publisher).to receive(:publish).and_raise(error) }
72
-
73
- it "requeues the message" do
74
- consumer = subject.consumer
75
- expect(consumer).to be_alive
76
- expect(subject).to receive(:await_network_reconnect).at_least(:once)
77
- subject.push(message)
78
- sleep 0.1 # Await results
79
- end
80
- end
81
-
82
- context "when an unknown error occurs" do
83
- before { allow(::ActionSubscriber::Publisher).to receive(:publish).and_raise(ArgumentError) }
84
-
85
- it "kills the consumer" do
86
- consumer = subject.consumer
87
- expect(consumer).to be_alive
88
- subject.push(message)
89
- sleep 0.1 # Await results
90
- expect(consumer).to_not be_alive
91
- end
92
- end
93
- end
94
-
95
- describe "#push" do
96
- after { ::ActionSubscriber.configuration.async_publisher_max_queue_size = 1000 }
97
- after { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = false }
98
-
99
- context "when the queue has room" do
100
- before { allow(::Queue).to receive(:new).and_return(mock_queue) }
101
-
102
- it "successfully adds to the queue" do
103
- expect(mock_queue).to receive(:push).with(message)
104
- subject.push(message)
105
- end
106
- end
107
-
108
- context "when the queue is full" do
109
- before { ::ActionSubscriber.configuration.async_publisher_max_queue_size = -1 }
110
-
111
- context "and we're dropping messages" do
112
- before { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = true }
113
-
114
- it "adding to the queue should not raise an error" do
115
- expect { subject.push(message) }.to_not raise_error
116
- end
117
- end
118
-
119
- context "and we're not dropping messages" do
120
- before { ::ActionSubscriber.configuration.async_publisher_drop_messages_when_queue_full = false }
121
-
122
- it "adding to the queue should raise error back to caller" do
123
- expect { subject.push(message) }.to raise_error(described_class::UnableToPersistMessageError)
124
- end
125
- end
126
- end
127
- end
128
-
129
- describe "#size" do
130
- it "can return the size of the queue" do
131
- expect(subject.size).to eq(0)
132
- end
133
- end
134
- end
135
- end