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 +5 -5
- data/active_publisher.gemspec +3 -0
- data/lib/active_publisher/async/in_memory_adapter/async_queue.rb +1 -1
- data/lib/active_publisher/async/in_memory_adapter/consumer_thread.rb +1 -1
- data/lib/active_publisher/async/redis_adapter/consumer.rb +42 -0
- data/lib/active_publisher/async/redis_adapter/redis_multi_pop_queue.rb +113 -0
- data/lib/active_publisher/async/redis_adapter.rb +74 -0
- data/lib/active_publisher/configuration.rb +1 -0
- data/lib/active_publisher/version.rb +1 -1
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9feb8c420d646d0eda3073f0f1971f9b616087667229ed178bde560b508b7d19
|
4
|
+
data.tar.gz: 5601e3a0bcd7d8497192193bca84bae8153feef915806de4bcc90db966e579f4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 29faadeaaf24e76b1cf34b19fa7d8c8e0437c2c1427f9fbc698cb9f05719563cb0b869dc111b8e7a4129203b6b6d21cf2ce586e8b1d8c9333d3630681fc0f0df
|
7
|
+
data.tar.gz: 7cf68c45c074c3880c39a3b5ecbd82f54686a648039b47e24e9f0aee84e8e6a98421a71f5cd451990f8dcdab306287517d0817b5976e7bb6f67110c2f440868b
|
data/active_publisher.gemspec
CHANGED
@@ -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)
|
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
|
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.
|
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-
|
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:
|
224
|
+
version: '0'
|
180
225
|
requirements: []
|
181
226
|
rubyforge_project:
|
182
|
-
rubygems_version: 2.6.
|
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
|