nsq-krakow 0.1.0
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 +7 -0
- data/CHANGELOG.md +84 -0
- data/CONTRIBUTING.md +25 -0
- data/LICENSE +13 -0
- data/README.md +249 -0
- data/krakow.gemspec +22 -0
- data/lib/krakow.rb +25 -0
- data/lib/krakow/command.rb +89 -0
- data/lib/krakow/command/auth.rb +36 -0
- data/lib/krakow/command/cls.rb +24 -0
- data/lib/krakow/command/fin.rb +31 -0
- data/lib/krakow/command/identify.rb +55 -0
- data/lib/krakow/command/mpub.rb +39 -0
- data/lib/krakow/command/nop.rb +14 -0
- data/lib/krakow/command/pub.rb +37 -0
- data/lib/krakow/command/rdy.rb +31 -0
- data/lib/krakow/command/req.rb +32 -0
- data/lib/krakow/command/sub.rb +36 -0
- data/lib/krakow/command/touch.rb +31 -0
- data/lib/krakow/connection.rb +417 -0
- data/lib/krakow/connection_features.rb +10 -0
- data/lib/krakow/connection_features/deflate.rb +82 -0
- data/lib/krakow/connection_features/snappy_frames.rb +129 -0
- data/lib/krakow/connection_features/ssl.rb +75 -0
- data/lib/krakow/consumer.rb +355 -0
- data/lib/krakow/consumer/queue.rb +151 -0
- data/lib/krakow/discovery.rb +57 -0
- data/lib/krakow/distribution.rb +229 -0
- data/lib/krakow/distribution/default.rb +159 -0
- data/lib/krakow/exceptions.rb +30 -0
- data/lib/krakow/frame_type.rb +66 -0
- data/lib/krakow/frame_type/error.rb +26 -0
- data/lib/krakow/frame_type/message.rb +74 -0
- data/lib/krakow/frame_type/response.rb +26 -0
- data/lib/krakow/ksocket.rb +102 -0
- data/lib/krakow/producer.rb +162 -0
- data/lib/krakow/producer/http.rb +224 -0
- data/lib/krakow/utils.rb +9 -0
- data/lib/krakow/utils/lazy.rb +125 -0
- data/lib/krakow/utils/logging.rb +43 -0
- data/lib/krakow/version.rb +4 -0
- metadata +184 -0
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
3
|
+
module Krakow
|
4
|
+
# Consume messages from a server
|
5
|
+
class Consumer
|
6
|
+
|
7
|
+
class Queue
|
8
|
+
|
9
|
+
include Celluloid
|
10
|
+
include Utils::Lazy
|
11
|
+
|
12
|
+
# @return [Consumer]
|
13
|
+
attr_reader :consumer
|
14
|
+
# @return [Array] order of message removal
|
15
|
+
attr_reader :pop_order
|
16
|
+
# @return [Symbol] callback method name
|
17
|
+
attr_reader :removal_callback
|
18
|
+
|
19
|
+
# Create new consumer queue instance
|
20
|
+
#
|
21
|
+
# @param consumer [Consumer]
|
22
|
+
# @return [self]
|
23
|
+
def initialize(consumer, *args)
|
24
|
+
opts = args.detect{|x| x.is_a?(Hash)}
|
25
|
+
@consumer = consumer
|
26
|
+
@removal_callback = opts[:removal_callback]
|
27
|
+
@messages = {}
|
28
|
+
@pop_order = []
|
29
|
+
@cleaner = nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Message container
|
33
|
+
#
|
34
|
+
# @yieldparam [Hash] messages
|
35
|
+
# @return [Hash] messages or block result
|
36
|
+
def messages
|
37
|
+
if(block_given?)
|
38
|
+
yield @messages
|
39
|
+
else
|
40
|
+
@messages
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Register a new connection
|
45
|
+
#
|
46
|
+
# @param connection [Connection]
|
47
|
+
# @return [TrueClass]
|
48
|
+
def register_connection(connection)
|
49
|
+
messages do |collection|
|
50
|
+
collection[connection.identifier] = []
|
51
|
+
end
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
# Remove connection registration and remove all messages
|
56
|
+
#
|
57
|
+
# @param identifier [String] connection identifier
|
58
|
+
# @return [Array<FrameType::Message>] messages queued for deregistered connection
|
59
|
+
def deregister_connection(identifier)
|
60
|
+
messages do |collection|
|
61
|
+
removed = collection.delete(identifier)
|
62
|
+
pop_order.delete(identifier)
|
63
|
+
removed
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Push new message into queue
|
68
|
+
#
|
69
|
+
# @param message [FrameType::Message]
|
70
|
+
# @return [self]
|
71
|
+
def push(message)
|
72
|
+
unless(message.is_a?(FrameType::Message))
|
73
|
+
abort TypeError.new "Expecting `FrameType::Message` but received `#{message.class}`!"
|
74
|
+
end
|
75
|
+
messages do |collection|
|
76
|
+
begin
|
77
|
+
collection[message.connection.identifier] << message
|
78
|
+
pop_order << message.connection.identifier
|
79
|
+
rescue Celluloid::DeadActorError
|
80
|
+
abort Error::ConnectionUnavailable.new
|
81
|
+
end
|
82
|
+
end
|
83
|
+
signal(:new_message)
|
84
|
+
current_actor
|
85
|
+
end
|
86
|
+
alias_method :<<, :push
|
87
|
+
alias_method :enq, :push
|
88
|
+
|
89
|
+
# Pop first item off the queue
|
90
|
+
#
|
91
|
+
# @return [Object]
|
92
|
+
def pop
|
93
|
+
message = nil
|
94
|
+
until(message)
|
95
|
+
wait(:new_message) if pop_order.empty?
|
96
|
+
messages do |collection|
|
97
|
+
key = pop_order.shift
|
98
|
+
if(key)
|
99
|
+
message = collection[key].shift
|
100
|
+
message = validate_message(message)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
message
|
105
|
+
end
|
106
|
+
alias_method :deq, :pop
|
107
|
+
|
108
|
+
# @return [Integer] number of queued messages
|
109
|
+
def size
|
110
|
+
messages do |collection|
|
111
|
+
collection.values.map(&:size).inject(&:+)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Remove duplicate message from queue if possible
|
116
|
+
#
|
117
|
+
# @param message [FrameType::Message]
|
118
|
+
# @return [TrueClass, FalseClass]
|
119
|
+
def scrub_duplicate_message(message)
|
120
|
+
messages do |collection|
|
121
|
+
idx = collection[message.connection.identifier].index do |msg|
|
122
|
+
msg.message_id == message.message_id
|
123
|
+
end
|
124
|
+
if(idx)
|
125
|
+
msg = collection[message.connection.identifier].delete_at(idx)
|
126
|
+
if(removal_callback)
|
127
|
+
consumer.send(removal_callback, [message])
|
128
|
+
end
|
129
|
+
true
|
130
|
+
else
|
131
|
+
false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Validate message
|
137
|
+
def validate_message(message)
|
138
|
+
if(message.instance_stamp > message.instance_stamp + (message.connection.endpoint_settings[:msg_timeout] / 1000.0))
|
139
|
+
warn "Message exceeded timeout! Discarding. (#{message})"
|
140
|
+
if(removal_callback)
|
141
|
+
consumer.send(removal_callback, [message])
|
142
|
+
end
|
143
|
+
nil
|
144
|
+
else
|
145
|
+
message
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'http'
|
3
|
+
require 'multi_json'
|
4
|
+
require 'krakow'
|
5
|
+
|
6
|
+
module Krakow
|
7
|
+
|
8
|
+
# Provides queue topic discovery
|
9
|
+
class Discovery
|
10
|
+
|
11
|
+
include Utils::Lazy
|
12
|
+
|
13
|
+
# @!group Attributes
|
14
|
+
|
15
|
+
# @!macro [attach] attribute
|
16
|
+
# @!method $1
|
17
|
+
# @return [$2] the $1 $0
|
18
|
+
# @!method $1?
|
19
|
+
# @return [TrueClass, FalseClass] truthiness of the $1 $0
|
20
|
+
attribute :nsqlookupd, [Array, String], :required => true
|
21
|
+
|
22
|
+
# @!endgroup
|
23
|
+
|
24
|
+
# Get list of end points with given topic name available
|
25
|
+
#
|
26
|
+
# @param topic [String] topic name
|
27
|
+
# @return [Array<Hash>]
|
28
|
+
def lookup(topic)
|
29
|
+
result = [nsqlookupd].flatten.map do |location|
|
30
|
+
uri = URI.parse(location)
|
31
|
+
uri.path = '/lookup'
|
32
|
+
uri.query = "topic=#{topic}&ts=#{Time.now.to_i}"
|
33
|
+
begin
|
34
|
+
debug "Requesting lookup for topic #{topic} - #{uri}"
|
35
|
+
content = HTTP.with(:accept => 'application/octet-stream').get(uri.to_s)
|
36
|
+
unless(content.respond_to?(:to_hash))
|
37
|
+
data = MultiJson.load(content.to_s)
|
38
|
+
else
|
39
|
+
data = content.to_hash
|
40
|
+
end
|
41
|
+
debug "Lookup response (#{uri.to_s}): #{data.inspect}"
|
42
|
+
if(data['data'] && data['data']['producers'])
|
43
|
+
data['data']['producers'].map do |producer|
|
44
|
+
Hash[*producer.map{|k,v| [k.to_sym, v]}.flatten]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rescue => e
|
48
|
+
warn "Lookup exception encountered: #{e.class.name} - #{e}"
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end.compact.flatten(1).uniq
|
52
|
+
debug "Discovery lookup result: #{result.inspect}"
|
53
|
+
result
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
3
|
+
module Krakow
|
4
|
+
# Message distribution
|
5
|
+
# @abstract
|
6
|
+
class Distribution
|
7
|
+
|
8
|
+
autoload :Default, 'krakow/distribution/default'
|
9
|
+
# autoload :ProducerWeighted, 'krakow/distribution/producer_weighted'
|
10
|
+
# autoload :ConsumerWeighted, 'krakow/distribution/consumer_weighted'
|
11
|
+
|
12
|
+
include Celluloid
|
13
|
+
include Utils::Lazy
|
14
|
+
# @!parse include Krakow::Utils::Lazy::InstanceMethods
|
15
|
+
# @!parse extend Krakow::Utils::Lazy::ClassMethods
|
16
|
+
|
17
|
+
attr_accessor :ideal, :flight_record, :registry
|
18
|
+
|
19
|
+
# @!group Attributes
|
20
|
+
|
21
|
+
# @!macro [attach] attribute
|
22
|
+
# @!method $1
|
23
|
+
# @return [$2] the $1 $0
|
24
|
+
# @!method $1?
|
25
|
+
# @return [TrueClass, FalseClass] truthiness of the $1 $0
|
26
|
+
attribute :consumer, Krakow::Consumer, :required => true
|
27
|
+
attribute :watch_dog_interval, Numeric, :default => 1.0
|
28
|
+
attribute :backoff_interval, Numeric
|
29
|
+
attribute :max_in_flight, Integer, :default => 1
|
30
|
+
|
31
|
+
# @!endgroup
|
32
|
+
|
33
|
+
def initialize(args={})
|
34
|
+
super
|
35
|
+
@ideal = 0
|
36
|
+
@flight_record = {}
|
37
|
+
@registry = {}
|
38
|
+
end
|
39
|
+
|
40
|
+
# [Abstract] Reset flight distributions
|
41
|
+
def redistribute!
|
42
|
+
raise NotImplementedError.new 'Custom `#redistrubute!` method must be provided!'
|
43
|
+
end
|
44
|
+
|
45
|
+
# [Abstract] Determine RDY value for given connection
|
46
|
+
# @param connection_identifier [String]
|
47
|
+
# @return [Integer]
|
48
|
+
def calculate_ready!(connection_identifier)
|
49
|
+
raise NotImplementedError.new 'Custom `#calculate_ready!` method must be provided!'
|
50
|
+
end
|
51
|
+
|
52
|
+
# Remove message metadata from registry
|
53
|
+
#
|
54
|
+
# @param message [Krakow::FrameType::Message, String] message or ID
|
55
|
+
# @return [Krakow::Connection, NilClass]
|
56
|
+
def unregister_message(message)
|
57
|
+
msg_id = message.respond_to?(:message_id) ? message.message_id : message.to_s
|
58
|
+
connection = connection_lookup(flight_record[msg_id])
|
59
|
+
flight_record.delete(msg_id)
|
60
|
+
if(connection)
|
61
|
+
begin
|
62
|
+
ident = connection.identifier
|
63
|
+
registry_info = registry_lookup(ident)
|
64
|
+
registry_info[:in_flight] -= 1
|
65
|
+
calculate_ready!(ident)
|
66
|
+
connection
|
67
|
+
rescue Celluloid::DeadActorError
|
68
|
+
warn 'Connection is dead. No recalculation applied on ready.'
|
69
|
+
end
|
70
|
+
else
|
71
|
+
warn 'No connection associated to message via lookup. No recalculation applied on ready.'
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# Return the currently configured RDY value for given connnection
|
76
|
+
#
|
77
|
+
# @param connection_identifier [String]
|
78
|
+
# @return [Integer]
|
79
|
+
def ready_for(connection_identifier)
|
80
|
+
registry_lookup(connection_identifier)[:ready]
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Send RDY for given connection
|
85
|
+
#
|
86
|
+
# @param connection [Krakow::Connection]
|
87
|
+
# @return [Krakow::FrameType::Error,nil]
|
88
|
+
def set_ready_for(connection, *_)
|
89
|
+
connection.transmit(
|
90
|
+
Command::Rdy.new(
|
91
|
+
:count => ready_for(connection.identifier)
|
92
|
+
)
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
# Initial ready value used for new connections
|
97
|
+
#
|
98
|
+
# @return [Integer]
|
99
|
+
def initial_ready
|
100
|
+
ideal > 0 ? 1 : 0
|
101
|
+
end
|
102
|
+
|
103
|
+
# Registers message into registry and configures for distribution
|
104
|
+
#
|
105
|
+
# @param message [FrameType::Message]
|
106
|
+
# @param connection_identifier [String]
|
107
|
+
# @return [Integer]
|
108
|
+
def register_message(message, connection_identifier)
|
109
|
+
if(flight_record[message.message_id])
|
110
|
+
abort KeyError.new "Message is already registered in flight record! (#{message.message_id})"
|
111
|
+
else
|
112
|
+
registry_info = registry_lookup(connection_identifier)
|
113
|
+
registry_info[:in_flight] += 1
|
114
|
+
flight_record[message.message_id] = connection_identifier
|
115
|
+
calculate_ready!(connection_identifier)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Add connection to make available for RDY distribution
|
120
|
+
#
|
121
|
+
# @param connection [Krakow::Connection]
|
122
|
+
# @return [TrueClass]
|
123
|
+
def add_connection(connection)
|
124
|
+
unless(registry[connection.identifier])
|
125
|
+
registry[connection.identifier] = {
|
126
|
+
:ready => initial_ready,
|
127
|
+
:in_flight => 0,
|
128
|
+
:failures => 0,
|
129
|
+
:backoff_until => 0
|
130
|
+
}
|
131
|
+
end
|
132
|
+
true
|
133
|
+
end
|
134
|
+
|
135
|
+
# Remove connection from RDY distribution
|
136
|
+
#
|
137
|
+
# @param connection_identifier [String]
|
138
|
+
# @return [TrueClass]
|
139
|
+
def remove_connection(connection_identifier, *args)
|
140
|
+
# remove connection from registry
|
141
|
+
registry.delete(connection_identifier)
|
142
|
+
# remove any in flight messages
|
143
|
+
flight_record.delete_if do |k,v|
|
144
|
+
if(v == connection_identifier)
|
145
|
+
warn "Removing in flight reference due to failed connection: #{v}"
|
146
|
+
true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
true
|
150
|
+
end
|
151
|
+
|
152
|
+
# Return connection associated with given registry key
|
153
|
+
#
|
154
|
+
# @param identifier [String] connection identifier
|
155
|
+
# @return [Krakow::Connection, nil]
|
156
|
+
def connection_lookup(identifier)
|
157
|
+
consumer.connection(identifier)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Return source connection for given message ID
|
161
|
+
#
|
162
|
+
# @param msg_id [String]
|
163
|
+
# @yield execute with connection
|
164
|
+
# @yieldparam connection [Krakow::Connection]
|
165
|
+
# @return [Krakow::Connection, Object]
|
166
|
+
def in_flight_lookup(msg_id)
|
167
|
+
connection = connection_lookup(flight_record[msg_id])
|
168
|
+
unless(connection)
|
169
|
+
abort Krakow::Error::LookupFailed.new("Failed to locate in flight message (ID: #{msg_id})")
|
170
|
+
end
|
171
|
+
if(block_given?)
|
172
|
+
begin
|
173
|
+
yield connection
|
174
|
+
rescue => e
|
175
|
+
abort e
|
176
|
+
end
|
177
|
+
else
|
178
|
+
connection
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# Return registry information for given connection
|
183
|
+
# @param connection_identifier [String]
|
184
|
+
# @return [Hash] registry information
|
185
|
+
# @raise [Krakow::Error::LookupFailed]
|
186
|
+
def registry_lookup(connection_identifier)
|
187
|
+
registry[connection_identifier] ||
|
188
|
+
abort(Krakow::Error::LookupFailed.new("Failed to locate connection information in registry (#{connection_identifier})"))
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [Array<Krakow::Connection>] connections in registry
|
192
|
+
def connections
|
193
|
+
registry.keys.map do |identifier|
|
194
|
+
connection_lookup(identifier)
|
195
|
+
end.compact
|
196
|
+
end
|
197
|
+
|
198
|
+
# Log failure of processed message
|
199
|
+
#
|
200
|
+
# @param connection_identifier [String]
|
201
|
+
# @return [TrueClass]
|
202
|
+
def failure(connection_identifier)
|
203
|
+
if(backoff_interval)
|
204
|
+
registry_info = registry_lookup(connection_identifier)
|
205
|
+
registry_info[:failures] += 1
|
206
|
+
registry_info[:backoff_until] = Time.now.to_i + (registry_info[:failures] * backoff_interval)
|
207
|
+
end
|
208
|
+
true
|
209
|
+
end
|
210
|
+
|
211
|
+
# Log success of processed message
|
212
|
+
#
|
213
|
+
# @param connection_identifier [String]
|
214
|
+
# @return [TrueClass]
|
215
|
+
def success(connection_identifier)
|
216
|
+
if(backoff_interval)
|
217
|
+
registry_info = registry_lookup(connection_identifier)
|
218
|
+
if(registry_info[:failures] > 1)
|
219
|
+
registry_info[:failures] -= 1
|
220
|
+
registry_info[:backoff_until] = Time.now.to_i + (registry_info[:failures] * backoff_interval)
|
221
|
+
else
|
222
|
+
registry_info[:failures] = 0
|
223
|
+
end
|
224
|
+
end
|
225
|
+
true
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'krakow'
|
2
|
+
|
3
|
+
module Krakow
|
4
|
+
class Distribution
|
5
|
+
# Default distribution implementation. This uses a round-robin
|
6
|
+
# approach for less than ideal states.
|
7
|
+
class Default < Distribution
|
8
|
+
|
9
|
+
attr_reader :less_than_ideal_stack, :watch_dog
|
10
|
+
|
11
|
+
# recalculate `ideal` and update RDY on connections
|
12
|
+
def redistribute!
|
13
|
+
@ideal = registry.size < 1 ? 0 : max_in_flight / registry.size
|
14
|
+
debug "Distribution calculated ideal: #{ideal}"
|
15
|
+
if(less_than_ideal?)
|
16
|
+
registry.each do |connection_id, reg_info|
|
17
|
+
reg_info[:ready] = 0
|
18
|
+
end
|
19
|
+
max_in_flight.times do
|
20
|
+
less_than_ideal_ready!
|
21
|
+
end
|
22
|
+
connections.each do |connection|
|
23
|
+
set_ready_for(connection, :force)
|
24
|
+
end
|
25
|
+
watch_dog.cancel if watch_dog
|
26
|
+
@watch_dog = every(watch_dog_interval) do
|
27
|
+
force_unready
|
28
|
+
end
|
29
|
+
else
|
30
|
+
if(watch_dog)
|
31
|
+
watch_dog.cancel
|
32
|
+
@watch_dog = nil
|
33
|
+
end
|
34
|
+
connections.each do |connection|
|
35
|
+
current_ready = ready_for(connection.identifier)
|
36
|
+
calculate_ready!(connection.identifier)
|
37
|
+
unless(current_ready == ready_for(connection.identifier))
|
38
|
+
debug "Redistribution ready setting update for connection #{connection}"
|
39
|
+
set_ready_for(connection)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Is ideal less than 1
|
46
|
+
#
|
47
|
+
# @return [TrueClass, FalseClass]
|
48
|
+
def less_than_ideal?
|
49
|
+
ideal < 1
|
50
|
+
end
|
51
|
+
|
52
|
+
# Find next connection to receive RDY count
|
53
|
+
#
|
54
|
+
# @return [Krakow::Connection, nil]
|
55
|
+
def less_than_ideal_ready!
|
56
|
+
admit_defeat = false
|
57
|
+
connection = nil
|
58
|
+
until(connection || (admit_defeat && less_than_ideal_stack.empty?))
|
59
|
+
if(less_than_ideal_stack.nil? || less_than_ideal_stack.empty?)
|
60
|
+
@less_than_ideal_stack = waiting_connections
|
61
|
+
admit_defeat = true
|
62
|
+
end
|
63
|
+
con = less_than_ideal_stack.pop
|
64
|
+
if(con)
|
65
|
+
unless(registry_lookup(con.identifier)[:backoff_until] > Time.now.to_i)
|
66
|
+
connection = con
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
if(connection)
|
71
|
+
registry_lookup(connection.identifier)[:ready] = 1
|
72
|
+
connection
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Adds extra functionality to provide round robin RDY setting
|
77
|
+
# when in less than ideal state
|
78
|
+
#
|
79
|
+
# @param connection [Krakow::Connection]
|
80
|
+
# @param args [Symbol]
|
81
|
+
# @return [Krakow::FrameType::Error, nil]
|
82
|
+
def set_ready_for(connection, *args)
|
83
|
+
super connection
|
84
|
+
if(less_than_ideal? && !args.include?(:force))
|
85
|
+
debug "RDY set ignored due to less than ideal state (con: #{connection})"
|
86
|
+
con = less_than_ideal_ready!
|
87
|
+
if(con)
|
88
|
+
watch_dog.reset if watch_dog
|
89
|
+
super con
|
90
|
+
else
|
91
|
+
warn 'Failed to set RDY state while less than ideal. Connection stack is empty!'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Update connection ready count
|
97
|
+
# @param connection_identifier [String]
|
98
|
+
# @return [Integer, nil]
|
99
|
+
def calculate_ready!(connection_identifier)
|
100
|
+
begin
|
101
|
+
registry_info = registry_lookup(connection_identifier)
|
102
|
+
unless(less_than_ideal?)
|
103
|
+
registry_info[:ready] = ideal - registry_info[:in_flight]
|
104
|
+
if(registry_info[:ready] < 0 || registry_info[:backoff_until] > Time.now.to_i)
|
105
|
+
registry_info[:ready] = 0
|
106
|
+
registry_info[:backoff_timer].cancel if registry[:backoff_timer]
|
107
|
+
registry_info[:backoff_timer] = after(registry_info[:backoff_until] - Time.now.to_i) do
|
108
|
+
calculate_ready!(connection_identifier)
|
109
|
+
set_ready_for(connection_lookup(connection_identifier)) unless less_than_ideal?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
registry_info[:ready]
|
113
|
+
else
|
114
|
+
registry_info[:ready] = 0
|
115
|
+
end
|
116
|
+
rescue Error::ConnectionFailure
|
117
|
+
warn 'Failed connection encountered!'
|
118
|
+
rescue Error::ConnectionUnavailable
|
119
|
+
warn 'Unavailable connection encountered!'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# All connections without RDY state
|
124
|
+
#
|
125
|
+
# @return [Array<Krakow::Connection>]
|
126
|
+
def waiting_connections
|
127
|
+
registry.find_all do |conn_id, info|
|
128
|
+
info[:ready] < 1 && info[:in_flight] < 1 && info[:backoff_until] < Time.now.to_i
|
129
|
+
end.map{|conn_id, info| connection_lookup(conn_id) }.compact
|
130
|
+
end
|
131
|
+
|
132
|
+
# All connections with RDY state
|
133
|
+
#
|
134
|
+
# @return [Array<Krakow::Connection>]
|
135
|
+
def rdy_connections
|
136
|
+
registry.find_all do |conn_id, info|
|
137
|
+
info[:ready] > 0
|
138
|
+
end.map{|conn_id, info| connection_lookup(conn_id) }.compact
|
139
|
+
end
|
140
|
+
|
141
|
+
# Force a connection to give up RDY state so next in stack can receive
|
142
|
+
#
|
143
|
+
# @return [nil]
|
144
|
+
def force_unready
|
145
|
+
debug 'Forcing a connection into an unready state due to less than ideal state'
|
146
|
+
connection = rdy_connections.shuffle.first
|
147
|
+
if(connection)
|
148
|
+
debug "Stripping RDY state from connection: #{connection}"
|
149
|
+
calculate_ready!(connection.identifier)
|
150
|
+
set_ready_for(connection)
|
151
|
+
else
|
152
|
+
warn "Failed to locate available connection for RDY aquisition!"
|
153
|
+
end
|
154
|
+
nil
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|