active_publisher 1.0.3-java → 1.1.0-java

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: c842c3825c85ddd417ae7b44992f0dd116e04952
4
- data.tar.gz: 52903e7be84dc6295e68446fdb80e1d6cf898421
3
+ metadata.gz: bbb7f6d380395ad17ca81b0128f6ff2cec2e0e98
4
+ data.tar.gz: 6ce766a9e768c7adc0834f94dd10473e73a8a144
5
5
  SHA512:
6
- metadata.gz: 7a5e4ec82f01a97f3c904ebaf1f3501a45857a072eb245f24a1a717479483cd4a452d8338beddb9d05bf786164d076a185270daa47a803c2ee36e4efe4598cd6
7
- data.tar.gz: 64521da6574d628d62a26fa98bcbfedf87ac54ddeca47fd9a791b3eaed545fcc867d4861ff6f0f509cfcc9e6f8dd327ac20854bc7321bfbced039ce7dbbbebb8
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
-
@@ -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 'multi_op_queue', '>= 0.1.2'
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
- # Notify the current queue size.
71
- ::ActiveSupport::Notifications.instrument "async_queue_size.active_publisher", queue.size
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
- # Pause before checking the consumer again.
74
- sleep supervisor_interval
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::Exception, ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
8
+ NETWORK_ERRORS = [::MarchHare::NetworkException, ::MarchHare::ConnectionRefused,
9
+ ::Java::ComRabbitmqClient::AlreadyClosedException, ::Java::JavaIo::IOException].freeze
9
10
  else
10
- NETWORK_ERRORS = [::Bunny::Exception, ::Timeout::Error, ::IOError].freeze
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
- begin
58
- current_messages -= messages
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
- potentially_retry = []
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
- begin
101
- options = ::ActivePublisher.publishing_options(message.route, message.options || {})
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, messages, potentially_retry)
119
+ wait_for_confirms(channel)
110
120
  end
111
121
  end
112
122
 
113
- def wait_for_confirms(channel, messages, potentially_retry)
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
@@ -1,6 +1,7 @@
1
1
  require "active_publisher/message"
2
2
  require "active_publisher/async/in_memory_adapter/async_queue"
3
3
  require "active_publisher/async/in_memory_adapter/consumer_thread"
4
+ require "concurrent"
4
5
  require "multi_op_queue"
5
6
 
6
7
  module ActivePublisher
@@ -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,
@@ -1,3 +1,3 @@
1
1
  module ActivePublisher
2
- VERSION = "1.0.3"
2
+ VERSION = "1.1.0"
3
3
  end
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.3
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: 2017-12-08 00:00:00.000000000 Z
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.1.2
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.1.2
72
+ version: 0.2.0
59
73
  - !ruby/object:Gem::Dependency
60
74
  requirement: !ruby/object:Gem::Requirement
61
75
  requirements: