]> git.saurik.com Git - redis.git/commitdiff
Merge pull request #426 from anydot/fix-rm-vm-comments
authorSalvatore Sanfilippo <antirez@gmail.com>
Thu, 5 Apr 2012 08:54:09 +0000 (01:54 -0700)
committerSalvatore Sanfilippo <antirez@gmail.com>
Thu, 5 Apr 2012 08:54:09 +0000 (01:54 -0700)
remove mentions of VM in comments

13 files changed:
src/Makefile
src/cluster.c
src/crc64.c [new file with mode: 0644]
src/help.h
src/rdb.c
src/rdb.h
src/redis.c
src/redis.h
src/zmalloc.h
tests/integration/replication.tcl
tests/test_helper.tcl
tests/unit/dump.tcl [new file with mode: 0644]
utils/generate-command-help.rb

index 43922c2de59720d0aaa6a374926611c08c78bad6..57b6d8aeb1b759655a5894c104312c5603fc4b9c 100644 (file)
@@ -73,7 +73,7 @@ QUIET_CC = @printf '    %b %b\n' $(CCCOLOR)CC$(ENDCOLOR) $(SRCCOLOR)$@$(ENDCOLOR
 QUIET_LINK = @printf '    %b %b\n' $(LINKCOLOR)LINK$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR);
 endif
 
-OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o
+OBJ = adlist.o ae.o anet.o dict.o redis.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o
 BENCHOBJ = ae.o anet.o redis-benchmark.o sds.o adlist.o zmalloc.o
 CLIOBJ = anet.o sds.o adlist.o redis-cli.o zmalloc.o release.o
 CHECKDUMPOBJ = redis-check-dump.o lzf_c.o lzf_d.o
index f76e8ff5cf8b1e57d9a1e363ecb8116ac1180bdb..8cd20c84d740c9e1686d5b71f2ab2be55f8fb70d 100644 (file)
@@ -1,4 +1,5 @@
 #include "redis.h"
+#include "endianconv.h"
 
 #include <arpa/inet.h>
 #include <fcntl.h>
@@ -1457,9 +1458,86 @@ void clusterCommand(redisClient *c) {
 }
 
 /* -----------------------------------------------------------------------------
- * RESTORE and MIGRATE commands
+ * DUMP, RESTORE and MIGRATE commands
  * -------------------------------------------------------------------------- */
 
+/* Generates a DUMP-format representation of the object 'o', adding it to the
+ * io stream pointed by 'rio'. This function can't fail. */
+void createDumpPayload(rio *payload, robj *o) {
+    unsigned char buf[2];
+    uint64_t crc;
+
+    /* Serialize the object in a RDB-like format. It consist of an object type
+     * byte followed by the serialized object. This is understood by RESTORE. */
+    rioInitWithBuffer(payload,sdsempty());
+    redisAssert(rdbSaveObjectType(payload,o));
+    redisAssert(rdbSaveObject(payload,o));
+
+    /* Write the footer, this is how it looks like:
+     * ----------------+---------------------+---------------+
+     * ... RDB payload | 2 bytes RDB version | 8 bytes CRC64 |
+     * ----------------+---------------------+---------------+
+     * RDB version and CRC are both in little endian.
+     */
+
+    /* RDB version */
+    buf[0] = REDIS_RDB_VERSION & 0xff;
+    buf[1] = (REDIS_RDB_VERSION >> 8) & 0xff;
+    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,buf,2);
+
+    /* CRC64 */
+    crc = crc64((unsigned char*)payload->io.buffer.ptr,
+                sdslen(payload->io.buffer.ptr));
+    memrev64ifbe(&crc);
+    payload->io.buffer.ptr = sdscatlen(payload->io.buffer.ptr,&crc,8);
+}
+
+/* Verify that the RDB version of the dump payload matches the one of this Redis
+ * instance and that the checksum is ok.
+ * If the DUMP payload looks valid REDIS_OK is returned, otherwise REDIS_ERR
+ * is returned. */
+int verifyDumpPayload(unsigned char *p, size_t len) {
+    unsigned char *footer;
+    uint16_t rdbver;
+    uint64_t crc;
+
+    /* At least 2 bytes of RDB version and 8 of CRC64 should be present. */
+    if (len < 10) return REDIS_ERR;
+    footer = p+(len-10);
+
+    /* Verify RDB version */
+    rdbver = (footer[1] << 8) | footer[0];
+    if (rdbver != REDIS_RDB_VERSION) return REDIS_ERR;
+
+    /* Verify CRC64 */
+    crc = crc64(p,len-8);
+    memrev64ifbe(&crc);
+    return (memcmp(&crc,footer+2,8) == 0) ? REDIS_OK : REDIS_ERR;
+}
+
+/* DUMP keyname
+ * DUMP is actually not used by Redis Cluster but it is the obvious
+ * complement of RESTORE and can be useful for different applications. */
+void dumpCommand(redisClient *c) {
+    robj *o, *dumpobj;
+    rio payload;
+
+    /* Check if the key is here. */
+    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
+        addReply(c,shared.nullbulk);
+        return;
+    }
+
+    /* Create the DUMP encoded representation. */
+    createDumpPayload(&payload,o);
+
+    /* Transfer to the client */
+    dumpobj = createObject(REDIS_STRING,payload.io.buffer.ptr);
+    addReplyBulk(c,dumpobj);
+    decrRefCount(dumpobj);
+    return;
+}
+
 /* RESTORE key ttl serialized-value */
 void restoreCommand(redisClient *c) {
     long ttl;
@@ -1481,6 +1559,12 @@ void restoreCommand(redisClient *c) {
         return;
     }
 
+    /* Verify RDB version and data checksum. */
+    if (verifyDumpPayload(c->argv[3]->ptr,sdslen(c->argv[3]->ptr)) == REDIS_ERR) {
+        addReplyError(c,"DUMP payload version or checksum are wrong");
+        return;
+    }
+
     rioInitWithBuffer(&payload,c->argv[3]->ptr);
     if (((type = rdbLoadObjectType(&payload)) == -1) ||
         ((obj = rdbLoadObject(type,&payload)) == NULL))
@@ -1491,7 +1575,7 @@ void restoreCommand(redisClient *c) {
 
     /* Create the key and set the TTL if any */
     dbAdd(c->db,c->argv[1],obj);
-    if (ttl) setExpire(c->db,c->argv[1],time(NULL)+ttl);
+    if (ttl) setExpire(c->db,c->argv[1],mstime()+ttl);
     signalModifiedKey(c->db,c->argv[1]);
     addReply(c,shared.ok);
     server.dirty++;
@@ -1502,7 +1586,7 @@ void migrateCommand(redisClient *c) {
     int fd;
     long timeout;
     long dbid;
-    time_t ttl;
+    long long ttl, expireat;
     robj *o;
     rio cmd, payload;
 
@@ -1530,28 +1614,32 @@ void migrateCommand(redisClient *c) {
         return;
     }
     if ((aeWait(fd,AE_WRITABLE,timeout*1000) & AE_WRITABLE) == 0) {
-        addReplyError(c,"Timeout connecting to the client");
+        addReplySds(c,sdsnew("-IOERR error or timeout connecting to the client\r\n"));
         return;
     }
 
+    /* Create RESTORE payload and generate the protocol to call the command. */
     rioInitWithBuffer(&cmd,sdsempty());
     redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"SELECT",6));
     redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,dbid));
 
