active_publisher 1.1.1.pre0-java → 1.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 67b8fcd037ff45746b81591c39dda1a031a91c73
4
- data.tar.gz: 48d2f53ffac8ab519c618bb860d2760812f14a63
2
+ SHA256:
3
+ metadata.gz: 9feb8c420d646d0eda3073f0f1971f9b616087667229ed178bde560b508b7d19
4
+ data.tar.gz: 5601e3a0bcd7d8497192193bca84bae8153feef915806de4bcc90db966e579f4
5
5
  SHA512:
6
- metadata.gz: 86df2154a9225fcc25a02081da27dc5b5c2753beb18d167edde392d394e96add3bfdfe3c93803d765c73f7ddf7b1ac3d99cd1aa936a8ae33a48d79be5dc9a17c
7
- data.tar.gz: 655086d301b518f5e6747eef10972529943c02af529e3097a174ec369da3ba4c1514452c3dd3a1332b4cbf00e1d24f59ee454d6f2370cdf1debb26c6c543811d
6
+ metadata.gz: 29faadeaaf24e76b1cf34b19fa7d8c8e0437c2c1427f9fbc698cb9f05719563cb0b869dc111b8e7a4129203b6b6d21cf2ce586e8b1d8c9333d3630681fc0f0df
7
+ data.tar.gz: 7cf68c45c074c3880c39a3b5ecbd82f54686a648039b47e24e9f0aee84e8e6a98421a71f5cd451990f8dcdab306287517d0817b5976e7bb6f67110c2f440868b
@@ -30,7 +30,10 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency 'concurrent-ruby'
31
31
  spec.add_dependency 'multi_op_queue', '>= 0.2.0'
32
32
 
33
+ spec.add_development_dependency "benchmark-ips"
33
34
  spec.add_development_dependency "bundler"
35
+ spec.add_development_dependency "connection_pool"
36
+ spec.add_development_dependency "fakeredis"
34
37
  spec.add_development_dependency "pry"
35
38
  spec.add_development_dependency "rake", "~> 10.0"
36
39
  spec.add_development_dependency "rspec", "~> 3.2"
@@ -71,7 +71,7 @@ module ActivePublisher
71
71
 
72
72
  # Check to see if we should restart the consumer.
73
73
  if !consumer.alive? || consumer_is_lagging
74
- consumer.kill
74
+ consumer.kill rescue nil
75
75
  @consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
76
76
  end
77
77
 
@@ -101,7 +101,7 @@ module ActivePublisher
101
101
  raise unknown_error
102
102
  ensure
103
103
  # Always requeue anything that gets stuck.
104
- queue.concat(current_messages) unless current_messages.nil?
104
+ queue.concat(current_messages) if current_messages && !current_messages.empty?
105
105
  end
106
106
  end
107
107
  end
