active_publisher 1.2.0.pre-java → 1.2.5-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
  SHA256:
3
- metadata.gz: 205bf97d0f41e320989b1399fcb27607d0a8090c67de4cefa91077b999738244
4
- data.tar.gz: fb7f739ec964c77f51554d14c53afc200d1aa8adce0a5c114189dbe1943424cb
3
+ metadata.gz: 7a878924c71e32c34435e80052a8f241b357d2f7b70f3582f7b8920b942e63c0
4
+ data.tar.gz: 60b4eb97d20e389ce4865ef393d69a7d49bdac2e47a3aa26eb6e7ca32e459335
5
5
  SHA512:
6
- metadata.gz: 77f537b065063574df42f2a2926048bed6e69fc2f72608b9be5030d5af3ab78bb1c8c67ed1369911422b7b04a757191d2c5f1c5aeb1b4149518574d185306482
7
- data.tar.gz: 4624d5868cc61dc97b5cbd6acd61e798f6b5c02f567f6baf3279831e79b5c0f1ed6bca90a5e1c586d4ed2bb8eb49ae73350d9d3a80bd3433ede1804a8c82a6d2
6
+ metadata.gz: 8f6399701888401993164b31ccb4fff4f14eb13414a7586d7baacac0c49f2c876dfe33035ce091ec4b62ee4a5b64ad961a7deab79691098401388153404c724b
7
+ data.tar.gz: 2a1d1347293457b9e5b663d7f4815a176e8d9afdc80e472893c4f1f5206bdfc057bfb6d692067924727aac1e3a6f42bc2fff20cd13698bca12be61e1b72d84bc
@@ -0,0 +1,56 @@
1
+ # Inspired by: http://mikebian.co/running-tests-against-multiple-ruby-versions-using-circleci/
2
+
3
+ version: 2.1
4
+
5
+ orbs:
6
+ ruby: circleci/ruby@1.1
7
+
8
+ jobs:
9
+ test:
10
+ parallelism: 1
11
+ parameters:
12
+ ruby-image:
13
+ type: string
14
+ docker:
15
+ - image: << parameters.ruby-image >>
16
+ - image: rabbitmq
17
+
18
+ steps:
19
+ - checkout
20
+ - run:
21
+ name: install dockerize
22
+ command: wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz && rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz
23
+ environment:
24
+ DOCKERIZE_VERSION: v0.3.0
25
+ - run:
26
+ name: Wait for rabbitmq
27
+ command: dockerize -wait tcp://localhost:5672 -timeout 1m
28
+ - run:
29
+ name: Install bundler
30
+ command: gem install bundler
31
+ - run:
32
+ name: Which bundler?
33
+ command: bundle -v
34
+ - run:
35
+ name: bundle install
36
+ command: bundle install
37
+ - run:
38
+ name: rspec
39
+ command: bundle exec rspec
40
+
41
+ # strangely, there seems to be very little documentation about exactly how martix builds work.
42
+ # By defining a param inside your job definition, Circle CI will automatically spawn a job for
43
+ # unique param value passed via `matrix`. Neat!
44
+ # https://circleci.com/blog/circleci-matrix-jobs/
45
+ workflows:
46
+ build_and_test:
47
+ jobs:
48
+ - test:
49
+ matrix:
50
+ parameters:
51
+ ruby-image:
52
+ - circleci/ruby:2.5
53
+ - circleci/ruby:2.6
54
+ - circleci/ruby:2.7
55
+ - circleci/jruby:9.1
56
+ - circleci/jruby:9.2
@@ -35,6 +35,6 @@ Gem::Specification.new do |spec|
35
35
  spec.add_development_dependency "connection_pool"
36
36
  spec.add_development_dependency "fakeredis"
37
37
  spec.add_development_dependency "pry"
38
- spec.add_development_dependency "rake", "~> 10.0"
38
+ spec.add_development_dependency "rake"
39
39
  spec.add_development_dependency "rspec", "~> 3.2"
40
40
  end
@@ -35,7 +35,7 @@ module ActivePublisher
35
35
  # @param [Hash] options hash to set message parameters (e.g. headers)
36
36
  def self.publish(route, payload, exchange_name, options = {})
