active_publisher 1.1.1.pre0-java → 1.2.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
- 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