@@ -0,0 +1,42 @@
1
+ require "active_publisher/async/redis_adapter/redis_multi_pop_queue"
2
+
3
+ module ActivePublisher
4
+ module Async
5
+ module RedisAdapter
6
+ class Consumer
7
+ SUPERVISOR_INTERVAL = {
8
+ :execution_interval => 10, # seconds
9
+ :timeout_interval => 5, # seconds
10
+ }
11
+
12
+ attr_reader :consumer, :queue, :supervisor
13
+
14
+ def initialize(redis_pool)
15
+ @queue = ::ActivePublisher::Async::RedisAdapter::RedisMultiPopQueue.new(redis_pool, ::ActivePublisher::Async::RedisAdapter::REDIS_LIST_KEY)
16
+ create_and_supervise_consumer!
17
+ end
18
+
19
+ def create_and_supervise_consumer!
20
+ @consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
21
+
22
+ supervisor_task = ::Concurrent::TimerTask.new(SUPERVISOR_INTERVAL) do
23
+ # This may also be the place to start additional publishers when we are getting backed up ... ?
24
+ unless consumer.alive?
25
+ consumer.kill rescue nil
26
+ @consumer = ::ActivePublisher::Async::InMemoryAdapter::ConsumerThread.new(queue)
27
+ end
28
+
29
+ # Notify the current queue size.
30
+ ::ActiveSupport::Notifications.instrument "redis_async_queue_size.active_publisher", queue.size
31
+ end
32
+
33
+ supervisor_task.execute
34
+ end
35
+
36
+ def size
37
+ queue.size
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,113 @@
1
+ module ActivePublisher
2
+ module Async
3
+ module RedisAdapter
4
+ class RedisMultiPopQueue
5
+ attr_reader :list_key, :redis_pool
6
+
7
+ def initialize(redis_connection_pool, new_list_key)
8
+ @redis_pool = redis_connection_pool
9
+ @list_key = new_list_key
10
+ end
11
+
12
+ def <<(message)
13
+ encoded_message = ::Marshal.dump(message)
14
+
15
+ redis_pool.with do |redis|
16
+ redis.rpush(list_key, encoded_message)
17
+ end
18
+ end
19
+
20
+ def concat(*messages)
21
+ messages = messages.flatten
22
+ messages.compact!
23
+ return if messages.empty?
24
+
25
+ encoded_messages = []
26
+ messages.each do |message|
27
+ encoded_messages << ::Marshal.dump(message)
28
+ end
29
+
30
+ redis_pool.with do |redis|
31
+ redis.rpush(list_key, encoded_messages)
32
+ end
33
+ end
34
+
35
+ def empty?
36
+ size <= 0
37
+ end
38
+
39
+ def pop_up_to(num_to_pop, opts = {})
40
+ case opts
41
+ when TrueClass, FalseClass
42
+ non_bock = opts
43
+ when Hash
44
+ timeout = opts.fetch(:timeout, nil)
45
+ non_block = opts.fetch(:non_block, false)
46
+ end
47
+
48
+ queue_size = size
49
+ if queue_size <= 0
50
+ if non_block
51
+ raise ThreadError, "queue empty"
52
+ else
53
+ total_waited_time = 0
54
+
55
+ loop do
56
+ total_waited_time += 0.1
57
+ sleep 0.1
58
+ queue_size = size
59
+
60
+ if queue_size > 0
61
+ num_to_pop = [num_to_pop, queue_size].min # make sure we don't pop more than size
62
+ return shift(num_to_pop)
63
+ end
64
+
65
+ return if timeout && total_waited_time > timeout
66
+ end
67
+ end
68
+ else
69
+ shift(num_to_pop)
70
+ end
71
+ end
72
+
73
+ def shift(number)
74
+ number = [number, size].min
75
+ return [] if number <= 0
76
+
77
+ messages = []
78
+ multi_response = []
79
+ redis_pool.with do |redis|
80
+ multi_response = redis.multi do
81
+ redis.lrange(list_key, 0, number - 1)
82
+ redis.ltrim(list_key, number, -1)
83
+ end
84
+ end
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
+
91
+ messages = [] if messages.nil?
92
+ messages = [messages] unless messages.respond_to?(:each)
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
102
+ end
103
+
104
+ def size
105
+ redis_pool.with do |redis|
106
+ redis.llen(list_key) || 0
107
+ end
108
+ end
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,74 @@
1
+ require "active_publisher"
2
+ require "active_publisher/message"
3
+ require "active_publisher/async/redis_adapter/consumer"
4
+ require "multi_op_queue"
5
+
6
+ module ActivePublisher
7
+ module Async
8
+ module RedisAdapter
9
+ REDIS_LIST_KEY = "ACTIVE_PUBLISHER_LIST".freeze
10
+
11
+ def self.new(*args)
12
+ ::ActivePublisher::Async::RedisAdapter::Adapter.new(*args)
13
+ end
14
+
15
+ class Adapter
16
+ SUPERVISOR_INTERVAL = {
17
+ :execution_interval => 1.5, # seconds
18
+ :timeout_interval => 1, # seconds
19
+ }
20
+ include ::ActivePublisher::Logging
21
+
22
+ attr_reader :async_queue, :redis_pool, :queue
23
+
24
+ def initialize(new_redis_pool)
25
+ logger.info "Starting redis publisher adapter"
26
+ # do something with supervision ?
27
+ @redis_pool = new_redis_pool
28
+ @async_queue = ::ActivePublisher::Async::RedisAdapter::Consumer.new(redis_pool)
29
+ @queue = ::MultiOpQueue::Queue.new
30
+
31
+ supervisor_task = ::Concurrent::TimerTask.new(SUPERVISOR_INTERVAL) do
32
+ queue_size = queue.size
33
+ number_of_times = [queue_size / 50, 1].max # get the max number of times to flush
34
+ number_of_times = [number_of_times, 5].min # don't allow it to be more than 5 per run
35
+ number_of_times.times { flush_queue! }
36
+ end
37
+
38
+ supervisor_task.execute
39
+ end
40
+
41
+ def publish(route, payload, exchange_name, options = {})
42
+ message = ::ActivePublisher::Message.new(route, payload, exchange_name, options)
43
+ queue << ::Marshal.dump(message)
44
+ flush_queue! if queue.size >= 20 || options[:flush_queue]
45
+
46
+ nil
47
+ end
48
+
49
+ def shutdown!
50
+ logger.info "Draining async publisher redis adapter before shutdown."
51
+ flush_queue! until queue.empty?
52
+ # Sleeping 2.1 seconds because the most common redis `fsync` command in AOF mode is run every 1 second
53
+ # this will give at least 1 full `fsync` to run before the process dies
54
+ sleep 2.1
55
+ end
56
+
57
+ private
58
+
59
+ def flush_queue!
60
+ return if queue.empty?
61
+ encoded_messages = queue.pop_up_to(25, :timeout => 0.001)
62
+
63
+ return if encoded_messages.nil?
64
+ return unless encoded_messages.respond_to?(:each)
65
+ return unless encoded_messages.size > 0
66
+
67
+ redis_pool.with do |redis|
68
+ redis.rpush(::ActivePublisher::Async::RedisAdapter::REDIS_LIST_KEY, encoded_messages)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,4 +1,5 @@
1
1
  require "yaml"
