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
#include "redis.h"
+#include "endianconv.h"
#include <arpa/inet.h>
#include <fcntl.h>
}
/* -----------------------------------------------------------------------------
- * 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;
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))
/* 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++;
int fd;
long timeout;
long dbid;
- time_t ttl;
+ long long ttl, expireat;
robj *o;
rio cmd, payload;
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. */
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;
}
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
--- /dev/null
+/* 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
-/* 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
"pubsub",
"transactions",
"connection",
- "server"
+ "server",
+ "scripting"
};
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
dictIterator *di = NULL;
dictEntry *de;
char tmpfile[256];
+ char magic[10];
int j;
long long now = mstime();
FILE *fp;
}
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;
/* 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:
#include <float.h>
#include <math.h>
#include <sys/resource.h>
+#include <sys/utsname.h>
/* Our shared "common" objects */
/*================================= Globals ================================= */
+/* Alternate stack for SIGSEGV/etc handlers */
+char altstack[SIGSTKSZ];
+
/* Global vars */
struct redisServer server; /* server global state */
struct redisCommand *commandTable;
{"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},
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);
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);
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;
/* 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 {
/* 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"
REDIS_VERSION,
redisGitSHA1(),
strtol(redisGitDirty(),NULL,10) > 0,
+ name.sysname, name.release, name.machine,
server.arch_bits,
aeGetApiName(),
#ifdef __GNUC__
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;
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);
#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
#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
[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]} {
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
--- /dev/null
+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
+ }
+ }
+}
"pubsub",
"transactions",
"connection",
- "server"
+ "server",
+ "scripting"
].freeze
GROUPS_BY_NAME = Hash[*
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