-
Notifications
You must be signed in to change notification settings - Fork 0
/
roundrobin.rb
146 lines (115 loc) · 2.52 KB
/
roundrobin.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
require 'pp'
class RRBalancer
def initialize(nodes)
newnodes = nodes.dup
only_key, gcd, max_weight = get_gcd(newnodes)
last_id = get_random_node_id(nodes)
@nodes = newnodes
@only_key = only_key
@max_weight = max_weight
@gcd = gcd
@cw = max_weight
@last_id = last_id
end
def set(id, new_weight=0)
old_weight = @nodes[id]
return if old_weight == new_weight
if old_weight < new_weight
return _incr(id, new_weight - old_weight)
end
return _decr(id, old_weight - new_weight)
end
def find
only_key = @only_key
if only_key then
return only_key
end
nodes = @nodes
last_id, cw = [@last_id, @cw]
while true do
while true do
last_id, weight = next_in_hash(nodes, last_id)
if not last_id
break
end
if weight >= cw
@cw = cw
@last_id = last_id
return last_id
end
end
cw = cw - @gcd
if cw <= 0
cw = @max_weight
end
end
end
def next_in_hash(hash, starting_key=nil)
keys = hash.keys
if keys.size == 0
return nil
end
if starting_key == nil
starting_key = keys.first
end
index = hash.keys.index(starting_key)
unless index
return nil
end
if index+1 == keys.size
return nil
end
key = keys[index+1]
return [key, hash[key]]
end
private
def _incr(id, weight=1)
@nodes[id] = (@nodes[id] || 0) + weight
@only_key, @gcd, @max_weight = get_gcd(@nodes)
end
def _decr(id, weight=1)
old_weight = @nodes[id]
return unless old_weight
if old_weight <= weight
return _delete(id)
end
@nodes[id] = old_weight - weight
@only_key, @gcd, @max_weight = get_gcd(@nodes)
if @cw > @max_weight
@cw = @max_weight
end
end
def _delete(id)
@nodes[id] = nil
@only_key, @gcd, @max_weight = get_gcd(@nodes)
if id == @last_id
@last_id = nil
end
if @cw > @max_weight
@cw = @max_weight
end
end
def get_gcd(nodes)
first_id, max_weight = nodes.first
unless first_id
raise "empty nodes"
end
only_key = first_id
gcd = max_weight
nodes.each do |id, weight|
only_key = nil
gcd = _gcd(gcd, weight)
max_weight = weight > max_weight ? weight : max_weight
end
return [only_key, gcd, max_weight]
end
def _gcd(a, b)
if b == 0
return a
end
return _gcd(b, a % b)
end
def get_random_node_id(nodes)
nodes.keys.sample
end
end