2
+ require "erb"
2
3
 
3
4
  module ActivePublisher
4
5
  class Configuration
@@ -1,3 +1,3 @@
1
1
  module ActivePublisher
2
- VERSION = "1.1.1.pre0"
2
+ VERSION = "1.2.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.1.1.pre0
4
+ version: 1.2.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: 2018-01-10 00:00:00.000000000 Z
15
+ date: 2018-01-26 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  requirement: !ruby/object:Gem::Requirement
@@ -70,6 +70,20 @@ dependencies:
70
70
  - - ">="
71
71
  - !ruby/object:Gem::Version
72
72
  version: 0.2.0
73
+ - !ruby/object:Gem::Dependency
74
+ requirement: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ name: benchmark-ips
80
+ prerelease: false
81
+ type: :development
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
73
87
  - !ruby/object:Gem::Dependency
74
88
  requirement: !ruby/object:Gem::Requirement
75
89
  requirements:
@@ -84,6 +98,34 @@ dependencies:
84
98
  - - ">="
85
99
  - !ruby/object:Gem::Version
86
100
  version: '0'
101
+ - !ruby/object:Gem::Dependency
102
+ requirement: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ name: connection_pool
108
+ prerelease: false
109
+ type: :development
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: '0'
115
+ - !ruby/object:Gem::Dependency
116
+ requirement: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ name: fakeredis
122
+ prerelease: false
123
+ type: :development
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
87
129
  - !ruby/object:Gem::Dependency
88
130
  requirement: !ruby/object:Gem::Requirement
89
131
  requirements:
@@ -154,6 +196,9 @@ files:
154
196
  - lib/active_publisher/async/in_memory_adapter/async_queue.rb
155
197
  - lib/active_publisher/async/in_memory_adapter/channel.rb
156
198
  - lib/active_publisher/async/in_memory_adapter/consumer_thread.rb
199
+ - lib/active_publisher/async/redis_adapter.rb
200
+ - lib/active_publisher/async/redis_adapter/consumer.rb
201
+ - lib/active_publisher/async/redis_adapter/redis_multi_pop_queue.rb
157
202
  - lib/active_publisher/configuration.rb
158
203
  - lib/active_publisher/connection.rb
159
204
  - lib/active_publisher/logging.rb
@@ -174,12 +219,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
219
  version: '0'
175
220
  required_rubygems_version: !ruby/object:Gem::Requirement
176
221
  requirements:
177
- - - ">"
222
+ - - ">="
178
223
  - !ruby/object:Gem::Version
179
- version: 1.3.1
224
+ version: '0'
180
225
  requirements: []
181
226
  rubyforge_project:
182
- rubygems_version: 2.6.13
227
+ rubygems_version: 2.6.11
183
228
  signing_key:
184
229
  specification_version: 4
185
230
  summary: Aims to make publishing work across MRI and jRuby painless and add some nice