]> git.saurik.com Git - redis.git/blobdiff - src/redis.c
comment describing the Redis command table enhanced
[redis.git] / src / redis.c
index dc8a0032caf5329ae462f439df77b78ed8767b25..ef0c2fecf731dfe70593ff15503da1958f51eb05 100644 (file)
@@ -28,6 +28,8 @@
  */
 
 #include "redis.h"
+#include "slowlog.h"
+#include "bio.h"
 
 #ifdef HAVE_BACKTRACE
 #include <execinfo.h>
 #include <limits.h>
 #include <float.h>
 #include <math.h>
-#include <pthread.h>
 #include <sys/resource.h>
 
 /* Our shared "common" objects */
 
 struct sharedObjectsStruct shared;
 
-/* Global vars that are actally used as constants. The following double
+/* Global vars that are actually used as constants. The following double
  * values are used for double on-disk serialization, and are initialized
  * at runtime to avoid strange compiler optimizations. */
 
@@ -68,132 +69,182 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
 /* Global vars */
 struct redisServer server; /* server global state */
 struct redisCommand *commandTable;
+
+/* Our command table.
+ *
+ * Every entry is composed of the following fields:
+ *
+ * name: a string representing the command name.
+ * function: pointer to the C function implementing the command.
+ * arity: number of arguments, it is possible to use -N to say >= N
+ * sflags: command flags as string. See below for a table of flags.
+ * flags: flags as bitmask. Computed by Redis using the 'sflags' field.
+ * get_keys_proc: an optional function to get key arguments from a command.
+ *                This is only used when the following three fields are not
+ *                enough to specify what arguments are keys.
+ * first_key_index: first argument that is a key
+ * last_key_index: last argument that is a key
+ * key_step: step to get all the keys from first to last argument. For instance
+ *           in MSET the step is two since arguments are key,val,key,val,...
+ * microseconds: microseconds of total execution time for this command.
+ * calls: total number of calls of this command.
+ *
+ * The flags, microseconds and calls fields are computed by Redis and should
+ * always be set to zero.
+ *
+ * Command flags are expressed using strings where every character represents
+ * a flag. Later the populateCommandTable() function will take care of
+ * populating the real 'flags' field using this characters.
+ *
+ * This is the meaning of the flags:
+ *
+ * w: write command (may modify the key space).
+ * r: read command  (will never modify the key space).
+ * m: may increase memory usage once called. Don't allow if out of memory.
+ * a: admin command, like SAVE or SHUTDOWN.
+ * p: Pub/Sub related command.
+ * f: force replication of this command, regarless of server.dirty.
+ * s: command not allowed in scripts.
+ * R: random command. Command is not deterministic, that is, the same command
+ *    with the same arguments, with the same key space, may have different
+ *    results. For instance SPOP and RANDOMKEY are two random commands. */
 struct redisCommand redisCommandTable[] = {
-    {"get",getCommand,2,0,NULL,1,1,1,0,0},
-    {"set",setCommand,3,REDIS_CMD_DENYOOM,noPreloadGetKeys,1,1,1,0,0},
-    {"setnx",setnxCommand,3,REDIS_CMD_DENYOOM,noPreloadGetKeys,1,1,1,0,0},
-    {"setex",setexCommand,4,REDIS_CMD_DENYOOM,noPreloadGetKeys,2,2,1,0,0},
-    {"append",appendCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"strlen",strlenCommand,2,0,NULL,1,1,1,0,0},
-    {"del",delCommand,-2,0,noPreloadGetKeys,1,-1,1,0,0},
-    {"exists",existsCommand,2,0,NULL,1,1,1,0,0},
-    {"setbit",setbitCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"getbit",getbitCommand,3,0,NULL,1,1,1,0,0},
-    {"setrange",setrangeCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"getrange",getrangeCommand,4,0,NULL,1,1,1,0,0},
-    {"substr",getrangeCommand,4,0,NULL,1,1,1,0,0},
-    {"incr",incrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"decr",decrCommand,2,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"mget",mgetCommand,-2,0,NULL,1,-1,1,0,0},
-    {"rpush",rpushCommand,-3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"lpush",lpushCommand,-3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"rpushx",rpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"lpushx",lpushxCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"linsert",linsertCommand,5,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"rpop",rpopCommand,2,0,NULL,1,1,1,0,0},
-    {"lpop",lpopCommand,2,0,NULL,1,1,1,0,0},
-    {"brpop",brpopCommand,-3,0,NULL,1,1,1,0,0},
-    {"brpoplpush",brpoplpushCommand,4,REDIS_CMD_DENYOOM,NULL,1,2,1,0,0},
-    {"blpop",blpopCommand,-3,0,NULL,1,-2,1,0,0},
-    {"llen",llenCommand,2,0,NULL,1,1,1,0,0},
-    {"lindex",lindexCommand,3,0,NULL,1,1,1,0,0},
-    {"lset",lsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"lrange",lrangeCommand,4,0,NULL,1,1,1,0,0},
-    {"ltrim",ltrimCommand,4,0,NULL,1,1,1,0,0},
-    {"lrem",lremCommand,4,0,NULL,1,1,1,0,0},
-    {"rpoplpush",rpoplpushCommand,3,REDIS_CMD_DENYOOM,NULL,1,2,1,0,0},
-    {"sadd",saddCommand,-3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"srem",sremCommand,-3,0,NULL,1,1,1,0,0},
-    {"smove",smoveCommand,4,0,NULL,1,2,1,0,0},
-    {"sismember",sismemberCommand,3,0,NULL,1,1,1,0,0},
-    {"scard",scardCommand,2,0,NULL,1,1,1,0,0},
-    {"spop",spopCommand,2,0,NULL,1,1,1,0,0},
-    {"srandmember",srandmemberCommand,2,0,NULL,1,1,1,0,0},
-    {"sinter",sinterCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0,0},
-    {"sinterstore",sinterstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0,0},
-    {"sunion",sunionCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0,0},
-    {"sunionstore",sunionstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0,0},
-    {"sdiff",sdiffCommand,-2,REDIS_CMD_DENYOOM,NULL,1,-1,1,0,0},
-    {"sdiffstore",sdiffstoreCommand,-3,REDIS_CMD_DENYOOM,NULL,2,-1,1,0,0},
-    {"smembers",sinterCommand,2,0,NULL,1,1,1,0,0},
-    {"zadd",zaddCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"zincrby",zincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"zrem",zremCommand,-3,0,NULL,1,1,1,0,0},
-    {"zremrangebyscore",zremrangebyscoreCommand,4,0,NULL,1,1,1,0,0},
-    {"zremrangebyrank",zremrangebyrankCommand,4,0,NULL,1,1,1,0,0},
-    {"zunionstore",zunionstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
-    {"zinterstore",zinterstoreCommand,-4,REDIS_CMD_DENYOOM,zunionInterGetKeys,0,0,0,0,0},
-    {"zrange",zrangeCommand,-4,0,NULL,1,1,1,0,0},
-    {"zrangebyscore",zrangebyscoreCommand,-4,0,NULL,1,1,1,0,0},
-    {"zrevrangebyscore",zrevrangebyscoreCommand,-4,0,NULL,1,1,1,0,0},
-    {"zcount",zcountCommand,4,0,NULL,1,1,1,0,0},
-    {"zrevrange",zrevrangeCommand,-4,0,NULL,1,1,1,0,0},
-    {"zcard",zcardCommand,2,0,NULL,1,1,1,0,0},
-    {"zscore",zscoreCommand,3,0,NULL,1,1,1,0,0},
-    {"zrank",zrankCommand,3,0,NULL,1,1,1,0,0},
-    {"zrevrank",zrevrankCommand,3,0,NULL,1,1,1,0,0},
-    {"hset",hsetCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"hsetnx",hsetnxCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"hget",hgetCommand,3,0,NULL,1,1,1,0,0},
-    {"hmset",hmsetCommand,-4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"hmget",hmgetCommand,-3,0,NULL,1,1,1,0,0},
-    {"hincrby",hincrbyCommand,4,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"hdel",hdelCommand,-3,0,NULL,1,1,1,0,0},
-    {"hlen",hlenCommand,2,0,NULL,1,1,1,0,0},
-    {"hkeys",hkeysCommand,2,0,NULL,1,1,1,0,0},
-    {"hvals",hvalsCommand,2,0,NULL,1,1,1,0,0},
-    {"hgetall",hgetallCommand,2,0,NULL,1,1,1,0,0},
-    {"hexists",hexistsCommand,3,0,NULL,1,1,1,0,0},
-    {"incrby",incrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"decrby",decrbyCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"getset",getsetCommand,3,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"mset",msetCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2,0,0},
-    {"msetnx",msetnxCommand,-3,REDIS_CMD_DENYOOM,NULL,1,-1,2,0,0},
-    {"randomkey",randomkeyCommand,1,0,NULL,0,0,0,0,0},
-    {"select",selectCommand,2,0,NULL,0,0,0,0,0},
-    {"move",moveCommand,3,0,NULL,1,1,1,0,0},
-    {"rename",renameCommand,3,0,renameGetKeys,1,2,1,0,0},
-    {"renamenx",renamenxCommand,3,0,renameGetKeys,1,2,1,0,0},
-    {"expire",expireCommand,3,0,NULL,1,1,1,0,0},
-    {"expireat",expireatCommand,3,0,NULL,1,1,1,0,0},
-    {"keys",keysCommand,2,0,NULL,0,0,0,0,0},
-    {"dbsize",dbsizeCommand,1,0,NULL,0,0,0,0,0},
-    {"auth",authCommand,2,0,NULL,0,0,0,0,0},
-    {"ping",pingCommand,1,0,NULL,0,0,0,0,0},
-    {"echo",echoCommand,2,0,NULL,0,0,0,0,0},
-    {"save",saveCommand,1,0,NULL,0,0,0,0,0},
-    {"bgsave",bgsaveCommand,1,0,NULL,0,0,0,0,0},
-    {"bgrewriteaof",bgrewriteaofCommand,1,0,NULL,0,0,0,0,0},
-    {"shutdown",shutdownCommand,1,0,NULL,0,0,0,0,0},
-    {"lastsave",lastsaveCommand,1,0,NULL,0,0,0,0,0},
-    {"type",typeCommand,2,0,NULL,1,1,1,0,0},
-    {"multi",multiCommand,1,0,NULL,0,0,0,0,0},
-    {"exec",execCommand,1,REDIS_CMD_DENYOOM,NULL,0,0,0,0,0},
-    {"discard",discardCommand,1,0,NULL,0,0,0,0,0},
-    {"sync",syncCommand,1,0,NULL,0,0,0,0,0},
-    {"flushdb",flushdbCommand,1,0,NULL,0,0,0,0,0},
-    {"flushall",flushallCommand,1,0,NULL,0,0,0,0,0},
-    {"sort",sortCommand,-2,REDIS_CMD_DENYOOM,NULL,1,1,1,0,0},
-    {"info",infoCommand,-1,0,NULL,0,0,0,0,0},
-    {"monitor",monitorCommand,1,0,NULL,0,0,0,0,0},
-    {"ttl",ttlCommand,2,0,NULL,1,1,1,0,0},
-    {"persist",persistCommand,2,0,NULL,1,1,1,0,0},
-    {"slaveof",slaveofCommand,3,0,NULL,0,0,0,0,0},
-    {"debug",debugCommand,-2,0,NULL,0,0,0,0,0},
-    {"config",configCommand,-2,0,NULL,0,0,0,0,0},
-    {"subscribe",subscribeCommand,-2,0,NULL,0,0,0,0,0},
-    {"unsubscribe",unsubscribeCommand,-1,0,NULL,0,0,0,0,0},
-    {"psubscribe",psubscribeCommand,-2,0,NULL,0,0,0,0,0},
-    {"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0,0,0},
-    {"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0,0,0},
-    {"watch",watchCommand,-2,0,noPreloadGetKeys,1,-1,1,0,0},
-    {"unwatch",unwatchCommand,1,0,NULL,0,0,0,0,0},
-    {"cluster",clusterCommand,-2,0,NULL,0,0,0,0,0},
-    {"restore",restoreCommand,4,0,NULL,0,0,0,0,0},
-    {"migrate",migrateCommand,6,0,NULL,0,0,0,0,0},
-    {"dump",dumpCommand,2,0,NULL,0,0,0,0,0},
-    {"object",objectCommand,-2,0,NULL,0,0,0,0,0},
-    {"client",clientCommand,-2,0,NULL,0,0,0,0,0}
+    {"get",getCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"set",setCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+    {"setnx",setnxCommand,3,"wm",0,noPreloadGetKeys,1,1,1,0,0},
+    {"setex",setexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0},
+    {"psetex",psetexCommand,4,"wm",0,noPreloadGetKeys,2,2,1,0,0},
+    {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"strlen",strlenCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"del",delCommand,-2,"w",0,noPreloadGetKeys,1,-1,1,0,0},
+    {"exists",existsCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"getbit",getbitCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+    {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+    {"incr",incrCommand,2,"wm",0,NULL,1,1,1,0,0},
+    {"decr",decrCommand,2,"wm",0,NULL,1,1,1,0,0},
+    {"mget",mgetCommand,-2,"r",0,NULL,1,-1,1,0,0},
+    {"rpush",rpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
+    {"lpush",lpushCommand,-3,"wm",0,NULL,1,1,1,0,0},
+    {"rpushx",rpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"lpushx",lpushxCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
+    {"rpop",rpopCommand,2,"w",0,NULL,1,1,1,0,0},
+    {"lpop",lpopCommand,2,"w",0,NULL,1,1,1,0,0},
+    {"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},
+    {"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},
+    {"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
+    {"llen",llenCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"lindex",lindexCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"lset",lsetCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"lrange",lrangeCommand,4,"r",0,NULL,1,1,1,0,0},
+    {"ltrim",ltrimCommand,4,"w",0,NULL,1,1,1,0,0},
+    {"lrem",lremCommand,4,"w",0,NULL,1,1,1,0,0},
+    {"rpoplpush",rpoplpushCommand,3,"wm",0,NULL,1,2,1,0,0},
+    {"sadd",saddCommand,-3,"wm",0,NULL,1,1,1,0,0},
+    {"srem",sremCommand,-3,"w",0,NULL,1,1,1,0,0},
+    {"smove",smoveCommand,4,"w",0,NULL,1,2,1,0,0},
+    {"sismember",sismemberCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"scard",scardCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"spop",spopCommand,2,"wRs",0,NULL,1,1,1,0,0},
+    {"srandmember",srandmemberCommand,2,"rR",0,NULL,1,1,1,0,0},
+    {"sinter",sinterCommand,-2,"r",0,NULL,1,-1,1,0,0},
+    {"sinterstore",sinterstoreCommand,-3,"wm",0,NULL,2,-1,1,0,0},
+    {"sunion",sunionCommand,-2,"r",0,NULL,1,-1,1,0,0},
+    {"sunionstore",sunionstoreCommand,-3,"wm",0,NULL,2,-1,1,0,0},
+    {"sdiff",sdiffCommand,-2,"r",0,NULL,1,-1,1,0,0},
+    {"sdiffstore",sdiffstoreCommand,-3,"wm",0,NULL,2,-1,1,0,0},
+    {"smembers",sinterCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"zadd",zaddCommand,-4,"wm",0,NULL,1,1,1,0,0},
+    {"zincrby",zincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"zrem",zremCommand,-3,"w",0,NULL,1,1,1,0,0},
+    {"zremrangebyscore",zremrangebyscoreCommand,4,"w",0,NULL,1,1,1,0,0},
+    {"zremrangebyrank",zremrangebyrankCommand,4,"w",0,NULL,1,1,1,0,0},
+    {"zunionstore",zunionstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
+    {"zinterstore",zinterstoreCommand,-4,"wm",0,zunionInterGetKeys,0,0,0,0,0},
+    {"zrange",zrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
+    {"zrangebyscore",zrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
+    {"zrevrangebyscore",zrevrangebyscoreCommand,-4,"r",0,NULL,1,1,1,0,0},
+    {"zcount",zcountCommand,4,"r",0,NULL,1,1,1,0,0},
+    {"zrevrange",zrevrangeCommand,-4,"r",0,NULL,1,1,1,0,0},
+    {"zcard",zcardCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"zscore",zscoreCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"zrank",zrankCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"zrevrank",zrevrankCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"hset",hsetCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"hsetnx",hsetnxCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"hget",hgetCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"hmset",hmsetCommand,-4,"wm",0,NULL,1,1,1,0,0},
+    {"hmget",hmgetCommand,-3,"r",0,NULL,1,1,1,0,0},
+    {"hincrby",hincrbyCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"hincrbyfloat",hincrbyfloatCommand,4,"wm",0,NULL,1,1,1,0,0},
+    {"hdel",hdelCommand,-3,"w",0,NULL,1,1,1,0,0},
+    {"hlen",hlenCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"hkeys",hkeysCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"hvals",hvalsCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"hgetall",hgetallCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"hexists",hexistsCommand,3,"r",0,NULL,1,1,1,0,0},
+    {"incrby",incrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"decrby",decrbyCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"incrbyfloat",incrbyfloatCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"getset",getsetCommand,3,"wm",0,NULL,1,1,1,0,0},
+    {"mset",msetCommand,-3,"wm",0,NULL,1,-1,2,0,0},
+    {"msetnx",msetnxCommand,-3,"wm",0,NULL,1,-1,2,0,0},
+    {"randomkey",randomkeyCommand,1,"rR",0,NULL,0,0,0,0,0},
+    {"select",selectCommand,2,"r",0,NULL,0,0,0,0,0},
+    {"move",moveCommand,3,"w",0,NULL,1,1,1,0,0},
+    {"rename",renameCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
+    {"renamenx",renamenxCommand,3,"w",0,renameGetKeys,1,2,1,0,0},
+    {"expire",expireCommand,3,"w",0,NULL,1,1,1,0,0},
+    {"expireat",expireatCommand,3,"w",0,NULL,1,1,1,0,0},
+    {"pexpire",pexpireCommand,3,"w",0,NULL,1,1,1,0,0},
+    {"pexpireat",pexpireatCommand,3,"w",0,NULL,1,1,1,0,0},
+    {"keys",keysCommand,2,"r",0,NULL,0,0,0,0,0},
+    {"dbsize",dbsizeCommand,1,"r",0,NULL,0,0,0,0,0},
+    {"auth",authCommand,2,"rs",0,NULL,0,0,0,0,0},
+    {"ping",pingCommand,1,"r",0,NULL,0,0,0,0,0},
+    {"echo",echoCommand,2,"r",0,NULL,0,0,0,0,0},
+    {"save",saveCommand,1,"ars",0,NULL,0,0,0,0,0},
+    {"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
+    {"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
+    {"shutdown",shutdownCommand,-1,"ar",0,NULL,0,0,0,0,0},
+    {"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
+    {"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
+    {"exec",execCommand,1,"wms",0,NULL,0,0,0,0,0},
+    {"discard",discardCommand,1,"rs",0,NULL,0,0,0,0,0},
+    {"sync",syncCommand,1,"ars",0,NULL,0,0,0,0,0},
+    {"flushdb",flushdbCommand,1,"w",0,NULL,0,0,0,0,0},
+    {"flushall",flushallCommand,1,"w",0,NULL,0,0,0,0,0},
+    {"sort",sortCommand,-2,"wm",0,NULL,1,1,1,0,0},
+    {"info",infoCommand,-1,"r",0,NULL,0,0,0,0,0},
+    {"monitor",monitorCommand,1,"ars",0,NULL,0,0,0,0,0},
+    {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
+    {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
+    {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
+    {"debug",debugCommand,-2,"aws",0,NULL,0,0,0,0,0},
+    {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
+    {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+    {"unsubscribe",unsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
+    {"psubscribe",psubscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
+    {"punsubscribe",punsubscribeCommand,-1,"rps",0,NULL,0,0,0,0,0},
+    {"publish",publishCommand,3,"rpf",0,NULL,0,0,0,0,0},
+    {"watch",watchCommand,-2,"rs",0,noPreloadGetKeys,1,-1,1,0,0},
+    {"unwatch",unwatchCommand,1,"rs",0,NULL,0,0,0,0,0},
+    {"cluster",clusterCommand,-2,"ar",0,NULL,0,0,0,0,0},
+    {"restore",restoreCommand,4,"awm",0,NULL,1,1,1,0,0},
+    {"migrate",migrateCommand,6,"aw",0,NULL,0,0,0,0,0},
+    {"asking",askingCommand,1,"r",0,NULL,0,0,0,0,0},
+    {"dump",dumpCommand,2,"ar",0,NULL,0,0,0,0,0},
+    {"object",objectCommand,-2,"r",0,NULL,0,0,0,0,0},
+    {"client",clientCommand,-2,"ar",0,NULL,0,0,0,0,0},
+    {"eval",evalCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0},
+    {"evalsha",evalShaCommand,-3,"wms",0,zunionInterGetKeys,0,0,0,0,0},
+    {"slowlog",slowlogCommand,-2,"r",0,NULL,0,0,0,0,0},
+    {"script",scriptCommand,-2,"ras",0,NULL,0,0,0,0,0}
 };
 
 /*============================ Utility functions ============================ */
@@ -265,6 +316,11 @@ long long ustime(void) {
     return ust;
 }
 
+/* Return the UNIX time in milliseconds */
+long long mstime(void) {
+    return ustime()/1000;
+}
+
 /*====================== Hash table type implementation  ==================== */
 
 /* This is an hash table type that uses the SDS dynamic strings libary as
@@ -380,7 +436,7 @@ unsigned int dictEncObjHash(const void *key) {
     }
 }
 
-/* Sets type and diskstore negative caching hash table */
+/* Sets type hash table */
 dictType setDictType = {
     dictEncObjHash,            /* hash function */
     NULL,                      /* key dup */
@@ -530,19 +586,19 @@ void activeExpireCycle(void) {
          * of the keys were expired. */
         do {
             long num = dictSize(db->expires);
-            time_t now = time(NULL);
+            long long now = mstime();
 
             expired = 0;
             if (num > REDIS_EXPIRELOOKUPS_PER_CRON)
                 num = REDIS_EXPIRELOOKUPS_PER_CRON;
             while (num--) {
                 dictEntry *de;
-                time_t t;
+                long long t;
 
                 if ((de = dictGetRandomKey(db->expires)) == NULL) break;
-                t = (time_t) dictGetEntryVal(de);
+                t = dictGetSignedIntegerVal(de);
                 if (now > t) {
-                    sds key = dictGetEntryKey(de);
+                    sds key = dictGetKey(de);
                     robj *keyobj = createStringObject(key,sdslen(key));
 
                     propagateExpire(db,keyobj);
@@ -572,6 +628,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
      * in objects at every object access, and accuracy is not needed.
      * To access a global var is faster than calling time(NULL) */
     server.unixtime = time(NULL);
+
     /* We have just 22 bits per object for LRU information.
      * So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
      * 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
@@ -593,7 +650,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
     /* We received a SIGTERM, shutting down here in a safe way, as it is
      * not ok doing so inside the signal handler. */
     if (server.shutdown_asap) {
-        if (prepareForShutdown() == REDIS_OK) exit(0);
+        if (prepareForShutdown(0) == REDIS_OK) exit(0);
         redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
     }
 
@@ -633,6 +690,14 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
     if ((server.maxidletime && !(loops % 100)) || server.bpop_blocked_clients)
         closeTimedoutClients();
 
+    /* Start a scheduled AOF rewrite if this was requested by the user while
+     * a BGSAVE was in progress. */
+    if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
+        server.aofrewrite_scheduled)
+    {
+        rewriteAppendOnlyFileBackground();
+    }
+
     /* Check if a background saving or AOF rewrite in progress terminated. */
     if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) {
         int statloc;
@@ -651,25 +716,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
             }
             updateDictResizePolicy();
         }
-    } else if (server.bgsavethread != (pthread_t) -1) {
-        if (server.bgsavethread != (pthread_t) -1) {
-            int state;
-
-            pthread_mutex_lock(&server.bgsavethread_mutex);
-            state = server.bgsavethread_state;
-            pthread_mutex_unlock(&server.bgsavethread_mutex);
-
-            if (state == REDIS_BGSAVE_THREAD_DONE_OK ||
-                state == REDIS_BGSAVE_THREAD_DONE_ERR)
-            {
-                backgroundSaveDoneHandler(
-                    (state == REDIS_BGSAVE_THREAD_DONE_OK) ? 0 : 1, 0);
-            }
-        }
-    } else if (!server.ds_enabled) {
-        /* If there is not a background saving in progress check if
-         * we have to save now */
+    } else {
          time_t now = time(NULL);
+
+        /* If there is not a background saving/rewrite in progress check if
+         * we have to save/rewrite now */
          for (j = 0; j < server.saveparamslen; j++) {
             struct saveparam *sp = server.saveparams+j;
 
@@ -681,17 +732,33 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
                 break;
             }
          }
+
+         /* Trigger an AOF rewrite if needed */
+         if (server.bgsavechildpid == -1 &&
+             server.bgrewritechildpid == -1 &&
+             server.auto_aofrewrite_perc &&
+             server.appendonly_current_size > server.auto_aofrewrite_min_size)
+         {
+            long long base = server.auto_aofrewrite_base_size ?
+                            server.auto_aofrewrite_base_size : 1;
+            long long growth = (server.appendonly_current_size*100/base) - 100;
+            if (growth >= server.auto_aofrewrite_perc) {
+                redisLog(REDIS_NOTICE,"Starting automatic rewriting of AOF on %lld%% growth",growth);
+                rewriteAppendOnlyFileBackground();
+            }
+         }
     }
 
+
+    /* If we postponed an AOF buffer flush, let's try to do it every time the
+     * cron function is called. */
+    if (server.aof_flush_postponed_start) flushAppendOnlyFile(0);
+
     /* Expire a few keys per cycle, only if this is a master.
      * On slaves we wait for DEL operations synthesized by the master
      * in order to guarantee a strict consistency. */
     if (server.masterhost == NULL) activeExpireCycle();
 
-    /* Remove a few cached objects from memory if we are over the
-     * configured memory limit */
-    if (server.ds_enabled) cacheCron();
-
     /* Replication cron function -- used to reconnect to master and
      * to detect transfer failures. */
     if (!(loops % 10)) replicationCron();
@@ -711,31 +778,6 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
     listNode *ln;
     redisClient *c;
 
-    /* Awake clients that got all the on disk keys they requested */
-    if (server.ds_enabled && listLength(server.io_ready_clients)) {
-        listIter li;
-
-        listRewind(server.io_ready_clients,&li);
-        while((ln = listNext(&li))) {
-            c = ln->value;
-            struct redisCommand *cmd;
-
-            /* Resume the client. */
-            listDelNode(server.io_ready_clients,ln);
-            c->flags &= (~REDIS_IO_WAIT);
-            server.cache_blocked_clients--;
-            aeCreateFileEvent(server.el, c->fd, AE_READABLE,
-                readQueryFromClient, c);
-            cmd = lookupCommand(c->argv[0]->ptr);
-            redisAssert(cmd != NULL);
-            call(c,cmd);
-            resetClient(c);
-            /* There may be more data to process in the input buffer. */
-            if (c->querybuf && sdslen(c->querybuf) > 0)
-                processInputBuffer(c);
-        }
-    }
-
     /* Try to process pending commands for clients that were just unblocked. */
     while (listLength(server.unblocked_clients)) {
         ln = listFirst(server.unblocked_clients);
@@ -750,7 +792,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
     }
 
     /* Write the AOF buffer on disk */
-    flushAppendOnlyFile();
+    flushAppendOnlyFile(0);
 }
 
 /* =========================== Server initialization ======================== */
@@ -780,8 +822,12 @@ void createSharedObjects(void) {
         "-ERR source and destination objects are the same\r\n"));
     shared.outofrangeerr = createObject(REDIS_STRING,sdsnew(
         "-ERR index out of range\r\n"));
+    shared.noscripterr = createObject(REDIS_STRING,sdsnew(
+        "-NOSCRIPT No matching script. Please use EVAL.\r\n"));
     shared.loadingerr = createObject(REDIS_STRING,sdsnew(
         "-LOADING Redis is loading the dataset in memory\r\n"));
+    shared.slowscripterr = createObject(REDIS_STRING,sdsnew(
+        "-BUSY Redis is busy running a script. You can only call SCRIPT KILL or SHUTDOWN NOSAVE.\r\n"));
     shared.space = createObject(REDIS_STRING,sdsnew(" "));
     shared.colon = createObject(REDIS_STRING,sdsnew(":"));
     shared.plus = createObject(REDIS_STRING,sdsnew("+"));
@@ -813,11 +859,13 @@ void initServerConfig() {
     server.port = REDIS_SERVERPORT;
     server.bindaddr = NULL;
     server.unixsocket = NULL;
+    server.unixsocketperm = 0;
     server.ipfd = -1;
     server.sofd = -1;
     server.dbnum = REDIS_DEFAULT_DBNUM;
     server.verbosity = REDIS_VERBOSE;
     server.maxidletime = REDIS_MAXIDLETIME;
+    server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
     server.saveparams = NULL;
     server.loading = 0;
     server.logfile = NULL; /* NULL = log on standard output */
@@ -828,24 +876,25 @@ void initServerConfig() {
     server.appendonly = 0;
     server.appendfsync = APPENDFSYNC_EVERYSEC;
     server.no_appendfsync_on_rewrite = 0;
+    server.auto_aofrewrite_perc = REDIS_AUTO_AOFREWRITE_PERC;
+    server.auto_aofrewrite_min_size = REDIS_AUTO_AOFREWRITE_MIN_SIZE;
+    server.auto_aofrewrite_base_size = 0;
+    server.aofrewrite_scheduled = 0;
     server.lastfsync = time(NULL);
     server.appendfd = -1;
     server.appendseldb = -1; /* Make sure the first time will not match */
+    server.aof_flush_postponed_start = 0;
     server.pidfile = zstrdup("/var/run/redis.pid");
     server.dbfilename = zstrdup("dump.rdb");
     server.appendfilename = zstrdup("appendonly.aof");
     server.requirepass = NULL;
     server.rdbcompression = 1;
     server.activerehashing = 1;
-    server.maxclients = 0;
+    server.maxclients = REDIS_MAX_CLIENTS;
     server.bpop_blocked_clients = 0;
     server.maxmemory = 0;
     server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
     server.maxmemory_samples = 3;
-    server.ds_enabled = 0;
-    server.ds_path = sdsnew("/tmp/redis.ds");
-    server.cache_max_memory = 64LL*1024*1024; /* 64 MB of RAM */
-    server.cache_blocked_clients = 0;
     server.hash_max_zipmap_entries = REDIS_HASH_MAX_ZIPMAP_ENTRIES;
     server.hash_max_zipmap_value = REDIS_HASH_MAX_ZIPMAP_VALUE;
     server.list_max_ziplist_entries = REDIS_LIST_MAX_ZIPLIST_ENTRIES;
@@ -854,9 +903,14 @@ void initServerConfig() {
     server.zset_max_ziplist_entries = REDIS_ZSET_MAX_ZIPLIST_ENTRIES;
     server.zset_max_ziplist_value = REDIS_ZSET_MAX_ZIPLIST_VALUE;
     server.shutdown_asap = 0;
-    server.cache_flush_delay = 0;
+    server.repl_ping_slave_period = REDIS_REPL_PING_SLAVE_PERIOD;
+    server.repl_timeout = REDIS_REPL_TIMEOUT;
     server.cluster_enabled = 0;
     server.cluster.configfile = zstrdup("nodes.conf");
+    server.lua_caller = NULL;
+    server.lua_time_limit = REDIS_LUA_TIME_LIMIT;
+    server.lua_client = NULL;
+    server.lua_timedout = 0;
 
     updateLRUClock();
     resetServerSaveParams();
@@ -873,6 +927,7 @@ void initServerConfig() {
     server.replstate = REDIS_REPL_NONE;
     server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
     server.repl_serve_stale_data = 1;
+    server.repl_down_since = -1;
 
     /* Double constants initialization */
     R_Zero = 0.0;
@@ -887,6 +942,10 @@ void initServerConfig() {
     populateCommandTable();
     server.delCommand = lookupCommandByCString("del");
     server.multiCommand = lookupCommandByCString("multi");
+    
+    /* Slow log */
+    server.slowlog_log_slower_than = REDIS_SLOWLOG_LOG_SLOWER_THAN;
+    server.slowlog_max_len = REDIS_SLOWLOG_MAX_LEN;
 }
 
 void initServer() {
@@ -901,12 +960,10 @@ void initServer() {
             server.syslog_facility);
     }
 
-    server.mainthread = pthread_self();
     server.clients = listCreate();
     server.slaves = listCreate();
     server.monitors = listCreate();
     server.unblocked_clients = listCreate();
-    server.cache_io_queue = listCreate();
 
     createSharedObjects();
     server.el = aeCreateEventLoop();
@@ -915,13 +972,14 @@ void initServer() {
     if (server.port != 0) {
         server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
         if (server.ipfd == ANET_ERR) {
-            redisLog(REDIS_WARNING, "Opening port: %s", server.neterr);
+            redisLog(REDIS_WARNING, "Opening port %d: %s",
+                server.port, server.neterr);
             exit(1);
         }
     }
     if (server.unixsocket != NULL) {
         unlink(server.unixsocket); /* don't care if this fails */
-        server.sofd = anetUnixServer(server.neterr,server.unixsocket);
+        server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
         if (server.sofd == ANET_ERR) {
             redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
             exit(1);
@@ -936,11 +994,6 @@ void initServer() {
         server.db[j].expires = dictCreate(&keyptrDictType,NULL);
         server.db[j].blocking_keys = dictCreate(&keylistDictType,NULL);
         server.db[j].watched_keys = dictCreate(&keylistDictType,NULL);
-        if (server.ds_enabled) {
-            server.db[j].io_keys = dictCreate(&keylistDictType,NULL);
-            server.db[j].io_negcache = dictCreate(&setDictType,NULL);
-            server.db[j].io_queued = dictCreate(&setDictType,NULL);
-        }
         server.db[j].id = j;
     }
     server.pubsub_channels = dictCreate(&keylistDictType,NULL);
@@ -950,8 +1003,6 @@ void initServer() {
     server.cronloops = 0;
     server.bgsavechildpid = -1;
     server.bgrewritechildpid = -1;
-    server.bgsavethread_state = REDIS_BGSAVE_THREAD_UNACTIVE;
-    server.bgsavethread = (pthread_t) -1;
     server.bgrewritebuf = sdsempty();
     server.aofbuf = sdsempty();
     server.lastsave = time(NULL);
@@ -981,9 +1032,44 @@ void initServer() {
         }
     }
 
-    if (server.ds_enabled) dsInit();
     if (server.cluster_enabled) clusterInit();
+    scriptingInit();
+    slowlogInit();
+    bioInit();
     srand(time(NULL)^getpid());
+
+    /* Try to raise the max number of open files accordingly to the
+     * configured max number of clients. Also account for 32 additional
+     * file descriptors as we need a few more for persistence, listening
+     * sockets, log files and so forth. */
+    {
+        rlim_t maxfiles = server.maxclients+32;
+        struct rlimit limit;
+
+        if (maxfiles < 1024) maxfiles = 1024;
+        if (getrlimit(RLIMIT_NOFILE,&limit) == -1) {
+            redisLog(REDIS_WARNING,"Unable to obtain the current NOFILE limit (%s), assuming 1024 and setting the max clients configuration accordingly.",
+                strerror(errno));
+            server.maxclients = 1024-32;
+        } else {
+            rlim_t oldlimit = limit.rlim_cur;
+
+            /* Set the max number of files if the current limit is not enough
+             * for our needs. */
+            if (oldlimit < maxfiles) {
+                limit.rlim_cur = maxfiles;
+                limit.rlim_max = maxfiles;
+                if (setrlimit(RLIMIT_NOFILE,&limit) == -1) {
+                    server.maxclients = oldlimit-32;
+                    redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
+                        (int) maxfiles, strerror(errno), (int) server.maxclients);
+                } else {
+                    redisLog(REDIS_NOTICE,"Max number of open files set to %d",
+                        (int) maxfiles);
+                }
+            }
+        }
+    }
 }
 
 /* Populates the Redis Command Table starting from the hard coded list
@@ -994,8 +1080,24 @@ void populateCommandTable(void) {
 
     for (j = 0; j < numcommands; j++) {
         struct redisCommand *c = redisCommandTable+j;
+        char *f = c->sflags;
         int retval;
 
+        while(*f != '\0') {
+            switch(*f) {
+            case 'w': c->flags |= REDIS_CMD_WRITE; break;
+            case 'r': c->flags |= REDIS_CMD_READONLY; break;
+            case 'm': c->flags |= REDIS_CMD_DENYOOM; break;
+            case 'a': c->flags |= REDIS_CMD_ADMIN; break;
+            case 'p': c->flags |= REDIS_CMD_PUBSUB; break;
+            case 'f': c->flags |= REDIS_CMD_FORCE_REPLICATION; break;
+            case 's': c->flags |= REDIS_CMD_NOSCRIPT; break;
+            case 'R': c->flags |= REDIS_CMD_RANDOM; break;
+            default: redisPanic("Unsupported command flag"); break;
+            }
+            f++;
+        }
+
         retval = dictAdd(server.commands, sdsnew(c->name), c);
         assert(retval == DICT_OK);
     }
@@ -1029,18 +1131,20 @@ struct redisCommand *lookupCommandByCString(char *s) {
 }
 
 /* Call() is the core of Redis execution of a command */
-void call(redisClient *c, struct redisCommand *cmd) {
-    long long dirty, start = ustime();
+void call(redisClient *c) {
+    long long dirty, start = ustime(), duration;
 
     dirty = server.dirty;
-    cmd->proc(c);
+    c->cmd->proc(c);
     dirty = server.dirty-dirty;
-    cmd->microseconds += ustime()-start;
-    cmd->calls++;
-
-    if (server.appendonly && dirty)
-        feedAppendOnlyFile(cmd,c->db->id,c->argv,c->argc);
-    if ((dirty || cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&
+    duration = ustime()-start;
+    c->cmd->microseconds += duration;
+    slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
+    c->cmd->calls++;
+
+    if (server.appendonly && dirty > 0)
+        feedAppendOnlyFile(c->cmd,c->db->id,c->argv,c->argc);
+    if ((dirty > 0 || c->cmd->flags & REDIS_CMD_FORCE_REPLICATION) &&
         listLength(server.slaves))
         replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
     if (listLength(server.monitors))
@@ -1057,8 +1161,6 @@ void call(redisClient *c, struct redisCommand *cmd) {
  * and other operations can be performed by the caller. Otherwise
  * if 0 is returned the client was destroied (i.e. after QUIT). */
 int processCommand(redisClient *c) {
-    struct redisCommand *cmd;
-
     /* The QUIT command is handled separately. Normal command procs will
      * go through checking for replication and QUIT will cause trouble
      * when FORCE_REPLICATION is enabled and would be implemented in
@@ -1070,28 +1172,29 @@ int processCommand(redisClient *c) {
     }
 
     /* Now lookup the command and check ASAP about trivial error conditions
-     * such wrong arity, bad command name and so forth. */
-    cmd = lookupCommand(c->argv[0]->ptr);
-    if (!cmd) {
+     * such as wrong arity, bad command name and so forth. */
+    c->cmd = lookupCommand(c->argv[0]->ptr);
+    if (!c->cmd) {
         addReplyErrorFormat(c,"unknown command '%s'",
             (char*)c->argv[0]->ptr);
         return REDIS_OK;
-    } else if ((cmd->arity > 0 && cmd->arity != c->argc) ||
-               (c->argc < -cmd->arity)) {
+    } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
+               (c->argc < -c->cmd->arity)) {
         addReplyErrorFormat(c,"wrong number of arguments for '%s' command",
-            cmd->name);
+            c->cmd->name);
         return REDIS_OK;
     }
 
     /* Check if the user is authenticated */
-    if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
+    if (server.requirepass && !c->authenticated && c->cmd->proc != authCommand)
+    {
         addReplyError(c,"operation not permitted");
         return REDIS_OK;
     }
 
     /* If cluster is enabled, redirect here */
     if (server.cluster_enabled &&
-                !(cmd->getkeys_proc == NULL && cmd->firstkey == 0)) {
+                !(c->cmd->getkeys_proc == NULL && c->cmd->firstkey == 0)) {
         int hashslot;
 
         if (server.cluster.state != REDIS_CLUSTER_OK) {
@@ -1099,7 +1202,7 @@ int processCommand(redisClient *c) {
             return REDIS_OK;
         } else {
             int ask;
-            clusterNode *n = getNodeByQuery(c,cmd,c->argv,c->argc,&hashslot,&ask);
+            clusterNode *n = getNodeByQuery(c,c->cmd,c->argv,c->argc,&hashslot,&ask);
             if (n == NULL) {
                 addReplyError(c,"Multi keys request invalid in cluster");
                 return REDIS_OK;
@@ -1118,7 +1221,7 @@ int processCommand(redisClient *c) {
      * keys in the dataset). If there are not the only thing we can do
      * is returning an error. */
     if (server.maxmemory) freeMemoryIfNeeded();
-    if (server.maxmemory && (cmd->flags & REDIS_CMD_DENYOOM) &&
+    if (server.maxmemory && (c->cmd->flags & REDIS_CMD_DENYOOM) &&
         zmalloc_used_memory() > server.maxmemory)
     {
         addReplyError(c,"command not allowed when used memory > 'maxmemory'");
@@ -1128,8 +1231,10 @@ int processCommand(redisClient *c) {
     /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
     if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
         &&
-        cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
-        cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
+        c->cmd->proc != subscribeCommand &&
+        c->cmd->proc != unsubscribeCommand &&
+        c->cmd->proc != psubscribeCommand &&
+        c->cmd->proc != punsubscribeCommand) {
         addReplyError(c,"only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context");
         return REDIS_OK;
     }
@@ -1138,7 +1243,7 @@ int processCommand(redisClient *c) {
      * we are a slave with a broken link with master. */
     if (server.masterhost && server.replstate != REDIS_REPL_CONNECTED &&
         server.repl_serve_stale_data == 0 &&
-        cmd->proc != infoCommand && cmd->proc != slaveofCommand)
+        c->cmd->proc != infoCommand && c->cmd->proc != slaveofCommand)
     {
         addReplyError(c,
             "link with MASTER is down and slave-serve-stale-data is set to no");
@@ -1146,44 +1251,66 @@ int processCommand(redisClient *c) {
     }
 
     /* Loading DB? Return an error if the command is not INFO */
-    if (server.loading && cmd->proc != infoCommand) {
+    if (server.loading && c->cmd->proc != infoCommand) {
         addReply(c, shared.loadingerr);
         return REDIS_OK;
     }
 
+    /* Lua script too slow? Only allow SHUTDOWN NOSAVE and SCRIPT KILL. */
+    if (server.lua_timedout &&
+        !(c->cmd->proc != shutdownCommand &&
+          c->argc == 2 &&
+          tolower(((char*)c->argv[1]->ptr)[0]) == 'n') &&
+        !(c->cmd->proc == scriptCommand &&
+          c->argc == 2 &&
+          tolower(((char*)c->argv[1]->ptr)[0]) == 'k'))
+    {
+        addReply(c, shared.slowscripterr);
+        return REDIS_OK;
+    }
+
     /* Exec the command */
     if (c->flags & REDIS_MULTI &&
-        cmd->proc != execCommand && cmd->proc != discardCommand &&
-        cmd->proc != multiCommand && cmd->proc != watchCommand)
+        c->cmd->proc != execCommand && c->cmd->proc != discardCommand &&
+        c->cmd->proc != multiCommand && c->cmd->proc != watchCommand)
     {
-        queueMultiCommand(c,cmd);
+        queueMultiCommand(c);
         addReply(c,shared.queued);
     } else {
-        if (server.ds_enabled && blockClientOnSwappedKeys(c,cmd))
-            return REDIS_ERR;
-        call(c,cmd);
+        call(c);
     }
     return REDIS_OK;
 }
 
 /*================================== Shutdown =============================== */
 
-int prepareForShutdown() {
-    redisLog(REDIS_WARNING,"User requested shutdown, saving DB...");
+int prepareForShutdown(int flags) {
+    int save = flags & REDIS_SHUTDOWN_SAVE;
+    int nosave = flags & REDIS_SHUTDOWN_NOSAVE;
+
+    redisLog(REDIS_WARNING,"User requested shutdown...");
     /* Kill the saving child if there is a background saving in progress.
        We want to avoid race conditions, for instance our saving child may
        overwrite the synchronous saving did by SHUTDOWN. */
     if (server.bgsavechildpid != -1) {
-        redisLog(REDIS_WARNING,"There is a live saving child. Killing it!");
+        redisLog(REDIS_WARNING,"There is a child saving an .rdb. Killing it!");
         kill(server.bgsavechildpid,SIGKILL);
         rdbRemoveTempFile(server.bgsavechildpid);
     }
-    if (server.ds_enabled) {
-        /* FIXME: flush all objects on disk */
-    } else if (server.appendonly) {
+    if (server.appendonly) {
+        /* Kill the AOF saving child as the AOF we already have may be longer
+         * but contains the full dataset anyway. */
+        if (server.bgrewritechildpid != -1) {
+            redisLog(REDIS_WARNING,
+                "There is a child rewriting the AOF. Killing it!");
+            kill(server.bgrewritechildpid,SIGKILL);
+        }
         /* Append only file: fsync() the AOF and exit */
+        redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file.");
         aof_fsync(server.appendfd);
-    } else if (server.saveparamslen > 0) {
+    }
+    if ((server.saveparamslen > 0 && !nosave) || save) {
+        redisLog(REDIS_NOTICE,"Saving the final RDB snapshot before exiting.");
         /* Snapshotting. Perform a SYNC SAVE and exit */
         if (rdbSave(server.dbfilename) != REDIS_OK) {
             /* Ooops.. error saving! The best we can do is to continue
@@ -1191,21 +1318,32 @@ int prepareForShutdown() {
              * in the next cron() Redis will be notified that the background
              * saving aborted, handling special stuff like slaves pending for
              * synchronization... */
-            redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit");
+            redisLog(REDIS_WARNING,"Error trying to save the DB, can't exit.");
             return REDIS_ERR;
         }
-    } else {
-        redisLog(REDIS_WARNING,"Not saving DB.");
     }
-    if (server.daemonize) unlink(server.pidfile);
-    redisLog(REDIS_WARNING,"Server exit now, bye bye...");
+    if (server.daemonize) {
+        redisLog(REDIS_NOTICE,"Removing the pid file.");
+        unlink(server.pidfile);
+    }
+    /* Close the listening sockets. Apparently this allows faster restarts. */
+    if (server.ipfd != -1) close(server.ipfd);
+    if (server.sofd != -1) close(server.sofd);
+    if (server.unixsocket) {
+        redisLog(REDIS_NOTICE,"Removing the unix socket file.");
+        unlink(server.unixsocket); /* don't care if this fails */
+    }
+
+    redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
     return REDIS_OK;
 }
 
 /*================================== Commands =============================== */
 
 void authCommand(redisClient *c) {
-    if (!server.requirepass || !strcmp(c->argv[1]->ptr, server.requirepass)) {
+    if (!server.requirepass) {
+        addReplyError(c,"Client sent AUTH, but no password is set");
+    } else if (!strcmp(c->argv[1]->ptr, server.requirepass)) {
       c->authenticated = 1;
       addReply(c,shared.ok);
     } else {
@@ -1320,6 +1458,7 @@ sds genRedisInfoString(char *section) {
             "used_memory_rss:%zu\r\n"
             "used_memory_peak:%zu\r\n"
             "used_memory_peak_human:%s\r\n"
+            "used_memory_lua:%lld\r\n"
             "mem_fragmentation_ratio:%.2f\r\n"
             "mem_allocator:%s\r\n",
             zmalloc_used_memory(),
@@ -1327,27 +1466,12 @@ sds genRedisInfoString(char *section) {
             zmalloc_get_rss(),
             server.stat_peak_memory,
             peak_hmem,
+            ((long long)lua_gc(server.lua,LUA_GCCOUNT,0))*1024LL,
             zmalloc_get_fragmentation_ratio(),
-            REDIS_MALLOC
+            ZMALLOC_LIB
             );
     }
 
-    /* Allocation statistics */
-    if (allsections || !strcasecmp(section,"allocstats")) {
-        if (sections++) info = sdscat(info,"\r\n");
-        info = sdscat(info, "# Allocstats\r\nallocation_stats:");
-        for (j = 0; j <= ZMALLOC_MAX_ALLOC_STAT; j++) {
-            size_t count = zmalloc_allocations_for_size(j);
-            if (count) {
-                if (info[sdslen(info)-1] != ':') info = sdscatlen(info,",",1);
-                info = sdscatprintf(info,"%s%d=%zu",
-                    (j == ZMALLOC_MAX_ALLOC_STAT) ? ">=" : "",
-                    j,count);
-            }
-        }
-        info = sdscat(info,"\r\n");
-    }
-
     /* Persistence */
     if (allsections || defsections || !strcasecmp(section,"persistence")) {
         if (sections++) info = sdscat(info,"\r\n");
@@ -1362,11 +1486,20 @@ sds genRedisInfoString(char *section) {
             server.loading,
             server.appendonly,
             server.dirty,
-                server.bgsavechildpid != -1 ||
-                server.bgsavethread != (pthread_t) -1,
+            server.bgsavechildpid != -1,
             server.lastsave,
             server.bgrewritechildpid != -1);
 
+        if (server.appendonly) {
+            info = sdscatprintf(info,
+                "aof_current_size:%lld\r\n"
+                "aof_base_size:%lld\r\n"
+                "aof_pending_rewrite:%d\r\n",
+                (long long) server.appendonly_current_size,
+                (long long) server.auto_aofrewrite_base_size,
+                server.aofrewrite_scheduled);
+        }
+
         if (server.loading) {
             double perc;
             time_t eta, elapsed;
@@ -1399,35 +1532,6 @@ sds genRedisInfoString(char *section) {
         }
     }
 
-    /* Diskstore */
-    if (allsections || defsections || !strcasecmp(section,"diskstore")) {
-        if (sections++) info = sdscat(info,"\r\n");
-        info = sdscatprintf(info,
-            "# Diskstore\r\n"
-            "ds_enabled:%d\r\n",
-            server.ds_enabled != 0);
-        if (server.ds_enabled) {
-            lockThreadedIO();
-            info = sdscatprintf(info,
-                "cache_max_memory:%llu\r\n"
-                "cache_blocked_clients:%lu\r\n"
-                "cache_io_queue_len:%lu\r\n"
-                "cache_io_jobs_new:%lu\r\n"
-                "cache_io_jobs_processing:%lu\r\n"
-                "cache_io_jobs_processed:%lu\r\n"
-                "cache_io_ready_clients:%lu\r\n"
-                ,(unsigned long long) server.cache_max_memory,
-                (unsigned long) server.cache_blocked_clients,
-                (unsigned long) listLength(server.cache_io_queue),
-                (unsigned long) listLength(server.io_newjobs),
-                (unsigned long) listLength(server.io_processing),
-                (unsigned long) listLength(server.io_processed),
-                (unsigned long) listLength(server.io_ready_clients)
-            );
-            unlockThreadedIO();
-        }
-    }
-
     /* Stats */
     if (allsections || defsections || !strcasecmp(section,"stats")) {
         if (sections++) info = sdscat(info,"\r\n");
@@ -1484,6 +1588,12 @@ sds genRedisInfoString(char *section) {
                     (int)(time(NULL)-server.repl_transfer_lastio)
                 );
             }
+
+            if (server.replstate != REDIS_REPL_CONNECTED) {
+                info = sdscatprintf(info,
+                    "master_link_down_since_seconds:%ld\r\n",
+                    (long)time(NULL)-server.repl_down_since);
+            }
         }
         info = sdscatprintf(info,
             "connected_slaves:%d\r\n",
@@ -1497,12 +1607,12 @@ sds genRedisInfoString(char *section) {
         "# CPU\r\n"
         "used_cpu_sys:%.2f\r\n"
         "used_cpu_user:%.2f\r\n"
-        "used_cpu_sys_childrens:%.2f\r\n"
-        "used_cpu_user_childrens:%.2f\r\n",
-        (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
+        "used_cpu_sys_children:%.2f\r\n"
+        "used_cpu_user_children:%.2f\r\n",
         (float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
-        (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
-        (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000);
+        (float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
+        (float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,
+        (float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000);
     }
 
     /* cmdtime */
@@ -1614,7 +1724,7 @@ void freeMemoryIfNeeded(void) {
                 server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_RANDOM)
             {
                 de = dictGetRandomKey(dict);
-                bestkey = dictGetEntryKey(de);
+                bestkey = dictGetKey(de);
             }
 
             /* volatile-lru and allkeys-lru policy */
@@ -1627,12 +1737,12 @@ void freeMemoryIfNeeded(void) {
                     robj *o;
 
                     de = dictGetRandomKey(dict);
-                    thiskey = dictGetEntryKey(de);
+                    thiskey = dictGetKey(de);
                     /* When policy is volatile-lru we need an additonal lookup
                      * to locate the real key, as dict is set to db->expires. */
                     if (server.maxmemory_policy == REDIS_MAXMEMORY_VOLATILE_LRU)
                         de = dictFind(db->dict, thiskey);
-                    o = dictGetEntryVal(de);
+                    o = dictGetVal(de);
                     thisval = estimateObjectIdleTime(o);
 
                     /* Higher idle time is better candidate for deletion */
@@ -1650,8 +1760,8 @@ void freeMemoryIfNeeded(void) {
                     long thisval;
 
                     de = dictGetRandomKey(dict);
-                    thiskey = dictGetEntryKey(de);
-                    thisval = (long) dictGetEntryVal(de);
+                    thiskey = dictGetKey(de);
+                    thisval = (long) dictGetVal(de);
 
                     /* Expire sooner (minor expire unix timestamp) is better
                      * candidate for deletion */
@@ -1758,6 +1868,7 @@ void redisAsciiArt(void) {
 int main(int argc, char **argv) {
     long long start;
 
+    zmalloc_enable_thread_safeness();
     initServerConfig();
     if (argc == 2) {
         if (strcmp(argv[1], "-v") == 0 ||
@@ -1779,14 +1890,17 @@ int main(int argc, char **argv) {
     linuxOvercommitMemoryWarning();
 #endif
     start = ustime();
-    if (server.ds_enabled) {
-        redisLog(REDIS_NOTICE,"DB not loaded (running with disk back end)");
-    } else if (server.appendonly) {
+    if (server.appendonly) {
         if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK)
             redisLog(REDIS_NOTICE,"DB loaded from append only file: %.3f seconds",(float)(ustime()-start)/1000000);
     } else {
-        if (rdbLoad(server.dbfilename) == REDIS_OK)
-            redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",(float)(ustime()-start)/1000000);
+        if (rdbLoad(server.dbfilename) == REDIS_OK) {
+            redisLog(REDIS_NOTICE,"DB loaded from disk: %.3f seconds",
+                (float)(ustime()-start)/1000000);
+        } else if (errno != ENOENT) {
+            redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
+            exit(1);
+        }
     }
     if (server.ipfd > 0)
         redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);
@@ -1807,8 +1921,10 @@ static void *getMcontextEip(ucontext_t *uc) {
 #elif defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
   #if __x86_64__
     return (void*) uc->uc_mcontext->__ss.__rip;
-  #else
+  #elif __i386__
     return (void*) uc->uc_mcontext->__ss.__eip;
+  #else
+    return (void*) uc->uc_mcontext->__ss.__srr0;
   #endif
 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
   #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)