]> git.saurik.com Git - redis.git/blobdiff - redis.c
More precise memory used guesswork in zmalloc.c
[redis.git] / redis.c
diff --git a/redis.c b/redis.c
index 63679fc7232a679cc858c98b60d6e68e0acedcea..53d5c215c7ba11488c1eeb110b357f7046f8485b 100644 (file)
--- a/redis.c
+++ b/redis.c
@@ -3058,7 +3058,7 @@ static robj *getDecodedObject(robj *o) {
         dec = createStringObject(buf,strlen(buf));
         return dec;
     } else {
-        redisAssert(1 != 1);
+        redisPanic("Unknown encoding type");
     }
 }
 
@@ -3104,74 +3104,94 @@ static size_t stringObjectLen(robj *o) {
     }
 }
 
-static int getDoubleFromObject(redisClient *c, robj *o, double *value) {
-    double parsedValue;
-    char *eptr = NULL;
+static int getDoubleFromObject(robj *o, double *target) {
+    double value;
+    char *eptr;
 
-    if (o && o->type != REDIS_STRING) {
-        addReplySds(c,sdsnew("-ERR value is not a double\r\n"));
-        return REDIS_ERR;
+    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 {
+            redisAssert(1 != 1);
+        }
     }
 
-    if (o == NULL)
-        parsedValue = 0;
-    else if (o->encoding == REDIS_ENCODING_RAW)
-        parsedValue = strtod(o->ptr, &eptr);
-    else if (o->encoding == REDIS_ENCODING_INT)
-        parsedValue = (long)o->ptr;
-    else
-        redisAssert(1 != 1);
+    *target = value;
+    return REDIS_OK;
+}
 
-    if (eptr != NULL && *eptr != '\0') {
-        addReplySds(c,sdsnew("-ERR value is not a double\r\n"));
+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;
     }
 
-    *value = parsedValue;
-
+    *target = value;
     return REDIS_OK;
 }
 
-static int getLongLongFromObject(redisClient *c, robj *o, long long *value) {
-    long long parsedValue;
-    char *eptr = NULL;
+static int getLongLongFromObject(robj *o, long long *target) {
+    long long value;
+    char *eptr;
 
-    if (o && o->type != REDIS_STRING) {
-        addReplySds(c,sdsnew("-ERR value is not an integer\r\n"));
-        return REDIS_ERR;
+    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 {
+            redisAssert(1 != 1);
+        }
     }
 
-    if (o == NULL)
-        parsedValue = 0;
-    else if (o->encoding == REDIS_ENCODING_RAW)
-        parsedValue = strtoll(o->ptr, &eptr, 10);
-    else if (o->encoding == REDIS_ENCODING_INT)
-        parsedValue = (long)o->ptr;
-    else
-        redisAssert(1 != 1);
+    *target = value;
+    return REDIS_OK;
+}
 
-    if (eptr != NULL && *eptr != '\0') {
-        addReplySds(c,sdsnew("-ERR value is not an integer\r\n"));
+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;
     }
 
-    *value = parsedValue;
-
+    *target = value;
     return REDIS_OK;
 }
 
