From 1c708b25ee663985bcfb91082f8acdd6c3cb7489 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 9c726151..22c65354 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 407798c1e1552e1f5e583ead63ed6b7c8ea20299 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 00000000..d83c9f8e --- /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 b800a3ab20440fdd55947f52f3efcc689ef858d3 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 d83c9f8e..8456ed29 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 8c4c50906cc91d8dffbb22cb4a520d11a1c6344b 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 e2b82044..0a580fa7 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 f29d1fb0b20d31c39ea4d092ecd4eb1e4d2f5e1b 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 8456ed29..98ed0a0e 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 e9ee513b6a940ecd3e30a152d6ad2e8fb25147a5 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 e7943307..3032392e 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 =========