From: Salvatore Sanfilippo Date: Thu, 5 Apr 2012 08:54:09 +0000 (-0700) Subject: Merge pull request #426 from anydot/fix-rm-vm-comments X-Git-Url: https://git.saurik.com/redis.git/commitdiff_plain/d84f776e873ea93f22dc230e2b10ee70a8a6cd2e?hp=8918de9202669609442c40af7f7625b5685b8a7c Merge pull request #426 from anydot/fix-rm-vm-comments remove mentions of VM in comments --- diff --git a/src/Makefile b/src/Makefile index 43922c2d..57b6d8ae 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 diff --git a/src/cluster.c b/src/cluster.c index f76e8ff5..8cd20c84 100644 --- a/src/cluster.c +++ b/src/cluster.c @@ -1,4 +1,5 @@ #include "redis.h" +#include "endianconv.h" #include #include @@ -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: . */ - 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 index 00000000..f2ea8d4a --- /dev/null +++ b/src/crc64.c @@ -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 + * 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 + +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 +int main(void) { + printf("e9c6d914c4b8d9ca == %016llx\n", + (unsigned long long) crc64((unsigned char*)"123456789",9)); + return 0; +} +#endif diff --git a/src/help.h b/src/help.h index 51613c9b..cde95c1f 100644 --- a/src/help.h +++ b/src/help.h @@ -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 diff --git a/src/rdb.c b/src/rdb.c index 1e23fa70..481efe9d 100644 --- 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; diff --git a/src/rdb.h b/src/rdb.h index 45beaa93..60157ad8 100644 --- 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: diff --git a/src/redis.c b/src/redis.c index 866076b1..bb97c8d5 100644 --- a/src/redis.c +++ b/src/redis.c @@ -48,6 +48,7 @@ #include #include #include +#include /* 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; diff --git a/src/redis.h b/src/redis.h index 7502aa11..b386f16c 100644 --- a/src/redis.h +++ b/src/redis.h @@ -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); diff --git a/src/zmalloc.h b/src/zmalloc.h index 8079fb53..ff555619 100644 --- a/src/zmalloc.h +++ b/src/zmalloc.h @@ -38,7 +38,7 @@ #if defined(USE_TCMALLOC) #define ZMALLOC_LIB ("tcmalloc-" __xstr(TC_VERSION_MAJOR) "." __xstr(TC_VERSION_MINOR)) #include -#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 -#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 diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl index 2c7d98de..7c1edb55 100644 --- a/tests/integration/replication.tcl +++ b/tests/integration/replication.tcl @@ -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]} { diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 7978d8a4..34d96606 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -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 index 00000000..b73cde0c --- /dev/null +++ b/tests/unit/dump.tcl @@ -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 + } + } +} diff --git a/utils/generate-command-help.rb b/utils/generate-command-help.rb index 96cccc2b..f6ca8874 100755 --- a/utils/generate-command-help.rb +++ b/utils/generate-command-help.rb @@ -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