From 0c9547bd31b9a86f0e8a294f67f21dfd0317354c Mon Sep 17 00:00:00 2001 From: Salvatore Sanfilippo Date: Mon, 11 Apr 2011 16:39:39 +0200 Subject: [PATCH 1/6] added minimal cluster section in INFO output. This is only useful to check if the instance is or not configured as a cluster node, all the other informations are accessible using the CLUSTER command. --- src/redis.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/redis.c b/src/redis.c index 9c726151f..22c653543 100644 --- a/src/redis.c +++ b/src/redis.c @@ -1498,6 +1498,15 @@ sds genRedisInfoString(char *section) { } } + /* Clusetr */ + if (allsections || defsections || !strcasecmp(section,"cluster")) { + if (sections++) info = sdscat(info,"\r\n"); + info = sdscatprintf(info, + "# Cluster\r\n" + "cluster_enabled:%d\r\n", + server.cluster_enabled); + } + /* Key space */ if (allsections || defsections || !strcasecmp(section,"keyspace")) { if (sections++) info = sdscat(info,"\r\n"); From b78745d0c9dac4d3217b72b97712783ad2a8dd5f Mon Sep 17 00:00:00 2001 From: Salvatore Sanfilippo Date: Mon, 11 Apr 2011 16:41:06 +0200 Subject: [PATCH 2/6] Redis-trib initial implementation (currently can not do any actual work) --- src/redis-trib.rb | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100755 src/redis-trib.rb diff --git a/src/redis-trib.rb b/src/redis-trib.rb new file mode 100755 index 000000000..d83c9f8eb --- /dev/null +++ b/src/redis-trib.rb @@ -0,0 +1,70 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'redis' + +class RedisTrib + def xputs(s) + printf s + STDOUT.flush + end + + def check_arity(req_args, num_args) + if ((req_args > 0 and num_args != req_args) || + (req_args < 0 and num_args < req_args.abs)) + puts "Wrong number of arguments for specified sub command" + exit 1 + end + end + + def parse_node(node) + s = node.split(":") + if s.length != 2 + puts "Invalid node name #{node}" + exit 1 + end + return {:host => s[0], :port => s[1].to_i} + end + + def connect_to_node(naddr) + xputs "Connecting to node #{naddr[:host]}:#{naddr[:port]}: " + begin + r = Redis.new(:host => naddr[:host], :port => naddr[:port]) + r.ping + rescue + puts "ERROR" + puts "Sorry, can't connect to node #{naddr[:host]}:#{naddr[:port]}" + exit 1 + end + puts "OK" + end + + def create_cluster + puts "Creating cluster" + ARGV[1..-1].each{|node| + naddr = parse_node(node) + r = connect_to_node(naddr) + } + end +end + +COMMANDS={ + "create-cluster" => ["create_cluster", -2] +} + +# Sanity check +if ARGV.length == 0 + puts "Usage: redis-trib " + exit 1 +end + +rt = RedisTrib.new +cmd_spec = COMMANDS[ARGV[0].downcase] +if !cmd_spec + puts "Unknown redis-trib subcommand '#{ARGV[0]}'" + exit 1 +end +rt.check_arity(cmd_spec[1],ARGV.length) + +# Dispatch +rt.send(cmd_spec[0]) From 85b05eac9bf00818393d0d068d2a8595840d4bbc Mon Sep 17 00:00:00 2001 From: Salvatore Sanfilippo Date: Mon, 11 Apr 2011 16:58:47 +0200 Subject: [PATCH 3/6] a first refactoring of redis-trib.rb --- src/redis-trib.rb | 78 +++++++++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/src/redis-trib.rb b/src/redis-trib.rb index d83c9f8eb..8456ed29f 100755 --- a/src/redis-trib.rb +++ b/src/redis-trib.rb @@ -3,12 +3,52 @@ require 'rubygems' require 'redis' -class RedisTrib - def xputs(s) - printf s - STDOUT.flush +def xputs(s) + printf s + STDOUT.flush +end + +class ClusterNode + def initialize(addr) + s = addr.split(":") + if s.length != 2 + puts "Invalid node name #{node}" + exit 1 + end + @host = s[0] + @port = s[1] end + def to_s + "#{@host}:#{@port}" + end + + def connect + xputs "Connecting to node #{self}: " + begin + @r = Redis.new(:host => @ost, :port => @port) + @r.ping + rescue + puts "ERROR" + puts "Sorry, can't connect to node #{self}" + end + puts "OK" + end + + def assert_cluster + info = @r.info + if !info["cluster_enabled"] || info["cluster_enabled"].to_i == 0 + puts "Error: Node #{self} is not configured as a cluster node." + exit 1 + end + end + + def r + @r + end +end + +class RedisTrib def check_arity(req_args, num_args) if ((req_args > 0 and num_args != req_args) || (req_args < 0 and num_args < req_args.abs)) @@ -17,33 +57,13 @@ class RedisTrib end end - def parse_node(node) - s = node.split(":") - if s.length != 2 - puts "Invalid node name #{node}" - exit 1 - end - return {:host => s[0], :port => s[1].to_i} - end - - def connect_to_node(naddr) - xputs "Connecting to node #{naddr[:host]}:#{naddr[:port]}: " - begin - r = Redis.new(:host => naddr[:host], :port => naddr[:port]) - r.ping - rescue - puts "ERROR" - puts "Sorry, can't connect to node #{naddr[:host]}:#{naddr[:port]}" - exit 1 - end - puts "OK" - end - def create_cluster puts "Creating cluster" - ARGV[1..-1].each{|node| - naddr = parse_node(node) - r = connect_to_node(naddr) + ARGV[1..-1].each{|n| + node = ClusterNode.new(n) + node.connect + node.assert_cluster + # node.assert_empty } end end From e469604ec541d58221c9915303ec092a61690357 Mon Sep 17 00:00:00 2001 From: Salvatore Sanfilippo Date: Mon, 11 Apr 2011 17:40:35 +0200 Subject: [PATCH 4/6] added known nodes info in CLUSTER INFO --- src/cluster.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/cluster.c b/src/cluster.c index e2b820446..0a580fa75 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1192,11 +1192,13 @@ void clusterCommand(redisClient *c) { "cluster_slots_ok:%d\r\n" "cluster_slots_pfail:%d\r\n" "cluster_slots_fail:%d\r\n" + "cluster_known_nodes:%lu\r\n" , statestr[server.cluster.state], slots_assigned, slots_ok, slots_pfail, - slots_fail + slots_fail, + dictSize(server.cluster.nodes) ); addReplySds(c,sdscatprintf(sdsempty(),"$%lu\r\n", (unsigned long)sdslen(info))); From 3aa6046a53849c002240d19e18120572071782c8 Mon Sep 17 00:00:00 2001 From: Salvatore Sanfilippo Date: Mon, 11 Apr 2011 18:26:00 +0200 Subject: [PATCH 5/6] assert_empty in redis-trib --- src/redis-trib.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/redis-trib.rb b/src/redis-trib.rb index 8456ed29f..98ed0a0e1 100755 --- a/src/redis-trib.rb +++ b/src/redis-trib.rb @@ -43,6 +43,14 @@ class ClusterNode end end + def assert_empty + if !(@r.cluster("info").split("\r\n").index("cluster_known_nodes:1")) || + (@r.info['db0']) + puts "Error: Node #{self} is not empty. Either the node already knows other nodes (check with nodes-info) or contains some key in database 0." + exit 1 + end + end + def r @r end @@ -63,7 +71,7 @@ class RedisTrib node = ClusterNode.new(n) node.connect node.assert_cluster - # node.assert_empty + node.assert_empty } end end From eef64246ad3d5915b60561627ccfaf128a6f2329 Mon Sep 17 00:00:00 2001 From: antirez Date: Mon, 11 Apr 2011 21:47:45 +0200 Subject: [PATCH 6/6] TODO modified --- TODO | 2 ++ 1 file changed, 2 insertions(+) diff --git a/TODO b/TODO index e79433072..3032392e2 100644 --- a/TODO +++ b/TODO @@ -36,6 +36,8 @@ OPTIMIZATIONS * SORT: Don't copy the list into a vector when BY argument is constant. * Write the hash table size of every db in the dump, so that Redis can resize the hash table just one time when loading a big DB. * Read-only mode for slaves. +* Redis big lists as linked lists of small ziplists? + Possibly a simple heuristic that join near nodes when some node gets smaller than the low_level, and split it into two if gets bigger than high_level. REPORTING =========