37
37
  with_exchange(exchange_name) do |exchange|
38
- ::ActiveSupport::Notifications.instrument "message_published.active_publisher" do
38
+ ::ActiveSupport::Notifications.instrument "message_published.active_publisher", :route => route do
39
39
  exchange.publish(payload, publishing_options(route, options))
40
40
  end
41
41
  end
@@ -51,7 +51,7 @@ module ActivePublisher
51
51
  fail ActivePublisher::ExchangeMismatchError, "bulk publish messages must match publish_all exchange_name" if message.exchange_name != exchange_name
52
52
 
53
53
  begin
54
- ::ActiveSupport::Notifications.instrument "message_published.active_publisher" do
54
+ ::ActiveSupport::Notifications.instrument "message_published.active_publisher", :route => message.route do
55
55
  exchange.publish(message.payload, publishing_options(message.route, message.options || {}))
56
56
  end
57
57
  rescue
@@ -2,7 +2,13 @@ module ActivePublisher
2
2
  module Async
3
3
  module InMemoryAdapter
4
4
  class ConsumerThread
5
- attr_reader :thread, :queue, :sampled_queue_size, :last_tick_at
5
+ attr_reader :channel, :thread, :queue, :sampled_queue_size, :last_tick_at
6
+
7
+ if ::RUBY_PLATFORM == "java"
8
+ CHANNEL_CLOSED_ERRORS = [::MarchHare::ChannelAlreadyClosed]
9
+ else
10
+ CHANNEL_CLOSED_ERRORS = [::Bunny::ChannelAlreadyClosed]
11
+ end
6
12
 
7
13
  if ::RUBY_PLATFORM == "java"
8
14
  NETWORK_ERRORS = [::MarchHare::NetworkException, ::MarchHare::ConnectionRefused,
@@ -45,6 +51,32 @@ module ActivePublisher
45
51
  end
46
52
  end
47
53
 
54
+ def cleanup_up_channel
55
+ return if channel.nil?
56
+ channel.close
57
+ rescue => error
58
+ ::ActivePublisher.configuration.error_handler.call(error, {:status => "Cleaning up the channel"})
59
+ end
60
+
61
+ def handle_current_messages_on_unknown_error(current_messages)
62
+ current_messages.each do |message|
63
+ # Degrade to single message publish ... or at least attempt to
64
+ begin
65
+ ::ActivePublisher.publish(message.route, message.payload, message.exchange_name, message.options)
66
+ current_messages.delete(message)
67
+ rescue *CHANNEL_CLOSED_ERRORS
68
+ # If the channel is bad, raise!
69
+ raise
70
+ rescue *PRECONDITION_ERRORS => error
71
+ # Delete messages if rabbitmq cannot declare the exchange (or somet other precondition failed).
72
+ ::ActivePublisher.configuration.error_handler.call(error, {:reason => "precondition failed", :message => message})
73
+ current_messages.delete(message)
74
+ rescue => other_error
75
+ ::ActivePublisher.configuration.error_handler.call(other_error, {:route => message.route, :payload => message.payload, :exchange_name => message.exchange_name, :options => message.options})
76
+ end
77
+ end
78
+ end
79
+
48
80
  def make_channel
49
81
  channel = ::ActivePublisher::Async::InMemoryAdapter::Channel.new
50
82
  channel.confirm_select if ::ActivePublisher.configuration.publisher_confirms
@@ -57,70 +89,66 @@ module ActivePublisher
57
89
 
58
90
  def start_thread
59
91
  return if alive?
60
- @thread = ::Thread.new do
61
- loop do
62
- # Sample the queue size so we don't shutdown when messages are in flight.
63
- @sampled_queue_size = queue.size
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) }
71
-
72
- begin
73
- @channel ||= make_channel
74
-
75
- # Only open a single connection for each group of messages to an exchange
76
- current_messages.group_by(&:exchange_name).each do |exchange_name, messages|
77
- publish_all(@channel, exchange_name, messages)
78
- current_messages -= messages
79
- end
80
- rescue *NETWORK_ERRORS
81
- # Sleep because connection is down
82
- await_network_reconnect
83
- rescue => unknown_error
84
- ::ActivePublisher.configuration.error_handler.call(unknown_error, {:number_of_messages => current_messages.size})
85
- current_messages.each do |message|
86
- # Degrade to single message publish ... or at least attempt to
87
- begin
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)
94
- rescue => individual_error
95
- ::ActivePublisher.configuration.error_handler.call(individual_error, {:route => message.route, :payload => message.payload, :exchange_name => message.exchange_name, :options => message.options})
96
- end
97
- end
98
-
99
- # TODO: Find a way to bubble this out of the thread for logging purposes.
100
- # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
101
- raise unknown_error
102
- ensure
103
- # Always requeue anything that gets stuck.
104
- queue.concat(current_messages) unless current_messages.nil?
92
+ @thread = ::Thread.new { start_consuming_thread }
93
+ end
94
+
95
+ def start_consuming_thread
96
+ loop do
97
+ # Sample the queue size so we don't shutdown when messages are in flight.
98
+ @sampled_queue_size = queue.size
99
+ current_messages = queue.pop_up_to(50, :timeout => 0.1)
100
+ update_last_tick_at
101
+ # If the queue is empty, we should continue to update to "last_tick_at" time.
102
+ next if current_messages.nil?
103
+
104
+ @channel ||= make_channel
105
+
106
+ # We only look at active publisher messages. Everything else is dropped.
107
+ current_messages.select! { |message| message.is_a?(::ActivePublisher::Message) }
108
+
109
+ begin
110
+ # Only open a single connection for each group of messages to an exchange
111
+ current_messages.group_by(&:exchange_name).each do |exchange_name, messages|
112
+ publish_all(exchange_name, messages)
113
+ current_messages -= messages
105
114
  end
