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 ========= diff --git a/src/cluster.c b/src/cluster.c index 70c34aab..4e3cf746 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1193,11 +1193,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))); diff --git a/src/redis-trib.rb b/src/redis-trib.rb new file mode 100755 index 00000000..98ed0a0e --- /dev/null +++ b/src/redis-trib.rb @@ -0,0 +1,98 @@ +#!/usr/bin/env ruby + +require 'rubygems' +require 'redis' + +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 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 +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)) + puts "Wrong number of arguments for specified sub command" + exit 1 + end + end + + def create_cluster + puts "Creating cluster" + ARGV[1..-1].each{|n| + node = ClusterNode.new(n) + node.connect + node.assert_cluster + node.assert_empty + } + 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]) 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");