-    ttl = getExpire(c->db,c->argv[3]);
+    expireat = getExpire(c->db,c->argv[3]);
+    if (expireat != -1) {
+        ttl = expireat-mstime();
+        if (ttl < 1) ttl = 1;
+    }
     redisAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',4));
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"RESTORE",7));
     redisAssertWithInfo(c,NULL,c->argv[3]->encoding == REDIS_ENCODING_RAW);
     redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,c->argv[3]->ptr,sdslen(c->argv[3]->ptr)));
-    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,(ttl == -1) ? 0 : ttl));
+    redisAssertWithInfo(c,NULL,rioWriteBulkLongLong(&cmd,(expireat==-1) ? 0 : ttl));
 
     /* Finally the last argument that is the serailized object payload
-     * in the form: <type><rdb-serialized-object>. */
-    rioInitWithBuffer(&payload,sdsempty());
-    redisAssertWithInfo(c,NULL,rdbSaveObjectType(&payload,o));
-    redisAssertWithInfo(c,NULL,rdbSaveObject(&payload,o) != -1);
-    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,payload.io.buffer.ptr,sdslen(payload.io.buffer.ptr)));
+     * in the DUMP format. */
+    createDumpPayload(&payload,o);
+    redisAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,payload.io.buffer.ptr,
+                                sdslen(payload.io.buffer.ptr)));
     sdsfree(payload.io.buffer.ptr);
 
     /* Tranfer the query to the other node in 64K chunks. */
