]> git.saurik.com Git - redis.git/blobdiff - redis.c
Merge branch 'vm-speedup'
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 1afe8967a7e9ae0dc962bcc80589a5226dc6b914..90fe6f95b82f45f0104eec866474b77db3d7d5f9 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -27,7 +27,7 @@
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
  * POSSIBILITY OF SUCH DAMAGE.
  */
 
-#define REDIS_VERSION "1.3.8"
+#define REDIS_VERSION "1.3.10"
 
 #include "fmacros.h"
 #include "config.h"
 
 #include "fmacros.h"
 #include "config.h"
@@ -37,8 +37,6 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
-#define __USE_POSIX199309
-#define __USE_UNIX98
 #include <signal.h>
 
 #ifdef HAVE_BACKTRACE
 #include <signal.h>
 
 #ifdef HAVE_BACKTRACE
@@ -91,7 +89,7 @@
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_OBJFREELIST_MAX   1000000 /* Max number of objects to cache */
 #define REDIS_MAX_SYNC_TIME     60      /* Slave can't take more to sync */
 #define REDIS_CONFIGLINE_MAX    1024
 #define REDIS_OBJFREELIST_MAX   1000000 /* Max number of objects to cache */
 #define REDIS_MAX_SYNC_TIME     60      /* Slave can't take more to sync */
-#define REDIS_EXPIRELOOKUPS_PER_CRON    10 /* try to expire 10 keys/loop */
+#define REDIS_EXPIRELOOKUPS_PER_CRON    10 /* lookup 10 expires per loop */
 #define REDIS_MAX_WRITE_PER_EVENT (1024*64)
 #define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
 
 #define REDIS_MAX_WRITE_PER_EVENT (1024*64)
 #define REDIS_REQUEST_MAX_SIZE (1024*1024*256) /* max bytes in inline command */
 
