]> git.saurik.com Git - redis.git/blobdiff - redis.c
tool to check if AOF is valid
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 8844acc64621e4da6b0637cbb9bc7246c100479b..05f1c64a4f2bab232315fc81ca25c5ce26c000f0 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -37,8 +37,6 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
-#define __USE_POSIX199309
-#define __USE_UNIX98
 #include <signal.h>
 
 #ifdef HAVE_BACKTRACE
@@ -515,8 +513,9 @@ struct sharedObjectsStruct {
     *outofrangeerr, *plus,
     *select0, *select1, *select2, *select3, *select4,
     *select5, *select6, *select7, *select8, *select9,
-    *messagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
-    *psubscribebulk, *punsubscribebulk, *integers[REDIS_SHARED_INTEGERS];
+    *messagebulk, *pmessagebulk, *subscribebulk, *unsubscribebulk, *mbulk3,
+    *mbulk4, *psubscribebulk, *punsubscribebulk,
+    *integers[REDIS_SHARED_INTEGERS];
 } shared;
 
 /* Global vars that are actally used as constants. The following double
@@ -557,6 +556,7 @@ 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 void replicationFeedMonitors(list *monitors, int dictid, robj **argv, int argc);
 static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int argc);
 static int syncWithMaster(void);
 static robj *tryObjectEncoding(robj *o);
@@ -621,12 +621,14 @@ static void freePubsubPattern(void *p);
 static int listMatchPubsubPattern(void *a, void *b);
 static int compareStringObjects(robj *a, robj *b);
 static void usage();
+static int rewriteAppendOnlyFileBackground(void);
 
 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);
@@ -735,6 +737,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},
+    {"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},
@@ -1513,6 +1516,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");
+            if (server.appendonly) rewriteAppendOnlyFileBackground();
         }
     }
     return 100;
@@ -1588,11 +1592,13 @@ 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.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.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;
@@ -2205,7 +2211,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))
-        replicationFeedSlaves(server.monitors,c->db->id,c->argv,c->argc);
+        replicationFeedMonitors(server.monitors,c->db->id,c->argv,c->argc);
     server.stat_numcommands++;
 }
 
@@ -2460,6 +2466,64 @@ static void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int arg
     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
@@ -3981,40 +4045,55 @@ static void echoCommand(redisClient *c) {
 
 /*=================================== Strings =============================== */
 
-static void setGenericCommand(redisClient *c, int nx) {
+static void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire) {
     int retval;
+    long seconds = 0; /* initialized to avoid an harmness warning */
 
-    if (nx) deleteIfVolatile(c->db,c->argv[1]);
-    retval = dictAdd(c->db->dict,c->argv[1],c->argv[2]);
+    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,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 (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 {
-        incrRefCount(c->argv[1]);
-        incrRefCount(c->argv[2]);
+        incrRefCount(key);
+        incrRefCount(val);
     }
     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) {
-    setGenericCommand(c,0);
+    setGenericCommand(c,0,c->argv[1],c->argv[2],NULL);
 }
 
 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) {
@@ -8009,7 +8088,9 @@ static void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv
         (server.appendfsync == APPENDFSYNC_EVERYSEC &&
          now-server.lastfsync > 1))
     {
-        fsync(server.appendfd); /* Let's try to get this data on the disk */
+        /* 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;
     }
 }
@@ -9817,8 +9898,9 @@ static int pubsubPublishMessage(robj *channel, robj *message) {
                                 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++;