115
+ rescue *CHANNEL_CLOSED_ERRORS
116
+ # If the channel is bad, raise without sending one-by-one!
117
+ raise
118
+ rescue *NETWORK_ERRORS
119
+ # Sleep because connection is down
120
+ await_network_reconnect
121
+ rescue => unknown_error
122
+ ::ActivePublisher.configuration.error_handler.call(unknown_error, {:number_of_messages => current_messages.size})
123
+
124
+ # Attempt to deliver a message one-by-one. Raise if a closed channel error appears.
125
+ handle_current_messages_on_unknown_error(current_messages)
126
+
127
+ # TODO: Find a way to bubble this out of the thread for logging purposes.
128
+ # Reraise the error out of the publisher loop. The Supervisor will restart the consumer.
129
+ raise unknown_error
130
+ ensure
131
+ # Always requeue anything that gets stuck.
132
+ queue.concat(current_messages) if current_messages && !current_messages.empty?
106
133
  end
107
134
  end
135
+ ensure
136
+ cleanup_up_channel
108
137
  end
109
138
 
110
- def publish_all(channel, exchange_name, messages)
111
- ::ActiveSupport::Notifications.instrument "message_published.active_publisher", :message_count => messages.size do
112
- exchange = channel.topic(exchange_name)
113
- messages.each do |message|
114
- fail ActivePublisher::ExchangeMismatchError, "bulk publish messages must match publish_all exchange_name" if message.exchange_name != exchange_name
115
-
139
+ def publish_all(exchange_name, messages)
140
+ exchange = channel.topic(exchange_name)
141
+ messages.each do |message|
142
+ fail ::ActivePublisher::ExchangeMismatchError, "bulk publish messages must match publish_all exchange_name" if message.exchange_name != exchange_name
143
+ ::ActiveSupport::Notifications.instrument "message_published.active_publisher", :route => message.route, :message_count => 1 do
116
144
  options = ::ActivePublisher.publishing_options(message.route, message.options || {})
117
145
  exchange.publish(message.payload, options)
118
146
  end
119
- wait_for_confirms(channel)
120
147
  end
148
+ wait_for_confirms
121
149
  end
122
150
 
123
- def wait_for_confirms(channel)
151
+ def wait_for_confirms
124
152
  return true unless channel.using_publisher_confirms?
125
153
  channel.wait_for_confirms(::ActivePublisher.configuration.publisher_confirms_timeout)
126
154
  end
