right_support 2.10.1 → 2.11.2
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 +4 -4
- data/VERSION +1 -1
- data/lib/right_support/data/token.rb +51 -0
- data/lib/right_support/data.rb +1 -0
- data/lib/right_support/net/lb/base.rb +96 -0
- data/lib/right_support/net/lb/health_check.rb +22 -46
- data/lib/right_support/net/lb/round_robin.rb +5 -23
- data/lib/right_support/net/lb/sticky.rb +10 -27
- data/lib/right_support/net/lb.rb +2 -1
- data/lib/right_support/net/request_balancer.rb +184 -60
- data/lib/right_support/rack/request_logger.rb +221 -42
- data/lib/right_support/rack/request_tracker.rb +112 -25
- data/right_support.gemspec +11 -6
- data/spec/data/token_spec.rb +21 -0
- data/spec/net/{balancing → lb}/health_check_spec.rb +56 -21
- data/spec/net/{balancing → lb}/round_robin_spec.rb +0 -0
- data/spec/net/{balancing/sticky_policy_spec.rb → lb/sticky_spec.rb} +1 -1
- data/spec/net/request_balancer_spec.rb +161 -57
- data/spec/rack/request_logger_spec.rb +91 -27
- data/spec/rack/request_tracker_spec.rb +111 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 169232720914f75758ba15e8bb1b14864c143226
|
4
|
+
data.tar.gz: a7a1d9eb7ba2a0686ee2ddc0a5839a0d4ede6166
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57e4fd72803654f3e772cebe5c9f4a22066067dac8926b507edcadad35931ddebf9fd51a33916dbd06a27abae561144e075b840d77c9a4bd1e37286c4d1b26b1
|
7
|
+
data.tar.gz: ca11d752bb148b96d426f228d23215b7a751d26df821d1e43f8be7200b7a71fb395f18df9640d02d276b7642787a89b26cdb8fc173e6cb047073994cb0d03a38
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.11.2
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'digest/md5'
|
24
|
+
|
25
|
+
module RightSupport::Data
|
26
|
+
|
27
|
+
# unique token generator. short tokens that are friendler than raw UUIDs for
|
28
|
+
# logging purposes, etc.
|
29
|
+
class Token
|
30
|
+
|
31
|
+
# exact length of all generated tokens.
|
32
|
+
TOKEN_LENGTH = 13
|
33
|
+
|
34
|
+
# Generate new token
|
35
|
+
#
|
36
|
+
# @return [String] token
|
37
|
+
def self.generate
|
38
|
+
# MD5 is 128bit = 25 chars in base 36, need to fit in less
|
39
|
+
# The code below produces a 64 bit value evenly distributed.
|
40
|
+
i = ::Digest::MD5.hexdigest(::RightSupport::Data::UUID.generate).to_i(16)
|
41
|
+
t = ((i >> 64) ^ (i & 0xFFFFFFFFFFFFFFFF)).to_s(36)
|
42
|
+
|
43
|
+
# left-pad with zeros to keep token length consistant.
|
44
|
+
if t.length < TOKEN_LENGTH
|
45
|
+
t = '0' * (TOKEN_LENGTH - t.length) + t
|
46
|
+
end
|
47
|
+
t
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/right_support/data.rb
CHANGED
@@ -0,0 +1,96 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2016 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
module RightSupport::Net::LB
|
24
|
+
|
25
|
+
# base class for load balancer policies.
|
26
|
+
class Base
|
27
|
+
|
28
|
+
# @param [Hash] options provided for derived policies
|
29
|
+
def initialize(options = {})
|
30
|
+
@options = options.dup
|
31
|
+
@endpoints = []
|
32
|
+
@counter = rand(0xffff)
|
33
|
+
|
34
|
+
# note that not all policies require health-checking but the request
|
35
|
+
# balancer will provide the callback by default.
|
36
|
+
@health_check = @options.delete(:health_check)
|
37
|
+
if @health_check && !@health_check.respond_to?(:call)
|
38
|
+
raise ::ArgumentError, ':health_check is invalid.'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# override only if endpoint selection is non-trivial
|
43
|
+
def set_endpoints(endpoints)
|
44
|
+
@endpoints = endpoints
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
# override only if endpoint selection is non-trivial
|
49
|
+
def next
|
50
|
+
result = nil
|
51
|
+
unless @endpoints.empty?
|
52
|
+
# endpoint, no health-check required.
|
53
|
+
result = [ @endpoints[@counter % @endpoints.size], false ]
|
54
|
+
@counter += 1
|
55
|
+
end
|
56
|
+
result
|
57
|
+
end
|
58
|
+
|
59
|
+
# override only if statisics are kept
|
60
|
+
#
|
61
|
+
# @return [TrueClass] always true
|
62
|
+
def good(endpoint, t0, t1)
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# override only if statisics are kept
|
67
|
+
#
|
68
|
+
# @return [TrueClass] always true
|
69
|
+
def bad(endpoint, t0, t1)
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
# performs a basic health-check for given endpoint using functor and updates
|
74
|
+
# statistics, if kept.
|
75
|
+
#
|
76
|
+
# @param [String] endpoint for check
|
77
|
+
#
|
78
|
+
# @return [TrueClass] always true
|
79
|
+
def health_check(endpoint)
|
80
|
+
t0 = ::Time.now
|
81
|
+
result = @health_check.call(endpoint)
|
82
|
+
t1 = ::Time.now
|
83
|
+
if result
|
84
|
+
good(endpoint, t0, t1)
|
85
|
+
return true
|
86
|
+
end
|
87
|
+
bad(endpoint, t0, t1)
|
88
|
+
return false
|
89
|
+
rescue ::Exception
|
90
|
+
t1 = ::Time.now
|
91
|
+
bad(endpoint, t0, t1)
|
92
|
+
raise
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -89,9 +89,10 @@ module RightSupport::Net::LB
|
|
89
89
|
# Returns a hash of endpoints and their colored health status
|
90
90
|
# Useful for logging and debugging
|
91
91
|
def get_stats
|
92
|
-
stats
|
93
|
-
|
94
|
-
|
92
|
+
@endpoints.inject({}) do |stats, (k, v)|
|
93
|
+
stats[k] = state_color(v[:n_level])
|
94
|
+
stats
|
95
|
+
end
|
95
96
|
end
|
96
97
|
|
97
98
|
# Replace the set of endpoints that this object knows about. If any
|
@@ -129,71 +130,46 @@ module RightSupport::Net::LB
|
|
129
130
|
# reported on the way back down, e.g., yellow is reported as soon as the first endpoint
|
130
131
|
# transitions from red to yellow, and so on.
|
131
132
|
|
132
|
-
class HealthCheck
|
133
|
+
class HealthCheck < ::RightSupport::Net::LB::Base
|
133
134
|
include RightSupport::Log::Mixin
|
134
135
|
|
135
136
|
def initialize(options = {})
|
136
|
-
|
137
|
+
super
|
138
|
+
@stack = nil
|
137
139
|
end
|
138
140
|
|
139
141
|
def set_endpoints(endpoints)
|
140
142
|
if @stack
|
141
143
|
@stack.update!(endpoints)
|
142
144
|
else
|
143
|
-
@health_check = @options.delete(:health_check)
|
144
|
-
@counter = Process.pid
|
145
|
-
@last_size = endpoints.size
|
146
145
|
@stack = EndpointsStack.new(self, endpoints, @options[:yellow_states], @options[:reset_time], @options[:on_health_change])
|
147
146
|
end
|
147
|
+
true
|
148
148
|
end
|
149
149
|
|
150
150
|
def next
|
151
|
-
#
|
152
|
-
#
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
# Hash#select returns a Hash in ruby1.9, but an Array of pairs in ruby1.8.
|
163
|
-
# This should really be encapsulated in EndpointsStack...
|
164
|
-
if RUBY_VERSION >= '1.9'
|
165
|
-
key = endpoints.keys[@counter]
|
166
|
-
next_endpoint = [ key, endpoints[key][:n_level] != 0 ]
|
167
|
-
else
|
168
|
-
next_endpoint = [ endpoints[@counter][0], endpoints[@counter][1][:n_level] != 0 ]
|
151
|
+
# avoid red endpoints until they timeout, which may leave us with none
|
152
|
+
# temporarily. the idea is to have multiple endpoints such that one or
|
153
|
+
# more can go down while others continue to respond.
|
154
|
+
result = nil
|
155
|
+
yg_eps = @stack.sweep_and_return_yellow_and_green
|
156
|
+
unless yg_eps.empty?
|
157
|
+
@endpoints = yg_eps.keys
|
158
|
+
if result = super
|
159
|
+
result[1] = yg_eps[result[0]][:n_level] != 0
|
160
|
+
end
|
169
161
|
end
|
170
|
-
|
171
|
-
next_endpoint
|
162
|
+
result
|
172
163
|
end
|
173
164
|
|
174
165
|
def good(endpoint, t0, t1)
|
175
166
|
@stack.decrease_state(endpoint, t0, t1)
|
167
|
+
true
|
176
168
|
end
|
177
169
|
|
178
170
|
def bad(endpoint, t0, t1)
|
179
171
|
@stack.increase_state(endpoint, t0, t1)
|
180
|
-
|
181
|
-
|
182
|
-
def health_check(endpoint)
|
183
|
-
t0 = Time.now
|
184
|
-
result = @health_check.call(endpoint)
|
185
|
-
t1 = Time.now
|
186
|
-
if result
|
187
|
-
@stack.decrease_state(endpoint, t0, t1)
|
188
|
-
return true
|
189
|
-
else
|
190
|
-
@stack.increase_state(endpoint, t0, t1)
|
191
|
-
return false
|
192
|
-
end
|
193
|
-
rescue Exception => e
|
194
|
-
t1 = Time.now
|
195
|
-
@stack.increase_state(endpoint, t0, t1)
|
196
|
-
raise
|
172
|
+
true
|
197
173
|
end
|
198
174
|
|
199
175
|
# Proxy to EndpointStack
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -21,32 +21,14 @@
|
|
21
21
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
22
|
|
23
23
|
module RightSupport::Net::LB
|
24
|
-
class RoundRobin
|
24
|
+
class RoundRobin < ::RightSupport::Net::LB::Base
|
25
25
|
|
26
|
-
def initialize(options ={})
|
27
|
-
|
28
|
-
@counter = rand(0xffff)
|
29
|
-
end
|
30
|
-
|
31
|
-
def set_endpoints(endpoints)
|
32
|
-
@endpoints = endpoints
|
33
|
-
end
|
34
|
-
|
35
|
-
def next
|
36
|
-
@counter += 1
|
37
|
-
[ @endpoints[@counter % @endpoints.size], false ] unless @endpoints.empty?
|
38
|
-
end
|
39
|
-
|
40
|
-
def good(endpoint, t0, t1)
|
41
|
-
#no-op; round robin does not care about failures
|
42
|
-
end
|
43
|
-
|
44
|
-
def bad(endpoint, t0, t1)
|
45
|
-
#no-op; round robin does not care about failures
|
26
|
+
def initialize(options = {})
|
27
|
+
super
|
46
28
|
end
|
47
29
|
|
48
30
|
def health_check(endpoint)
|
49
|
-
#no-op; round robin does not perform health checks
|
31
|
+
# no-op; round robin does not perform health checks
|
50
32
|
true
|
51
33
|
end
|
52
34
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -29,52 +29,35 @@ module RightSupport::Net::LB
|
|
29
29
|
# - re-iterate through each endpoint when it's endpoint loses validity;
|
30
30
|
# - return an Exception if it performs a complete iteration though each endpoint and finds none valid;
|
31
31
|
|
32
|
-
class Sticky
|
32
|
+
class Sticky < ::RightSupport::Net::LB::Base
|
33
33
|
|
34
34
|
def initialize(options = {})
|
35
|
-
|
36
|
-
@endpoints = []
|
37
|
-
@counter = rand(0xffff)
|
35
|
+
super
|
38
36
|
end
|
39
37
|
|
40
38
|
def set_endpoints(endpoints)
|
41
39
|
unless @endpoints.empty?
|
42
|
-
last_chosen =
|
40
|
+
last_chosen = @endpoints[@counter % @endpoints.size]
|
43
41
|
@endpoints = []
|
44
42
|
if endpoints.include?(last_chosen)
|
45
43
|
@endpoints << last_chosen
|
46
44
|
@counter = 0
|
47
45
|
end
|
48
46
|
end
|
49
|
-
@endpoints |= endpoints
|
47
|
+
@endpoints |= endpoints # a union of endpoints
|
48
|
+
true
|
50
49
|
end
|
51
50
|
|
52
51
|
def next
|
52
|
+
# note that counter is not incremented here; hence stickyness.
|
53
53
|
[ @endpoints[@counter % @endpoints.size], false ] unless @endpoints.empty?
|
54
54
|
end
|
55
55
|
|
56
|
-
def good(endpoint, t0, t1)
|
57
|
-
#no-op; round robin does not care about failures
|
58
|
-
end
|
59
|
-
|
60
56
|
def bad(endpoint, t0, t1)
|
57
|
+
# increment from bad endpoint index to unstick. this is only meaningful if
|
58
|
+
# there are multiple endpoints.
|
61
59
|
@counter += 1
|
62
|
-
|
63
|
-
|
64
|
-
def health_check(endpoint)
|
65
|
-
t0 = Time.now
|
66
|
-
result = @health_check.call(endpoint)
|
67
|
-
t1 = Time.now
|
68
|
-
if result
|
69
|
-
return true
|
70
|
-
else
|
71
|
-
@counter += 1
|
72
|
-
return false
|
73
|
-
end
|
74
|
-
rescue Exception => e
|
75
|
-
t1 = Time.now
|
76
|
-
@counter += 1
|
77
|
-
raise
|
60
|
+
nil
|
78
61
|
end
|
79
62
|
|
80
63
|
end
|
data/lib/right_support/net/lb.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
#
|
2
|
-
# Copyright (c) 2011 RightScale Inc
|
2
|
+
# Copyright (c) 2011-2016 RightScale Inc
|
3
3
|
#
|
4
4
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
5
|
# a copy of this software and associated documentation files (the
|
@@ -28,6 +28,7 @@ module RightSupport::Net::LB
|
|
28
28
|
|
29
29
|
end
|
30
30
|
|
31
|
+
require 'right_support/net/lb/base'
|
31
32
|
require 'right_support/net/lb/health_check'
|
32
33
|
require 'right_support/net/lb/round_robin'
|
33
34
|
require 'right_support/net/lb/sticky'
|