Improve redis-trib replica assignment

This tiny bit of code has gone through so many revisions.  Hopefully
it's more correct now.

Fixes #2204
This commit is contained in:
Matt Stancliff 2014-12-19 21:52:48 -05:00
parent e3436dd9b8
commit b55f742e46

View File

@ -540,7 +540,6 @@ class RedisTrib
nodes_count = @nodes.length nodes_count = @nodes.length
masters_count = @nodes.length / (@replicas+1) masters_count = @nodes.length / (@replicas+1)
masters = [] masters = []
slaves = []
# The first step is to split instances by IP. This is useful as # The first step is to split instances by IP. This is useful as
# we'll try to allocate master nodes in different physical machines # we'll try to allocate master nodes in different physical machines
@ -558,16 +557,22 @@ class RedisTrib
# Select master instances # Select master instances
puts "Using #{masters_count} masters:" puts "Using #{masters_count} masters:"
while masters.length < masters_count interleaved = []
ips.each{|ip,nodes_list| stop = false
next if nodes_list.length == 0 while not stop do
masters << nodes_list.shift # Take one node from each IP until we run out of nodes
puts masters[-1] # across every IP.
nodes_count -= 1 ips.each do |ip,nodes|
break if masters.length == masters_count stop = nodes.empty? and next
} interleaved.push nodes.shift
end
end end
masters = interleaved.slice!(0, masters_count)
nodes_count -= masters.length
masters.each{|m| puts m}
# Alloc slots on masters # Alloc slots on masters
slots_per_node = ClusterHashSlots.to_f / masters_count slots_per_node = ClusterHashSlots.to_f / masters_count
first = 0 first = 0
@ -594,8 +599,8 @@ class RedisTrib
# all nodes will be used. # all nodes will be used.
assignment_verbose = false assignment_verbose = false
[:requested,:unused].each{|assign| [:requested,:unused].each do |assign|
masters.each{|m| masters.each do |m|
assigned_replicas = 0 assigned_replicas = 0
while assigned_replicas < @replicas while assigned_replicas < @replicas
break if nodes_count == 0 break if nodes_count == 0
@ -609,21 +614,33 @@ class RedisTrib
"role too (#{nodes_count} remaining)." "role too (#{nodes_count} remaining)."
end end
end end
ips.each{|ip,nodes_list|
next if nodes_list.length == 0 # Return the first node not matching our current master
# Skip instances with the same IP as the master if we node = interleaved.find{|n| n.info[:host] != m.info[:host]}
# have some more IPs available.
next if ip == m.info[:host] && nodes_count > nodes_list.length # If we found a node, use it as a best-first match.
slave = nodes_list.shift # Otherwise, we didn't find a node on a different IP, so we
slave.set_as_replica(m.info[:name]) # go ahead and use a same-IP replica.
nodes_count -= 1 if node
assigned_replicas += 1 slave = node
puts "Adding replica #{slave} to #{m}" interleaved.delete node
break else
} slave = interleaved.shift
end
slave.set_as_replica(m.info[:name])
nodes_count -= 1
assigned_replicas += 1
puts "Adding replica #{slave} to #{m}"
# If we are in the "assign extra nodes" loop,
# we want to assign one extra replica to each
# master before repeating masters.
# This break lets us assign extra replicas to masters
# in a round-robin way.
break if assign == :unused
end end
} end
} end
end end
def flush_nodes_config def flush_nodes_config