@@ -1562,7 +1650,7 @@ void migrateCommand(redisClient *c) {
 
         while ((towrite = sdslen(buf)-pos) > 0) {
             towrite = (towrite > (64*1024) ? (64*1024) : towrite);
-            nwritten = syncWrite(fd,buf+nwritten,towrite,timeout);
+            nwritten = syncWrite(fd,buf+pos,towrite,timeout);
             if (nwritten != (signed)towrite) goto socket_wr_err;
             pos += nwritten;
         }
@@ -1601,50 +1689,18 @@ void migrateCommand(redisClient *c) {
     return;
 
 socket_wr_err:
-    redisLog(REDIS_NOTICE,"Can't write to target node for MIGRATE: %s",
-        strerror(errno));
-    addReplyErrorFormat(c,"MIGRATE failed, writing to target node: %s.",
-        strerror(errno));
+    addReplySds(c,sdsnew("-IOERR error or timeout writing to target instance\r\n"));
     sdsfree(cmd.io.buffer.ptr);
     close(fd);
     return;
 
 socket_rd_err:
-    redisLog(REDIS_NOTICE,"Can't read from target node for MIGRATE: %s",
-        strerror(errno));
-    addReplyErrorFormat(c,"MIGRATE failed, reading from target node: %s.",
-        strerror(errno));
+    addReplySds(c,sdsnew("-IOERR error or timeout reading from target node\r\n"));
     sdsfree(cmd.io.buffer.ptr);
     close(fd);
     return;
 }
 
-/* DUMP keyname
- * DUMP is actually not used by Redis Cluster but it is the obvious
- * complement of RESTORE and can be useful for different applications. */
-void dumpCommand(redisClient *c) {
-    robj *o, *dumpobj;
-    rio payload;
-
-    /* Check if the key is here. */
-    if ((o = lookupKeyRead(c->db,c->argv[1])) == NULL) {
-        addReply(c,shared.nullbulk);
-        return;
-    }
-
-    /* Serialize the object in a RDB-like format. It consist of an object type
-     * byte followed by the serialized object. This is understood by RESTORE. */
-    rioInitWithBuffer(&payload,sdsempty());
-    redisAssertWithInfo(c,NULL,rdbSaveObjectType(&payload,o));
-    redisAssertWithInfo(c,NULL,rdbSaveObject(&payload,o));
-
-    /* Transfer to the client */
-    dumpobj = createObject(REDIS_STRING,payload.io.buffer.ptr);
-    addReplyBulk(c,dumpobj);
-    decrRefCount(dumpobj);
-    return;
-}
-
 /* The ASKING command is required after a -ASK redirection.
  * The client should issue ASKING before to actualy send the command to
  * the target instance. See the Redis Cluster specification for more
diff --git a/src/crc64.c b/src/crc64.c
new file mode 100644 (file)
index 0000000..f2ea8d4
--- /dev/null
@@ -0,0 +1,192 @@
+/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
+ *
+ * Specification of this CRC64 variant follows:
+ * Name: crc-64-jones
+ * Width: 64 bites
+ * Poly: 0xad93d23594c935a9
+ * Reflected In: True
+ * Xor_In: 0xffffffffffffffff
+ * Reflected_Out: True
+ * Xor_Out: 0x0
+ * Check("123456789"): 0xe9c6d914c4b8d9ca
+ *
+ * Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ *   * Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ *   * Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *   * Neither the name of Redis nor the names of its contributors may be used
+ *     to endorse or promote products derived from this software without
+ *     specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE. */
+
+#include <stdint.h>
+
+static const uint64_t crc64_tab[256] = {
+    UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
+    UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
+    UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
+    UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
+    UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
+    UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
+    UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
+    UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
+    UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
+    UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
+    UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
+    UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
+    UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
+    UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
+    UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
+    UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
+    UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
+    UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
+    UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
+    UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
+    UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
+    UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
+    UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
+    UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
+    UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
+    UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
+    UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
+    UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
+    UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
+    UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
+    UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
+    UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
+    UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
+    UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
+    UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
+    UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
+    UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
+    UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
+    UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
+    UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
+    UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
+    UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
+    UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
+    UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
+    UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
+    UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
+    UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
+    UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
+    UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
+    UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
+    UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
+    UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
+    UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
+    UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
+    UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
+    UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
+    UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
+    UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
+    UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
+    UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
+    UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
+    UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
+    UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
+    UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
+    UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
+    UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
+    UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
+    UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
+    UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
+    UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
+    UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
+    UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
+    UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
+    UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
+    UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
+    UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
+    UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
+    UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
+    UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
+    UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
+    UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
+    UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
+    UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
+    UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
+    UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
+    UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
+    UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
+    UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
+    UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
+    UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
+    UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
+    UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
+    UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
+    UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
+    UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
+    UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
+    UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
+    UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
+    UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
+    UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
+    UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
+    UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
+    UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
+    UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
+    UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
+    UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
+    UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
+    UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
+    UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
+    UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
+    UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
+    UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
+    UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
+    UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
+    UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
+    UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
+    UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
+    UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
+    UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
+    UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
+    UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
+    UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
+    UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
+    UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
+    UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
+    UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
+    UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
+    UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
+};
+
+uint64_t crc64(const unsigned char *s, uint64_t l) {
+    uint64_t crc = 0;
+    uint64_t j;
+
+    for (j = 0; j < l; j++) {
+        uint8_t byte = s[j];
+        crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
+    }
+    return crc;
+}
+
+/* Test main */
+#ifdef TEST_MAIN
+#include <stdio.h>
+int main(void) {
+    printf("e9c6d914c4b8d9ca == %016llx\n",
+        (unsigned long long) crc64((unsigned char*)"123456789",9));
+    return 0;
+}
+#endif
index 51613c9b3b42317144899116a4c079d14ba32e0d..cde95c1f8414d7c1415b9c4cd2b4cb166e8526db 100644 (file)
@@ -1,4 +1,4 @@
-/* Automatically generated by generate-command-help.rb, do not edit. */
+/* Automatically generated by utils/generate-command-help.rb, do not edit. */
 
 #ifndef __REDIS_HELP_H
 #define __REDIS_HELP_H
@@ -13,7 +13,8 @@ static char *commandGroups[] = {
     "pubsub",
     "transactions",
     "connection",
-    "server"
+    "server",
+    "scripting"
 };
 
 struct commandHelp {
@@ -27,612 +28,697 @@ struct commandHelp {
     "key value",
     "Append a value to a key",
     1,
-    "1.3.3" },
+    "2.0.0" },
     { "AUTH",
     "password",
     "Authenticate to the server",
     8,
-    "0.08" },
+    "1.0.0" },
     { "BGREWRITEAOF",
     "-",
     "Asynchronously rewrite the append-only file",
     9,
-    "1.07" },
+    "1.0.0" },
     { "BGSAVE",
     "-",
     "Asynchronously save the dataset to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "BLPOP",
     "key [key ...] timeout",
     "Remove and get the first element in a list, or block until one is available",
     2,
-    "1.3.1" },
+    "2.0.0" },
     { "BRPOP",
     "key [key ...] timeout",
     "Remove and get the last element in a list, or block until one is available",
     2,
-    "1.3.1" },
+    "2.0.0" },
     { "BRPOPLPUSH",
     "source destination timeout",
     "Pop a value from a list, push it to another list and return it; or block until one is available",
     2,
-    "2.1.7" },
+    "2.2.0" },
     { "CONFIG GET",
     "parameter",
     "Get the value of a configuration parameter",
     9,
-    "2.0" },
+    "2.0.0" },
     { "CONFIG RESETSTAT",
     "-",
     "Reset the stats returned by INFO",
     9,
-    "2.0" },
+    "2.0.0" },
     { "CONFIG SET",
     "parameter value",
     "Set a configuration parameter to the given value",
     9,
-    "2.0" },
+    "2.0.0" },
     { "DBSIZE",
     "-",
     "Return the number of keys in the selected database",
     9,
-    "0.07" },
+    "1.0.0" },
     { "DEBUG OBJECT",
     "key",
     "Get debugging information about a key",
     9,
-    "0.101" },
+    "1.0.0" },
     { "DEBUG SEGFAULT",
     "-",
     "Make the server crash",
     9,
-    "0.101" },
+    "1.0.0" },
     { "DECR",
     "key",
     "Decrement the integer value of a key by one",
     1,
-    "0.07" },
+    "1.0.0" },
     { "DECRBY",
     "key decrement",
     "Decrement the integer value of a key by the given number",
     1,
-    "0.07" },
+    "1.0.0" },
     { "DEL",
     "key [key ...]",
     "Delete a key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "DISCARD",
     "-",
     "Discard all commands issued after MULTI",
     7,
-    "1.3.3" },
+    "2.0.0" },
+    { "DUMP",
+    "key",
+    "Return a serialized verison of the value stored at the specified key.",
+    0,
+    "2.6.0" },
     { "ECHO",
     "message",
     "Echo the given string",
     8,
-    "0.07" },
+    "1.0.0" },
+    { "EVAL",
+    "script numkeys key [key ...] arg [arg ...]",
+    "Execute a Lua script server side",
+    10,
+    "2.6.0" },
     { "EXEC",
     "-",
     "Execute all commands issued after MULTI",
     7,
-    "1.1.95" },
+    "1.2.0" },
     { "EXISTS",
     "key",
     "Determine if a key exists",
-    9,
-    "0.07" },
+    0,
+    "1.0.0" },
     { "EXPIRE",
     "key seconds",
     "Set a key's time to live in seconds",
     0,
-    "0.09" },
+    "1.0.0" },
     { "EXPIREAT",
     "key timestamp",
     "Set the expiration for a key as a UNIX timestamp",
     0,
-    "1.1" },
+    "1.2.0" },
     { "FLUSHALL",
     "-",
     "Remove all keys from all databases",
     9,
-    "0.07" },
+    "1.0.0" },
     { "FLUSHDB",
     "-",
     "Remove all keys from the current database",
     9,
-    "0.07" },
+    "1.0.0" },
     { "GET",
     "key",
     "Get the value of a key",
     1,
-    "0.07" },
+    "1.0.0" },
     { "GETBIT",
     "key offset",
     "Returns the bit value at offset in the string value stored at key",
     1,
-    "2.1.8" },
+    "2.2.0" },
+    { "GETRANGE",
+    "key start end",
+    "Get a substring of the string stored at a key",
+    1,
+    "2.4.0" },
     { "GETSET",
     "key value",
     "Set the string value of a key and return its old value",
     1,
-    "0.091" },
+    "1.0.0" },
     { "HDEL",
-    "key field",
-    "Delete a hash field",
+    "key field [field ...]",
+    "Delete one or more hash fields",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HEXISTS",
     "key field",
     "Determine if a hash field exists",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HGET",
     "key field",
     "Get the value of a hash field",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HGETALL",
     "key",
     "Get all the fields and values in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HINCRBY",
     "key field increment",
     "Increment the integer value of a hash field by the given number",
     5,
-    "1.3.10" },
+    "2.0.0" },
+    { "HINCRBYFLOAT",
+    "key field increment",
+    "Increment the float value of a hash field by the given amount",
+    5,
+    "2.6.0" },
     { "HKEYS",
     "key",
     "Get all the fields in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HLEN",
     "key",
     "Get the number of fields in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HMGET",
     "key field [field ...]",
     "Get the values of all the given hash fields",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HMSET",
     "key field value [field value ...]",
     "Set multiple hash fields to multiple values",
     5,
-    "1.3.8" },
+    "2.0.0" },
     { "HSET",
     "key field value",
     "Set the string value of a hash field",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "HSETNX",
     "key field value",
     "Set the value of a hash field, only if the field does not exist",
     5,
-    "1.3.8" },
+    "2.0.0" },
     { "HVALS",
     "key",
     "Get all the values in a hash",
     5,
-    "1.3.10" },
+    "2.0.0" },
     { "INCR",
     "key",
     "Increment the integer value of a key by one",
     1,
-    "0.07" },
+    "1.0.0" },
     { "INCRBY",
     "key increment",
-    "Increment the integer value of a key by the given number",
+    "Increment the integer value of a key by the given amount",
     1,
-    "0.07" },
+    "1.0.0" },
+    { "INCRBYFLOAT",
+    "key increment",
+    "Increment the float value of a key by the given amount",
+    1,
+    "2.6.0" },
     { "INFO",
     "-",
     "Get information and statistics about the server",
     9,
-    "0.07" },
+    "1.0.0" },
     { "KEYS",
     "pattern",
     "Find all keys matching the given pattern",
     0,
-    "0.07" },
+    "1.0.0" },
     { "LASTSAVE",
     "-",
     "Get the UNIX time stamp of the last successful save to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "LINDEX",
     "key index",
     "Get an element from a list by its index",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LINSERT",
     "key BEFORE|AFTER pivot value",
     "Insert an element before or after another element in a list",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "LLEN",
     "key",
     "Get the length of a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPOP",
     "key",
     "Remove and get the first element in a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPUSH",
-    "key value",
-    "Prepend a value to a list",
+    "key value [value ...]",
+    "Prepend one or multiple values to a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LPUSHX",
     "key value",
     "Prepend a value to a list, only if the list exists",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "LRANGE",
     "key start stop",
     "Get a range of elements from a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LREM",
     "key count value",
     "Remove elements from a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LSET",
     "key index value",
     "Set the value of an element in a list by its index",
     2,
-    "0.07" },
+    "1.0.0" },
     { "LTRIM",
     "key start stop",
     "Trim a list to the specified range",
     2,
-    "0.07" },
+    "1.0.0" },
     { "MGET",
     "key [key ...]",
     "Get the values of all the given keys",
     1,
-    "0.07" },
+    "1.0.0" },
+    { "MIGRATE",
+    "host port key destination db timeout",
+    "Atomically transfer a key from a Redis instance to another one.",
+    0,
+    "2.6.0" },
     { "MONITOR",
     "-",
     "Listen for all requests received by the server in real time",
     9,
-    "0.07" },
+    "1.0.0" },
     { "MOVE",
     "key db",
     "Move a key to another database",
     0,
-    "0.07" },
+    "1.0.0" },
     { "MSET",
     "key value [key value ...]",
     "Set multiple keys to multiple values",
     1,
-    "1.001" },
+    "1.0.1" },
     { "MSETNX",
     "key value [key value ...]",
     "Set multiple keys to multiple values, only if none of the keys exist",
     1,
-    "1.001" },
+    "1.0.1" },
     { "MULTI",
     "-",
     "Mark the start of a transaction block",
     7,
-    "1.1.95" },
+    "1.2.0" },
+    { "OBJECT",
+    "subcommand [arguments [arguments ...]]",
+    "Inspect the internals of Redis objects",
+    0,
+    "2.2.3" },
     { "PERSIST",
     "key",
     "Remove the expiration from a key",
     0,
-    "2.1.2" },
+    "2.2.0" },
+    { "PEXPIRE",
+    "key milliseconds",
+    "Set a key's time to live in milliseconds",
+    0,
+    "2.6.0" },
+    { "PEXPIREAT",
+    "key milliseconds timestamp",
+    "Set the expiration for a key as a UNIX timestamp specified in milliseconds",
+    0,
+    "2.6.0" },
     { "PING",
     "-",
     "Ping the server",
     8,
-    "0.07" },
+    "1.0.0" },
+    { "PSETEX",
+    "key milliseconds value",
+    "Set the value and expiration in milliseconds of a key",
+    1,
+    "2.6.0" },
     { "PSUBSCRIBE",
-    "pattern",
+    "pattern [pattern ...]",
     "Listen for messages published to channels matching the given patterns",
     6,
-    "1.3.8" },
+    "2.0.0" },
+    { "PTTL",
+    "key",
+    "Get the time to live for a key in milliseconds",
+    0,
+    "2.6.0" },
     { "PUBLISH",
     "channel message",
     "Post a message to a channel",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "PUNSUBSCRIBE",
     "[pattern [pattern ...]]",
     "Stop listening for messages posted to channels matching the given patterns",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "QUIT",
     "-",
     "Close the connection",
     8,
-    "0.07" },
+    "1.0.0" },
     { "RANDOMKEY",
     "-",
     "Return a random key from the keyspace",
     0,
-    "0.07" },
+    "1.0.0" },
     { "RENAME",
     "key newkey",
     "Rename a key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "RENAMENX",
     "key newkey",
     "Rename a key, only if the new key does not exist",
     0,
-    "0.07" },
+    "1.0.0" },
+    { "RESTORE",
+    "key ttl serialized value",
+    "Create a key using the provided serialized value, previously obtained using DUMP.",
+    0,
+    "2.6.0" },
     { "RPOP",
     "key",
     "Remove and get the last element in a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "RPOPLPUSH",
     "source destination",
     "Remove the last element in a list, append it to another list and return it",
     2,
-    "1.1" },
+    "1.2.0" },
     { "RPUSH",
-    "key value",
-    "Append a value to a list",
+    "key value [value ...]",
+    "Append one or multiple values to a list",
     2,
-    "0.07" },
+    "1.0.0" },
     { "RPUSHX",
     "key value",
     "Append a value to a list, only if the list exists",
     2,
-    "2.1.1" },
+    "2.2.0" },
     { "SADD",
-    "key member",
-    "Add a member to a set",
+    "key member [member ...]",
+    "Add one or more members to a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SAVE",
     "-",
     "Synchronously save the dataset to disk",
     9,
-    "0.07" },
+    "1.0.0" },
     { "SCARD",
     "key",
     "Get the number of members in a set",
     3,
-    "0.07" },
+    "1.0.0" },
+    { "SCRIPT EXISTS",
+    "script [script ...]",
+    "Check existence of scripts in the script cache.",
+    10,
+    "2.6.0" },
+    { "SCRIPT FLUSH",
+    "-",
+    "Remove all the scripts from the script cache.",
+    10,
+    "2.6.0" },
+    { "SCRIPT KILL",
+    "-",
+    "Kill the script currently in execution.",
+    10,
+    "2.6.0" },
+    { "SCRIPT LOAD",
+    "script",
+    "Load the specified Lua script into the script cache.",
+    10,
+    "2.6.0" },
     { "SDIFF",
     "key [key ...]",
     "Subtract multiple sets",
     3,
-    "0.100" },
+    "1.0.0" },
     { "SDIFFSTORE",
     "destination key [key ...]",
     "Subtract multiple sets and store the resulting set in a key",
     3,
-    "0.100" },
+    "1.0.0" },
     { "SELECT",
     "index",
     "Change the selected database for the current connection",
     8,
-    "0.07" },
+    "1.0.0" },
     { "SET",
     "key value",
     "Set the string value of a key",
     1,
-    "0.07" },
+    "1.0.0" },
     { "SETBIT",
     "key offset value",
     "Sets or clears the bit at offset in the string value stored at key",
     1,
-    "2.1.8" },
+    "2.2.0" },
     { "SETEX",
     "key seconds value",
     "Set the value and expiration of a key",
     1,
-    "1.3.10" },
+    "2.0.0" },
     { "SETNX",
     "key value",
     "Set the value of a key, only if the key does not exist",
     1,
-    "0.07" },
+    "1.0.0" },
     { "SETRANGE",
     "key offset value",
     "Overwrite part of a string at key starting at the specified offset",
     1,
-    "2.1.8" },
+    "2.2.0" },
     { "SHUTDOWN",
-    "-",
+    "[NOSAVE] [SAVE]",
     "Synchronously save the dataset to disk and then shut down the server",
     9,
-    "0.07" },
+    "1.0.0" },
     { "SINTER",
     "key [key ...]",
     "Intersect multiple sets",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SINTERSTORE",
     "destination key [key ...]",
     "Intersect multiple sets and store the resulting set in a key",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SISMEMBER",
     "key member",
     "Determine if a given value is a member of a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SLAVEOF",
     "host port",
     "Make the server a slave of another instance, or promote it as master",
     9,
-    "0.100" },
+    "1.0.0" },
+    { "SLOWLOG",
+    "subcommand [argument]",
+    "Manages the Redis slow queries log",
+    9,
+    "2.2.12" },
     { "SMEMBERS",
     "key",
     "Get all the members in a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "SMOVE",
     "source destination member",
     "Move a member from one set to another",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SORT",
     "key [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
     "Sort the elements in a list, set or sorted set",
     0,
-    "0.07" },
+    "1.0.0" },
     { "SPOP",
     "key",
     "Remove and return a random member from a set",
     3,
-    "0.101" },
+    "1.0.0" },
     { "SRANDMEMBER",
     "key",
     "Get a random member from a set",
     3,
-    "1.001" },
+    "1.0.0" },
     { "SREM",
-    "key member",
-    "Remove a member from a set",
+    "key member [member ...]",
+    "Remove one or more members from a set",
     3,
-    "0.07" },
+    "1.0.0" },
     { "STRLEN",
     "key",
     "Get the length of the value stored in a key",
     1,
-    "2.1.2" },
+    "2.2.0" },
     { "SUBSCRIBE",
-    "channel",
+    "channel [channel ...]",
     "Listen for messages published to the given channels",
     6,
-    "1.3.8" },
-    { "SUBSTR",
-    "key start end",
-    "Get a substring of the string stored at a key",
-    1,
-    "1.3.4" },
+    "2.0.0" },
     { "SUNION",
     "key [key ...]",
     "Add multiple sets",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SUNIONSTORE",
     "destination key [key ...]",
     "Add multiple sets and store the resulting set in a key",
     3,
-    "0.091" },
+    "1.0.0" },
     { "SYNC",
     "-",
     "Internal command used for replication",
     9,
-    "0.07" },
+    "1.0.0" },
+    { "TIME",
+    "-",
+    "Return the current server time",
+    9,
+    "2.6.0" },
     { "TTL",
     "key",
     "Get the time to live for a key",
     0,
-    "0.100" },
+    "1.0.0" },
     { "TYPE",
     "key",
     "Determine the type stored at key",
     0,
-    "0.07" },
+    "1.0.0" },
     { "UNSUBSCRIBE",
     "[channel [channel ...]]",
     "Stop listening for messages posted to the given channels",
     6,
-    "1.3.8" },
+    "2.0.0" },
     { "UNWATCH",
     "-",
     "Forget about all watched keys",
     7,
-    "2.1.0" },
+    "2.2.0" },
     { "WATCH",
     "key [key ...]",
     "Watch the given keys to determine execution of the MULTI/EXEC block",
     7,
-    "2.1.0" },
+    "2.2.0" },
     { "ZADD",
-    "key score member",
-    "Add a member to a sorted set, or update its score if it already exists",
+    "key score member [score] [member]",
+    "Add one or more members to a sorted set, or update its score if it already exists",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZCARD",
     "key",
     "Get the number of members in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZCOUNT",
     "key min max",
     "Count the members in a sorted set with scores within the given values",
     4,
-    "1.3.3" },
+    "2.0.0" },
     { "ZINCRBY",
     "key increment member",
     "Increment the score of a member in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZINTERSTORE",
     "destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
     "Intersect multiple sorted sets and store the resulting sorted set in a new key",
     4,
-    "1.3.10" },
+    "2.0.0" },
     { "ZRANGE",
     "key start stop [WITHSCORES]",
     "Return a range of members in a sorted set, by index",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZRANGEBYSCORE",
     "key min max [WITHSCORES] [LIMIT offset count]",
     "Return a range of members in a sorted set, by score",
     4,
-    "1.050" },
+    "1.0.5" },
     { "ZRANK",
     "key member",
     "Determine the index of a member in a sorted set",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZREM",
-    "key member",
-    "Remove a member from a sorted set",
+    "key member [member ...]",
+    "Remove one or more members from a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREMRANGEBYRANK",
     "key start stop",
     "Remove all members in a sorted set within the given indexes",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZREMRANGEBYSCORE",
     "key min max",
     "Remove all members in a sorted set within the given scores",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREVRANGE",
     "key start stop [WITHSCORES]",
     "Return a range of members in a sorted set, by index, with scores ordered from high to low",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZREVRANGEBYSCORE",
     "key max min [WITHSCORES] [LIMIT offset count]",
     "Return a range of members in a sorted set, by score, with scores ordered from high to low",
     4,
-    "2.1.6" },
+    "2.2.0" },
     { "ZREVRANK",
     "key member",
     "Determine the index of a member in a sorted set, with scores ordered from high to low",
     4,
-    "1.3.4" },
+    "2.0.0" },
     { "ZSCORE",
     "key member",
     "Get the score associated with the given member in a sorted set",
     4,
-    "1.1" },
+    "1.2.0" },
     { "ZUNIONSTORE",
     "destination numkeys key [key ...] [WEIGHTS weight] [AGGREGATE SUM|MIN|MAX]",
     "Add multiple sorted sets and store the resulting sorted set in a new key",
     4,
-    "1.3.10" }
+    "2.0.0" }
 };
 
 #endif