@@ -1,3 +1,4 @@
1
+ require "active_publisher"
1
2
  require "active_publisher/message"
2
3
  require "active_publisher/async/redis_adapter/consumer"
3
4
  require "multi_op_queue"
@@ -64,7 +65,7 @@ module ActivePublisher
64
65
  return unless encoded_messages.size > 0
65
66
 
66
67
  redis_pool.with do |redis|
67
- redis.lpush(::ActivePublisher::Async::RedisAdapter::REDIS_LIST_KEY, encoded_messages)
68
+ redis.rpush(::ActivePublisher::Async::RedisAdapter::REDIS_LIST_KEY, encoded_messages)
68
69
  end
69
70
  end
70
71
  end
@@ -32,8 +32,11 @@ module ActivePublisher
32
32
 
33
33
  supervisor_task.execute
34
34
  end
35
- end
36
35
 
36
+ def size
37
+ queue.size
38
+ end
39
+ end
37
40
  end
38
41
  end
39
42
  end
@@ -13,13 +13,14 @@ module ActivePublisher
13
13
  encoded_message = ::Marshal.dump(message)
14
14
 
15
15
  redis_pool.with do |redis|
16
- redis.lpush(list_key, encoded_message)
16
+ redis.rpush(list_key, encoded_message)
17
17
  end
18
18
  end
19
19
 
20
20
  def concat(*messages)
21
21
  messages = messages.flatten
22
22
  messages.compact!
23
+ return if messages.empty?
23
24
 
24
25
  encoded_messages = []
25
26
  messages.each do |message|
@@ -27,7 +28,7 @@ module ActivePublisher
27
28
  end
28
29
 
29
30
  redis_pool.with do |redis|
30
- redis.lpush(list_key, encoded_messages)
31
+ redis.rpush(list_key, encoded_messages)
31
32
  end
32
33
  end
33
34
 
@@ -70,20 +71,34 @@ module ActivePublisher
70
71
  end
71
72
 
72
73
  def shift(number)
73
- messages = []
74
74
  number = [number, size].min
75
+ return [] if number <= 0
76
+
77
+ messages = []
78
+ multi_response = []
75
79
  redis_pool.with do |redis|
76
- redis.pipelined do
77
- number.times do
78
- messages << redis.rpop(list_key)
79
- end
80
+ multi_response = redis.multi do
81
+ redis.lrange(list_key, 0, number - 1)
82
+ redis.ltrim(list_key, number, -1)
80
83
  end
81
84
  end
82
85
 
86
+ messages = multi_response.first
87
+ success = multi_response.last
88
+ return [] if multi_response.size != 2 || success.nil?
89
+ return [] unless success =~ /ok/i
90
+
83
91
  messages = [] if messages.nil?
84
92
  messages = [messages] unless messages.respond_to?(:each)
85
- messages.compact!
86
- messages.map { |message| ::Marshal.load(message.value) }
93
+
94
+ shifted_messages = []
95
+ messages.each do |message|
96
+ next if message.nil?
97
+
98
+ shifted_messages << ::Marshal.load(message)
99
+ end
100
+
101
+ shifted_messages
87
102
  end
88
103
 
89
104
  def size
@@ -63,8 +63,8 @@ module ActivePublisher
63
63
 
64
64
  yaml_config = attempt_to_load_yaml_file(env)
65
65
  DEFAULTS.each_pair do |key, value|
66
- setting = cli_options[key] || cli_options[key.to_s] || yaml_config[key] || yaml_config[key.to_s]
67
- ::ActivePublisher.configuration.public_send("#{key}=", setting) if setting
66
+ exists, setting = fetch_config_value(key, cli_options, yaml_config)
67
+ ::ActivePublisher.configuration.public_send("#{key}=", setting) if exists
68
68
  end
69
69
 
70
70
  true
@@ -88,6 +88,15 @@ module ActivePublisher
88
88
  end
89
89
  private_class_method :attempt_to_load_yaml_file
90
90
 