@@ -237,7 +235,9 @@ static char* strencoding[] = {
 
 /* We can print the stacktrace, so our assert is defined this way: */
 #define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
 
 /* We can print the stacktrace, so our assert is defined this way: */
 #define redisAssert(_e) ((_e)?(void)0 : (_redisAssert(#_e,__FILE__,__LINE__),_exit(1)))
+#define redisPanic(_e) _redisPanic(#_e,__FILE__,__LINE__),_exit(1)
 static void _redisAssert(char *estr, char *file, int line);
 static void _redisAssert(char *estr, char *file, int line);
+static void _redisPanic(char *msg, char *file, int line);
 
 /*================================= Data types ============================== */
 
 
 /*================================= Data types ============================== */
 
@@ -370,6 +370,7 @@ struct redisServer {
     pid_t bgsavechildpid;
     pid_t bgrewritechildpid;
     sds bgrewritebuf; /* buffer taken by parent during oppend only rewrite */
     pid_t bgsavechildpid;
     pid_t bgrewritechildpid;
     sds bgrewritebuf; /* buffer taken by parent during oppend only rewrite */
+    sds aofbuf;       /* AOF buffer, written before entering the event loop */
     struct saveparam *saveparams;
     int saveparamslen;
     char *logfile;
     struct saveparam *saveparams;
     int saveparamslen;
     char *logfile;
@@ -377,8 +378,8 @@ struct redisServer {
     char *dbfilename;
     char *appendfilename;
     char *requirepass;
     char *dbfilename;
     char *appendfilename;
     char *requirepass;
-    int shareobjects;
     int rdbcompression;
     int rdbcompression;
+    int activerehashing;
     /* Replication related */
     int isslave;
     char *masterauth;
     /* Replication related */
     int isslave;
     char *masterauth;
@@ -505,6 +506,7 @@ typedef struct zset {
 
 /* Our shared "common" objects */
 
 
 /* Our shared "common" objects */
 
+#define REDIS_SHARED_INTEGERS 10000
 struct sharedObjectsStruct {
     robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
     *colon, *nullbulk, *nullmultibulk, *queued,
 struct sharedObjectsStruct {
     robj *crlf, *ok, *err, *emptybulk, *czero, *cone, *pong, *space,
     *colon, *nullbulk, *nullmultibulk, *queued,
@@ -512,8 +514,9 @@ struct sharedObjectsStruct {
     *outofrangeerr, *plus,
     *select0, *select1, *select2, *select3, *select4,
     *select5, *select6, *select7, *select8, *select9,
     *outofrangeerr, *plus,
     *select0, *select1, *select2, *select3, *select4,
     *select5, *select6, *select7, *select8, *select9,
-    *messagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
-    *psubscribebulk, *punsubscribebulk;
+    *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
+    *mbulk4, *psubscribebulk, *punsubscribebulk,
+    *integers[REDIS_SHARED_INTEGERS];
 } shared;
 
 /* Global vars that are actally used as constants. The following double
 } shared;
 
 /* Global vars that are actally used as constants. The following double
@@ -554,9 +557,11 @@ static int rdbSaveBackground(char *filename);
 static robj *createStringObject(char *ptr, size_t len);
 static robj *dupStringObject(robj *o);
 static void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
 static robj *createStringObject(char *ptr, size_t len);
 static robj *dupStringObject(robj *o);
 static void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc);
+static void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc);
+static void flushAppendOnlyFile(void);
 static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);
 static int syncWithMaster(void);
 static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);
 static int syncWithMaster(void);
-static int tryObjectEncoding(robj *o);
+static robj *tryObjectEncoding(robj *o);
 static robj *getDecodedObject(robj *o);
 static int removeExpire(redisDb *db, robj *key);
 static int expireIfNeeded(redisDb *db, robj *key);
 static robj *getDecodedObject(robj *o);
 static int removeExpire(redisDb *db, robj *key);
 static int expireIfNeeded(redisDb *db, robj *key);
@@ -617,13 +622,17 @@ static int pubsubUnsubscribeAllPatterns(redisClient *c, int notify);
 static void freePubsubPattern(void *p);
 static int listMatchPubsubPattern(void *a, void *b);
 static int compareStringObjects(robj *a, robj *b);
 static void freePubsubPattern(void *p);
 static int listMatchPubsubPattern(void *a, void *b);
 static int compareStringObjects(robj *a, robj *b);
+static int equalStringObjects(robj *a, robj *b);
 static void usage();
 static void usage();
+static int rewriteAppendOnlyFileBackground(void);
+static int vmSwapObjectBlocking(robj *key, robj *val);
 
 static void authCommand(redisClient *c);
 static void pingCommand(redisClient *c);
 static void echoCommand(redisClient *c);
 static void setCommand(redisClient *c);
 static void setnxCommand(redisClient *c);
 
 static void authCommand(redisClient *c);
 static void pingCommand(redisClient *c);
 static void echoCommand(redisClient *c);
 static void setCommand(redisClient *c);
 static void setnxCommand(redisClient *c);
+static void setexCommand(redisClient *c);
 static void getCommand(redisClient *c);
 static void delCommand(redisClient *c);
 static void existsCommand(redisClient *c);
 static void getCommand(redisClient *c);
 static void delCommand(redisClient *c);
 static void existsCommand(redisClient *c);
@@ -703,7 +712,10 @@ static void substrCommand(redisClient *c);
 static void zrankCommand(redisClient *c);
 static void zrevrankCommand(redisClient *c);
 static void hsetCommand(redisClient *c);
 static void zrankCommand(redisClient *c);
 static void zrevrankCommand(redisClient *c);
 static void hsetCommand(redisClient *c);
+static void hsetnxCommand(redisClient *c);
 static void hgetCommand(redisClient *c);
 static void hgetCommand(redisClient *c);
+static void hmsetCommand(redisClient *c);
+static void hmgetCommand(redisClient *c);
 static void hdelCommand(redisClient *c);
 static void hlenCommand(redisClient *c);
 static void zremrangebyrankCommand(redisClient *c);
 static void hdelCommand(redisClient *c);
 static void hlenCommand(redisClient *c);
 static void zremrangebyrankCommand(redisClient *c);
@@ -729,6 +741,7 @@ static struct redisCommand cmdTable[] = {
     {"get",getCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
     {"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
     {"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
     {"get",getCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
     {"set",setCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
     {"setnx",setnxCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
+    {"setex",setexCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,0,0,0},
     {"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"substr",substrCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
     {"del",delCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
     {"append",appendCommand,3,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"substr",substrCommand,4,REDIS_CMD_INLINE,NULL,1,1,1},
     {"del",delCommand,-2,REDIS_CMD_INLINE,NULL,0,0,0},
@@ -779,8 +792,11 @@ static struct redisCommand cmdTable[] = {
     {"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"zrank",zrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"zrevrank",zrevrankCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hset",hsetCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
-    {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"hsetnx",hsetnxCommand,4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hget",hgetCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"hmset",hmsetCommand,-4,REDIS_CMD_BULK|REDIS_CMD_DENYOOM,NULL,1,1,1},
+    {"hmget",hmgetCommand,-3,REDIS_CMD_BULK,NULL,1,1,1},
+    {"hincrby",hincrbyCommand,4,REDIS_CMD_INLINE|REDIS_CMD_DENYOOM,NULL,1,1,1},
     {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
     {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
     {"hdel",hdelCommand,3,REDIS_CMD_BULK,NULL,1,1,1},
     {"hlen",hlenCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
     {"hkeys",hkeysCommand,2,REDIS_CMD_INLINE,NULL,1,1,1},
@@ -960,6 +976,77 @@ static int stringmatch(const char *pattern, const char *string, int nocase) {
     return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
 }
 
     return stringmatchlen(pattern,strlen(pattern),string,strlen(string),nocase);
 }
 
+/* Convert a string representing an amount of memory into the number of
+ * bytes, so for instance memtoll("1Gi") will return 1073741824 that is
+ * (1024*1024*1024).
+ *
+ * On parsing error, if *err is not NULL, it's set to 1, otherwise it's
+ * set to 0 */
+static long long memtoll(const char *p, int *err) {
+    const char *u;
+    char buf[128];
+    long mul; /* unit multiplier */
+    long long val;
+    unsigned int digits;
+
+    if (err) *err = 0;
+    /* Search the first non digit character. */
+    u = p;
+    if (*u == '-') u++;
+    while(*u && isdigit(*u)) u++;
+    if (*u == '\0' || !strcasecmp(u,"b")) {
+        mul = 1;
+    } else if (!strcasecmp(u,"k")) {
+        mul = 1000;
+    } else if (!strcasecmp(u,"kb")) {
+        mul = 1024;
+    } else if (!strcasecmp(u,"m")) {
+        mul = 1000*1000;
+    } else if (!strcasecmp(u,"mb")) {
+        mul = 1024*1024;
+    } else if (!strcasecmp(u,"g")) {
+        mul = 1000L*1000*1000;
+    } else if (!strcasecmp(u,"gb")) {
+        mul = 1024L*1024*1024;
+    } else {
+        if (err) *err = 1;
+        mul = 1;
+    }
+    digits = u-p;
+    if (digits >= sizeof(buf)) {
+        if (err) *err = 1;
+        return LLONG_MAX;
+    }
+    memcpy(buf,p,digits);
+    buf[digits] = '\0';
+    val = strtoll(buf,NULL,10);
+    return val*mul;
+}
+
+/* Convert a long long into a string. Returns the number of
+ * characters needed to represent the number, that can be shorter if passed
+ * buffer length is not enough to store the whole number. */
+static int ll2string(char *s, size_t len, long long value) {
+    char buf[32], *p;
+    unsigned long long v;
+    size_t l;
+
+    if (len == 0) return 0;
+    v = (value < 0) ? -value : value;
+    p = buf+31; /* point to the last character */
+    do {
+        *p-- = '0'+(v%10);
+        v /= 10;
+    } while(v);
+    if (value < 0) *p-- = '-';
+    p++;
+    l = 32-(p-buf);
+    if (l+1 > len) l = len-1; /* Make sure it fits, including the nul term */
+    memcpy(s,p,l);
+    s[l] = '\0';
+    return l;
+}
+
 static void redisLog(int level, const char *fmt, ...) {
     va_list ap;
     FILE *fp;
 static void redisLog(int level, const char *fmt, ...) {
     va_list ap;
     FILE *fp;
@@ -1042,8 +1129,8 @@ static int dictEncObjKeyCompare(void *privdata, const void *key1,
     int cmp;
 
     if (o1->encoding == REDIS_ENCODING_INT &&
     int cmp;
 
     if (o1->encoding == REDIS_ENCODING_INT &&
-        o2->encoding == REDIS_ENCODING_INT &&
-        o1->ptr == o2->ptr) return 1;
+        o2->encoding == REDIS_ENCODING_INT)
+            return o1->ptr == o2->ptr;
 
     o1 = getDecodedObject(o1);
     o2 = getDecodedObject(o2);
 
     o1 = getDecodedObject(o1);
     o2 = getDecodedObject(o2);
@@ -1063,7 +1150,7 @@ static unsigned int dictEncObjHash(const void *key) {
             char buf[32];
             int len;
 
             char buf[32];
             int len;
 
-            len = snprintf(buf,32,"%ld",(long)o->ptr);
+            len = ll2string(buf,32,(long)o->ptr);
             return dictGenHashFunction((unsigned char*)buf, len);
         } else {
             unsigned int hash;
             return dictGenHashFunction((unsigned char*)buf, len);
         } else {
             unsigned int hash;
@@ -1196,16 +1283,28 @@ static void tryResizeHashTables(void) {
     int j;
 
     for (j = 0; j < server.dbnum; j++) {
     int j;
 
     for (j = 0; j < server.dbnum; j++) {
-        if (htNeedsResize(server.db[j].dict)) {
-            redisLog(REDIS_VERBOSE,"The hash table %d is too sparse, resize it...",j);
+        if (htNeedsResize(server.db[j].dict))
             dictResize(server.db[j].dict);
             dictResize(server.db[j].dict);
-            redisLog(REDIS_VERBOSE,"Hash table %d resized.",j);
-        }
         if (htNeedsResize(server.db[j].expires))
             dictResize(server.db[j].expires);
     }
 }
 
         if (htNeedsResize(server.db[j].expires))
             dictResize(server.db[j].expires);
     }
 }
 
+/* Our hash table implementation performs rehashing incrementally while
+ * we write/read from the hash table. Still if the server is idle, the hash
+ * table will use two tables for a long time. So we try to use 1 millisecond
+ * of CPU time at every serverCron() loop in order to rehash some key. */
+static void incrementallyRehash(void) {
+    int j;
+
+    for (j = 0; j < server.dbnum; j++) {
+        if (dictIsRehashing(server.db[j].dict)) {
+            dictRehashMilliseconds(server.db[j].dict,1);
+            break; /* already used our millisecond for this loop... */
+        }
+    }
+}
+
 /* A background saving child (BGSAVE) terminated its work. Handle this. */
 void backgroundSaveDoneHandler(int statloc) {
     int exitcode = WEXITSTATUS(statloc);
 /* A background saving child (BGSAVE) terminated its work. Handle this. */
 void backgroundSaveDoneHandler(int statloc) {
     int exitcode = WEXITSTATUS(statloc);
@@ -1335,10 +1434,9 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
      * if we resize the HT while there is the saving child at work actually
      * a lot of memory movements in the parent will cause a lot of pages
      * copied. */
      * if we resize the HT while there is the saving child at work actually
      * a lot of memory movements in the parent will cause a lot of pages
      * copied. */
-    if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1 &&
-        !(loops % 10))
-    {
-        tryResizeHashTables();
+    if (server.bgsavechildpid == -1 && server.bgrewritechildpid == -1) {
+        if (!(loops % 10)) tryResizeHashTables();
+        if (server.activerehashing) incrementallyRehash();
     }
 
     /* Show information about connected clients */
     }
 
     /* Show information about connected clients */
@@ -1446,6 +1544,7 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
         redisLog(REDIS_NOTICE,"Connecting to MASTER...");
         if (syncWithMaster() == REDIS_OK) {
             redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync succeeded");
         redisLog(REDIS_NOTICE,"Connecting to MASTER...");
         if (syncWithMaster() == REDIS_OK) {
             redisLog(REDIS_NOTICE,"MASTER <-> SLAVE sync succeeded");
+            if (server.appendonly) rewriteAppendOnlyFileBackground();
         }
     }
     return 100;
         }
     }
     return 100;
@@ -1457,6 +1556,7 @@ static int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientD
 static void beforeSleep(struct aeEventLoop *eventLoop) {
     REDIS_NOTUSED(eventLoop);
 
 static void beforeSleep(struct aeEventLoop *eventLoop) {
     REDIS_NOTUSED(eventLoop);
 
+    /* Awake clients that got all the swapped keys they requested */
     if (server.vm_enabled && listLength(server.io_ready_clients)) {
         listIter li;
         listNode *ln;
     if (server.vm_enabled && listLength(server.io_ready_clients)) {
         listIter li;
         listNode *ln;
@@ -1481,9 +1581,13 @@ static void beforeSleep(struct aeEventLoop *eventLoop) {
                 processInputBuffer(c);
         }
     }
                 processInputBuffer(c);
         }
     }
+    /* Write the AOF buffer on disk */
+    flushAppendOnlyFile();
 }
 
 static void createSharedObjects(void) {
 }
 
 static void createSharedObjects(void) {
+    int j;
+
     shared.crlf = createObject(REDIS_STRING,sdsnew("\r\n"));
     shared.ok = createObject(REDIS_STRING,sdsnew("+OK\r\n"));
     shared.err = createObject(REDIS_STRING,sdsnew("-ERR\r\n"));
     shared.crlf = createObject(REDIS_STRING,sdsnew("\r\n"));
     shared.ok = createObject(REDIS_STRING,sdsnew("+OK\r\n"));
     shared.err = createObject(REDIS_STRING,sdsnew("-ERR\r\n"));
@@ -1519,11 +1623,17 @@ static void createSharedObjects(void) {
     shared.select8 = createStringObject("select 8\r\n",10);
     shared.select9 = createStringObject("select 9\r\n",10);
     shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
     shared.select8 = createStringObject("select 8\r\n",10);
     shared.select9 = createStringObject("select 9\r\n",10);
     shared.messagebulk = createStringObject("$7\r\nmessage\r\n",13);
+    shared.pmessagebulk = createStringObject("$8\r\npmessage\r\n",14);
     shared.subscribebulk = createStringObject("$9\r\nsubscribe\r\n",15);
     shared.unsubscribebulk = createStringObject("$11\r\nunsubscribe\r\n",18);
     shared.psubscribebulk = createStringObject("$10\r\npsubscribe\r\n",17);
     shared.punsubscribebulk = createStringObject("$12\r\npunsubscribe\r\n",19);
     shared.mbulk3 = createStringObject("*3\r\n",4);
     shared.subscribebulk = createStringObject("$9\r\nsubscribe\r\n",15);
     shared.unsubscribebulk = createStringObject("$11\r\nunsubscribe\r\n",18);
     shared.psubscribebulk = createStringObject("$10\r\npsubscribe\r\n",17);
     shared.punsubscribebulk = createStringObject("$12\r\npunsubscribe\r\n",19);
     shared.mbulk3 = createStringObject("*3\r\n",4);
+    shared.mbulk4 = createStringObject("*4\r\n",4);
+    for (j = 0; j < REDIS_SHARED_INTEGERS; j++) {
+        shared.integers[j] = createObject(REDIS_STRING,(void*)(long)j);
+        shared.integers[j]->encoding = REDIS_ENCODING_INT;
+    }
 }
 
 static void appendServerSaveParams(time_t seconds, int changes) {
 }
 
 static void appendServerSaveParams(time_t seconds, int changes) {
@@ -1550,7 +1660,7 @@ static void initServerConfig() {
     server.glueoutputbuf = 1;
     server.daemonize = 0;
     server.appendonly = 0;
     server.glueoutputbuf = 1;
     server.daemonize = 0;
     server.appendonly = 0;
-    server.appendfsync = APPENDFSYNC_ALWAYS;
+    server.appendfsync = APPENDFSYNC_EVERYSEC;
     server.lastfsync = time(NULL);
     server.appendfd = -1;
     server.appendseldb = -1; /* Make sure the first time will not match */
     server.lastfsync = time(NULL);
     server.appendfd = -1;
     server.appendseldb = -1; /* Make sure the first time will not match */
@@ -1558,8 +1668,8 @@ static void initServerConfig() {
     server.dbfilename = zstrdup("dump.rdb");
     server.appendfilename = zstrdup("appendonly.aof");
     server.requirepass = NULL;
     server.dbfilename = zstrdup("dump.rdb");
     server.appendfilename = zstrdup("appendonly.aof");
     server.requirepass = NULL;
-    server.shareobjects = 0;
     server.rdbcompression = 1;
     server.rdbcompression = 1;
+    server.activerehashing = 1;
     server.maxclients = 0;
     server.blpop_blocked_clients = 0;
     server.maxmemory = 0;
     server.maxclients = 0;
     server.blpop_blocked_clients = 0;
     server.maxmemory = 0;
@@ -1633,6 +1743,7 @@ static void initServer() {
     server.bgsavechildpid = -1;
     server.bgrewritechildpid = -1;
     server.bgrewritebuf = sdsempty();
     server.bgsavechildpid = -1;
     server.bgrewritechildpid = -1;
     server.bgrewritebuf = sdsempty();
+    server.aofbuf = sdsempty();
     server.lastsave = time(NULL);
     server.dirty = 0;
     server.stat_numcommands = 0;
     server.lastsave = time(NULL);
     server.dirty = 0;
     server.stat_numcommands = 0;
@@ -1682,15 +1793,12 @@ static void loadServerConfig(char *filename) {
     char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
     int linenum = 0;
     sds line = NULL;
     char buf[REDIS_CONFIGLINE_MAX+1], *err = NULL;
     int linenum = 0;
     sds line = NULL;
-    char *errormsg = "Fatal error, can't open config file '%s'";
-    char *errorbuf = zmalloc(sizeof(char)*(strlen(errormsg)+strlen(filename)));
-    sprintf(errorbuf, errormsg, filename);
 
     if (filename[0] == '-' && filename[1] == '\0')
         fp = stdin;
     else {
         if ((fp = fopen(filename,"r")) == NULL) {
 
     if (filename[0] == '-' && filename[1] == '\0')
         fp = stdin;
     else {
         if ((fp = fopen(filename,"r")) == NULL) {
-            redisLog(REDIS_WARNING, errorbuf);
+            redisLog(REDIS_WARNING, "Fatal error, can't open config file '%s'", filename);
             exit(1);
         }
     }
             exit(1);
         }
     }
@@ -1777,7 +1885,7 @@ static void loadServerConfig(char *filename) {
         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
             server.maxclients = atoi(argv[1]);
         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
         } else if (!strcasecmp(argv[0],"maxclients") && argc == 2) {
             server.maxclients = atoi(argv[1]);
         } else if (!strcasecmp(argv[0],"maxmemory") && argc == 2) {
-            server.maxmemory = strtoll(argv[1], NULL, 10);
+            server.maxmemory = memtoll(argv[1],NULL);
         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
             server.masterhost = sdsnew(argv[1]);
             server.masterport = atoi(argv[2]);
         } else if (!strcasecmp(argv[0],"slaveof") && argc == 3) {
             server.masterhost = sdsnew(argv[1]);
             server.masterport = atoi(argv[2]);
@@ -1788,14 +1896,14 @@ static void loadServerConfig(char *filename) {
             if ((server.glueoutputbuf = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
             if ((server.glueoutputbuf = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
-        } else if (!strcasecmp(argv[0],"shareobjects") && argc == 2) {
-            if ((server.shareobjects = yesnotoi(argv[1])) == -1) {
-                err = "argument must be 'yes' or 'no'"; goto loaderr;
-            }
         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
             if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
         } else if (!strcasecmp(argv[0],"rdbcompression") && argc == 2) {
             if ((server.rdbcompression = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
             }
+        } else if (!strcasecmp(argv[0],"activerehashing") && argc == 2) {
+            if ((server.activerehashing = yesnotoi(argv[1])) == -1) {
+                err = "argument must be 'yes' or 'no'"; goto loaderr;
+            }
         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
         } else if (!strcasecmp(argv[0],"daemonize") && argc == 2) {
             if ((server.daemonize = yesnotoi(argv[1])) == -1) {
                 err = "argument must be 'yes' or 'no'"; goto loaderr;
@@ -1831,19 +1939,17 @@ static void loadServerConfig(char *filename) {
             zfree(server.vm_swap_file);
             server.vm_swap_file = zstrdup(argv[1]);
         } else if (!strcasecmp(argv[0],"vm-max-memory") && argc == 2) {
             zfree(server.vm_swap_file);
             server.vm_swap_file = zstrdup(argv[1]);
         } else if (!strcasecmp(argv[0],"vm-max-memory") && argc == 2) {
-            server.vm_max_memory = strtoll(argv[1], NULL, 10);
+            server.vm_max_memory = memtoll(argv[1],NULL);
         } else if (!strcasecmp(argv[0],"vm-page-size") && argc == 2) {
         } else if (!strcasecmp(argv[0],"vm-page-size") && argc == 2) {
-            server.vm_page_size = strtoll(argv[1], NULL, 10);
+            server.vm_page_size = memtoll(argv[1], NULL);
         } else if (!strcasecmp(argv[0],"vm-pages") && argc == 2) {
         } else if (!strcasecmp(argv[0],"vm-pages") && argc == 2) {
-            server.vm_pages = strtoll(argv[1], NULL, 10);
+            server.vm_pages = memtoll(argv[1], NULL);
         } else if (!strcasecmp(argv[0],"vm-max-threads") && argc == 2) {
             server.vm_max_threads = strtoll(argv[1], NULL, 10);
         } else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2){
         } else if (!strcasecmp(argv[0],"vm-max-threads") && argc == 2) {
             server.vm_max_threads = strtoll(argv[1], NULL, 10);
         } else if (!strcasecmp(argv[0],"hash-max-zipmap-entries") && argc == 2){
-            server.hash_max_zipmap_entries = strtol(argv[1], NULL, 10);
+            server.hash_max_zipmap_entries = memtoll(argv[1], NULL);
         } else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2){
         } else if (!strcasecmp(argv[0],"hash-max-zipmap-value") && argc == 2){
-            server.hash_max_zipmap_value = strtol(argv[1], NULL, 10);
-        } else if (!strcasecmp(argv[0],"vm-max-threads") && argc == 2) {
-            server.vm_max_threads = strtoll(argv[1], NULL, 10);
+            server.hash_max_zipmap_value = memtoll(argv[1], NULL);
         } else {
             err = "Bad directive or wrong number of arguments"; goto loaderr;
         }
         } else {
             err = "Bad directive or wrong number of arguments"; goto loaderr;
         }
@@ -1972,7 +2078,7 @@ static void sendReplyToClient(aeEventLoop *el, int fd, void *privdata, int mask)
 
     /* Use writev() if we have enough buffers to send */
     if (!server.glueoutputbuf &&
 
     /* Use writev() if we have enough buffers to send */
     if (!server.glueoutputbuf &&
-        listLength(c->reply) > REDIS_WRITEV_THRESHOLD && 
+        listLength(c->reply) > REDIS_WRITEV_THRESHOLD &&
         !(c->flags & REDIS_MASTER))
     {
         sendReplyToClientWritev(el, fd, privdata, mask);
         !(c->flags & REDIS_MASTER))
     {
         sendReplyToClientWritev(el, fd, privdata, mask);
@@ -2050,7 +2156,7 @@ static void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int
             o = listNodeValue(node);
             objlen = sdslen(o->ptr);
 
             o = listNodeValue(node);
             objlen = sdslen(o->ptr);
 
-            if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT) 
+            if (totwritten + objlen - offset > REDIS_MAX_WRITE_PER_EVENT)
                 break;
 
             if(ion == REDIS_WRITEV_IOVEC_COUNT)
                 break;
 
             if(ion == REDIS_WRITEV_IOVEC_COUNT)
@@ -2098,7 +2204,7 @@ static void sendReplyToClientWritev(aeEventLoop *el, int fd, void *privdata, int
         }
     }
 
         }
     }
 
-    if (totwritten > 0) 
+    if (totwritten > 0)
         c->lastinteraction = time(NULL);
 
     if (listLength(c->reply) == 0) {
         c->lastinteraction = time(NULL);
 
     if (listLength(c->reply) == 0) {
@@ -2137,7 +2243,7 @@ static void call(redisClient *c, struct redisCommand *cmd) {
         listLength(server.slaves))
         replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
     if (listLength(server.monitors))
         listLength(server.slaves))
         replicationFeedSlaves(server.slaves,c->db->id,c->argv,c->argc);
     if (listLength(server.monitors))
-        replicationFeedSlaves(server.monitors,c->db->id,c->argv,c->argc);
+        replicationFeedMonitors(server.monitors,c->db->id,c->argv,c->argc);
     server.stat_numcommands++;
 }
 
     server.stat_numcommands++;
 }
 
@@ -2277,7 +2383,7 @@ static int processCommand(redisClient *c) {
     }
     /* Let's try to encode the bulk object to save space. */
     if (cmd->flags & REDIS_CMD_BULK)
     }
     /* Let's try to encode the bulk object to save space. */
     if (cmd->flags & REDIS_CMD_BULK)
-        tryObjectEncoding(c->argv[c->argc-1]);
+        c->argv[c->argc-1] = tryObjectEncoding(c->argv[c->argc-1]);
 
     /* Check if the user is authenticated */
     if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
 
     /* Check if the user is authenticated */
     if (server.requirepass && !c->authenticated && cmd->proc != authCommand) {
@@ -2296,7 +2402,8 @@ static int processCommand(redisClient *c) {
     }
 
     /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
     }
 
     /* Only allow SUBSCRIBE and UNSUBSCRIBE in the context of Pub/Sub */
-    if (dictSize(c->pubsub_channels) > 0 &&
+    if ((dictSize(c->pubsub_channels) > 0 || listLength(c->pubsub_patterns) > 0)
+        &&
         cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
         cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
         addReplySds(c,sdsnew("-ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context\r\n"));
         cmd->proc != subscribeCommand && cmd->proc != unsubscribeCommand &&
         cmd->proc != psubscribeCommand && cmd->proc != punsubscribeCommand) {
         addReplySds(c,sdsnew("-ERR only (P)SUBSCRIBE / (P)UNSUBSCRIBE / QUIT allowed in this context\r\n"));
@@ -2391,6 +2498,64 @@ static void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int arg
     if (outv != static_outv) zfree(outv);
 }
 
     if (outv != static_outv) zfree(outv);
 }
 
+static sds sdscatrepr(sds s, char *p, size_t len) {
+    s = sdscatlen(s,"\"",1);
+    while(len--) {
+        switch(*p) {
+        case '\\':
+        case '"':
+            s = sdscatprintf(s,"\\%c",*p);
+            break;
+        case '\n': s = sdscatlen(s,"\\n",1); break;
+        case '\r': s = sdscatlen(s,"\\r",1); break;
+        case '\t': s = sdscatlen(s,"\\t",1); break;
+        case '\a': s = sdscatlen(s,"\\a",1); break;
+        case '\b': s = sdscatlen(s,"\\b",1); break;
+        default:
+            if (isprint(*p))
+                s = sdscatprintf(s,"%c",*p);
+            else
+                s = sdscatprintf(s,"\\x%02x",(unsigned char)*p);
+            break;
+        }
+        p++;
+    }
+    return sdscatlen(s,"\"",1);
+}
+
+static void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc) {
+    listNode *ln;
+    listIter li;
+    int j;
+    sds cmdrepr = sdsnew("+");
+    robj *cmdobj;
+    struct timeval tv;
+
+    gettimeofday(&tv,NULL);
+    cmdrepr = sdscatprintf(cmdrepr,"%ld.%ld ",(long)tv.tv_sec,(long)tv.tv_usec);
+    if (dictid != 0) cmdrepr = sdscatprintf(cmdrepr,"(db %d) ", dictid);
+
+    for (j = 0; j < argc; j++) {
+        if (argv[j]->encoding == REDIS_ENCODING_INT) {
+            cmdrepr = sdscatprintf(cmdrepr, "%ld", (long)argv[j]->ptr);
+        } else {
+            cmdrepr = sdscatrepr(cmdrepr,(char*)argv[j]->ptr,
+                        sdslen(argv[j]->ptr));
+        }
+        if (j != argc-1)
+            cmdrepr = sdscatlen(cmdrepr," ",1);
+    }
+    cmdrepr = sdscatlen(cmdrepr,"\r\n",2);
+    cmdobj = createObject(REDIS_STRING,cmdrepr);
+
+    listRewind(monitors,&li);
+    while((ln = listNext(&li))) {
+        redisClient *monitor = ln->value;
+        addReply(monitor,cmdobj);
+    }
+    decrRefCount(cmdobj);
+}
+
 static void processInputBuffer(redisClient *c) {
 again:
     /* Before to process the input buffer, make sure the client is not
 static void processInputBuffer(redisClient *c) {
 again:
     /* Before to process the input buffer, make sure the client is not
@@ -2408,7 +2573,7 @@ again:
         if (p) {
             sds query, *argv;
             int argc, j;
         if (p) {
             sds query, *argv;
             int argc, j;
-            
+
             query = c->querybuf;
             c->querybuf = sdsempty();
             querylen = 1+(p-(query));
             query = c->querybuf;
             c->querybuf = sdsempty();
             querylen = 1+(p-(query));
@@ -2516,7 +2681,7 @@ static void *dupClientReplyValue(void *o) {
 }
 
 static int listMatchObjects(void *a, void *b) {
 }
 
 static int listMatchObjects(void *a, void *b) {
-    return compareStringObjects(a,b) == 0;
+    return equalStringObjects(a,b);
 }
 
 static redisClient *createClient(int fd) {
 }
 
 static redisClient *createClient(int fd) {
@@ -2745,6 +2910,23 @@ static robj *createStringObject(char *ptr, size_t len) {
     return createObject(REDIS_STRING,sdsnewlen(ptr,len));
 }
 
     return createObject(REDIS_STRING,sdsnewlen(ptr,len));
 }
 
+static robj *createStringObjectFromLongLong(long long value) {
+    robj *o;
+    if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
+        incrRefCount(shared.integers[value]);
+        o = shared.integers[value];
+    } else {
+        o = createObject(REDIS_STRING, NULL);
+        if (value >= LONG_MIN && value <= LONG_MAX) {
+            o->encoding = REDIS_ENCODING_INT;
+            o->ptr = (void*)((long)value);
+        } else {
+            o = createObject(REDIS_STRING,sdsfromlonglong(value));
+        }
+    }
+    return o;
+}
+
 static robj *dupStringObject(robj *o) {
     assert(o->encoding == REDIS_ENCODING_RAW);
     return createStringObject(o->ptr,sdslen(o->ptr));
 static robj *dupStringObject(robj *o) {
     assert(o->encoding == REDIS_ENCODING_RAW);
     return createStringObject(o->ptr,sdslen(o->ptr));
@@ -2811,7 +2993,7 @@ static void freeHashObject(robj *o) {
         zfree(o->ptr);
         break;
     default:
         zfree(o->ptr);
         break;
     default:
-        redisAssert(0);
+        redisPanic("Unknown hash encoding type");
         break;
     }
 }
         break;
     }
 }
@@ -2823,6 +3005,7 @@ static void incrRefCount(robj *o) {
 static void decrRefCount(void *obj) {
     robj *o = obj;
 
 static void decrRefCount(void *obj) {
     robj *o = obj;
 
+    if (o->refcount <= 0) redisPanic("decrRefCount against refcount <= 0");
     /* Object is a key of a swapped out value, or in the process of being
      * loaded. */
     if (server.vm_enabled &&
     /* Object is a key of a swapped out value, or in the process of being
      * loaded. */
     if (server.vm_enabled &&
@@ -2850,7 +3033,7 @@ static void decrRefCount(void *obj) {
         case REDIS_SET: freeSetObject(o); break;
         case REDIS_ZSET: freeZsetObject(o); break;
         case REDIS_HASH: freeHashObject(o); break;
         case REDIS_SET: freeSetObject(o); break;
         case REDIS_ZSET: freeZsetObject(o); break;
         case REDIS_HASH: freeHashObject(o); break;
-        default: redisAssert(0); break;
+        default: redisPanic("Unknown object type"); break;
         }
         if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
         if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
         }
         if (server.vm_enabled) pthread_mutex_lock(&server.obj_freelist_mutex);
         if (listLength(server.objfreelist) > REDIS_OBJFREELIST_MAX ||
@@ -2950,10 +3133,10 @@ static int isStringRepresentableAsLong(sds s, long *longval) {
     char buf[32], *endptr;
     long value;
     int slen;
     char buf[32], *endptr;
     long value;
     int slen;
-    
+
     value = strtol(s, &endptr, 10);
     if (endptr[0] != '\0') return REDIS_ERR;
     value = strtol(s, &endptr, 10);
     if (endptr[0] != '\0') return REDIS_ERR;
-    slen = snprintf(buf,32,"%ld",value);
+    slen = ll2string(buf,32,value);
 
     /* If the number converted back into a string is not identical
      * then it's not possible to encode the string as integer */
 
     /* If the number converted back into a string is not identical
      * then it's not possible to encode the string as integer */
@@ -2963,36 +3146,42 @@ static int isStringRepresentableAsLong(sds s, long *longval) {
 }
 
 /* Try to encode a string object in order to save space */
 }
 
 /* Try to encode a string object in order to save space */
-static int tryObjectEncoding(robj *o) {
+static robj *tryObjectEncoding(robj *o) {
     long value;
     sds s = o->ptr;
 
     if (o->encoding != REDIS_ENCODING_RAW)
     long value;
     sds s = o->ptr;
 
     if (o->encoding != REDIS_ENCODING_RAW)
-        return REDIS_ERR; /* Already encoded */
+        return o; /* Already encoded */
 
 
-    /* It's not save to encode shared objects: shared objects can be shared
+    /* It's not safe to encode shared objects: shared objects can be shared
      * everywhere in the "object space" of Redis. Encoded objects can only
      * appear as "values" (and not, for instance, as keys) */
      * everywhere in the "object space" of Redis. Encoded objects can only
      * appear as "values" (and not, for instance, as keys) */
-     if (o->refcount > 1) return REDIS_ERR;
+     if (o->refcount > 1) return o;
 
     /* Currently we try to encode only strings */
     redisAssert(o->type == REDIS_STRING);
 
     /* Check if we can represent this string as a long integer */
 
     /* Currently we try to encode only strings */
     redisAssert(o->type == REDIS_STRING);
 
     /* Check if we can represent this string as a long integer */
-    if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return REDIS_ERR;
+    if (isStringRepresentableAsLong(s,&value) == REDIS_ERR) return o;
 
     /* Ok, this object can be encoded */
 
     /* Ok, this object can be encoded */
-    o->encoding = REDIS_ENCODING_INT;
-    sdsfree(o->ptr);
-    o->ptr = (void*) value;
-    return REDIS_OK;
+    if (value >= 0 && value < REDIS_SHARED_INTEGERS) {
+        decrRefCount(o);
+        incrRefCount(shared.integers[value]);
+        return shared.integers[value];
+    } else {
+        o->encoding = REDIS_ENCODING_INT;
+        sdsfree(o->ptr);
+        o->ptr = (void*) value;
+        return o;
+    }
 }
 
 /* Get a decoded version of an encoded object (returned as a new object).
  * If the object is already raw-encoded just increment the ref count. */
 static robj *getDecodedObject(robj *o) {
     robj *dec;
 }
 
 /* Get a decoded version of an encoded object (returned as a new object).
  * If the object is already raw-encoded just increment the ref count. */
 static robj *getDecodedObject(robj *o) {
     robj *dec;
-    
+
     if (o->encoding == REDIS_ENCODING_RAW) {
         incrRefCount(o);
         return o;
     if (o->encoding == REDIS_ENCODING_RAW) {
         incrRefCount(o);
         return o;
@@ -3000,17 +3189,17 @@ static robj *getDecodedObject(robj *o) {
     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
         char buf[32];
 
     if (o->type == REDIS_STRING && o->encoding == REDIS_ENCODING_INT) {
         char buf[32];
 
-        snprintf(buf,32,"%ld",(long)o->ptr);
+        ll2string(buf,32,(long)o->ptr);
         dec = createStringObject(buf,strlen(buf));
         return dec;
     } else {
         dec = createStringObject(buf,strlen(buf));
         return dec;
     } else {
-        redisAssert(1 != 1);
+        redisPanic("Unknown encoding type");
     }
 }
 
 /* Compare two string objects via strcmp() or alike.
  * Note that the objects may be integer-encoded. In such a case we
     }
 }
 
 /* Compare two string objects via strcmp() or alike.
  * Note that the objects may be integer-encoded. In such a case we
- * use snprintf() to get a string representation of the numbers on the stack
+ * use ll2string() to get a string representation of the numbers on the stack
  * and compare the strings, it's much faster than calling getDecodedObject().
  *
  * Important note: if objects are not integer encoded, but binary-safe strings,
  * and compare the strings, it's much faster than calling getDecodedObject().
  *
  * Important note: if objects are not integer encoded, but binary-safe strings,
@@ -3023,14 +3212,14 @@ static int compareStringObjects(robj *a, robj *b) {
 
     if (a == b) return 0;
     if (a->encoding != REDIS_ENCODING_RAW) {
 
     if (a == b) return 0;
     if (a->encoding != REDIS_ENCODING_RAW) {
-        snprintf(bufa,sizeof(bufa),"%ld",(long) a->ptr);
+        ll2string(bufa,sizeof(bufa),(long) a->ptr);
         astr = bufa;
         bothsds = 0;
     } else {
         astr = a->ptr;
     }
     if (b->encoding != REDIS_ENCODING_RAW) {
         astr = bufa;
         bothsds = 0;
     } else {
         astr = a->ptr;
     }
     if (b->encoding != REDIS_ENCODING_RAW) {
-        snprintf(bufb,sizeof(bufb),"%ld",(long) b->ptr);
+        ll2string(bufb,sizeof(bufb),(long) b->ptr);
         bstr = bufb;
         bothsds = 0;
     } else {
         bstr = bufb;
         bothsds = 0;
     } else {
@@ -3039,6 +3228,18 @@ static int compareStringObjects(robj *a, robj *b) {
     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
 }
 
     return bothsds ? sdscmp(astr,bstr) : strcmp(astr,bstr);
 }
 
+/* Equal string objects return 1 if the two objects are the same from the
+ * point of view of a string comparison, otherwise 0 is returned. Note that
+ * this function is faster then checking for (compareStringObject(a,b) == 0)
+ * because it can perform some more optimization. */
+static int equalStringObjects(robj *a, robj *b) {
+    if (a->encoding != REDIS_ENCODING_RAW && b->encoding != REDIS_ENCODING_RAW){
+        return a->ptr == b->ptr;
+    } else {
+        return compareStringObjects(a,b) == 0;
+    }
+}
+
 static size_t stringObjectLen(robj *o) {
     redisAssert(o->type == REDIS_STRING);
     if (o->encoding == REDIS_ENCODING_RAW) {
 static size_t stringObjectLen(robj *o) {
     redisAssert(o->type == REDIS_STRING);
     if (o->encoding == REDIS_ENCODING_RAW) {
@@ -3046,8 +3247,99 @@ static size_t stringObjectLen(robj *o) {
     } else {
         char buf[32];
 
     } else {
         char buf[32];
 
-        return snprintf(buf,32,"%ld",(long)o->ptr);
+        return ll2string(buf,32,(long)o->ptr);
+    }
+}
+
+static int getDoubleFromObject(robj *o, double *target) {
+    double value;
+    char *eptr;
+
+    if (o == NULL) {
+        value = 0;
+    } else {
+        redisAssert(o->type == REDIS_STRING);
+        if (o->encoding == REDIS_ENCODING_RAW) {
+            value = strtod(o->ptr, &eptr);
+            if (eptr[0] != '\0') return REDIS_ERR;
+        } else if (o->encoding == REDIS_ENCODING_INT) {
+            value = (long)o->ptr;
+        } else {
+            redisPanic("Unknown string encoding");
+        }
+    }
+
+    *target = value;
+    return REDIS_OK;
+}
+
+static int getDoubleFromObjectOrReply(redisClient *c, robj *o, double *target, const char *msg) {
+    double value;
+    if (getDoubleFromObject(o, &value) != REDIS_OK) {
+        if (msg != NULL) {
+            addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
+        } else {
+            addReplySds(c, sdsnew("-ERR value is not a double\r\n"));
+        }
+        return REDIS_ERR;
     }
     }
+
+    *target = value;
+    return REDIS_OK;
+}
+
+static int getLongLongFromObject(robj *o, long long *target) {
+    long long value;
+    char *eptr;
+
+    if (o == NULL) {
+        value = 0;
+    } else {
+        redisAssert(o->type == REDIS_STRING);
+        if (o->encoding == REDIS_ENCODING_RAW) {
+            value = strtoll(o->ptr, &eptr, 10);
+            if (eptr[0] != '\0') return REDIS_ERR;
+        } else if (o->encoding == REDIS_ENCODING_INT) {
+            value = (long)o->ptr;
+        } else {
+            redisPanic("Unknown string encoding");
+        }
+    }
+
+    *target = value;
+    return REDIS_OK;
+}
+
+static int getLongLongFromObjectOrReply(redisClient *c, robj *o, long long *target, const char *msg) {
+    long long value;
+    if (getLongLongFromObject(o, &value) != REDIS_OK) {
+        if (msg != NULL) {
+            addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
+        } else {
+            addReplySds(c, sdsnew("-ERR value is not an integer\r\n"));
+        }
+        return REDIS_ERR;
+    }
+
+    *target = value;
+    return REDIS_OK;
+}
+
+static int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
+    long long value;
+
+    if (getLongLongFromObjectOrReply(c, o, &value, msg) != REDIS_OK) return REDIS_ERR;
+    if (value < LONG_MIN || value > LONG_MAX) {
+        if (msg != NULL) {
+            addReplySds(c, sdscatprintf(sdsempty(), "-ERR %s\r\n", msg));
+        } else {
+            addReplySds(c, sdsnew("-ERR value is out of range\r\n"));
+        }
+        return REDIS_ERR;
+    }
+
+    *target = value;
+    return REDIS_OK;
 }
 
 /*============================ RDB saving/loading =========================== */
 }
 
 /*============================ RDB saving/loading =========================== */
@@ -3096,7 +3388,7 @@ static int rdbTryIntegerEncoding(char *s, size_t len, unsigned char *enc) {
     /* Check if it's possible to encode this value as a number */
     value = strtoll(s, &endptr, 10);
     if (endptr[0] != '\0') return 0;
     /* Check if it's possible to encode this value as a number */
     value = strtoll(s, &endptr, 10);
     if (endptr[0] != '\0') return 0;
-    snprintf(buf,32,"%lld",value);
+    ll2string(buf,32,value);
 
     /* If the number converted back into a string is not identical
      * then it's not possible to encode the string as integer */
 
     /* If the number converted back into a string is not identical
      * then it's not possible to encode the string as integer */
@@ -3303,7 +3595,7 @@ static int rdbSaveObject(FILE *fp, robj *o) {
             dictReleaseIterator(di);
         }
     } else {
             dictReleaseIterator(di);
         }
     } else {
-        redisAssert(0);
+        redisPanic("Unknown object type");
     }
     return 0;
 }
     }
     return 0;
 }
@@ -3322,7 +3614,7 @@ static off_t rdbSavedObjectLen(robj *o, FILE *fp) {
 /* Return the number of pages required to save this object in the swap file */
 static off_t rdbSavedObjectPages(robj *o, FILE *fp) {
     off_t bytes = rdbSavedObjectLen(o,fp);
 /* Return the number of pages required to save this object in the swap file */
 static off_t rdbSavedObjectPages(robj *o, FILE *fp) {
     off_t bytes = rdbSavedObjectLen(o,fp);
-    
+
     return (bytes+(server.vm_page_size-1))/server.vm_page_size;
 }
 
     return (bytes+(server.vm_page_size-1))/server.vm_page_size;
 }
 
@@ -3405,7 +3697,7 @@ static int rdbSave(char *filename) {
     fflush(fp);
     fsync(fileno(fp));
     fclose(fp);
     fflush(fp);
     fsync(fileno(fp));
     fclose(fp);
-    
+
     /* Use RENAME to make sure the DB file is changed atomically only
      * if the generate DB file is ok. */
     if (rename(tmpfile,filename) == -1) {
     /* Use RENAME to make sure the DB file is changed atomically only
      * if the generate DB file is ok. */
     if (rename(tmpfile,filename) == -1) {
@@ -3505,7 +3797,11 @@ static uint32_t rdbLoadLen(FILE *fp, int *isencoded) {
     }
 }
 
     }
 }
 
-static robj *rdbLoadIntegerObject(FILE *fp, int enctype) {
+/* Load an integer-encoded object from file 'fp', with the specified
+ * encoding type 'enctype'. If encode is true the function may return
+ * an integer-encoded object as reply, otherwise the returned object
+ * will always be encoded as a raw string. */
+static robj *rdbLoadIntegerObject(FILE *fp, int enctype, int encode) {
     unsigned char enc[4];
     long long val;
 
     unsigned char enc[4];
     long long val;
 
@@ -3524,9 +3820,12 @@ static robj *rdbLoadIntegerObject(FILE *fp, int enctype) {
         val = (int32_t)v;
     } else {
         val = 0; /* anti-warning */
         val = (int32_t)v;
     } else {
         val = 0; /* anti-warning */
-        redisAssert(0);
+        redisPanic("Unknown RDB integer encoding type");
     }
     }
-    return createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",val));
+    if (encode)
+        return createStringObjectFromLongLong(val);
+    else
+        return createObject(REDIS_STRING,sdsfromlonglong(val));
 }
 
 static robj *rdbLoadLzfStringObject(FILE*fp) {
 }
 
 static robj *rdbLoadLzfStringObject(FILE*fp) {
@@ -3548,7 +3847,7 @@ err:
     return NULL;
 }
 
     return NULL;
 }
 
-static robj *rdbLoadStringObject(FILE*fp) {
+static robj *rdbGenericLoadStringObject(FILE*fp, int encode) {
     int isencoded;
     uint32_t len;
     sds val;
     int isencoded;
     uint32_t len;
     sds val;
@@ -3559,11 +3858,11 @@ static robj *rdbLoadStringObject(FILE*fp) {
         case REDIS_RDB_ENC_INT8:
         case REDIS_RDB_ENC_INT16:
         case REDIS_RDB_ENC_INT32:
         case REDIS_RDB_ENC_INT8:
         case REDIS_RDB_ENC_INT16:
         case REDIS_RDB_ENC_INT32:
-            return rdbLoadIntegerObject(fp,len);
+            return rdbLoadIntegerObject(fp,len,encode);
         case REDIS_RDB_ENC_LZF:
             return rdbLoadLzfStringObject(fp);
         default:
         case REDIS_RDB_ENC_LZF:
             return rdbLoadLzfStringObject(fp);
         default:
-            redisAssert(0);
+            redisPanic("Unknown RDB encoding type");
         }
     }
 
         }
     }
 
@@ -3576,6 +3875,14 @@ static robj *rdbLoadStringObject(FILE*fp) {
     return createObject(REDIS_STRING,val);
 }
 
     return createObject(REDIS_STRING,val);
 }
 
+static robj *rdbLoadStringObject(FILE *fp) {
+    return rdbGenericLoadStringObject(fp,0);
+}
+
+static robj *rdbLoadEncodedStringObject(FILE *fp) {
+    return rdbGenericLoadStringObject(fp,1);
+}
+
 /* For information about double serialization check rdbSaveDoubleValue() */
 static int rdbLoadDoubleValue(FILE *fp, double *val) {
     char buf[128];
 /* For information about double serialization check rdbSaveDoubleValue() */
 static int rdbLoadDoubleValue(FILE *fp, double *val) {
     char buf[128];
@@ -3602,8 +3909,8 @@ static robj *rdbLoadObject(int type, FILE *fp) {
     redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",type,ftell(fp));
     if (type == REDIS_STRING) {
         /* Read string value */
     redisLog(REDIS_DEBUG,"LOADING OBJECT %d (at %d)\n",type,ftell(fp));
     if (type == REDIS_STRING) {
         /* Read string value */
-        if ((o = rdbLoadStringObject(fp)) == NULL) return NULL;
-        tryObjectEncoding(o);
+        if ((o = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
+        o = tryObjectEncoding(o);
     } else if (type == REDIS_LIST || type == REDIS_SET) {
         /* Read list/set value */
         uint32_t listlen;
     } else if (type == REDIS_LIST || type == REDIS_SET) {
         /* Read list/set value */
         uint32_t listlen;
@@ -3618,8 +3925,8 @@ static robj *rdbLoadObject(int type, FILE *fp) {
         while(listlen--) {
             robj *ele;
 
         while(listlen--) {
             robj *ele;
 
-            if ((ele = rdbLoadStringObject(fp)) == NULL) return NULL;
-            tryObjectEncoding(ele);
+            if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
+            ele = tryObjectEncoding(ele);
             if (type == REDIS_LIST) {
                 listAddNodeTail((list*)o->ptr,ele);
             } else {
             if (type == REDIS_LIST) {
                 listAddNodeTail((list*)o->ptr,ele);
             } else {
@@ -3639,8 +3946,8 @@ static robj *rdbLoadObject(int type, FILE *fp) {
             robj *ele;
             double *score = zmalloc(sizeof(double));
 
             robj *ele;
             double *score = zmalloc(sizeof(double));
 
-            if ((ele = rdbLoadStringObject(fp)) == NULL) return NULL;
-            tryObjectEncoding(ele);
+            if ((ele = rdbLoadEncodedStringObject(fp)) == NULL) return NULL;
+            ele = tryObjectEncoding(ele);
             if (rdbLoadDoubleValue(fp,score) == -1) return NULL;
             dictAdd(zs->dict,ele,score);
             zslInsert(zs->zsl,*score,ele);
             if (rdbLoadDoubleValue(fp,score) == -1) return NULL;
             dictAdd(zs->dict,ele,score);
             zslInsert(zs->zsl,*score,ele);
@@ -3679,26 +3986,26 @@ static robj *rdbLoadObject(int type, FILE *fp) {
                 decrRefCount(key);
                 decrRefCount(val);
             } else {
                 decrRefCount(key);
                 decrRefCount(val);
             } else {
-                tryObjectEncoding(key);
-                tryObjectEncoding(val);
+                key = tryObjectEncoding(key);
+                val = tryObjectEncoding(val);
                 dictAdd((dict*)o->ptr,key,val);
             }
         }
     } else {
                 dictAdd((dict*)o->ptr,key,val);
             }
         }
     } else {
-        redisAssert(0);
+        redisPanic("Unknown object type");
     }
     return o;
 }
 
 static int rdbLoad(char *filename) {
     FILE *fp;
     }
     return o;
 }
 
 static int rdbLoad(char *filename) {
     FILE *fp;
-    robj *keyobj = NULL;
     uint32_t dbid;
     int type, retval, rdbver;
     uint32_t dbid;
     int type, retval, rdbver;
+    int swap_all_values = 0;
     dict *d = server.db[0].dict;
     redisDb *db = server.db+0;
     char buf[1024];
     dict *d = server.db[0].dict;
     redisDb *db = server.db+0;
     char buf[1024];
-    time_t expiretime = -1, now = time(NULL);
+    time_t expiretime, now = time(NULL);
     long long loadedkeys = 0;
 
     fp = fopen(filename,"r");
     long long loadedkeys = 0;
 
     fp = fopen(filename,"r");
@@ -3717,8 +4024,9 @@ static int rdbLoad(char *filename) {
         return REDIS_ERR;
     }
     while(1) {
         return REDIS_ERR;
     }
     while(1) {
-        robj *o;
+        robj *key, *val;
 
 
+        expiretime = -1;
         /* Read type. */
         if ((type = rdbLoadType(fp)) == -1) goto eoferr;
         if (type == REDIS_EXPIRETIME) {
         /* Read type. */
         if ((type = rdbLoadType(fp)) == -1) goto eoferr;
         if (type == REDIS_EXPIRETIME) {
@@ -3740,36 +4048,61 @@ static int rdbLoad(char *filename) {
             continue;
         }
         /* Read key */
             continue;
         }
         /* Read key */
-        if ((keyobj = rdbLoadStringObject(fp)) == NULL) goto eoferr;
+        if ((key = rdbLoadStringObject(fp)) == NULL) goto eoferr;
         /* Read value */
         /* Read value */
-        if ((o = rdbLoadObject(type,fp)) == NULL) goto eoferr;
+        if ((val = rdbLoadObject(type,fp)) == NULL) goto eoferr;
+        /* Check if the key already expired */
+        if (expiretime != -1 && expiretime < now) {
+            decrRefCount(key);
+            decrRefCount(val);
+            continue;
+        }
         /* Add the new object in the hash table */
         /* Add the new object in the hash table */
-        retval = dictAdd(d,keyobj,o);
+        retval = dictAdd(d,key,val);
         if (retval == DICT_ERR) {
         if (retval == DICT_ERR) {
-            redisLog(REDIS_WARNING,"Loading DB, duplicated key (%s) found! Unrecoverable error, exiting now.", keyobj->ptr);
+            redisLog(REDIS_WARNING,"Loading DB, duplicated key (%s) found! Unrecoverable error, exiting now.", key->ptr);
             exit(1);
         }
             exit(1);
         }
+        loadedkeys++;
         /* Set the expire time if needed */
         /* Set the expire time if needed */
-        if (expiretime != -1) {
-            setExpire(db,keyobj,expiretime);
-            /* Delete this key if already expired */
-            if (expiretime < now) deleteKey(db,keyobj);
-            expiretime = -1;
-        }
-        keyobj = o = NULL;
+        if (expiretime != -1) setExpire(db,key,expiretime);
+
         /* Handle swapping while loading big datasets when VM is on */
         /* Handle swapping while loading big datasets when VM is on */
-        loadedkeys++;
-        if (server.vm_enabled && (loadedkeys % 5000) == 0) {
+
+        /* If we detecter we are hopeless about fitting something in memory
+         * we just swap every new key on disk. Directly...
+         * Note that's important to check for this condition before resorting
+         * to random sampling, otherwise we may try to swap already
+         * swapped keys. */
+        if (swap_all_values) {
+            dictEntry *de = dictFind(d,key);
+
+            /* de may be NULL since the key already expired */
+            if (de) {
+                key = dictGetEntryKey(de);
+                val = dictGetEntryVal(de);
+
+                if (vmSwapObjectBlocking(key,val) == REDIS_OK) {
+                    dictGetEntryVal(de) = NULL;
+                }
+            }
+            continue;
+        }
+
+        /* If we have still some hope of having some value fitting memory
+         * then we try random sampling. */
+        if (!swap_all_values && server.vm_enabled && (loadedkeys % 5000) == 0) {
             while (zmalloc_used_memory() > server.vm_max_memory) {
                 if (vmSwapOneObjectBlocking() == REDIS_ERR) break;
             }
             while (zmalloc_used_memory() > server.vm_max_memory) {
                 if (vmSwapOneObjectBlocking() == REDIS_ERR) break;
             }
+            if (zmalloc_used_memory() > server.vm_max_memory)
+                swap_all_values = 1; /* We are already using too much mem */
         }
     }
     fclose(fp);
     return REDIS_OK;
 
 eoferr: /* unexpected end of file is handled here with a fatal exit */
         }
     }
     fclose(fp);
     return REDIS_OK;
 
 eoferr: /* unexpected end of file is handled here with a fatal exit */
-    if (keyobj) decrRefCount(keyobj);
     redisLog(REDIS_WARNING,"Short read or OOM loading DB. Unrecoverable error, aborting now.");
     exit(1);
     return REDIS_ERR; /* Just to avoid warning */
     redisLog(REDIS_WARNING,"Short read or OOM loading DB. Unrecoverable error, aborting now.");
     exit(1);
     return REDIS_ERR; /* Just to avoid warning */
@@ -3797,45 +4130,60 @@ static void echoCommand(redisClient *c) {
 
 /*=================================== Strings =============================== */
 
 
 /*=================================== Strings =============================== */
 
-static void setGenericCommand(redisClient *c, int nx) {
+static void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
     int retval;
     int retval;
+    long seconds = 0; /* initialized to avoid an harmness warning */
+
+    if (expire) {
+        if (getLongFromObjectOrReply(c, expire, &seconds, NULL) != REDIS_OK)
+            return;
+        if (seconds <= 0) {
+            addReplySds(c,sdsnew("-ERR invalid expire time in SETEX\r\n"));
+            return;
+        }
+    }
 
 
-    if (nx) deleteIfVolatile(c->db,c->argv[1]);
-    retval = dictAdd(c->db->dict,c->argv[1],c->argv[2]);
+    if (nx) deleteIfVolatile(c->db,key);
+    retval = dictAdd(c->db->dict,key,val);
     if (retval == DICT_ERR) {
         if (!nx) {
             /* If the key is about a swapped value, we want a new key object
              * to overwrite the old. So we delete the old key in the database.
              * This will also make sure that swap pages about the old object
              * will be marked as free. */
     if (retval == DICT_ERR) {
         if (!nx) {
             /* If the key is about a swapped value, we want a new key object
              * to overwrite the old. So we delete the old key in the database.
              * This will also make sure that swap pages about the old object
              * will be marked as free. */
-            if (server.vm_enabled && deleteIfSwapped(c->db,c->argv[1]))
-                incrRefCount(c->argv[1]);
-            dictReplace(c->db->dict,c->argv[1],c->argv[2]);
-            incrRefCount(c->argv[2]);
+            if (server.vm_enabled && deleteIfSwapped(c->db,key))
+                incrRefCount(key);
+            dictReplace(c->db->dict,key,val);
+            incrRefCount(val);
         } else {
             addReply(c,shared.czero);
             return;
         }
     } else {
         } else {
             addReply(c,shared.czero);
             return;
         }
     } else {
-        incrRefCount(c->argv[1]);
-        incrRefCount(c->argv[2]);
+        incrRefCount(key);
+        incrRefCount(val);
     }
     server.dirty++;
     }
     server.dirty++;
-    removeExpire(c->db,c->argv[1]);
+    removeExpire(c->db,key);
+    if (expire) setExpire(c->db,key,time(NULL)+seconds);
     addReply(c, nx ? shared.cone : shared.ok);
 }
 
 static void setCommand(redisClient *c) {
     addReply(c, nx ? shared.cone : shared.ok);
 }
 
 static void setCommand(redisClient *c) {
-    setGenericCommand(c,0);
+    setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
 }
 
 static void setnxCommand(redisClient *c) {
 }
 
 static void setnxCommand(redisClient *c) {
-    setGenericCommand(c,1);
+    setGenericCommand(c,1,c->argv[1],c->argv[2],NULL);
+}
+
+static void setexCommand(redisClient *c) {
+    setGenericCommand(c,0,c->argv[1],c->argv[3],c->argv[2]);
 }
 
 static int getGenericCommand(redisClient *c) {
     robj *o;
 }
 
 static int getGenericCommand(redisClient *c) {
     robj *o;
-    
+
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
         return REDIS_OK;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL)
         return REDIS_OK;
 
@@ -3866,7 +4214,7 @@ static void getsetCommand(redisClient *c) {
 
 static void mgetCommand(redisClient *c) {
     int j;
 
 static void mgetCommand(redisClient *c) {
     int j;
-  
+
     addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1));
     for (j = 1; j < c->argc; j++) {
         robj *o = lookupKeyRead(c->db,c->argv[j]);
     addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-1));
     for (j = 1; j < c->argc; j++) {
         robj *o = lookupKeyRead(c->db,c->argv[j]);
@@ -3906,7 +4254,7 @@ static void msetGenericCommand(redisClient *c, int nx) {
     for (j = 1; j < c->argc; j += 2) {
         int retval;
 
     for (j = 1; j < c->argc; j += 2) {
         int retval;
 
-        tryObjectEncoding(c->argv[j+1]);
+        c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
         retval = dictAdd(c->db->dict,c->argv[j],c->argv[j+1]);
         if (retval == DICT_ERR) {
             dictReplace(c->db->dict,c->argv[j],c->argv[j+1]);
         retval = dictAdd(c->db->dict,c->argv[j],c->argv[j+1]);
         if (retval == DICT_ERR) {
             dictReplace(c->db->dict,c->argv[j],c->argv[j+1]);
@@ -3933,28 +4281,14 @@ static void incrDecrCommand(redisClient *c, long long incr) {
     long long value;
     int retval;
     robj *o;
     long long value;
     int retval;
     robj *o;
-    
-    o = lookupKeyWrite(c->db,c->argv[1]);
-    if (o == NULL) {
-        value = 0;
-    } else {
-        if (o->type != REDIS_STRING) {
-            value = 0;
-        } else {
-            char *eptr;
 
 
-            if (o->encoding == REDIS_ENCODING_RAW)
-                value = strtoll(o->ptr, &eptr, 10);
-            else if (o->encoding == REDIS_ENCODING_INT)
-                value = (long)o->ptr;
-            else
-                redisAssert(1 != 1);
-        }
-    }
+    o = lookupKeyWrite(c->db,c->argv[1]);
+    if (o != NULL && checkType(c,o,REDIS_STRING)) return;
+    if (getLongLongFromObjectOrReply(c,o,&value,NULL) != REDIS_OK) return;
 
     value += incr;
     o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
 
     value += incr;
     o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
-    tryObjectEncoding(o);
+    o = tryObjectEncoding(o);
     retval = dictAdd(c->db->dict,c->argv[1],o);
     if (retval == DICT_ERR) {
         dictReplace(c->db->dict,c->argv[1],o);
     retval = dictAdd(c->db->dict,c->argv[1],o);
     if (retval == DICT_ERR) {
         dictReplace(c->db->dict,c->argv[1],o);
@@ -3977,12 +4311,16 @@ static void decrCommand(redisClient *c) {
 }
 
 static void incrbyCommand(redisClient *c) {
 }
 
 static void incrbyCommand(redisClient *c) {
-    long long incr = strtoll(c->argv[2]->ptr, NULL, 10);
+    long long incr;
+
+    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     incrDecrCommand(c,incr);
 }
 
 static void decrbyCommand(redisClient *c) {
     incrDecrCommand(c,incr);
 }
 
 static void decrbyCommand(redisClient *c) {
-    long long incr = strtoll(c->argv[2]->ptr, NULL, 10);
+    long long incr;
+
+    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     incrDecrCommand(c,-incr);
 }
 
     incrDecrCommand(c,-incr);
 }
 
@@ -4000,7 +4338,7 @@ static void appendCommand(redisClient *c) {
         totlen = stringObjectLen(c->argv[2]);
     } else {
         dictEntry *de;
         totlen = stringObjectLen(c->argv[2]);
     } else {
         dictEntry *de;
-       
+
         de = dictFind(c->db->dict,c->argv[1]);
         assert(de != NULL);
 
         de = dictFind(c->db->dict,c->argv[1]);
         assert(de != NULL);
 
@@ -4089,7 +4427,7 @@ static void existsCommand(redisClient *c) {
 
 static void selectCommand(redisClient *c) {
     int id = atoi(c->argv[1]->ptr);
 
 static void selectCommand(redisClient *c) {
     int id = atoi(c->argv[1]->ptr);
-    
+
     if (selectDb(c,id) == REDIS_ERR) {
         addReplySds(c,sdsnew("-ERR invalid DB index\r\n"));
     } else {
     if (selectDb(c,id) == REDIS_ERR) {
         addReplySds(c,sdsnew("-ERR invalid DB index\r\n"));
     } else {
@@ -4099,18 +4437,25 @@ static void selectCommand(redisClient *c) {
 
 static void randomkeyCommand(redisClient *c) {
     dictEntry *de;
 
 static void randomkeyCommand(redisClient *c) {
     dictEntry *de;
-   
+    robj *key;
+
     while(1) {
         de = dictGetRandomKey(c->db->dict);
         if (!de || expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break;
     }
     while(1) {
         de = dictGetRandomKey(c->db->dict);
         if (!de || expireIfNeeded(c->db,dictGetEntryKey(de)) == 0) break;
     }
+
     if (de == NULL) {
     if (de == NULL) {
-        addReply(c,shared.plus);
-        addReply(c,shared.crlf);
+        addReply(c,shared.nullbulk);
+        return;
+    }
+
+    key = dictGetEntryKey(de);
+    if (server.vm_enabled) {
+        key = dupStringObject(key);
+        addReplyBulk(c,key);
+        decrRefCount(key);
     } else {
     } else {
-        addReply(c,shared.plus);
-        addReply(c,dictGetEntryKey(de));
-        addReply(c,shared.crlf);
+        addReplyBulk(c,key);
     }
 }
 
     }
 }
 
@@ -4219,7 +4564,6 @@ static void shutdownCommand(redisClient *c) {
                 unlink(server.pidfile);
             redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
             redisLog(REDIS_WARNING,"Server exit now, bye bye...");
                 unlink(server.pidfile);
             redisLog(REDIS_WARNING,"%zu bytes used at exit",zmalloc_used_memory());
             redisLog(REDIS_WARNING,"Server exit now, bye bye...");
-            if (server.vm_enabled) unlink(server.vm_swap_file);
             exit(0);
         } else {
             /* Ooops.. error saving! The best we can do is to continue
             exit(0);
         } else {
             /* Ooops.. error saving! The best we can do is to continue
@@ -4227,7 +4571,7 @@ static void shutdownCommand(redisClient *c) {
              * in the next cron() Redis will be notified that the background
              * saving aborted, handling special stuff like slaves pending for
              * synchronization... */
              * 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");
             addReplySds(c,
                 sdsnew("-ERR can't quit, problems saving the DB\r\n"));
         }
             addReplySds(c,
                 sdsnew("-ERR can't quit, problems saving the DB\r\n"));
         }
@@ -4371,7 +4715,7 @@ static void llenCommand(redisClient *c) {
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_LIST)) return;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_LIST)) return;
-    
+
     l = o->ptr;
     addReplyUlong(c,listLength(l));
 }
     l = o->ptr;
     addReplyUlong(c,listLength(l));
 }
@@ -4462,8 +4806,8 @@ static void lrangeCommand(redisClient *c) {
     listNode *ln;
     robj *ele;
 
     listNode *ln;
     robj *ele;
 
-    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL ||
-        checkType(c,o,REDIS_LIST)) return;
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
+         || checkType(c,o,REDIS_LIST)) return;
     list = o->ptr;
     llen = listLength(list);
 
     list = o->ptr;
     llen = listLength(list);
 
@@ -4558,7 +4902,7 @@ static void lremCommand(redisClient *c) {
         robj *ele = listNodeValue(ln);
 
         next = fromtail ? ln->prev : ln->next;
         robj *ele = listNodeValue(ln);
 
         next = fromtail ? ln->prev : ln->next;
-        if (compareStringObjects(ele,c->argv[3]) == 0) {
+        if (equalStringObjects(ele,c->argv[3])) {
             listDelNode(list,ln);
             server.dirty++;
             removed++;
             listDelNode(list,ln);
             server.dirty++;
             removed++;
@@ -4727,7 +5071,7 @@ static void scardCommand(redisClient *c) {
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_SET)) return;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_SET)) return;
-    
+
     s = o->ptr;
     addReplyUlong(c,dictSize(s));
 }
     s = o->ptr;
     addReplyUlong(c,dictSize(s));
 }
@@ -4796,7 +5140,7 @@ static void sinterGenericCommand(redisClient *c, robj **setskeys, unsigned long
                     server.dirty++;
                 addReply(c,shared.czero);
             } else {
                     server.dirty++;
                 addReply(c,shared.czero);
             } else {
-                addReply(c,shared.nullmultibulk);
+                addReply(c,shared.emptymultibulk);
             }
             return;
         }
             }
             return;
         }
@@ -5018,7 +5362,7 @@ static zskiplistNode *zslCreateNode(int level, double score, robj *obj) {
 static zskiplist *zslCreate(void) {
     int j;
     zskiplist *zsl;
 static zskiplist *zslCreate(void) {
     int j;
     zskiplist *zsl;
-    
+
     zsl = zmalloc(sizeof(*zsl));
     zsl->level = 1;
     zsl->length = 0;
     zsl = zmalloc(sizeof(*zsl));
     zsl->level = 1;
     zsl->length = 0;
@@ -5162,7 +5506,7 @@ static int zslDelete(zskiplist *zsl, double score, robj *obj) {
     /* We may have multiple elements with the same score, what we need
      * is to find the element with both the right score and object. */
     x = x->forward[0];
     /* We may have multiple elements with the same score, what we need
      * is to find the element with both the right score and object. */
     x = x->forward[0];
-    if (x && score == x->score && compareStringObjects(x->obj,obj) == 0) {
+    if (x && score == x->score && equalStringObjects(x->obj,obj)) {
         zslDeleteNode(zsl, x, update);
         zslFreeNode(x);
         return 1;
         zslDeleteNode(zsl, x, update);
         zslFreeNode(x);
         return 1;
@@ -5267,7 +5611,7 @@ static unsigned long zslGetRank(zskiplist *zsl, double score, robj *o) {
         }
 
         /* x might be equal to zsl->header, so test if obj is non-NULL */
         }
 
         /* x might be equal to zsl->header, so test if obj is non-NULL */
-        if (x->obj && compareStringObjects(x->obj,o) == 0) {
+        if (x->obj && equalStringObjects(x->obj,o)) {
             return rank;
         }
     }
             return rank;
         }
     }
@@ -5351,7 +5695,7 @@ static void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scor
     } else {
         dictEntry *de;
         double *oldscore;
     } else {
         dictEntry *de;
         double *oldscore;
-        
+
         /* case 2: Score update operation */
         de = dictFind(zs->dict,ele);
         redisAssert(de != NULL);
         /* case 2: Score update operation */
         de = dictFind(zs->dict,ele);
         redisAssert(de != NULL);
@@ -5380,14 +5724,14 @@ static void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scor
 static void zaddCommand(redisClient *c) {
     double scoreval;
 
 static void zaddCommand(redisClient *c) {
     double scoreval;
 
-    scoreval = strtod(c->argv[2]->ptr,NULL);
+    if (getDoubleFromObjectOrReply(c, c->argv[2], &scoreval, NULL) != REDIS_OK) return;
     zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
 }
 
 static void zincrbyCommand(redisClient *c) {
     double scoreval;
 
     zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,0);
 }
 
 static void zincrbyCommand(redisClient *c) {
     double scoreval;
 
-    scoreval = strtod(c->argv[2]->ptr,NULL);
+    if (getDoubleFromObjectOrReply(c, c->argv[2], &scoreval, NULL) != REDIS_OK) return;
     zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
 }
 
     zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
 }
 
@@ -5421,12 +5765,15 @@ static void zremCommand(redisClient *c) {
 }
 
 static void zremrangebyscoreCommand(redisClient *c) {
 }
 
 static void zremrangebyscoreCommand(redisClient *c) {
-    double min = strtod(c->argv[2]->ptr,NULL);
-    double max = strtod(c->argv[3]->ptr,NULL);
+    double min;
+    double max;
     long deleted;
     robj *zsetobj;
     zset *zs;
 
     long deleted;
     robj *zsetobj;
     zset *zs;
 
+    if ((getDoubleFromObjectOrReply(c, c->argv[2], &min, NULL) != REDIS_OK) ||
+        (getDoubleFromObjectOrReply(c, c->argv[3], &max, NULL) != REDIS_OK)) return;
+
     if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,zsetobj,REDIS_ZSET)) return;
 
     if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,zsetobj,REDIS_ZSET)) return;
 
@@ -5439,13 +5786,16 @@ static void zremrangebyscoreCommand(redisClient *c) {
 }
 
 static void zremrangebyrankCommand(redisClient *c) {
 }
 
 static void zremrangebyrankCommand(redisClient *c) {
-    int start = atoi(c->argv[2]->ptr);
-    int end = atoi(c->argv[3]->ptr);
+    long start;
+    long end;
     int llen;
     long deleted;
     robj *zsetobj;
     zset *zs;
 
     int llen;
     long deleted;
     robj *zsetobj;
     zset *zs;
 
+    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
+        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
+
     if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,zsetobj,REDIS_ZSET)) return;
     zs = zsetobj->ptr;
     if ((zsetobj = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,zsetobj,REDIS_ZSET)) return;
     zs = zsetobj->ptr;
@@ -5499,7 +5849,7 @@ inline static void zunionInterAggregate(double *target, double val, int aggregat
         *target = val > *target ? val : *target;
     } else {
         /* safety net */
         *target = val > *target ? val : *target;
     } else {
         /* safety net */
-        redisAssert(0 != 0);
+        redisPanic("Unknown ZUNION/INTER aggregate type");
     }
 }
 
     }
 }
 
@@ -5552,7 +5902,8 @@ static void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
             if (remaining >= (zsetnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {
                 j++; remaining--;
                 for (i = 0; i < zsetnum; i++, j++, remaining--) {
             if (remaining >= (zsetnum + 1) && !strcasecmp(c->argv[j]->ptr,"weights")) {
                 j++; remaining--;
                 for (i = 0; i < zsetnum; i++, j++, remaining--) {
-                    src[i].weight = strtod(c->argv[j]->ptr, NULL);
+                    if (getDoubleFromObjectOrReply(c, c->argv[j], &src[i].weight, NULL) != REDIS_OK)
+                        return;
                 }
             } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
                 j++; remaining--;
                 }
             } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
                 j++; remaining--;
@@ -5674,8 +6025,8 @@ static void zinterCommand(redisClient *c) {
 
 static void zrangeGenericCommand(redisClient *c, int reverse) {
     robj *o;
 
 static void zrangeGenericCommand(redisClient *c, int reverse) {
     robj *o;
-    int start = atoi(c->argv[2]->ptr);
-    int end = atoi(c->argv[3]->ptr);
+    long start;
+    long end;
     int withscores = 0;
     int llen;
     int rangelen, j;
     int withscores = 0;
     int llen;
     int rangelen, j;
@@ -5684,6 +6035,9 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
     zskiplistNode *ln;
     robj *ele;
 
     zskiplistNode *ln;
     robj *ele;
 
+    if ((getLongFromObjectOrReply(c, c->argv[2], &start, NULL) != REDIS_OK) ||
+        (getLongFromObjectOrReply(c, c->argv[3], &end, NULL) != REDIS_OK)) return;
+
     if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
         withscores = 1;
     } else if (c->argc >= 5) {
     if (c->argc == 5 && !strcasecmp(c->argv[4]->ptr,"withscores")) {
         withscores = 1;
     } else if (c->argc >= 5) {
@@ -5691,8 +6045,8 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
         return;
     }
 
         return;
     }
 
-    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL ||
-        checkType(c,o,REDIS_ZSET)) return;
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
+         || checkType(c,o,REDIS_ZSET)) return;
     zsetobj = o->ptr;
     zsl = zsetobj->zsl;
     llen = zsl->length;
     zsetobj = o->ptr;
     zsl = zsetobj->zsl;
     llen = zsl->length;
@@ -5798,7 +6152,7 @@ static void genericZrangebyscoreCommand(redisClient *c, int justcount) {
     /* Ok, lookup the key and get the range */
     o = lookupKeyRead(c->db,c->argv[1]);
     if (o == NULL) {
     /* Ok, lookup the key and get the range */
     o = lookupKeyRead(c->db,c->argv[1]);
     if (o == NULL) {
-        addReply(c,justcount ? shared.czero : shared.nullmultibulk);
+        addReply(c,justcount ? shared.czero : shared.emptymultibulk);
     } else {
         if (o->type != REDIS_ZSET) {
             addReply(c,shared.wrongtypeerr);
     } else {
         if (o->type != REDIS_ZSET) {
             addReply(c,shared.wrongtypeerr);
@@ -5935,292 +6289,389 @@ static void zrevrankCommand(redisClient *c) {
     zrankGenericCommand(c, 1);
 }
 
     zrankGenericCommand(c, 1);
 }
 
-/* =================================== Hashes =============================== */
-static void hsetCommand(redisClient *c) {
-    int update = 0;
-    robj *o = lookupKeyWrite(c->db,c->argv[1]);
+/* ========================= Hashes utility functions ======================= */
+#define REDIS_HASH_KEY 1
+#define REDIS_HASH_VALUE 2
 
 
-    if (o == NULL) {
-        o = createHashObject();
-        dictAdd(c->db->dict,c->argv[1],o);
-        incrRefCount(c->argv[1]);
-    } else {
-        if (o->type != REDIS_HASH) {
-            addReply(c,shared.wrongtypeerr);
+/* Check the length of a number of objects to see if we need to convert a
+ * zipmap to a real hash. Note that we only check string encoded objects
+ * as their string length can be queried in constant time. */
+static void hashTryConversion(robj *subject, robj **argv, int start, int end) {
+    int i;
+    if (subject->encoding != REDIS_ENCODING_ZIPMAP) return;
+
+    for (i = start; i <= end; i++) {
+        if (argv[i]->encoding == REDIS_ENCODING_RAW &&
+            sdslen(argv[i]->ptr) > server.hash_max_zipmap_value)
+        {
+            convertToRealHash(subject);
             return;
         }
     }
             return;
         }
     }
-    /* We want to convert the zipmap into an hash table right now if the
-     * entry to be added is too big. Note that we check if the object
-     * is integer encoded before to try fetching the length in the test below.
-     * This is because integers are small, but currently stringObjectLen()
-     * performs a slow conversion: not worth it. */
-    if (o->encoding == REDIS_ENCODING_ZIPMAP &&
-        ((c->argv[2]->encoding == REDIS_ENCODING_RAW &&
-          sdslen(c->argv[2]->ptr) > server.hash_max_zipmap_value) ||
-         (c->argv[3]->encoding == REDIS_ENCODING_RAW &&
-          sdslen(c->argv[3]->ptr) > server.hash_max_zipmap_value)))
-    {
-        convertToRealHash(o);
+}
+
+/* Encode given objects in-place when the hash uses a dict. */
+static void hashTryObjectEncoding(robj *subject, robj **o1, robj **o2) {
+    if (subject->encoding == REDIS_ENCODING_HT) {
+        if (o1) *o1 = tryObjectEncoding(*o1);
+        if (o2) *o2 = tryObjectEncoding(*o2);
     }
     }
+}
 
 
+/* Get the value from a hash identified by key. Returns either a string
+ * object or NULL if the value cannot be found. The refcount of the object
+ * is always increased by 1 when the value was found. */
+static robj *hashGet(robj *o, robj *key) {
+    robj *value = NULL;
     if (o->encoding == REDIS_ENCODING_ZIPMAP) {
     if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        unsigned char *zm = o->ptr;
-        robj *valobj = getDecodedObject(c->argv[3]);
+        unsigned char *v;
+        unsigned int vlen;
+        key = getDecodedObject(key);
+        if (zipmapGet(o->ptr,key->ptr,sdslen(key->ptr),&v,&vlen)) {
+            value = createStringObject((char*)v,vlen);
+        }
+        decrRefCount(key);
+    } else {
+        dictEntry *de = dictFind(o->ptr,key);
+        if (de != NULL) {
+            value = dictGetEntryVal(de);
+            incrRefCount(value);
+        }
+    }
+    return value;
+}
 
 
-        zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
-            valobj->ptr,sdslen(valobj->ptr),&update);
-        decrRefCount(valobj);
-        o->ptr = zm;
+/* Test if the key exists in the given hash. Returns 1 if the key
+ * exists and 0 when it doesn't. */
+static int hashExists(robj *o, robj *key) {
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        key = getDecodedObject(key);
+        if (zipmapExists(o->ptr,key->ptr,sdslen(key->ptr))) {
+            decrRefCount(key);
+            return 1;
+        }
+        decrRefCount(key);
+    } else {
+        if (dictFind(o->ptr,key) != NULL) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* Add an element, discard the old if the key already exists.
+ * Return 0 on insert and 1 on update. */
+static int hashSet(robj *o, robj *key, robj *value) {
+    int update = 0;
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        key = getDecodedObject(key);
+        value = getDecodedObject(value);
+        o->ptr = zipmapSet(o->ptr,
+            key->ptr,sdslen(key->ptr),
+            value->ptr,sdslen(value->ptr), &update);
+        decrRefCount(key);
+        decrRefCount(value);
 
 
-        /* And here there is the second check for hash conversion. */
-        if (zipmapLen(zm) > server.hash_max_zipmap_entries)
+        /* Check if the zipmap needs to be upgraded to a real hash table */
+        if (zipmapLen(o->ptr) > server.hash_max_zipmap_entries)
             convertToRealHash(o);
     } else {
             convertToRealHash(o);
     } else {
-        tryObjectEncoding(c->argv[2]);
-        /* note that c->argv[3] is already encoded, as the latest arg
-         * of a bulk command is always integer encoded if possible. */
-        if (dictReplace(o->ptr,c->argv[2],c->argv[3])) {
-            incrRefCount(c->argv[2]);
+        if (dictReplace(o->ptr,key,value)) {
+            /* Insert */
+            incrRefCount(key);
         } else {
         } else {
+            /* Update */
             update = 1;
         }
             update = 1;
         }
-        incrRefCount(c->argv[3]);
+        incrRefCount(value);
     }
     }
-    server.dirty++;
-    addReplySds(c,sdscatprintf(sdsempty(),":%d\r\n",update == 0));
+    return update;
 }
 
 }
 
-static void hincrbyCommand(redisClient *c) {
-    long long value = 0, incr = 0;
-    robj *o = lookupKeyWrite(c->db,c->argv[1]);
+/* Delete an element from a hash.
+ * Return 1 on deleted and 0 on not found. */
+static int hashDelete(robj *o, robj *key) {
+    int deleted = 0;
+    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
+        key = getDecodedObject(key);
+        o->ptr = zipmapDel(o->ptr,key->ptr,sdslen(key->ptr), &deleted);
+        decrRefCount(key);
+    } else {
+        deleted = dictDelete((dict*)o->ptr,key) == DICT_OK;
+        /* Always check if the dictionary needs a resize after a delete. */
+        if (deleted && htNeedsResize(o->ptr)) dictResize(o->ptr);
+    }
+    return deleted;
+}
+
+/* Return the number of elements in a hash. */
+static unsigned long hashLength(robj *o) {
+    return (o->encoding == REDIS_ENCODING_ZIPMAP) ?
+        zipmapLen((unsigned char*)o->ptr) : dictSize((dict*)o->ptr);
+}
+
+/* Structure to hold hash iteration abstration. Note that iteration over
+ * hashes involves both fields and values. Because it is possible that
+ * not both are required, store pointers in the iterator to avoid
+ * unnecessary memory allocation for fields/values. */
+typedef struct {
+    int encoding;
+    unsigned char *zi;
+    unsigned char *zk, *zv;
+    unsigned int zklen, zvlen;
+
+    dictIterator *di;
+    dictEntry *de;
+} hashIterator;
+
+static hashIterator *hashInitIterator(robj *subject) {
+    hashIterator *hi = zmalloc(sizeof(hashIterator));
+    hi->encoding = subject->encoding;
+    if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
+        hi->zi = zipmapRewind(subject->ptr);
+    } else if (hi->encoding == REDIS_ENCODING_HT) {
+        hi->di = dictGetIterator(subject->ptr);
+    } else {
+        redisAssert(NULL);
+    }
+    return hi;
+}
+
+static void hashReleaseIterator(hashIterator *hi) {
+    if (hi->encoding == REDIS_ENCODING_HT) {
+        dictReleaseIterator(hi->di);
+    }
+    zfree(hi);
+}
+
+/* Move to the next entry in the hash. Return REDIS_OK when the next entry
+ * could be found and REDIS_ERR when the iterator reaches the end. */
+static int hashNext(hashIterator *hi) {
+    if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
+        if ((hi->zi = zipmapNext(hi->zi, &hi->zk, &hi->zklen,
+            &hi->zv, &hi->zvlen)) == NULL) return REDIS_ERR;
+    } else {
+        if ((hi->de = dictNext(hi->di)) == NULL) return REDIS_ERR;
+    }
+    return REDIS_OK;
+}
+
+/* Get key or value object at current iteration position.
+ * This increases the refcount of the field object by 1. */
+static robj *hashCurrent(hashIterator *hi, int what) {
+    robj *o;
+    if (hi->encoding == REDIS_ENCODING_ZIPMAP) {
+        if (what & REDIS_HASH_KEY) {
+            o = createStringObject((char*)hi->zk,hi->zklen);
+        } else {
+            o = createStringObject((char*)hi->zv,hi->zvlen);
+        }
+    } else {
+        if (what & REDIS_HASH_KEY) {
+            o = dictGetEntryKey(hi->de);
+        } else {
+            o = dictGetEntryVal(hi->de);
+        }
+        incrRefCount(o);
+    }
+    return o;
+}
 
 
+static robj *hashLookupWriteOrCreate(redisClient *c, robj *key) {
+    robj *o = lookupKeyWrite(c->db,key);
     if (o == NULL) {
         o = createHashObject();
     if (o == NULL) {
         o = createHashObject();
-        dictAdd(c->db->dict,c->argv[1],o);
-        incrRefCount(c->argv[1]);
+        dictAdd(c->db->dict,key,o);
+        incrRefCount(key);
     } else {
         if (o->type != REDIS_HASH) {
             addReply(c,shared.wrongtypeerr);
     } else {
         if (o->type != REDIS_HASH) {
             addReply(c,shared.wrongtypeerr);
-            return;
+            return NULL;
         }
     }
         }
     }
+    return o;
+}
 
 
-    incr = strtoll(c->argv[3]->ptr, NULL, 10);
-    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        unsigned char *zm = o->ptr;
-        unsigned char *zval;
-        unsigned int zvlen;
-
-        /* Find value if already present in hash */
-        if (zipmapGet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
-            &zval,&zvlen)) {
-            /* strtoll needs the char* to have a trailing \0, but
-             * the zipmap doesn't include them. */
-            sds szval = sdsnewlen(zval, zvlen);
-            value = strtoll(szval,NULL,10);
-            sdsfree(szval);
-        }
-
-        value += incr;
-        sds svalue = sdscatprintf(sdsempty(),"%lld",value);
-        zm = zipmapSet(zm,c->argv[2]->ptr,sdslen(c->argv[2]->ptr),
-            (unsigned char*)svalue,sdslen(svalue),NULL);
-        sdsfree(svalue);
-        o->ptr = zm;
-
-        /* Check if the zipmap needs to be converted. */
-        if (zipmapLen(zm) > server.hash_max_zipmap_entries)
-            convertToRealHash(o);
+/* ============================= Hash commands ============================== */
+static void hsetCommand(redisClient *c) {
+    int update;
+    robj *o;
+
+    if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+    hashTryConversion(o,c->argv,2,3);
+    hashTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
+    update = hashSet(o,c->argv[2],c->argv[3]);
+    addReply(c, update ? shared.czero : shared.cone);
+    server.dirty++;
+}
+
+static void hsetnxCommand(redisClient *c) {
+    robj *o;
+    if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+    hashTryConversion(o,c->argv,2,3);
+
+    if (hashExists(o, c->argv[2])) {
+        addReply(c, shared.czero);
     } else {
     } else {
-        robj *hval;
-        dictEntry *de;
+        hashTryObjectEncoding(o,&c->argv[2], &c->argv[3]);
+        hashSet(o,c->argv[2],c->argv[3]);
+        addReply(c, shared.cone);
+        server.dirty++;
+    }
+}
 
 
-        /* Find value if already present in hash */
-        de = dictFind(o->ptr,c->argv[2]);
-        if (de != NULL) {
-            hval = dictGetEntryVal(de);
-            if (hval->encoding == REDIS_ENCODING_RAW)
-                value = strtoll(hval->ptr,NULL,10);
-            else if (hval->encoding == REDIS_ENCODING_INT)
-                value = (long)hval->ptr;
-            else
-                redisAssert(1 != 1);
-        }
+static void hmsetCommand(redisClient *c) {
+    int i;
+    robj *o;
+
+    if ((c->argc % 2) == 1) {
+        addReplySds(c,sdsnew("-ERR wrong number of arguments for HMSET\r\n"));
+        return;
+    }
 
 
-        value += incr;
-        hval = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
-        tryObjectEncoding(hval);
-        if (dictReplace(o->ptr,c->argv[2],hval)) {
-            incrRefCount(c->argv[2]);
+    if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+    hashTryConversion(o,c->argv,2,c->argc-1);
+    for (i = 2; i < c->argc; i += 2) {
+        hashTryObjectEncoding(o,&c->argv[i], &c->argv[i+1]);
+        hashSet(o,c->argv[i],c->argv[i+1]);
+    }
+    addReply(c, shared.ok);
+    server.dirty++;
+}
+
+static void hincrbyCommand(redisClient *c) {
+    long long value, incr;
+    robj *o, *current, *new;
+
+    if (getLongLongFromObjectOrReply(c,c->argv[3],&incr,NULL) != REDIS_OK) return;
+    if ((o = hashLookupWriteOrCreate(c,c->argv[1])) == NULL) return;
+    if ((current = hashGet(o,c->argv[2])) != NULL) {
+        if (getLongLongFromObjectOrReply(c,current,&value,
+            "hash value is not an integer") != REDIS_OK) {
+            decrRefCount(current);
+            return;
         }
         }
+        decrRefCount(current);
+    } else {
+        value = 0;
     }
 
     }
 
+    value += incr;
+    new = createStringObjectFromLongLong(value);
+    hashTryObjectEncoding(o,&c->argv[2],NULL);
+    hashSet(o,c->argv[2],new);
+    decrRefCount(new);
+    addReplyLongLong(c,value);
     server.dirty++;
     server.dirty++;
-    addReplyLongLong(c, value);
 }
 
 static void hgetCommand(redisClient *c) {
 }
 
 static void hgetCommand(redisClient *c) {
-    robj *o;
-
+    robj *o, *value;
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
-    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        unsigned char *zm = o->ptr;
-        unsigned char *val;
-        unsigned int vlen;
-        robj *field;
-
-        field = getDecodedObject(c->argv[2]);
-        if (zipmapGet(zm,field->ptr,sdslen(field->ptr), &val,&vlen)) {
-            addReplySds(c,sdscatprintf(sdsempty(),"$%u\r\n", vlen));
-            addReplySds(c,sdsnewlen(val,vlen));
-            addReply(c,shared.crlf);
-            decrRefCount(field);
-            return;
-        } else {
-            addReply(c,shared.nullbulk);
-            decrRefCount(field);
-            return;
-        }
+    if ((value = hashGet(o,c->argv[2])) != NULL) {
+        addReplyBulk(c,value);
+        decrRefCount(value);
     } else {
     } else {
-        struct dictEntry *de;
+        addReply(c,shared.nullbulk);
+    }
+}
 
 
-        de = dictFind(o->ptr,c->argv[2]);
-        if (de == NULL) {
-            addReply(c,shared.nullbulk);
-        } else {
-            robj *e = dictGetEntryVal(de);
+static void hmgetCommand(redisClient *c) {
+    int i;
+    robj *o, *value;
+    o = lookupKeyRead(c->db,c->argv[1]);
+    if (o != NULL && o->type != REDIS_HASH) {
+        addReply(c,shared.wrongtypeerr);
+    }
 
 
-            addReplyBulk(c,e);
+    /* Note the check for o != NULL happens inside the loop. This is
+     * done because objects that cannot be found are considered to be
+     * an empty hash. The reply should then be a series of NULLs. */
+    addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->argc-2));
+    for (i = 2; i < c->argc; i++) {
+        if (o != NULL && (value = hashGet(o,c->argv[i])) != NULL) {
+            addReplyBulk(c,value);
+            decrRefCount(value);
+        } else {
+            addReply(c,shared.nullbulk);
         }
     }
 }
 
 static void hdelCommand(redisClient *c) {
     robj *o;
         }
     }
 }
 
 static void hdelCommand(redisClient *c) {
     robj *o;
-    int deleted = 0;
-
     if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
     if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
-    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        robj *field = getDecodedObject(c->argv[2]);
-
-        o->ptr = zipmapDel((unsigned char*) o->ptr,
-            (unsigned char*) field->ptr,
-            sdslen(field->ptr), &deleted);
-        decrRefCount(field);
-        if (zipmapLen((unsigned char*) o->ptr) == 0)
-            deleteKey(c->db,c->argv[1]);
+    if (hashDelete(o,c->argv[2])) {
+        if (hashLength(o) == 0) deleteKey(c->db,c->argv[1]);
+        addReply(c,shared.cone);
+        server.dirty++;
     } else {
     } else {
-        deleted = dictDelete((dict*)o->ptr,c->argv[2]) == DICT_OK;
-        if (htNeedsResize(o->ptr)) dictResize(o->ptr);
-        if (dictSize((dict*)o->ptr) == 0) deleteKey(c->db,c->argv[1]);
+        addReply(c,shared.czero);
     }
     }
-    if (deleted) server.dirty++;
-    addReply(c,deleted ? shared.cone : shared.czero);
 }
 
 static void hlenCommand(redisClient *c) {
     robj *o;
 }
 
 static void hlenCommand(redisClient *c) {
     robj *o;
-    unsigned long len;
-
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
-    len = (o->encoding == REDIS_ENCODING_ZIPMAP) ?
-            zipmapLen((unsigned char*)o->ptr) : dictSize((dict*)o->ptr);
-    addReplyUlong(c,len);
+    addReplyUlong(c,hashLength(o));
 }
 
 }
 
-#define REDIS_GETALL_KEYS 1
-#define REDIS_GETALL_VALS 2
 static void genericHgetallCommand(redisClient *c, int flags) {
 static void genericHgetallCommand(redisClient *c, int flags) {
-    robj *o, *lenobj;
+    robj *o, *lenobj, *obj;
     unsigned long count = 0;
     unsigned long count = 0;
+    hashIterator *hi;
 
 
-    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullmultibulk)) == NULL
+    if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptymultibulk)) == NULL
         || checkType(c,o,REDIS_HASH)) return;
 
     lenobj = createObject(REDIS_STRING,NULL);
     addReply(c,lenobj);
     decrRefCount(lenobj);
 
         || checkType(c,o,REDIS_HASH)) return;
 
     lenobj = createObject(REDIS_STRING,NULL);
     addReply(c,lenobj);
     decrRefCount(lenobj);
 
-    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        unsigned char *p = zipmapRewind(o->ptr);
-        unsigned char *field, *val;
-        unsigned int flen, vlen;
-
-        while((p = zipmapNext(p,&field,&flen,&val,&vlen)) != NULL) {
-            robj *aux;
-
-            if (flags & REDIS_GETALL_KEYS) {
-                aux = createStringObject((char*)field,flen);
-                addReplyBulk(c,aux);
-                decrRefCount(aux);
-                count++;
-            }
-            if (flags & REDIS_GETALL_VALS) {
-                aux = createStringObject((char*)val,vlen);
-                addReplyBulk(c,aux);
-                decrRefCount(aux);
-                count++;
-            }
+    hi = hashInitIterator(o);
+    while (hashNext(hi) != REDIS_ERR) {
+        if (flags & REDIS_HASH_KEY) {
+            obj = hashCurrent(hi,REDIS_HASH_KEY);
+            addReplyBulk(c,obj);
+            decrRefCount(obj);
+            count++;
         }
         }
-    } else {
-        dictIterator *di = dictGetIterator(o->ptr);
-        dictEntry *de;
-
-        while((de = dictNext(di)) != NULL) {
-            robj *fieldobj = dictGetEntryKey(de);
-            robj *valobj = dictGetEntryVal(de);
-
-            if (flags & REDIS_GETALL_KEYS) {
-                addReplyBulk(c,fieldobj);
-                count++;
-            }
-            if (flags & REDIS_GETALL_VALS) {
-                addReplyBulk(c,valobj);
-                count++;
-            }
+        if (flags & REDIS_HASH_VALUE) {
+            obj = hashCurrent(hi,REDIS_HASH_VALUE);
+            addReplyBulk(c,obj);
+            decrRefCount(obj);
+            count++;
         }
         }
-        dictReleaseIterator(di);
     }
     }
+    hashReleaseIterator(hi);
+
     lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
 }
 
 static void hkeysCommand(redisClient *c) {
     lenobj->ptr = sdscatprintf(sdsempty(),"*%lu\r\n",count);
 }
 
 static void hkeysCommand(redisClient *c) {
-    genericHgetallCommand(c,REDIS_GETALL_KEYS);
+    genericHgetallCommand(c,REDIS_HASH_KEY);
 }
 
 static void hvalsCommand(redisClient *c) {
 }
 
 static void hvalsCommand(redisClient *c) {
-    genericHgetallCommand(c,REDIS_GETALL_VALS);
+    genericHgetallCommand(c,REDIS_HASH_VALUE);
 }
 
 static void hgetallCommand(redisClient *c) {
 }
 
 static void hgetallCommand(redisClient *c) {
-    genericHgetallCommand(c,REDIS_GETALL_KEYS|REDIS_GETALL_VALS);
+    genericHgetallCommand(c,REDIS_HASH_KEY|REDIS_HASH_VALUE);
 }
 
 static void hexistsCommand(redisClient *c) {
     robj *o;
 }
 
 static void hexistsCommand(redisClient *c) {
     robj *o;
-    int exists = 0;
-
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
     if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.czero)) == NULL ||
         checkType(c,o,REDIS_HASH)) return;
 
-    if (o->encoding == REDIS_ENCODING_ZIPMAP) {
-        robj *field;
-        unsigned char *zm = o->ptr;
-
-        field = getDecodedObject(c->argv[2]);
-        exists = zipmapExists(zm,field->ptr,sdslen(field->ptr));
-        decrRefCount(field);
-    } else {
-        exists = dictFind(o->ptr,c->argv[2]) != NULL;
-    }
-    addReply(c,exists ? shared.cone : shared.czero);
+    addReply(c, hashExists(o,c->argv[2]) ? shared.cone : shared.czero);
 }
 
 static void convertToRealHash(robj *o) {
 }
 
 static void convertToRealHash(robj *o) {
@@ -6235,8 +6686,8 @@ static void convertToRealHash(robj *o) {
 
         keyobj = createStringObject((char*)key,klen);
         valobj = createStringObject((char*)val,vlen);
 
         keyobj = createStringObject((char*)key,klen);
         valobj = createStringObject((char*)val,vlen);
-        tryObjectEncoding(keyobj);
-        tryObjectEncoding(valobj);
+        keyobj = tryObjectEncoding(keyobj);
+        valobj = tryObjectEncoding(valobj);
         dictAdd(dict,keyobj,valobj);
     }
     o->encoding = REDIS_ENCODING_HT;
         dictAdd(dict,keyobj,valobj);
     }
     o->encoding = REDIS_ENCODING_HT;
@@ -6272,23 +6723,26 @@ static redisSortOperation *createSortOperation(int type, robj *pattern) {
 }
 
 /* Return the value associated to the key with a name obtained
 }
 
 /* Return the value associated to the key with a name obtained
- * substituting the first occurence of '*' in 'pattern' with 'subst' */
+ * substituting the first occurence of '*' in 'pattern' with 'subst'.
+ * The returned object will always have its refcount increased by 1
+ * when it is non-NULL. */
 static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
 static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
-    char *p;
+    char *p, *f;
     sds spat, ssub;
     sds spat, ssub;
-    robj keyobj;
-    int prefixlen, sublen, postfixlen;
+    robj keyobj, fieldobj, *o;
+    int prefixlen, sublen, postfixlen, fieldlen;
     /* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
     struct {
         long len;
         long free;
         char buf[REDIS_SORTKEY_MAX+1];
     /* Expoit the internal sds representation to create a sds string allocated on the stack in order to make this function faster */
     struct {
         long len;
         long free;
         char buf[REDIS_SORTKEY_MAX+1];
-    } keyname;
+    } keyname, fieldname;
 
     /* If the pattern is "#" return the substitution object itself in order
      * to implement the "SORT ... GET #" feature. */
     spat = pattern->ptr;
     if (spat[0] == '#' && spat[1] == '\0') {
 
     /* If the pattern is "#" return the substitution object itself in order
      * to implement the "SORT ... GET #" feature. */
     spat = pattern->ptr;
     if (spat[0] == '#' && spat[1] == '\0') {
+        incrRefCount(subst);
         return subst;
     }
 
         return subst;
     }
 
@@ -6305,20 +6759,47 @@ static robj *lookupKeyByPattern(redisDb *db, robj *pattern, robj *subst) {
         return NULL;
     }
 
         return NULL;
     }
 
+    /* Find out if we're dealing with a hash dereference. */
+    if ((f = strstr(p+1, "->")) != NULL) {
+        fieldlen = sdslen(spat)-(f-spat);
+        /* this also copies \0 character */
+        memcpy(fieldname.buf,f+2,fieldlen-1);
+        fieldname.len = fieldlen-2;
+    } else {
+        fieldlen = 0;
+    }
+
     prefixlen = p-spat;
     sublen = sdslen(ssub);
     prefixlen = p-spat;
     sublen = sdslen(ssub);
-    postfixlen = sdslen(spat)-(prefixlen+1);
+    postfixlen = sdslen(spat)-(prefixlen+1)-fieldlen;
     memcpy(keyname.buf,spat,prefixlen);
     memcpy(keyname.buf+prefixlen,ssub,sublen);
     memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
     keyname.buf[prefixlen+sublen+postfixlen] = '\0';
     keyname.len = prefixlen+sublen+postfixlen;
     memcpy(keyname.buf,spat,prefixlen);
     memcpy(keyname.buf+prefixlen,ssub,sublen);
     memcpy(keyname.buf+prefixlen+sublen,p+1,postfixlen);
     keyname.buf[prefixlen+sublen+postfixlen] = '\0';
     keyname.len = prefixlen+sublen+postfixlen;
-
-    initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(long)*2))
     decrRefCount(subst);
 
     decrRefCount(subst);
 
-    /* printf("lookup '%s' => %p\n", keyname.buf,de); */
-    return lookupKeyRead(db,&keyobj);
+    /* Lookup substituted key */
+    initStaticStringObject(keyobj,((char*)&keyname)+(sizeof(long)*2));
+    o = lookupKeyRead(db,&keyobj);
+    if (o == NULL) return NULL;
+
+    if (fieldlen > 0) {
+        if (o->type != REDIS_HASH || fieldname.len < 1) return NULL;
+
+        /* Retrieve value from hash by the field name. This operation
+         * already increases the refcount of the returned object. */
+        initStaticStringObject(fieldobj,((char*)&fieldname)+(sizeof(long)*2));
+        o = hashGet(o, &fieldobj);
+    } else {
+        if (o->type != REDIS_STRING) return NULL;
+
+        /* Every object that this function returns needs to have its refcount
+         * increased. sortCommand decreases it again. */
+        incrRefCount(o);
+    }
+
+    return o;
 }
 
 /* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
 }
 
 /* sortCompare() is used by qsort in sortCommand(). Given that qsort_r with
@@ -6353,14 +6834,8 @@ static int sortCompare(const void *s1, const void *s2) {
                 cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
             }
         } else {
                 cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
             }
         } else {
-            /* Compare elements directly */
-            robj *dec1, *dec2;
-
-            dec1 = getDecodedObject(so1->obj);
-            dec2 = getDecodedObject(so2->obj);
-            cmp = strcoll(dec1->ptr,dec2->ptr);
-            decrRefCount(dec1);
-            decrRefCount(dec2);
+            /* Compare elements directly. */
+            cmp = compareStringObjects(so1->obj,so2->obj);
         }
     }
     return server.sort_desc ? -cmp : cmp;
         }
     }
     return server.sort_desc ? -cmp : cmp;