index 1e23fa70cf392b399806ce9a7086af65114dd7c2..481efe9de8be2fd6ad73762d66dbd9e2f0c69abc 100644 (file)
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -602,6 +602,7 @@ int rdbSave(char *filename) {
     dictIterator *di = NULL;
     dictEntry *de;
     char tmpfile[256];
+    char magic[10];
     int j;
     long long now = mstime();
     FILE *fp;
@@ -616,7 +617,8 @@ int rdbSave(char *filename) {
     }
 
     rioInitWithFile(&rdb,fp);
-    if (rdbWriteRaw(&rdb,"REDIS0004",9) == -1) goto werr;
+    snprintf(magic,sizeof(magic),"REDIS%04d",REDIS_RDB_VERSION);
+    if (rdbWriteRaw(&rdb,magic,9) == -1) goto werr;
 
     for (j = 0; j < server.dbnum; j++) {
         redisDb *db = server.db+j;
index 45beaa93a739332e7476576bbf90ed966bf94d5f..60157ad8740f969af3327f4f7ef446fca7bb4d11 100644 (file)
--- a/src/rdb.h
+++ b/src/rdb.h
@@ -7,6 +7,10 @@
 /* TBD: include only necessary headers. */
 #include "redis.h"
 
+/* The current RDB version. When the format changes in a way that is no longer
+ * backward compatible this number gets incremented. */
+#define REDIS_RDB_VERSION 4
+
 /* Defines related to the dump file format. To store 32 bits lengths for short
  * keys requires a lot of space, so we check the most significant 2 bits of
  * the first byte to interpreter the length:
index 866076b19e4baa1820ab258cdfbecb2b57953b53..bb97c8d5939c6dad1690459240ba93864a7de9fe 100644 (file)
@@ -48,6 +48,7 @@
 #include <float.h>
 #include <math.h>
 #include <sys/resource.h>
+#include <sys/utsname.h>
 
 /* Our shared "common" objects */
 
@@ -61,6 +62,9 @@ double R_Zero, R_PosInf, R_NegInf, R_Nan;
 
 /*================================= Globals ================================= */
 
+/* Alternate stack for SIGSEGV/etc handlers */
+char altstack[SIGSTKSZ];
+
 /* Global vars */
 struct redisServer server; /* server global state */
 struct redisCommand *commandTable;
@@ -222,7 +226,7 @@ struct redisCommand redisCommandTable[] = {
     {"ttl",ttlCommand,2,"r",0,NULL,1,1,1,0,0},
     {"pttl",pttlCommand,2,"r",0,NULL,1,1,1,0,0},
     {"persist",persistCommand,2,"w",0,NULL,1,1,1,0,0},
-    {"slaveof",slaveofCommand,3,"aws",0,NULL,0,0,0,0,0},
+    {"slaveof",slaveofCommand,3,"as",0,NULL,0,0,0,0,0},
     {"debug",debugCommand,-2,"as",0,NULL,0,0,0,0,0},
     {"config",configCommand,-2,"ar",0,NULL,0,0,0,0,0},
     {"subscribe",subscribeCommand,-2,"rps",0,NULL,0,0,0,0,0},
@@ -253,7 +257,6 @@ struct redisCommand redisCommandTable[] = {
 void redisLogRaw(int level, const char *msg) {
     const int syslogLevelMap[] = { LOG_DEBUG, LOG_INFO, LOG_NOTICE, LOG_WARNING };
     const char *c = ".-*#";
-    time_t now = time(NULL);
     FILE *fp;
     char buf[64];
     int rawmode = (level & REDIS_LOG_RAW);
@@ -267,7 +270,12 @@ void redisLogRaw(int level, const char *msg) {
     if (rawmode) {
         fprintf(fp,"%s",msg);
     } else {
-        strftime(buf,sizeof(buf),"%d %b %H:%M:%S",localtime(&now));
+        int off;
+        struct timeval tv;
+
+        gettimeofday(&tv,NULL);
+        off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec));
+        snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
         fprintf(fp,"[%d] %s %c %s\n",(int)getpid(),buf,c[level],msg);
     }
     fflush(fp);
@@ -1079,7 +1087,7 @@ void initServerConfig() {
     server.repl_syncio_timeout = REDIS_REPL_SYNCIO_TIMEOUT;
     server.repl_serve_stale_data = 1;
     server.repl_slave_ro = 1;
-    server.repl_down_since = -1;
+    server.repl_down_since = time(NULL);
 
     /* Client output buffer limits */
     server.client_obuf_limits[REDIS_CLIENT_LIMIT_CLASS_NORMAL].hard_limit_bytes = 0;
@@ -1142,10 +1150,18 @@ void adjustOpenFilesLimit(void) {
         /* Set the max number of files if the current limit is not enough
          * for our needs. */
         if (oldlimit < maxfiles) {
-            limit.rlim_cur = maxfiles;
-            limit.rlim_max = maxfiles;
-            if (setrlimit(RLIMIT_NOFILE,&limit) == -1) {
-                server.maxclients = oldlimit-32;
+            rlim_t f;
+            
+            f = maxfiles;
+            while(f > oldlimit) {
+                limit.rlim_cur = f;
+                limit.rlim_max = f;
+                if (setrlimit(RLIMIT_NOFILE,&limit) != -1) break;
+                f -= 128;
+            }
+            if (f < oldlimit) f = oldlimit;
+            if (f != maxfiles) {
+                server.maxclients = f-32;
                 redisLog(REDIS_WARNING,"Unable to set the max number of files limit to %d (%s), setting the max clients configuration to %d.",
                     (int) maxfiles, strerror(errno), (int) server.maxclients);
             } else {
@@ -1727,12 +1743,16 @@ sds genRedisInfoString(char *section) {
 
     /* Server */
     if (allsections || defsections || !strcasecmp(section,"server")) {
+        struct utsname name;
+
         if (sections++) info = sdscat(info,"\r\n");
+        uname(&name);
         info = sdscatprintf(info,
             "# Server\r\n"
             "redis_version:%s\r\n"
             "redis_git_sha1:%s\r\n"
             "redis_git_dirty:%d\r\n"
+            "os:%s %s %s\r\n"
             "arch_bits:%d\r\n"
             "multiplexing_api:%s\r\n"
             "gcc_version:%d.%d.%d\r\n"
@@ -1745,6 +1765,7 @@ sds genRedisInfoString(char *section) {
             REDIS_VERSION,
             redisGitSHA1(),
             strtol(redisGitDirty(),NULL,10) > 0,
+            name.sysname, name.release, name.machine,
             server.arch_bits,
             aeGetApiName(),
 #ifdef __GNUC__
@@ -2311,15 +2332,24 @@ static void sigtermHandler(int sig) {
 
 void setupSignalHandlers(void) {
     struct sigaction act;
+    stack_t stack;
+
+    stack.ss_sp = altstack;
+    stack.ss_flags = 0;
+    stack.ss_size = SIGSTKSZ;
+
+    sigaltstack(&stack, NULL);
 
     /* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction is used.
      * Otherwise, sa_handler is used. */
     sigemptyset(&act.sa_mask);
-    act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND;
+    act.sa_flags = 0;
     act.sa_handler = sigtermHandler;
     sigaction(SIGTERM, &act, NULL);
 
 #ifdef HAVE_BACKTRACE
+    /* Use alternate stack so we don't clobber stack in case of segv, or when we run out of stack ..
+     * also resethand & nodefer so we can get interrupted (and killed) if we cause SEGV during SEGV handler */
     sigemptyset(&act.sa_mask);
     act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
     act.sa_sigaction = sigsegvHandler;
index 7502aa1172de7540061467a821d60844302a70bc..b386f16c969677df6355afc1f0d14d1ba53a2b02 100644 (file)
@@ -827,6 +827,7 @@ extern dictType hashDictType;
 long long ustime(void);
 long long mstime(void);
 void getRandomHexChars(char *p, unsigned int len);
+uint64_t crc64(const unsigned char *s, uint64_t l);
 
 /* networking.c -- Networking and Client related operations */
 redisClient *createClient(int fd);
index 8079fb5319a0e568b8d64e0bc44c3c5117e3614b..ff555619e5f811edbdc37413ca1f782616570794 100644 (file)
@@ -38,7 +38,7 @@
 #if defined(USE_TCMALLOC)
 #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR))
 #include <google/tcmalloc.h>
-#if TC_VERSION_MAJOR >= 1 && TC_VERSION_MINOR >= 6
+#if (TC_VERSION_MAJOR == 1 && TC_VERSION_MINOR >= 6) || (TC_VERSION_MAJOR > 1)
 #define HAVE_MALLOC_SIZE 1
 #define zmalloc_size(p) tc_malloc_size(p)
 #else
@@ -49,7 +49,7 @@
 #define ZMALLOC_LIB ("jemalloc-" __xstr(JEMALLOC_VERSION_MAJOR) "." __xstr(JEMALLOC_VERSION_MINOR) "." __xstr(JEMALLOC_VERSION_BUGFIX))
 #define JEMALLOC_MANGLE
 #include <jemalloc/jemalloc.h>
-#if JEMALLOC_VERSION_MAJOR >= 2 && JEMALLOC_VERSION_MINOR >= 1
+#if (JEMALLOC_VERSION_MAJOR == 2 && JEMALLOC_VERSION_MINOR >= 1) || (JEMALLOC_VERSION_MAJOR > 2)
 #define HAVE_MALLOC_SIZE 1
 #define zmalloc_size(p) JEMALLOC_P(malloc_usable_size)(p)
 #else
index 2c7d98deaa09a679a2e8b744ac626b4030d092e2..7c1edb550a2ae0a65095610fca3259e529e41ea0 100644 (file)
@@ -97,7 +97,7 @@ start_server {tags {"repl"}} {
                     [lindex $slaves 2] slaveof $master_host $master_port
 
                     # Wait for all the three slaves to reach the "online" state
-                    set retry 100
+                    set retry 500
                     while {$retry} {
                         set info [r -3 info]
                         if {[string match {*slave0:*,online*slave1:*,online*slave2:*,online*} $info]} {
index 7978d8a4dee28639caaa763ddb30bcc0c35a8c40..34d96606d134eb077f3802f9548cded17e97a430 100644 (file)
@@ -39,6 +39,7 @@ set ::all_tests {
     unit/maxmemory
     unit/introspection
     unit/obuf-limits
+    unit/dump
 }
 # Index to the next test to run in the ::all_tests list.
 set ::next_test 0
diff --git a/tests/unit/dump.tcl b/tests/unit/dump.tcl
new file mode 100644 (file)
index 0000000..b73cde0
--- /dev/null
@@ -0,0 +1,112 @@
+start_server {tags {"dump"}} {
+    test {DUMP / RESTORE are able to serialize / unserialize a simple key} {
+        r set foo bar
+        set encoded [r dump foo]
+        r del foo
+        list [r exists foo] [r restore foo 0 $encoded] [r ttl foo] [r get foo]
+    } {0 OK -1 bar}
+
+    test {RESTORE can set an arbitrary expire to the materialized key} {
+        r set foo bar
+        set encoded [r dump foo]
+        r del foo
+        r restore foo 5000 $encoded
+        set ttl [r pttl foo]
+        assert {$ttl >= 3000 && $ttl <= 5000}
+        r get foo
+    } {bar}
+
+    test {RESTORE returns an error of the key already exists} {
+        r set foo bar
+        set e {}
+        catch {r restore foo 0 "..."} e
+        set e
+    } {*is busy*}
+
+    test {DUMP of non existing key returns nil} {
+        r dump nonexisting_key
+    } {}
+
+    test {MIGRATE is able to migrate a key between two instances} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            set ret [r -1 migrate $second_host $second_port key 9 5000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second get key] eq {Some Value}}
+            assert {[$second ttl key] == -1}
+        }
+    }
+
+    test {MIGRATE propagates TTL correctly} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            $first expire key 10
+            set ret [r -1 migrate $second_host $second_port key 9 5000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second get key] eq {Some Value}}
+            assert {[$second ttl key] >= 7 && [$second ttl key] <= 10}
+        }
+    }
+
+    test {MIGRATE can correctly transfer large values} {
+        set first [srv 0 client]
+        r del key
+        for {set j 0} {$j < 5000} {incr j} {
+            r rpush key 1 2 3 4 5 6 7 8 9 10
+            r rpush key "item 1" "item 2" "item 3" "item 4" "item 5" \
+                        "item 6" "item 7" "item 8" "item 9" "item 10"
+        }
+        assert {[string length [r dump key]] > (1024*64)}
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+            set ret [r -1 migrate $second_host $second_port key 9 10000]
+            assert {$ret eq {OK}}
+            assert {[$first exists key] == 0}
+            assert {[$second exists key] == 1}
+            assert {[$second ttl key] == -1}
+            assert {[$second llen key] == 5000*20}
+        }
+    }
+
+    test {MIGRATE timeout actually works} {
+        set first [srv 0 client]
+        r set key "Some Value"
+        start_server {tags {"repl"}} {
+            set second [srv 0 client]
+            set second_host [srv 0 host]
+            set second_port [srv 0 port]
+
+            assert {[$first exists key] == 1}
+            assert {[$second exists key] == 0}
+
+            set rd [redis_deferring_client]
+            $rd debug sleep 5.0 ; # Make second server unable to reply.
+            set e {}
+            catch {r -1 migrate $second_host $second_port key 9 1000} e
+            assert_match {IOERR*} $e
+        }
+    }
+}
index 96cccc2bfbc1a8e0be5cb4379c48c434068a1fde..f6ca8874b4ebb937d861f39aa5e6426465829569 100755 (executable)
@@ -10,7 +10,8 @@ GROUPS = [
   "pubsub",
   "transactions",
   "connection",
-  "server"
+  "server",
+  "scripting"
 ].freeze
 
 GROUPS_BY_NAME = Hash[*
@@ -48,7 +49,7 @@ def commands
   require "json"
   require "uri"
 
-  url = URI.parse "https://github.com/antirez/redis-doc/raw/master/commands.json"
+  url = URI.parse "https://raw.github.com/antirez/redis-doc/master/commands.json"
   client = Net::HTTP.new url.host, url.port
   client.use_ssl = true
   response = client.get url.path