From ffc76cd6e8b4a960c65513738a778af80578efd0 Mon Sep 17 00:00:00 2001 From: antirez Date: Thu, 21 Mar 2013 17:11:54 +0100 Subject: [PATCH] redis-trib: initial support to fix "open" slots. Open slots are slots found in importing or migrating slot when a cluster check is performed. --- src/redis-trib.rb | 59 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/redis-trib.rb b/src/redis-trib.rb index edf5648c1..4c6c2c9b1 100755 --- a/src/redis-trib.rb +++ b/src/redis-trib.rb @@ -57,6 +57,10 @@ class ClusterNode @info[:slots] end + def has_flag?(flag) + @info[:flags].index(flag) + end + def to_s "#{@info[:host]}:#{@info[:port]}" end @@ -197,7 +201,7 @@ class ClusterNode x.count == 1 ? x.first.to_s : "#{x.first}-#{x.last}" }.join(",") - "[#{@info[:cluster_state].upcase}] #{self.info[:name]} #{self.to_s} slots:#{slots} (#{self.slots.length} slots)" + "[#{@info[:cluster_state].upcase} #{(self.info[:flags]-["myself"]).join(",")}] #{self.info[:name]} #{self.to_s} slots:#{slots} (#{self.slots.length} slots)" end # Return a single string representing nodes and associated slots. @@ -257,6 +261,7 @@ class RedisTrib puts "Performing Cluster Check (using node #{@nodes[0]})" show_nodes check_config_consistency + check_open_slots check_slots_coverage end @@ -282,6 +287,26 @@ class RedisTrib end end + def check_open_slots + open_slots = [] + @nodes.each{|n| + if n.info[:migrating].size > 0 + puts "[WARNING] Node #{n} has slots in migrating state." + open_slots += n.info[:migrating].keys + elsif n.info[:importing].size > 0 + puts "[WARNING] Node #{n} has slots in importing state." + open_slots += n.info[:importing].keys + end + } + open_slots.uniq! + if open_slots.length + puts "[WARNING] The following slots are open: #{open_slots.join(",")}" + end + if @fix + open_slots.each{|slot| fix_open_slot slot} + end + end + def nodes_with_keys_in_slot(slot) nodes = [] @nodes.each{|n| @@ -351,6 +376,36 @@ class RedisTrib end end + # Slot 'slot' was found to be in importing or migrating state in one or + # more nodes. This function fixes this condition by migrating keys where + # it seems more sensible. + def fix_open_slot(slot) + migrating = [] + importing = [] + @nodes.each{|n| + next if n.has_flag? "slave" + if n.info[:migrating][slot] + migrating << n + elsif n.info[:importing][slot] + importing << n + elsif n.r.cluster("countkeysinslot",slot) > 0 + puts "Found keys about slot #{slot} in node #{n}!" + end + } + puts "Fixing open slot 0:" + puts "Set as migrating in: #{migrating.join(",")}" + puts "Set as importing in: #{importing.join(",")}" + + # Case 1: The slot is in migrating state in one slot, and in + # importing state in 1 slot. That's trivial to address. + if migrating.length == 1 && importing.length == 1 + puts "Moving slot zero to #{importing[1]}" + move_slot(migrating[0],importing[0],0,:verbose=>true) + else + puts "Sorry, Redis-trib can't fix this slot yet (work in progress)" + end + end + # Check if all the nodes agree about the cluster configuration def check_config_consistency signatures=[] @@ -472,7 +527,7 @@ class RedisTrib # and the slot as migrating in the target host. Note that the order of # the operations is important, as otherwise a client may be redirected # to the target node that does not yet know it is importing this slot. - print "Moving slot #{slot} from #{source.info_string}: "; STDOUT.flush + print "Moving slot #{slot} from #{source} to #{target}: "; STDOUT.flush target.r.cluster("setslot",slot,"importing",source.info[:name]) source.r.cluster("setslot",slot,"migrating",target.info[:name]) # Migrate all the keys from source to target using the MIGRATE command