action_subscriber 2.5.0.pre-java → 3.0.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.
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