active_publisher 1.0.3-java → 1.1.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/README.md +2 -1
- data/active_publisher.gemspec +2 -1
- data/lib/active_publisher/async/in_memory_adapter/async_queue.rb +15 -10
- data/lib/active_publisher/async/in_memory_adapter/consumer_thread.rb +39 -32
- data/lib/active_publisher/async/in_memory_adapter.rb +1 -0
- data/lib/active_publisher/configuration.rb +2 -0
- data/lib/active_publisher/version.rb +1 -1
- metadata +18 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bbb7f6d380395ad17ca81b0128f6ff2cec2e0e98
|
4
|
+
data.tar.gz: 6ce766a9e768c7adc0834f94dd10473e73a8a144
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ad04a249d94c9cb8fc91b5c195ddab86e3a6788e0512c9101d6652f0b7e3d5438b100951c50dc9f3c1406c24a3e847168cd6057ae5ba1ea76b0adb1841e084c5
|
7
|
+
data.tar.gz: d10b6b66a2239e5076ef55e78dcac184d9937c0f114423a1399adce9812b49f9cbd1428d7a7aa46fe9ac63a4e309c1e2bd23e4a0ef5588d438391a90f6ace209
|
data/README.md
CHANGED
@@ -51,6 +51,8 @@ Defaults for the configuration are:
|
|
51
51
|
:heartbeat => 5,
|
52
52
|
:host => "localhost",
|
53
53
|
:hosts => [],
|
54
|
+
:max_async_publisher_lag_time => 10,
|
55
|
+
:network_recovery_interval => 1,
|
54
56
|
:password => "guest",
|
55
57
|
:port => 5672,
|
56
58
|
:publisher_confirms => false,
|
@@ -110,4 +112,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERN
|
|
110
112
|
## License
|
111
113
|
|
112
114
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
113
|
-
|
data/active_publisher.gemspec
CHANGED
@@ -27,7 +27,8 @@ Gem::Specification.new do |spec|
|
|
27
27
|
end
|
28
28
|
|
29
29
|
spec.add_dependency 'activesupport', '>= 3.2'
|
30
|
-
spec.add_dependency '
|
30
|
+
spec.add_dependency 'concurrent-ruby'
|
31
|
+
spec.add_dependency 'multi_op_queue', '>= 0.2.0'
|
31
32
|
|
32
33
|
spec.add_development_dependency "bundler"
|
33
34
|
spec.add_development_dependency "pry"
|
@@ -60,20 +60,25 @@ module ActivePublisher
|
|
60
60
|
|
61
61
|
def create_and_supervise_consumer!
|
62
62
|
@consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
|
63
|
-
@supervisor = ::Thread.new do
|
64
|
-
loop do
|
65
|
-
unless consumer.alive?
|
66
|
-
consumer.kill
|
67
|
-
@consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
|
68
|
-
end
|
69
63
|
|
70
|
-
|
71
|
-
|
64
|
+
supervisor_task = ::Concurrent::TimerTask.new(:execution_interval => supervisor_interval) do
|
65
|
+
current_time = ::Time.now
|
66
|
+
|
67
|
+
# Consumer is lagging if it does not "tick" at least once every 10 seconds.
|
68
|
+
seconds_since_last_tick = current_time - consumer.last_tick_at
|
69
|
+
consumer_is_lagging = seconds_since_last_tick > ::ActivePublisher.configuration.max_async_publisher_lag_time
|
70
|
+
logger.error "ActivePublisher consumer is lagging. Last consumer tick was #{seconds_since_last_tick} seconds ago." if consumer_is_lagging
|
72
71
|
|
73
|
-
|
74
|
-
|
72
|
+
# Check to see if we should restart the consumer.
|
73
|
+
if !consumer.alive? || consumer_is_lagging
|
74
|
+
consumer.kill
|
75
|
+
@consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
|
75
76
|
end
|
77
|
+
|
78
|
+
# Notify the current queue size.
|
79
|
+
::ActiveSupport::Notifications.instrument "async_queue_size.active_publisher", queue.size
|
76
80
|
end
|
81
|
+
supervisor_task.execute
|
77
82
|
end
|
78
83
|
end
|
79
84
|
|
@@ -2,17 +2,27 @@ module ActivePublisher
|
|
2
2
|
module Async
|
3
3
|
module InMemoryAdapter
|
4
4
|
class ConsumerThread
|
5
|
-
attr_reader :thread, :queue, :sampled_queue_size
|
5
|
+
attr_reader :thread, :queue, :sampled_queue_size, :last_tick_at
|
6
6
|
|
7
7
|
if ::RUBY_PLATFORM == "java"
|
8
|
-
NETWORK_ERRORS = [::MarchHare::
|
8
|
+
NETWORK_ERRORS = [::MarchHare::NetworkException, ::MarchHare::ConnectionRefused,
|
9
|
+
::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
|
9
10
|
else
|
10
|
-
NETWORK_ERRORS = [::Bunny::
|
11
|
+
NETWORK_ERRORS = [::Bunny::NetworkFailure, ::Bunny::TCPConnectionFailed, ::Bunny::ConnectionTimeout,
|
12
|
+
::Timeout::Error, ::IOError].freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
if ::RUBY_PLATFORM == "java"
|
16
|
+
PRECONDITION_ERRORS = [::MarchHare::PreconditionFailed]
|
17
|
+
else
|
18
|
+
PRECONDITION_ERRORS = [::Bunny::PreconditionFailed]
|
11
19
|
end
|
12
20
|
|
13
21
|
def initialize(listen_queue)
|
14
22
|
@queue = listen_queue
|
15
23
|
@sampled_queue_size = queue.size
|
24
|
+
|
25
|
+
update_last_tick_at
|
16
26
|
start_thread
|
17
27
|
end
|
18
28
|
|
@@ -41,38 +51,46 @@ module ActivePublisher
|
|
41
51
|
channel
|
42
52
|
end
|
43
53
|
|
54
|
+
def update_last_tick_at
|
55
|
+
@last_tick_at = ::Time.now
|
56
|
+
end
|
57
|
+
|
44
58
|
def start_thread
|
45
59
|
return if alive?
|
46
60
|
@thread = ::Thread.new do
|
47
61
|
loop do
|
48
62
|
# Sample the queue size so we don't shutdown when messages are in flight.
|
49
63
|
@sampled_queue_size = queue.size
|
50
|
-
current_messages = queue.pop_up_to(50)
|
64
|
+
current_messages = queue.pop_up_to(50, :timeout => 0.1)
|
65
|
+
update_last_tick_at
|
66
|
+
# If the queue is empty, we should continue to update to "last_tick_at" time.
|
67
|
+
next if current_messages.nil?
|
68
|
+
|
69
|
+
# We only look at active publisher messages. Everything else is dropped.
|
70
|
+
current_messages.select! { |message| message.is_a?(::ActivePublisher::Message) }
|
51
71
|
|
52
72
|
begin
|
53
73
|
@channel ||= make_channel
|
54
74
|
|
55
75
|
# Only open a single connection for each group of messages to an exchange
|
56
76
|
current_messages.group_by(&:exchange_name).each do |exchange_name, messages|
|
57
|
-
|
58
|
-
|
59
|
-
publish_all(@channel, exchange_name, messages)
|
60
|
-
ensure
|
61
|
-
current_messages.concat(messages)
|
62
|
-
end
|
77
|
+
publish_all(@channel, exchange_name, messages)
|
78
|
+
current_messages -= messages
|
63
79
|
end
|
64
80
|
rescue *NETWORK_ERRORS
|
65
81
|
# Sleep because connection is down
|
66
82
|
await_network_reconnect
|
67
|
-
|
68
|
-
# Requeue and try again.
|
69
|
-
queue.concat(current_messages)
|
70
83
|
rescue => unknown_error
|
71
84
|
::ActivePublisher.configuration.error_handler.call(unknown_error, {:number_of_messages => current_messages.size})
|
72
85
|
current_messages.each do |message|
|
73
86
|
# Degrade to single message publish ... or at least attempt to
|
74
87
|
begin
|
75
88
|
::ActivePublisher.publish(message.route, message.payload, message.exchange_name, message.options)
|
89
|
+
current_messages.delete(message)
|
90
|
+
rescue *PRECONDITION_ERRORS => error
|
91
|
+
# Delete messages if rabbitmq cannot declare the exchange (or somet other precondition failed).
|
92
|
+
::ActivePublisher.configuration.error_handler.call(error, {:reason => "precondition failed", :message => message})
|
93
|
+
current_messages.delete(message)
|
76
94
|
rescue => individual_error
|
77
95
|
::ActivePublisher.configuration.error_handler.call(individual_error, {:route => message.route, :payload => message.payload, :exchange_name => message.exchange_name, :options => message.options})
|
78
96
|
end
|
@@ -81,6 +99,9 @@ module ActivePublisher
|
|
81
99
|
# TODO: Find a way to bubble this out of the thread for logging purposes.
|
82
100
|
# Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
|
83
101
|
raise unknown_error
|
102
|
+
ensure
|
103
|
+
# Always requeue anything that gets stuck.
|
104
|
+
queue.concat(current_messages) unless current_messages.nil?
|
84
105
|
end
|
85
106
|
end
|
86
107
|
end
|
@@ -89,37 +110,23 @@ module ActivePublisher
|
|
89
110
|
def publish_all(channel, exchange_name, messages)
|
90
111
|
::ActiveSupport::Notifications.instrument "message_published.active_publisher", :message_count => messages.size do
|
91
112
|
exchange = channel.topic(exchange_name)
|
92
|
-
|
93
|
-
loop do
|
94
|
-
break if messages.empty?
|
95
|
-
message = messages.shift
|
96
|
-
|
97
|
-
fail ActivePublisher::UnknownMessageClassError, "bulk publish messages must be ActivePublisher::Message" unless message.is_a?(ActivePublisher::Message)
|
113
|
+
messages.each do |message|
|
98
114
|
fail ActivePublisher::ExchangeMismatchError, "bulk publish messages must match publish_all exchange_name" if message.exchange_name != exchange_name
|
99
115
|
|
100
|
-
|
101
|
-
|
102
|
-
exchange.publish(message.payload, options)
|
103
|
-
potentially_retry << message
|
104
|
-
rescue
|
105
|
-
messages << message
|
106
|
-
raise
|
107
|
-
end
|
116
|
+
options = ::ActivePublisher.publishing_options(message.route, message.options || {})
|
117
|
+
exchange.publish(message.payload, options)
|
108
118
|
end
|
109
|
-
wait_for_confirms(channel
|
119
|
+
wait_for_confirms(channel)
|
110
120
|
end
|
111
121
|
end
|
112
122
|
|
113
|
-
def wait_for_confirms(channel
|
123
|
+
def wait_for_confirms(channel)
|
114
124
|
return true unless channel.using_publisher_confirms?
|
115
125
|
if channel.method(:wait_for_confirms).arity > 0
|
116
126
|
channel.wait_for_confirms(::ActivePublisher.configuration.publisher_confirms_timeout)
|
117
127
|
else
|
118
128
|
channel.wait_for_confirms
|
119
129
|
end
|
120
|
-
rescue
|
121
|
-
messages.concat(potentially_retry)
|
122
|
-
raise
|
123
130
|
end
|
124
131
|
end
|
125
132
|
end
|
@@ -6,6 +6,7 @@ module ActivePublisher
|
|
6
6
|
:heartbeat,
|
7
7
|
:host,
|
8
8
|
:hosts,
|
9
|
+
:max_async_publisher_lag_time,
|
9
10
|
:network_recovery_interval,
|
10
11
|
:password,
|
11
12
|
:port,
|
@@ -34,6 +35,7 @@ module ActivePublisher
|
|
34
35
|
:host => "localhost",
|
35
36
|
:hosts => [],
|
36
37
|
:password => "guest",
|
38
|
+
:max_async_publisher_lag_time => 10,
|
37
39
|
:network_recovery_interval => NETWORK_RECOVERY_INTERVAL,
|
38
40
|
:port => 5672,
|
39
41
|
:publisher_confirms => false,
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_publisher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: java
|
6
6
|
authors:
|
7
7
|
- Brian Stien
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: exe
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2018-01-08 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
requirement: !ruby/object:Gem::Requirement
|
@@ -47,7 +47,21 @@ dependencies:
|
|
47
47
|
requirements:
|
48
48
|
- - ">="
|
49
49
|
- !ruby/object:Gem::Version
|
50
|
-
version: 0
|
50
|
+
version: '0'
|
51
|
+
name: concurrent-ruby
|
52
|
+
prerelease: false
|
53
|
+
type: :runtime
|
54
|
+
version_requirements: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
- !ruby/object:Gem::Dependency
|
60
|
+
requirement: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 0.2.0
|
51
65
|
name: multi_op_queue
|
52
66
|
prerelease: false
|
53
67
|
type: :runtime
|
@@ -55,7 +69,7 @@ dependencies:
|
|
55
69
|
requirements:
|
56
70
|
- - ">="
|
57
71
|
- !ruby/object:Gem::Version
|
58
|
-
version: 0.
|
72
|
+
version: 0.2.0
|
59
73
|
- !ruby/object:Gem::Dependency
|
60
74
|
requirement: !ruby/object:Gem::Requirement
|
61
75
|
requirements:
|