In redis cluster, there are a total of 16384 logical slots, which are divided between multiple masters.
Often, when we need to add nodes to the redis cluster, we need to reshard the data.
This is done by redis-trib.rb utility file provided by redis.
However the redis-trib.rb utility can move N no of slots between masters, but it does not provide a function to move a single slot.
Below is shown an existing cluster where the slots are equally divided between nodes.
Also, each node has some data.
import java.util.List;
import com.google.common.collect.Lists;
import com.lambdaworks.redis.MigrateArgs;
import com.lambdaworks.redis.RedisClient;
import com.lambdaworks.redis.RedisURI;
import com.lambdaworks.redis.api.sync.RedisCommands;
import com.lambdaworks.redis.cluster.models.partitions.ClusterPartitionParser;
import com.lambdaworks.redis.cluster.models.partitions.Partitions;
import com.lambdaworks.redis.cluster.models.partitions.RedisClusterNode;
import com.lambdaworks.redis.codec.Utf8StringCodec;
import com.lambdaworks.redis.protocol.CommandArgs;
public class RedisMoveSingleSlot {
public static void main(String[] args){
String sourceHost = "127.0.0.1";
int sourcePort = 7000;
String destHost = "127.0.0.1";
int destPort = 7001;
int slotToMove = 102;
moveSlot(sourceHost, sourcePort, destHost, destPort, slotToMove);
}
private static void moveSlot(String sourceHost, int sourcePort, String destHost, int destPort, int slot) {
// create connections to source and destinations.
RedisClient sourceClient = RedisClient.create(RedisURI.create(sourceHost, sourcePort));
RedisClient destinationClient = RedisClient.create(RedisURI.create(destHost, destPort));
RedisCommands<String, String> source = sourceClient.connect().sync();
RedisCommands<String, String> destination = destinationClient.connect().sync();
// get the source node Id and destination Node Id.
String sourceNodeId = source.clusterMyId();
String destinationNodeId = destination.clusterMyId();
//STEP 1, set destination to be importing slot from source.
destination.clusterSetSlotImporting(slot, sourceNodeId);
//STEP 2, set source to be migrating slot to destination.
source.clusterSetSlotMigrating(slot, destinationNodeId);
// STEP 3 starts, we need to move all the keys from source to destination.
List<String> keys = source.clusterGetKeysInSlot(slot, Integer.MAX_VALUE);
// move keys in batches of 100,
int keySize = 100;
System.out.println("Slot : " + slot + " : got keys of size : " + keys.size() );
if(keys != null && keys.size() > 0){
List<List<String>> listOfListOfKeys = Lists.partition(keys, keySize);
int noOfKeysMoved = 0;
for(List<String> listOfKeys : listOfListOfKeys){
MigrateArgs<String> migrateArgs = new MigrateArgs<>();
migrateArgs.keys(listOfKeys);
migrateArgs.replace();
CommandArgs<String, String> args1 = new CommandArgs<String, String>(new Utf8StringCodec());
migrateArgs.build(args1);
// migrate the keys from source to destination in db 0(default), and will some timeout set, here set to 5000 secs.
source.migrate(destHost, destPort, 0, 5000000, migrateArgs);
noOfKeysMoved = noOfKeysMoved + listOfKeys.size();
System.out.println("totally moved " + noOfKeysMoved + " for slot " + slot);
}
//Thread.sleep(5000);
}
//STEP 3 finished.
// STEP 4, set cluster set slot on source and destination and close the source and destinations.
source.
clusterSetSlotNode(slot
, destinationNodeId
);
destination.
clusterSetSlotNode(slot
, destinationNodeId
);
source.close();
destination.close();
// STEP 4 done.
System.out.println("Moved slot " + slot);
}
}