1
0
mirror of https://github.com/fluencelabs/redis synced 2025-03-31 14:51:04 +00:00

redis-trib fix improved: move keys from N nodes to owner.

This commit is contained in:
antirez 2014-05-21 16:40:36 +02:00
parent 56161ca0a4
commit c68c78719f

@ -139,10 +139,10 @@ class ClusterNode
if s[0..0] == '[' if s[0..0] == '['
if s.index("->-") # Migrating if s.index("->-") # Migrating
slot,dst = s[1..-1].split("->-") slot,dst = s[1..-1].split("->-")
@info[:migrating][slot] = dst @info[:migrating][slot.to_i] = dst
elsif s.index("-<-") # Importing elsif s.index("-<-") # Importing
slot,src = s[1..-1].split("-<-") slot,src = s[1..-1].split("-<-")
@info[:importing][slot] = src @info[:importing][slot.to_i] = src
end end
elsif s.index("-") elsif s.index("-")
start,stop = s.split("-") start,stop = s.split("-")
@ -445,10 +445,33 @@ class RedisTrib
end end
end end
# Return the owner of the specified slot
def get_slot_owner(slot)
@nodes.each{|n|
n.slots.each{|s,_|
return n if s == slot
}
}
nil
end
# Slot 'slot' was found to be in importing or migrating state in one or # 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 # more nodes. This function fixes this condition by migrating keys where
# it seems more sensible. # it seems more sensible.
def fix_open_slot(slot) def fix_open_slot(slot)
puts ">>> Fixing open slot #{slot}"
# Try to obtain the current slot owner, according to the current
# nodes configuration.
owner = get_slot_owner(slot)
# If there is no slot owner, set as owner the slot with the biggest
# number of keys, among the set of migrating / importing nodes.
if !owner
xputs "*** Fix me, some work to do here."
exit 1
end
migrating = [] migrating = []
importing = [] importing = []
@nodes.each{|n| @nodes.each{|n|
@ -457,11 +480,11 @@ class RedisTrib
migrating << n migrating << n
elsif n.info[:importing][slot] elsif n.info[:importing][slot]
importing << n importing << n
elsif n.r.cluster("countkeysinslot",slot) > 0 elsif n.r.cluster("countkeysinslot",slot) > 0 && n != owner
xputs "*** Found keys about slot #{slot} in node #{n}!" xputs "*** Found keys about slot #{slot} in node #{n}!"
importing << n
end end
} }
puts ">>> Fixing open slot #{slot}"
puts "Set as migrating in: #{migrating.join(",")}" puts "Set as migrating in: #{migrating.join(",")}"
puts "Set as importing in: #{importing.join(",")}" puts "Set as importing in: #{importing.join(",")}"
@ -469,12 +492,14 @@ class RedisTrib
# importing state in 1 slot. That's trivial to address. # importing state in 1 slot. That's trivial to address.
if migrating.length == 1 && importing.length == 1 if migrating.length == 1 && importing.length == 1
move_slot(migrating[0],importing[0],slot,:verbose=>true,:fix=>true) move_slot(migrating[0],importing[0],slot,:verbose=>true,:fix=>true)
elsif migrating.length == 1 && importing.length == 0 elsif migrating.length == 0 && importing.length > 0
xputs ">>> Setting #{slot} as STABLE" xputs ">>> Moving all the #{slot} slot keys to its owner #{owner}"
migrating[0].r.cluster("setslot",slot,"stable") importing.each {|node|
elsif migrating.length == 0 && importing.length == 1 next if node == owner
xputs ">>> Setting #{slot} as STABLE" move_slot(node,owner,slot,:verbose=>true,:fix=>true,:cold=>true)
importing[0].r.cluster("setslot",slot,"stable") xputs ">>> Setting #{slot} as STABLE in #{node}"
importing[0].r.cluster("setslot",slot,"stable")
}
else else
xputs "[ERR] Sorry, Redis-trib can't fix this slot yet (work in progress)" xputs "[ERR] Sorry, Redis-trib can't fix this slot yet (work in progress)"
end end
@ -727,14 +752,22 @@ class RedisTrib
} }
end end
# Move slots between source and target nodes using MIGRATE.
#
# Options:
# :verbose -- Print a dot for every moved key.
# :fix -- We are moving in the context of a fix. Use REPLACE.
# :cold -- Move keys without opening / reconfiguring the nodes.
def move_slot(source,target,slot,o={}) def move_slot(source,target,slot,o={})
# We start marking the slot as importing in the destination node, # We start marking the slot as importing in the destination node,
# and the slot as migrating in the target host. Note that the order of # 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 # 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. # to the target node that does not yet know it is importing this slot.
print "Moving slot #{slot} from #{source} to #{target}: "; STDOUT.flush print "Moving slot #{slot} from #{source} to #{target}: "; STDOUT.flush
target.r.cluster("setslot",slot,"importing",source.info[:name]) if !o[:cold]
source.r.cluster("setslot",slot,"migrating",target.info[:name]) target.r.cluster("setslot",slot,"importing",source.info[:name])
source.r.cluster("setslot",slot,"migrating",target.info[:name])
end
# Migrate all the keys from source to target using the MIGRATE command # Migrate all the keys from source to target using the MIGRATE command
while true while true
keys = source.r.cluster("getkeysinslot",slot,10) keys = source.r.cluster("getkeysinslot",slot,10)
@ -756,11 +789,14 @@ class RedisTrib
STDOUT.flush STDOUT.flush
} }
end end
puts puts
# Set the new node as the owner of the slot in all the known nodes. # Set the new node as the owner of the slot in all the known nodes.
@nodes.each{|n| if !o[:cold]
n.r.cluster("setslot",slot,"node",target.info[:name]) @nodes.each{|n|
} n.r.cluster("setslot",slot,"node",target.info[:name])
}
end
end end
# redis-trib subcommands implementations # redis-trib subcommands implementations