-static int getLongFromObject(redisClient *c, robj *o, long *value) {
-    long long actualValue;
-
-    if (getLongLongFromObject(c, o, &actualValue) != REDIS_OK) return REDIS_ERR;
+static int getLongFromObjectOrReply(redisClient *c, robj *o, long *target, const char *msg) {
+    long long value;
 
-    if (actualValue < LONG_MIN || actualValue > LONG_MAX) {
-        addReplySds(c,sdsnew("-ERR value is out of range\r\n"));
+    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;
     }
 
-    *value = actualValue;
-
+    *target = value;
     return REDIS_OK;
 }
 
@@ -4061,7 +4081,7 @@ static void incrDecrCommand(redisClient *c, long long incr) {
 
     o = lookupKeyWrite(c->db,c->argv[1]);
 
-    if (getLongLongFromObject(c, o, &value) != REDIS_OK) return;
+    if (getLongLongFromObjectOrReply(c, o, &value, NULL) != REDIS_OK) return;
 
     value += incr;
     o = createObject(REDIS_STRING,sdscatprintf(sdsempty(),"%lld",value));
@@ -4090,16 +4110,14 @@ static void decrCommand(redisClient *c) {
 static void incrbyCommand(redisClient *c) {
     long long incr;
 
-    if (getLongLongFromObject(c, c->argv[2], &incr) != REDIS_OK) return;
-
+    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     incrDecrCommand(c,incr);
 }
 
 static void decrbyCommand(redisClient *c) {
     long long incr;
 
-    if (getLongLongFromObject(c, c->argv[2], &incr) != REDIS_OK) return;
-
+    if (getLongLongFromObjectOrReply(c, c->argv[2], &incr, NULL) != REDIS_OK) return;
     incrDecrCommand(c,-incr);
 }
 
@@ -5497,16 +5515,14 @@ static void zaddGenericCommand(redisClient *c, robj *key, robj *ele, double scor
 static void zaddCommand(redisClient *c) {
     double scoreval;
 
-    if (getDoubleFromObject(c, c->argv[2], &scoreval) != REDIS_OK) return;
-
+    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;
 
-    if (getDoubleFromObject(c, c->argv[2], &scoreval) != REDIS_OK) return;
-
+    if (getDoubleFromObjectOrReply(c, c->argv[2], &scoreval, NULL) != REDIS_OK) return;
     zaddGenericCommand(c,c->argv[1],c->argv[3],scoreval,1);
 }
 
@@ -5546,8 +5562,8 @@ static void zremrangebyscoreCommand(redisClient *c) {
     robj *zsetobj;
     zset *zs;
 
-    if ((getDoubleFromObject(c, c->argv[2], &min) != REDIS_OK) ||
-        (getDoubleFromObject(c, c->argv[3], &max) != REDIS_OK)) return;
+    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;
@@ -5568,8 +5584,8 @@ static void zremrangebyrankCommand(redisClient *c) {
     robj *zsetobj;
     zset *zs;
 
-    if ((getLongFromObject(c, c->argv[2], &start) != REDIS_OK) ||
-        (getLongFromObject(c, c->argv[3], &end) != REDIS_OK)) return;
+    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;
@@ -5677,7 +5693,7 @@ 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 (getDoubleFromObject(c, c->argv[j], &src[i].weight) != REDIS_OK)
+                    if (getDoubleFromObjectOrReply(c, c->argv[j], &src[i].weight, NULL) != REDIS_OK)
                         return;
                 }
             } else if (remaining >= 2 && !strcasecmp(c->argv[j]->ptr,"aggregate")) {
@@ -5810,8 +5826,8 @@ static void zrangeGenericCommand(redisClient *c, int reverse) {
     zskiplistNode *ln;
     robj *ele;
 
-    if ((getLongFromObject(c, c->argv[2], &start) != REDIS_OK) ||
-        (getLongFromObject(c, c->argv[3], &end) != REDIS_OK)) return;
+    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;
@@ -6318,7 +6334,7 @@ static void hincrbyCommand(redisClient *c) {
     long long value, incr;
     robj *o, *current, *new;
 
-    if (getLongLongFromObject(c,c->argv[3],&incr) != REDIS_OK) return;
+    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 (current->encoding == REDIS_ENCODING_RAW)
@@ -6610,9 +6626,8 @@ static int sortCompare(const void *s1, const void *s2) {
                 cmp = strcoll(so1->u.cmpobj->ptr,so2->u.cmpobj->ptr);
             }
         } else {
-            /* Compare elements directly. Note that these objects already
-             * need to be non-encoded (see sortCommand). */
-            cmp = strcoll(so1->obj->ptr,so2->obj->ptr);
+            /* Compare elements directly. */
+            cmp = compareStringObjects(so1->obj,so2->obj);
         }
     }
     return server.sort_desc ? -cmp : cmp;
@@ -6750,7 +6765,7 @@ static void sortCommand(redisClient *c) {
             }
 
             if (alpha) {
-                vector[j].u.cmpobj = getDecodedObject(byval);
+                if (sortby) vector[j].u.cmpobj = getDecodedObject(byval);
             } else {
                 if (byval->encoding == REDIS_ENCODING_RAW) {
                     vector[j].u.score = strtod(byval->ptr,NULL);
@@ -7096,7 +7111,7 @@ static void expireGenericCommand(redisClient *c, robj *key, robj *param, long of
     dictEntry *de;
     time_t seconds;
 
-    if (getLongFromObject(c, param, &seconds) != REDIS_OK) return;
+    if (getLongFromObjectOrReply(c, param, &seconds, NULL) != REDIS_OK) return;
 
     seconds -= offset;
 
@@ -7198,6 +7213,20 @@ static void discardCommand(redisClient *c) {
     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;
@@ -7208,6 +7237,13 @@ static void execCommand(redisClient *c) {
         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));
@@ -7221,6 +7257,10 @@ static void execCommand(redisClient *c) {
     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  ========================= */
@@ -9893,7 +9933,7 @@ static void _redisAssert(char *estr, char *file, int line) {
 
 static void _redisPanic(char *msg, char *file, int line) {
     redisLog(REDIS_WARNING,"!!! Software Failure. Press left mouse button to continue");
-    redisLog(REDIS_WARNING,"Guru Mediation: %s #%s:%d",msg,file,line);
+    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';
@@ -9919,7 +9959,7 @@ int linuxOvercommitMemoryValue(void) {
 
 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__ */