@@ -6381,7 +6856,7 @@ static void sortCommand(redisClient *c) {
     /* Lookup the key to sort. It must be of the right types */
     sortval = lookupKeyRead(c->db,c->argv[1]);
     if (sortval == NULL) {
     /* Lookup the key to sort. It must be of the right types */
     sortval = lookupKeyRead(c->db,c->argv[1]);
     if (sortval == NULL) {
-        addReply(c,shared.nullmultibulk);
+        addReply(c,shared.emptymultibulk);
         return;
     }
     if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST &&
         return;
     }
     if (sortval->type != REDIS_SET && sortval->type != REDIS_LIST &&
@@ -6443,7 +6918,7 @@ static void sortCommand(redisClient *c) {
     case REDIS_LIST: vectorlen = listLength((list*)sortval->ptr); break;
     case REDIS_SET: vectorlen =  dictSize((dict*)sortval->ptr); break;
     case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
     case REDIS_LIST: vectorlen = listLength((list*)sortval->ptr); break;
     case REDIS_SET: vectorlen =  dictSize((dict*)sortval->ptr); break;
     case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
-    default: vectorlen = 0; redisAssert(0); /* Avoid GCC warning */
+    default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */
     }
     vector = zmalloc(sizeof(redisSortObject)*vectorlen);
     j = 0;
     }
     vector = zmalloc(sizeof(redisSortObject)*vectorlen);
     j = 0;
@@ -6487,38 +6962,36 @@ static void sortCommand(redisClient *c) {
     /* Now it's time to load the right scores in the sorting vector */
     if (dontsort == 0) {
         for (j = 0; j < vectorlen; j++) {
     /* Now it's time to load the right scores in the sorting vector */
     if (dontsort == 0) {
         for (j = 0; j < vectorlen; j++) {
+            robj *byval;
             if (sortby) {
             if (sortby) {
-                robj *byval;
-
+                /* lookup value to sort by */
                 byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
                 byval = lookupKeyByPattern(c->db,sortby,vector[j].obj);
-                if (!byval || byval->type != REDIS_STRING) continue;
-                if (alpha) {
-                    vector[j].u.cmpobj = getDecodedObject(byval);
-                } else {
-                    if (byval->encoding == REDIS_ENCODING_RAW) {
-                        vector[j].u.score = strtod(byval->ptr,NULL);
-                    } else {
-                        /* Don't need to decode the object if it's
-                         * integer-encoded (the only encoding supported) so
-                         * far. We can just cast it */
-                        if (byval->encoding == REDIS_ENCODING_INT) {
-                            vector[j].u.score = (long)byval->ptr;
-                        } else
-                            redisAssert(1 != 1);
-                    }
-                }
+                if (!byval) continue;
             } else {
             } else {
-                if (!alpha) {
-                    if (vector[j].obj->encoding == REDIS_ENCODING_RAW)
-                        vector[j].u.score = strtod(vector[j].obj->ptr,NULL);
-                    else {
-                        if (vector[j].obj->encoding == REDIS_ENCODING_INT)
-                            vector[j].u.score = (long) vector[j].obj->ptr;
-                        else
-                            redisAssert(1 != 1);
-                    }
+                /* use object itself to sort by */
+                byval = vector[j].obj;
+            }
+
+            if (alpha) {
+                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
+            } else {
+                if (byval->encoding == REDIS_ENCODING_RAW) {
+                    vector[j].u.score = strtod(byval->ptr,NULL);
+                } else if (byval->encoding == REDIS_ENCODING_INT) {
+                    /* Don't need to decode the object if it's
+                     * integer-encoded (the only encoding supported) so
+                     * far. We can just cast it */
+                    vector[j].u.score = (long)byval->ptr;
+                } else {
+                    redisAssert(1 != 1);
                 }
             }
                 }
             }
+
+            /* when the object was retrieved using lookupKeyByPattern,
+             * its refcount needs to be decreased. */
+            if (sortby) {
+                decrRefCount(byval);
+            }
         }
     }
 
         }
     }
 
@@ -6560,10 +7033,11 @@ static void sortCommand(redisClient *c) {
                     vector[j].obj);
 
                 if (sop->type == REDIS_SORT_GET) {
                     vector[j].obj);
 
                 if (sop->type == REDIS_SORT_GET) {
-                    if (!val || val->type != REDIS_STRING) {
+                    if (!val) {
                         addReply(c,shared.nullbulk);
                     } else {
                         addReplyBulk(c,val);
                         addReply(c,shared.nullbulk);
                     } else {
                         addReplyBulk(c,val);
+                        decrRefCount(val);
                     }
                 } else {
                     redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
                     }
                 } else {
                     redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
@@ -6590,11 +7064,14 @@ static void sortCommand(redisClient *c) {
                     vector[j].obj);
 
                 if (sop->type == REDIS_SORT_GET) {
                     vector[j].obj);
 
                 if (sop->type == REDIS_SORT_GET) {
-                    if (!val || val->type != REDIS_STRING) {
+                    if (!val) {
                         listAddNodeTail(listPtr,createStringObject("",0));
                     } else {
                         listAddNodeTail(listPtr,createStringObject("",0));
                     } else {
+                        /* We should do a incrRefCount on val because it is
+                         * added to the list, but also a decrRefCount because
+                         * it is returned by lookupKeyByPattern. This results
+                         * in doing nothing at all. */
                         listAddNodeTail(listPtr,val);
                         listAddNodeTail(listPtr,val);
-                        incrRefCount(val);
                     }
                 } else {
                     redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
                     }
                 } else {
                     redisAssert(sop->type == REDIS_SORT_GET); /* always fails */
@@ -6615,7 +7092,7 @@ static void sortCommand(redisClient *c) {
     decrRefCount(sortval);
     listRelease(operations);
     for (j = 0; j < vectorlen; j++) {
     decrRefCount(sortval);
     listRelease(operations);
     for (j = 0; j < vectorlen; j++) {
-        if (sortby && alpha && vector[j].u.cmpobj)
+        if (alpha && vector[j].u.cmpobj)
             decrRefCount(vector[j].u.cmpobj);
     }
     zfree(vector);
             decrRefCount(vector[j].u.cmpobj);
     }
     zfree(vector);
@@ -6838,15 +7315,20 @@ static int deleteIfVolatile(redisDb *db, robj *key) {
     return dictDelete(db->dict,key) == DICT_OK;
 }
 
     return dictDelete(db->dict,key) == DICT_OK;
 }
 
-static void expireGenericCommand(redisClient *c, robj *key, time_t seconds) {
+static void expireGenericCommand(redisClient *c, robj *key, robj *param, long offset) {
     dictEntry *de;
     dictEntry *de;
+    time_t seconds;
+
+    if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return;
+
+    seconds -= offset;
 
     de = dictFind(c->db->dict,key);
     if (de == NULL) {
         addReply(c,shared.czero);
         return;
     }
 
     de = dictFind(c->db->dict,key);
     if (de == NULL) {
         addReply(c,shared.czero);
         return;
     }
-    if (seconds < 0) {
+    if (seconds <= 0) {
         if (deleteKey(c->db,key)) server.dirty++;
         addReply(c, shared.cone);
         return;
         if (deleteKey(c->db,key)) server.dirty++;
         addReply(c, shared.cone);
         return;
@@ -6863,11 +7345,11 @@ static void expireGenericCommand(redisClient *c, robj *key, time_t seconds) {
 }
 
 static void expireCommand(redisClient *c) {
 }
 
 static void expireCommand(redisClient *c) {
-    expireGenericCommand(c,c->argv[1],strtol(c->argv[2]->ptr,NULL,10));
+    expireGenericCommand(c,c->argv[1],c->argv[2],0);
 }
 
 static void expireatCommand(redisClient *c) {
 }
 
 static void expireatCommand(redisClient *c) {
-    expireGenericCommand(c,c->argv[1],strtol(c->argv[2]->ptr,NULL,10)-time(NULL));
+    expireGenericCommand(c,c->argv[1],c->argv[2],time(NULL));
 }
 
 static void ttlCommand(redisClient *c) {
 }
 
 static void ttlCommand(redisClient *c) {
@@ -6939,6 +7421,20 @@ static void discardCommand(redisClient *c) {
     addReply(c,shared.ok);
 }
 
     addReply(c,shared.ok);
 }
 
+/* Send a MULTI command to all the slaves and AOF file. Check the execCommand
+ * implememntation for more information. */
+static void execCommandReplicateMulti(redisClient *c) {
+    struct redisCommand *cmd;
+    robj *multistring = createStringObject("MULTI",5);
+
+    cmd = lookupCommand("multi");
+    if (server.appendonly)
+        feedAppendOnlyFile(cmd,c->db->id,&multistring,1);
+    if (listLength(server.slaves))
+        replicationFeedSlaves(server.slaves,c->db->id,&multistring,1);
+    decrRefCount(multistring);
+}
+
 static void execCommand(redisClient *c) {
     int j;
     robj **orig_argv;
 static void execCommand(redisClient *c) {
     int j;
     robj **orig_argv;
@@ -6949,6 +7445,13 @@ static void execCommand(redisClient *c) {
         return;
     }
 
         return;
     }
 
+    /* Replicate a MULTI request now that we are sure the block is executed.
+     * This way we'll deliver the MULTI/..../EXEC block as a whole and
+     * both the AOF and the replication link will have the same consistency
+     * and atomicity guarantees. */
+    execCommandReplicateMulti(c);
+
+    /* Exec all the queued commands */
     orig_argv = c->argv;
     orig_argc = c->argc;
     addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->mstate.count));
     orig_argv = c->argv;
     orig_argc = c->argc;
     addReplySds(c,sdscatprintf(sdsempty(),"*%d\r\n",c->mstate.count));
@@ -6962,6 +7465,10 @@ static void execCommand(redisClient *c) {
     freeClientMultiState(c);
     initClientMultiState(c);
     c->flags &= (~REDIS_MULTI);
     freeClientMultiState(c);
     initClientMultiState(c);
     c->flags &= (~REDIS_MULTI);
+    /* Make sure the EXEC command is always replicated / AOF, since we
+     * always send the MULTI command (we can't know beforehand if the
+     * next operations will contain at least a modification to the DB). */
+    server.dirty++;
 }
 
 /* =========================== Blocking Operations  ========================= */
 }
 
 /* =========================== Blocking Operations  ========================= */
@@ -7111,7 +7618,7 @@ static void blockingPopGenericCommand(redisClient *c, int where) {
                      * non-blocking POP operation */
                     robj *argv[2], **orig_argv;
                     int orig_argc;
                      * non-blocking POP operation */
                     robj *argv[2], **orig_argv;
                     int orig_argc;
-                   
+
                     /* We need to alter the command arguments before to call
                      * popGenericCommand() as the command takes a single key. */
                     orig_argv = c->argv;
                     /* We need to alter the command arguments before to call
                      * popGenericCommand() as the command takes a single key. */
                     orig_argv = c->argv;
@@ -7348,7 +7855,7 @@ static void updateSlavesWaitingBgsave(int bgsaveerr) {
             slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;
         } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {
             struct redis_stat buf;
             slave->replstate = REDIS_REPL_WAIT_BGSAVE_END;
         } else if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_END) {
             struct redis_stat buf;
-           
+
             if (bgsaveerr != REDIS_OK) {
                 freeClient(slave);
                 redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error");
             if (bgsaveerr != REDIS_OK) {
                 freeClient(slave);
                 redisLog(REDIS_WARNING,"SYNC failed. BGSAVE child returned an error");
@@ -7584,11 +8091,55 @@ static void freeMemoryIfNeeded(void) {
 
 /* ============================== Append Only file ========================== */
 
 
 /* ============================== Append Only file ========================== */
 
+/* Write the append only file buffer on disk.
+ *
+ * Since we are required to write the AOF before replying to the client,
+ * and the only way the client socket can get a write is entering when the
+ * the event loop, we accumulate all the AOF writes in a memory
+ * buffer and write it on disk using this function just before entering
+ * the event loop again. */
+static void flushAppendOnlyFile(void) {
+    time_t now;
+    ssize_t nwritten;
+
+    if (sdslen(server.aofbuf) == 0) return;
+
+    /* We want to perform a single write. This should be guaranteed atomic
+     * at least if the filesystem we are writing is a real physical one.
+     * While this will save us against the server being killed I don't think
+     * there is much to do about the whole server stopping for power problems
+     * or alike */
+     nwritten = write(server.appendfd,server.aofbuf,sdslen(server.aofbuf));
+     if (nwritten != (signed)sdslen(server.aofbuf)) {
+        /* Ooops, we are in troubles. The best thing to do for now is
+         * aborting instead of giving the illusion that everything is
+         * working as expected. */
+         if (nwritten == -1) {
+            redisLog(REDIS_WARNING,"Exiting on error writing to the append-only file: %s",strerror(errno));
+         } else {
+            redisLog(REDIS_WARNING,"Exiting on short write while writing to the append-only file: %s",strerror(errno));
+         }
+         exit(1);
+    }
+    sdsfree(server.aofbuf);
+    server.aofbuf = sdsempty();
+
+    /* Fsync if needed */
+    now = time(NULL);
+    if (server.appendfsync == APPENDFSYNC_ALWAYS ||
+        (server.appendfsync == APPENDFSYNC_EVERYSEC &&
+         now-server.lastfsync > 1))
+    {
+        /* aof_fsync is defined as fdatasync() for Linux in order to avoid
+         * flushing metadata. */
+        aof_fsync(server.appendfd); /* Let's try to get this data on the disk */
+        server.lastfsync = now;
+    }
+}
+
 static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
     sds buf = sdsempty();
     int j;
 static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc) {
     sds buf = sdsempty();
     int j;
-    ssize_t nwritten;
-    time_t now;
     robj *tmpargv[3];
 
     /* The DB this command was targetting is not the same as the last command
     robj *tmpargv[3];
 
     /* The DB this command was targetting is not the same as the last command
@@ -7634,23 +8185,11 @@ static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv
             decrRefCount(argv[j]);
     }
 
             decrRefCount(argv[j]);
     }
 
-    /* We want to perform a single write. This should be guaranteed atomic
-     * at least if the filesystem we are writing is a real physical one.
-     * While this will save us against the server being killed I don't think
-     * there is much to do about the whole server stopping for power problems
-     * or alike */
-     nwritten = write(server.appendfd,buf,sdslen(buf));
-     if (nwritten != (signed)sdslen(buf)) {
-        /* Ooops, we are in troubles. The best thing to do for now is
-         * to simply exit instead to give the illusion that everything is
-         * working as expected. */
-         if (nwritten == -1) {
-            redisLog(REDIS_WARNING,"Exiting on error writing to the append-only file: %s",strerror(errno));
-         } else {
-            redisLog(REDIS_WARNING,"Exiting on short write while writing to the append-only file: %s",strerror(errno));
-         }
-         exit(1);
-    }
+    /* Append to the AOF buffer. This will be flushed on disk just before
+     * of re-entering the event loop, so before the client will get a
+     * positive reply about the operation performed. */
+    server.aofbuf = sdscatlen(server.aofbuf,buf,sdslen(buf));
+
     /* If a background append only file rewriting is in progress we want to
      * accumulate the differences between the child DB and the current one
      * in a buffer, so that when the child process will do its work we
     /* If a background append only file rewriting is in progress we want to
      * accumulate the differences between the child DB and the current one
      * in a buffer, so that when the child process will do its work we
@@ -7659,14 +8198,6 @@ static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv
         server.bgrewritebuf = sdscatlen(server.bgrewritebuf,buf,sdslen(buf));
 
     sdsfree(buf);
         server.bgrewritebuf = sdscatlen(server.bgrewritebuf,buf,sdslen(buf));
 
     sdsfree(buf);
-    now = time(NULL);
-    if (server.appendfsync == APPENDFSYNC_ALWAYS ||
-        (server.appendfsync == APPENDFSYNC_EVERYSEC &&
-         now-server.lastfsync > 1))
-    {
-        fsync(server.appendfd); /* Let's try to get this data on the disk */
-        server.lastfsync = now;
-    }
 }
 
 /* In Redis commands are always executed in the context of a client, so in
 }
 
 /* In Redis commands are always executed in the context of a client, so in
@@ -7686,12 +8217,14 @@ static struct redisClient *createFakeClient(void) {
     c->reply = listCreate();
     listSetFreeMethod(c->reply,decrRefCount);
     listSetDupMethod(c->reply,dupClientReplyValue);
     c->reply = listCreate();
     listSetFreeMethod(c->reply,decrRefCount);
     listSetDupMethod(c->reply,dupClientReplyValue);
+    initClientMultiState(c);
     return c;
 }
 
 static void freeFakeClient(struct redisClient *c) {
     sdsfree(c->querybuf);
     listRelease(c->reply);
     return c;
 }
 
 static void freeFakeClient(struct redisClient *c) {
     sdsfree(c->querybuf);
     listRelease(c->reply);
+    freeClientMultiState(c);
     zfree(c);
 }
 
     zfree(c);
 }
 
@@ -7703,6 +8236,7 @@ int loadAppendOnlyFile(char *filename) {
     FILE *fp = fopen(filename,"r");
     struct redis_stat sb;
     unsigned long long loadedkeys = 0;
     FILE *fp = fopen(filename,"r");
     struct redis_stat sb;
     unsigned long long loadedkeys = 0;
+    int appendonly = server.appendonly;
 
     if (redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0)
         return REDIS_ERR;
 
     if (redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0)
         return REDIS_ERR;
@@ -7712,6 +8246,10 @@ int loadAppendOnlyFile(char *filename) {
         exit(1);
     }
 
         exit(1);
     }
 
+    /* Temporarily disable AOF, to prevent EXEC from feeding a MULTI
+     * to the same file we're about to read. */
+    server.appendonly = 0;
+
     fakeClient = createFakeClient();
     while(1) {
         int argc, j;
     fakeClient = createFakeClient();
     while(1) {
         int argc, j;
@@ -7748,7 +8286,7 @@ int loadAppendOnlyFile(char *filename) {
         }
         /* Try object encoding */
         if (cmd->flags & REDIS_CMD_BULK)
         }
         /* Try object encoding */
         if (cmd->flags & REDIS_CMD_BULK)
-            tryObjectEncoding(argv[argc-1]);
+            argv[argc-1] = tryObjectEncoding(argv[argc-1]);
         /* Run the command in the context of a fake client */
         fakeClient->argc = argc;
         fakeClient->argv = argv;
         /* Run the command in the context of a fake client */
         fakeClient->argc = argc;
         fakeClient->argv = argv;
@@ -7767,8 +8305,14 @@ int loadAppendOnlyFile(char *filename) {
             }
         }
     }
             }
         }
     }
+
+    /* This point can only be reached when EOF is reached without errors.
+     * If the client is in the middle of a MULTI/EXEC, log error and quit. */
+    if (fakeClient->flags & REDIS_MULTI) goto readerr;
+
     fclose(fp);
     freeFakeClient(fakeClient);
     fclose(fp);
     freeFakeClient(fakeClient);
+    server.appendonly = appendonly;
     return REDIS_OK;
 
 readerr:
     return REDIS_OK;
 
 readerr:
@@ -7985,7 +8529,7 @@ static int rewriteAppendOnlyFile(char *filename) {
                     dictReleaseIterator(di);
                 }
             } else {
                     dictReleaseIterator(di);
                 }
             } else {
-                redisAssert(0);
+                redisPanic("Unknown object type");
             }
             /* Save the expire time */
             if (expiretime != -1) {
             }
             /* Save the expire time */
             if (expiretime != -1) {
@@ -8005,7 +8549,7 @@ static int rewriteAppendOnlyFile(char *filename) {
     fflush(fp);
     fsync(fileno(fp));
     fclose(fp);
     fflush(fp);
     fsync(fileno(fp));
     fclose(fp);
-    
+
     /* Use RENAME to make sure the DB file is changed atomically only
      * if the generate DB file is ok. */
     if (rename(tmpfile,filename) == -1) {
     /* Use RENAME to make sure the DB file is changed atomically only
      * if the generate DB file is ok. */
     if (rename(tmpfile,filename) == -1) {
@@ -8118,42 +8662,38 @@ static void aofRemoveTempFile(pid_t childpid) {
 
 /* =================== Virtual Memory - Blocking Side  ====================== */
 
 
 /* =================== Virtual Memory - Blocking Side  ====================== */
 
-/* substitute the first occurrence of '%p' with the process pid in the
- * swap file name. */
-static void expandVmSwapFilename(void) {
-    char *p = strstr(server.vm_swap_file,"%p");
-    sds new;
-    
-    if (!p) return;
-    new = sdsempty();
-    *p = '\0';
-    new = sdscat(new,server.vm_swap_file);
-    new = sdscatprintf(new,"%ld",(long) getpid());
-    new = sdscat(new,p+2);
-    zfree(server.vm_swap_file);
-    server.vm_swap_file = new;
-}
-
 static void vmInit(void) {
     off_t totsize;
     int pipefds[2];
     size_t stacksize;
 static void vmInit(void) {
     off_t totsize;
     int pipefds[2];
     size_t stacksize;
+    struct flock fl;
 
     if (server.vm_max_threads != 0)
         zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */
 
 
     if (server.vm_max_threads != 0)
         zmalloc_enable_thread_safeness(); /* we need thread safe zmalloc() */
 
-    expandVmSwapFilename();
     redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file);
     redisLog(REDIS_NOTICE,"Using '%s' as swap file",server.vm_swap_file);
+    /* Try to open the old swap file, otherwise create it */
     if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) {
         server.vm_fp = fopen(server.vm_swap_file,"w+b");
     }
     if (server.vm_fp == NULL) {
         redisLog(REDIS_WARNING,
     if ((server.vm_fp = fopen(server.vm_swap_file,"r+b")) == NULL) {
         server.vm_fp = fopen(server.vm_swap_file,"w+b");
     }
     if (server.vm_fp == NULL) {
         redisLog(REDIS_WARNING,
-            "Impossible to open the swap file: %s. Exiting.",
+            "Can't open the swap file: %s. Exiting.",
             strerror(errno));
         exit(1);
     }
     server.vm_fd = fileno(server.vm_fp);
             strerror(errno));
         exit(1);
     }
     server.vm_fd = fileno(server.vm_fp);
+    /* Lock the swap file for writing, this is useful in order to avoid
+     * another instance to use the same swap file for a config error. */
+    fl.l_type = F_WRLCK;
+    fl.l_whence = SEEK_SET;
+    fl.l_start = fl.l_len = 0;
+    if (fcntl(server.vm_fd,F_SETLK,&fl) == -1) {
+        redisLog(REDIS_WARNING,
+            "Can't lock the swap file at '%s': %s. Make sure it is not used by another Redis instance.", server.vm_swap_file, strerror(errno));
+        exit(1);
+    }
+    /* Initialize */
     server.vm_next_page = 0;
     server.vm_near_pages = 0;
     server.vm_stats_used_pages = 0;
     server.vm_next_page = 0;
     server.vm_near_pages = 0;
     server.vm_stats_used_pages = 0;
@@ -8248,7 +8788,7 @@ static int vmFreePage(off_t page) {
 }
 
 /* Find N contiguous free pages storing the first page of the cluster in *first.
 }
 
 /* Find N contiguous free pages storing the first page of the cluster in *first.
- * Returns REDIS_OK if it was able to find N contiguous pages, otherwise 
+ * Returns REDIS_OK if it was able to find N contiguous pages, otherwise
  * REDIS_ERR is returned.
  *
  * This function uses a simple algorithm: we try to allocate
  * REDIS_ERR is returned.
  *
  * This function uses a simple algorithm: we try to allocate
@@ -8259,7 +8799,7 @@ static int vmFreePage(off_t page) {
  * we try to find less populated places doing a forward jump of
  * REDIS_VM_MAX_RANDOM_JUMP, then we start scanning again a few pages
  * without hurry, and then we jump again and so forth...
  * we try to find less populated places doing a forward jump of
  * REDIS_VM_MAX_RANDOM_JUMP, then we start scanning again a few pages
  * without hurry, and then we jump again and so forth...
- * 
+ *
  * This function can be improved using a free list to avoid to guess
  * too much, since we could collect data about freed pages.
  *
  * This function can be improved using a free list to avoid to guess
  * too much, since we could collect data about freed pages.
  *
@@ -8913,7 +9453,7 @@ static void *IOThreadEntryPoint(void *arg) {
         listDelNode(server.io_processing,ln);
         listAddNodeTail(server.io_processed,j);
         unlockThreadedIO();
         listDelNode(server.io_processing,ln);
         listAddNodeTail(server.io_processed,j);
         unlockThreadedIO();
-        
+
         /* Signal the main thread there is new stuff to process */
         assert(write(server.io_ready_pipe_write,"x",1) == 1);
     }
         /* Signal the main thread there is new stuff to process */
         assert(write(server.io_ready_pipe_write,"x",1) == 1);
     }
@@ -8991,7 +9531,7 @@ static void queueIOJob(iojob *j) {
 
 static int vmSwapObjectThreaded(robj *key, robj *val, redisDb *db) {
     iojob *j;
 
 static int vmSwapObjectThreaded(robj *key, robj *val, redisDb *db) {
     iojob *j;
-    
+
     assert(key->storage == REDIS_VM_MEMORY);
     assert(key->refcount == 1);
 
     assert(key->storage == REDIS_VM_MEMORY);
     assert(key->refcount == 1);
 
@@ -9035,7 +9575,7 @@ static int waitForSwappedKey(redisClient *c, robj *key) {
         vmCancelThreadedIOJob(o);
         return 0;
     }
         vmCancelThreadedIOJob(o);
         return 0;
     }
-    
+
     /* OK: the key is either swapped, or being loaded just now. */
 
     /* Add the key to the list of keys this client is waiting for.
     /* OK: the key is either swapped, or being loaded just now. */
 
     /* Add the key to the list of keys this client is waiting for.
@@ -9135,7 +9675,7 @@ static int dontWaitForSwappedKey(redisClient *c, robj *key) {
     /* Remove the key from the list of keys this client is waiting for. */
     listRewind(c->io_keys,&li);
     while ((ln = listNext(&li)) != NULL) {
     /* Remove the key from the list of keys this client is waiting for. */
     listRewind(c->io_keys,&li);
     while ((ln = listNext(&li)) != NULL) {
-        if (compareStringObjects(ln->value,key) == 0) {
+        if (equalStringObjects(ln->value,key)) {
             listDelNode(c->io_keys,ln);
             break;
         }
             listDelNode(c->io_keys,ln);
             break;
         }
@@ -9195,6 +9735,50 @@ static void configSetCommand(redisClient *c) {
         server.masterauth = zstrdup(o->ptr);
     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
         server.maxmemory = strtoll(o->ptr, NULL, 10);
         server.masterauth = zstrdup(o->ptr);
     } else if (!strcasecmp(c->argv[2]->ptr,"maxmemory")) {
         server.maxmemory = strtoll(o->ptr, NULL, 10);
+    } else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
+        if (!strcasecmp(o->ptr,"no")) {
+            server.appendfsync = APPENDFSYNC_NO;
+        } else if (!strcasecmp(o->ptr,"everysec")) {
+            server.appendfsync = APPENDFSYNC_EVERYSEC;
+        } else if (!strcasecmp(o->ptr,"always")) {
+            server.appendfsync = APPENDFSYNC_ALWAYS;
+        } else {
+            goto badfmt;
+        }
+    } else if (!strcasecmp(c->argv[2]->ptr,"save")) {
+        int vlen, j;
+        sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
+
+        /* Perform sanity check before setting the new config:
+         * - Even number of args
+         * - Seconds >= 1, changes >= 0 */
+        if (vlen & 1) {
+            sdsfreesplitres(v,vlen);
+            goto badfmt;
+        }
+        for (j = 0; j < vlen; j++) {
+            char *eptr;
+            long val;
+
+            val = strtoll(v[j], &eptr, 10);
+            if (eptr[0] != '\0' ||
+                ((j & 1) == 0 && val < 1) ||
+                ((j & 1) == 1 && val < 0)) {
+                sdsfreesplitres(v,vlen);
+                goto badfmt;
+            }
+        }
+        /* Finally set the new config */
+        resetServerSaveParams();
+        for (j = 0; j < vlen; j += 2) {
+            time_t seconds;
+            int changes;
+
+            seconds = strtoll(v[j],NULL,10);
+            changes = strtoll(v[j+1],NULL,10);
+            appendServerSaveParams(seconds, changes);
+        }
+        sdsfreesplitres(v,vlen);
     } else {
         addReplySds(c,sdscatprintf(sdsempty(),
             "-ERR not supported CONFIG parameter %s\r\n",
     } else {
         addReplySds(c,sdscatprintf(sdsempty(),
             "-ERR not supported CONFIG parameter %s\r\n",
@@ -9204,6 +9788,14 @@ static void configSetCommand(redisClient *c) {
     }
     decrRefCount(o);
     addReply(c,shared.ok);
     }
     decrRefCount(o);
     addReply(c,shared.ok);
+    return;
+
+badfmt: /* Bad format errors */
+    addReplySds(c,sdscatprintf(sdsempty(),
+        "-ERR invalid argument '%s' for CONFIG SET '%s'\r\n",
+            (char*)o->ptr,
+            (char*)c->argv[2]->ptr));
+    decrRefCount(o);
 }
 
 static void configGetCommand(redisClient *c) {
 }
 
 static void configGetCommand(redisClient *c) {
@@ -9238,6 +9830,35 @@ static void configGetCommand(redisClient *c) {
         addReplyBulkCString(c,buf);
         matches++;
     }
         addReplyBulkCString(c,buf);
         matches++;
     }
+    if (stringmatch(pattern,"appendfsync",0)) {
+        char *policy;
+
+        switch(server.appendfsync) {
+        case APPENDFSYNC_NO: policy = "no"; break;
+        case APPENDFSYNC_EVERYSEC: policy = "everysec"; break;
+        case APPENDFSYNC_ALWAYS: policy = "always"; break;
+        default: policy = "unknown"; break; /* too harmless to panic */
+        }
+        addReplyBulkCString(c,"appendfsync");
+        addReplyBulkCString(c,policy);
+        matches++;
+    }
+    if (stringmatch(pattern,"save",0)) {
+        sds buf = sdsempty();
+        int j;
+
+        for (j = 0; j < server.saveparamslen; j++) {
+            buf = sdscatprintf(buf,"%ld %d",
+                    server.saveparams[j].seconds,
+                    server.saveparams[j].changes);
+            if (j != server.saveparamslen-1)
+                buf = sdscatlen(buf," ",1);
+        }
+        addReplyBulkCString(c,"save");
+        addReplyBulkCString(c,buf);
+        sdsfree(buf);
+        matches++;
+    }
     decrRefCount(o);
     lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",matches*2);
 }
     decrRefCount(o);
     lenobj->ptr = sdscatprintf(sdsempty(),"*%d\r\n",matches*2);
 }
@@ -9281,7 +9902,7 @@ static int listMatchPubsubPattern(void *a, void *b) {
     pubsubPattern *pa = a, *pb = b;
 
     return (pa->client == pb->client) &&
     pubsubPattern *pa = a, *pb = b;
 
     return (pa->client == pb->client) &&
-           (compareStringObjects(pa->pattern,pb->pattern) == 0);
+           (equalStringObjects(pa->pattern,pb->pattern));
 }
 
 /* Subscribe a client to a channel. Returns 1 if the operation succeeded, or
 }
 
 /* Subscribe a client to a channel. Returns 1 if the operation succeeded, or
@@ -9472,8 +10093,9 @@ static int pubsubPublishMessage(robj *channel, robj *message) {
                                 sdslen(pat->pattern->ptr),
                                 (char*)channel->ptr,
                                 sdslen(channel->ptr),0)) {
                                 sdslen(pat->pattern->ptr),
                                 (char*)channel->ptr,
                                 sdslen(channel->ptr),0)) {
-                addReply(pat->client,shared.mbulk3);
-                addReply(pat->client,shared.messagebulk);
+                addReply(pat->client,shared.mbulk4);
+                addReply(pat->client,shared.pmessagebulk);
+                addReplyBulk(pat->client,pat->pattern);
                 addReplyBulk(pat->client,channel);
                 addReplyBulk(pat->client,message);
                 receivers++;
                 addReplyBulk(pat->client,channel);
                 addReplyBulk(pat->client,message);
                 receivers++;
@@ -9617,6 +10239,25 @@ static void debugCommand(redisClient *c) {
         } else {
             addReply(c,shared.err);
         }
         } else {
             addReply(c,shared.err);
         }
+    } else if (!strcasecmp(c->argv[1]->ptr,"populate") && c->argc == 3) {
+        long keys, j;
+        robj *key, *val;
+        char buf[128];
+
+        if (getLongFromObjectOrReply(c, c->argv[2], &keys, NULL) != REDIS_OK)
+            return;
+        for (j = 0; j < keys; j++) {
+            snprintf(buf,sizeof(buf),"key:%lu",j);
+            key = createStringObject(buf,strlen(buf));
+            if (lookupKeyRead(c->db,key) != NULL) {
+                decrRefCount(key);
+                continue;
+            }
+            snprintf(buf,sizeof(buf),"value:%lu",j);
+            val = createStringObject(buf,strlen(buf));
+            dictAdd(c->db->dict,key,val);
+        }
+        addReply(c,shared.ok);
     } else {
         addReplySds(c,sdsnew(
             "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]\r\n"));
     } else {
         addReplySds(c,sdsnew(
             "-ERR Syntax error, try DEBUG [SEGFAULT|OBJECT <key>|SWAPIN <key>|SWAPOUT <key>|RELOAD]\r\n"));
@@ -9632,6 +10273,15 @@ static void _redisAssert(char *estr, char *file, int line) {
 #endif
 }
 
 #endif
 }
 
+static void _redisPanic(char *msg, char *file, int line) {
+    redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
+    redisLog(REDIS_WARNING,"Guru Meditation: %s #%s:%d",msg,file,line);
+#ifdef HAVE_BACKTRACE
+    redisLog(REDIS_WARNING,"(forcing SIGSEGV in order to print the stack trace)");
+    *((char*)-1) = 'x';
+#endif
+}
+
 /* =================================== Main! ================================ */
 
 #ifdef __linux__
 /* =================================== Main! ================================ */
 
 #ifdef __linux__
@@ -9651,7 +10301,7 @@ int linuxOvercommitMemoryValue(void) {
 
 void linuxOvercommitMemoryWarning(void) {
     if (linuxOvercommitMemoryValue() == 0) {
 
 void linuxOvercommitMemoryWarning(void) {
     if (linuxOvercommitMemoryValue() == 0) {
-        redisLog(REDIS_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low condition memory. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
+        redisLog(REDIS_WARNING,"WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.");
     }
 }
 #endif /* __linux__ */
     }
 }
 #endif /* __linux__ */
@@ -9748,7 +10398,7 @@ static void *getMcontextEip(ucontext_t *uc) {
     return (void*) uc->uc_mcontext->__ss.__rip;
   #else
     return (void*) uc->uc_mcontext->__ss.__eip;
     return (void*) uc->uc_mcontext->__ss.__rip;
   #else
     return (void*) uc->uc_mcontext->__ss.__eip;
-  #endif 
+  #endif
 #elif defined(__i386__) || defined(__X86_64__) || defined(__x86_64__)
     return (void*) uc->uc_mcontext.gregs[REG_EIP]; /* Linux 32/64 bit */
 #elif defined(__ia64__) /* Linux IA64 */
 #elif defined(__i386__) || defined(__X86_64__) || defined(__x86_64__)
     return (void*) uc->uc_mcontext.gregs[REG_EIP]; /* Linux 32/64 bit */
 #elif defined(__ia64__) /* Linux IA64 */
@@ -9773,7 +10423,7 @@ static void segvHandler(int sig, siginfo_t *info, void *secret) {
     redisLog(REDIS_WARNING, "%s",infostring);
     /* It's not safe to sdsfree() the returned string under memory
      * corruption conditions. Let it leak as we are going to abort */
     redisLog(REDIS_WARNING, "%s",infostring);
     /* It's not safe to sdsfree() the returned string under memory
      * corruption conditions. Let it leak as we are going to abort */
-    
+
     trace_size = backtrace(trace, 100);
     /* overwrite sigaction with caller's address */
     if (getMcontextEip(uc) != NULL) {
     trace_size = backtrace(trace, 100);
     /* overwrite sigaction with caller's address */
     if (getMcontextEip(uc) != NULL) {