91
+ def self.fetch_config_value(key, cli_options, yaml_config)
92
+ return [true, cli_options[key]] if cli_options.key?(key)
93
+ return [true, cli_options[key.to_s]] if cli_options.key?(key.to_s)
94
+ return [true, yaml_config[key]] if yaml_config.key?(key)
95
+ return [true, yaml_config[key.to_s]] if yaml_config.key?(key.to_s)
96
+ [false, nil]
97
+ end
98
+ private_class_method :fetch_config_value
99
+
91
100
  ##
92
101
  # Instance Methods
93
102
  #
@@ -31,9 +31,22 @@ module ActivePublisher
31
31
  def self.create_connection
32
32
  if ::RUBY_PLATFORM == "java"
33
33
  connection = ::MarchHare.connect(connection_options)
34
+ connection.on_blocked do |reason|
35
+ on_blocked(reason)
36
+ end
37
+ connection.on_unblocked do
38
+ on_unblocked
39
+ end
40
+ connection
34
41
  else
35
42
  connection = ::Bunny.new(connection_options)
36
43
  connection.start
44
+ connection.on_blocked do |blocked_message|
45
+ on_blocked(blocked_message.reason)
46
+ end
47
+ connection.on_unblocked do
48
+ on_unblocked
49
+ end
37
50
  connection
38
51
  end
39
52
  end
@@ -58,5 +71,15 @@ module ActivePublisher
58
71
  }
59
72
  end
60
73
  private_class_method :connection_options
74
+
75
+ def self.on_blocked(reason)
76
+ ::ActiveSupport::Notifications.instrument("connection_blocked.active_publisher", :reason => reason)
77
+ end
78
+ private_class_method :on_blocked
79
+
80
+ def self.on_unblocked
81
+ ::ActiveSupport::Notifications.instrument("connection_unblocked.active_publisher")
82
+ end
83
+ private_class_method :on_unblocked
61
84
  end
62
85
  end
@@ -1,3 +1,3 @@
1
1
  module ActivePublisher
2
- VERSION = "1.2.0.pre"
2
+ VERSION = "1.2.5"
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.2.0.pre
4
+ version: 1.2.5
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: 2018-01-16 00:00:00.000000000 Z
15
+ date: 2020-11-23 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  requirement: !ruby/object:Gem::Requirement
@@ -143,17 +143,17 @@ dependencies:
143
143
  - !ruby/object:Gem::Dependency
144
144
  requirement: !ruby/object:Gem::Requirement
145
145
  requirements:
146
- - - "~>"
146
+ - - ">="
147
147
  - !ruby/object:Gem::Version
148
- version: '10.0'
148
+ version: '0'
149
149
  name: rake
150
150
  prerelease: false
151
151
  type: :development
152
152
  version_requirements: !ruby/object:Gem::Requirement
153
153
  requirements:
154
- - - "~>"
154
+ - - ">="
155
155
  - !ruby/object:Gem::Version
156
- version: '10.0'
156
+ version: '0'
157
157
  - !ruby/object:Gem::Dependency
158
158
  requirement: !ruby/object:Gem::Requirement
159
159
  requirements:
@@ -179,9 +179,9 @@ executables: []
179
179
  extensions: []
180
180
  extra_rdoc_files: []
181
181
  files:
182
+ - ".circleci/config.yml"
182
183
  - ".gitignore"
183
184
  - ".rspec"
184
- - ".travis.yml"
185
185
  - CODE_OF_CONDUCT.md
186
186
  - Gemfile
187
187
  - LICENSE.txt
@@ -219,12 +219,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
219
219
  version: '0'
220
220
  required_rubygems_version: !ruby/object:Gem::Requirement
221
221
  requirements:
222
- - - ">"
222
+ - - ">="
223
223
  - !ruby/object:Gem::Version
224
- version: 1.3.1
224
+ version: '0'
225
225
  requirements: []
226
226
  rubyforge_project:
227
- rubygems_version: 2.6.11
227
+ rubygems_version: 2.7.9
228
228
  signing_key:
229
229
  specification_version: 4
230
230
  summary: Aims to make publishing work across MRI and jRuby painless and add some nice
@@ -1,9 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.3
4
- - 2.4
5
- - jruby-9.1.12.0
6
- services:
7
- - rabbitmq
8
- sudo: false
9
- cache: bundler