Compare commits

..
65 Commits
Author SHA1 Message Date
antirez ec279203df Redis 2.2.8 2011-05-24 16:34:53 +02:00
antirez 480a2e73cf Comment typo fixed 2011-05-24 10:43:31 +02:00
antirez f447a7ebb4 ZUNIONSTORE new iterator API fix backported into 2.2 2011-05-21 20:34:59 +02:00
antirez 748a2da3e8 Print version info before running the test 2011-05-20 11:44:09 +02:00
antirez b02e81be07 Fixed SINTER[STORE] problem related to the new copy on write safe iterator 2011-05-15 12:15:54 +02:00
antirez 430719ca53 Fixed bug introduced with the copy-on-write friendly iteartor 2011-05-13 12:53:07 +02:00
antirez cc8a0f898b Fix for DEBUG DIGEST, key may expire on lookup 2011-05-13 11:42:02 +02:00
antirez cd128d2882 replication with expire test modified to produce no or less false failures 2011-05-12 20:15:13 +02:00
antirez c717adbc2e regression for zinterstore bug 2011-05-12 20:14:15 +02:00
Pieter Noordhuis 1ffa5d73ed Convert intset encoded set before Z*STORE 2011-05-12 16:18:43 +02:00
antirez a7fa2baf06 Redis 2.2.7 2011-05-11 10:40:24 +02:00
antirez f7aef5241b removed assert causing an illegal memory access. This was responsible of crashes during BLPOP and other list blocking operations. 2011-05-11 09:49:23 +02:00
antirez c86a4f9102 use safe Dict iteration in DEBUG DIGEST 2011-05-10 11:04:30 +02:00
antirez 2e638590ad Introduced a safe iterator interface that can be used to iterate while accessing the dictionary at the same time. Now the default interface is consireded unsafe and should be used only with dictNext() 2011-05-10 11:04:03 +02:00
antirez 3554f09ddc Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-05-07 12:10:47 +02:00
antirez ccab83e729 when Redis fails accepting a new connection reports the error at WARNING and not VERBOSE error level. Thanks to offby1 for proposing this in the Redis mailing list. #backport-candidate 2011-05-07 12:10:39 +02:00
antirez 97ddfbbfc3 Redis version 2.2.6 2011-05-05 15:56:47 +02:00
Pieter Noordhuis 50e50d6a25 Fix ziplist regression and update stresser 2011-05-05 15:17:49 +02:00
antirez 9d665825d9 Fixed a bug with replication where SLAVEOF NO ONE caused a slave to close the connection with its slaves 2011-04-29 14:18:33 +02:00
antirez 19b55f8ebc Redis 2.2.5 2011-04-22 16:25:13 +02:00
Pieter Noordhuis bf3692797d Use correct argc/argv for cleanup when loading AOF 2011-04-22 09:41:07 +02:00
Pieter Noordhuis da06854477 Reformat AOF tests 2011-04-22 09:40:10 +02:00
antirez 38de9362b2 Revert "tests for variadic list push"
This reverts commit 1b905277bb.
Moving all the new stuff in 2.4 branch.
2011-04-15 17:41:32 +02:00
antirez 7600994937 Revert "variadic LPUSH/RPUSH backported to 2.2"
This reverts commit c138dc7da4.
Moving all the new stuff in 2.4 branch.
2011-04-15 17:41:07 +02:00
antirez 6a9764d183 Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-15 17:28:03 +02:00
antirez c138dc7da4 variadic LPUSH/RPUSH backported to 2.2 2011-04-15 17:27:20 +02:00
antirez 1b905277bb tests for variadic list push 2011-04-15 17:15:59 +02:00
antirez 2b886275e9 Redis 2.2.4 2011-04-06 15:39:54 +02:00
antirez 93db956e06 make sure that OBJECT ENCODING returns skiplist for sorted sets, and not raw, so that once we will merge specially encoded sorted sets everything will make sense. 2011-04-06 15:38:08 +02:00
antirez 27fc6199ca CHANGELOG updated 2011-04-06 12:46:39 +02:00
antirez 0b01578d2e Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-04-06 12:23:44 +02:00
antirez 920c45b818 version is now 2.2.3 2011-04-06 12:23:16 +02:00
antirez cf6c3f4b04 OBJECT command implemented 2011-04-06 12:22:09 +02:00
antirez fb90934c47 fixed memory leak introduced with the previous commit. Many thanks to Pieter Noordhuis for spotting it in no time 2011-03-31 19:54:08 +02:00
antirez 207ca3cedd Merge branch '2.2' of github.com:antirez/redis into 2.2 2011-03-31 16:45:27 +02:00
antirez 97d3b7dc8d Fixed issue #503. MONITOR + QUIT could crash the server, there are actually other interactions that could have the same effect (for instance Pub/Sub). 2011-03-31 16:45:05 +02:00
antirez abdbfc14c0 Fixed sdssplitargs() handling of hex-style escapes. 2011-03-22 22:49:33 +01:00
antirez de5d4be994 Merge remote branch 'pietern/2.2-sigs' into 2.2 2011-03-07 12:21:41 +01:00
Pieter Noordhuis 69bfdcf7d4 Show database number in cli prompt when non-zero 2011-03-06 21:10:50 +01:00
Pieter Noordhuis 699465c31e Store SELECTed database for reconnect (issue #468) 2011-03-06 20:53:13 +01:00
Pieter Noordhuis 45332cc47b Minor memory leak in redis-cli (issue #464) 2011-03-06 20:53:12 +01:00
Pieter Noordhuis 2d2c17b075 Save RDB on SIGTERM (see issue #471) 2011-03-06 17:49:22 +01:00
antirez 048c0f0f37 RELEASE NOTES updated 2011-03-04 16:31:20 +01:00
antirez 3eb3cc9da4 version is now 2.2.2 2011-03-04 16:29:38 +01:00
antirez 74f6ee8dd5 Fixed return value of GETRANGE / SUBSTR 2011-03-04 16:23:18 +01:00
antirez 7a55d72f28 AOF file descriptor leak fixed 2011-03-04 16:14:09 +01:00
antirez 4e4b3b943c Redis 2.1.1, release notes updated 2011-02-23 15:48:24 +01:00
Pieter Noordhuis 61e2547675 Restore argc/argv in EXEC after command is executed 2011-02-23 09:39:29 +01:00
antirez 595b5974f8 fixed client-libraries/README to reflect the new site path instead of google code 2011-02-22 18:22:00 +01:00
antirez 58418d7c3e linenoise updated 2011-02-22 17:40:33 +01:00
antirez c02bda324f fixed color support for test suite. Now colors are shown as long as there is xterm somewhere inside the TERM env var 2011-02-22 16:40:24 +01:00
Paul Gideon Dann 1afef16d88 Allow port to be 0 in configuration, in order to allow disabling of TCP. 2011-02-22 12:12:30 +01:00
antirez 7377aacd84 seed the random number generator at startup 2011-02-22 00:01:48 +01:00
antirez eece2d528c suppress a Linux warning, for 2.2 sake 2011-02-21 17:50:54 +01:00
antirez eb5aeaf138 check return value of getcwd() 2011-02-21 17:41:25 +01:00
antirez 1a6281f52f version bumped to 2.2.0 and release notes added 2011-02-17 17:15:52 +01:00
antirez c2571b314a CONFIG SET/GET for all the special encoding parameters of sets, lists, hashes 2011-02-17 12:21:22 +01:00
Pieter Noordhuis 97aeda9828 Morph SPOP to SREM for correct behavior on log replay/replication 2011-02-16 00:24:54 +01:00
antirez ca9d961073 CONFIG GET/SET dir implemented 2011-02-14 02:51:49 +01:00
antirez 2446fbb3bb Merge remote branch 'pietern/2.2-brpoplpush' into 2.2 2011-02-07 15:27:45 +01:00
antirez 970e813be2 Fix for the previous port 0 patch, thanks to Pieter for noticing the error 2011-02-07 13:43:55 +01:00
antirez a36d524562 If port zero is specified, Redis will not listen for TCP connections 2011-02-07 12:52:40 +01:00
Pieter Noordhuis bbaf76ea2f Clarify comment 2011-01-31 16:50:17 +01:00
Pieter Noordhuis 669f302e16 Reply with single null bulk for unsuccesful BRPOPLPUSH 2011-01-31 16:50:00 +01:00
antirez 71791e7a8e propagate key eviction to slaves and AOF, like for expires, so that replicas and AOFs are always consistent even in maxmemory mode. 2011-01-27 16:53:23 +01:00
30 changed files with 537 additions and 134 deletions
+79
View File
@@ -12,6 +12,85 @@ for 2.0.
CHANGELOG
---------
What's new in Redis 2.2.8
=========================
* A new form of dict.c (hash table implementation) iterator that performs less
copy-on-write of pages, introduced in Redis 2.2.7, caused ZINTERSTORE,
ZUNIONSTORE, SINTER, SINTERSTORE commands to behave in the wrong way.
This bug is now fixed.
* Print version info before running the test with 'make test'. This is mainly
useful for the Continuous Integration system we run.
* Fix for DEBUG DIGEST, key may expire on lookup, producing the wrong result.
* Replication with expire test modified to produce no or less false failures.
* Fixed Z*STORE when dealing with intsets, regression test added.
What's new in Redis 2.2.7
=========================
* Fixed bug #543-2 (the issue was reopened with a completely different report)
that caused Redis to randomly crash on list push performed against lists
with other clients blocked with BLPOP (or variants).
What's new in Redis 2.2.6
=========================
* Fixed bug #543. If you saw Redis instances crashing on List operations
(only happening with a non-default max entry size ziplist setting in
redis.conf) it was almost certainly this problem.
* Fixed a bug with replication where SLAVEOF NO ONE caused a slave to close the
connection with all its slaves.
What's new in Redis 2.2.5
=========================
* Fixed a crash occurring when loading an AOF containing the SPOP command.
What's new in Redis 2.2.4
=========================
* Return value of OBJECT DEBUG against sorted sets fixed, now is "skiplist".
What's new in Redis 2.2.3
=========================
* Fixed issue #503. MONITOR + QUIT (and other combinations) could crash
the server.
* OBJECT command implemented. See http://redis.io/commands/object
* Fixed a problem in redis-cli related to escapes in the form "\x..".
* Fixed a minor memory leak in redis-cli
* Saved RDB on SIGTERM on archs where it was not working properly.
What's new in Redis 2.2.2
=========================
Redis 2.2.2 is a bugfix release. Changelog:
* AOF file descriptor leak after the first rewrite fixed.
* Return value of GETRANGE / SUBSTR is now an empty string if the interval
specified is not valid, or the key does not exist. Was a null bulk reply
before, but this is against the Redis way.
What's new in Redis 2.2.1
=========================
Redis 2.2.1 is a bugfix release. Changelog:
* Fixed an SPOP crash. When using SPOP in a MULTI/EXEC block there was a problem
introduced in the latest release when fixing an SPOP replication/AOF related
bug.
What's new in Redis 2.2.0 final
===============================
Redis 2.2.0 final is both a bug fix and minor enhancement release:
* SPOP is now replicated correctly in AOF and across slaves.
* CONFIG SET/GET for all the special encoding parameters of sets, lists, hashes.
* Now BRPOPLPUSH will reply with single null bulk on timeout.
* Specifying port 0 in redis.conf will tell Redis to don't listen on TCP socket.
* Propagate key eviction to slaves and AOF, similarly to expires.
What's new in Redis 2.1.10 (2.2 Release Candidate 4)
====================================================
+1 -1
View File
@@ -1,6 +1,6 @@
An updated list of client libraries for Redis can be found here:
http://code.google.com/p/redis
http://redis.io/clients
All the links are in the front page.
+13 -2
View File
@@ -320,10 +320,9 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
switch(c) {
case 13: /* enter */
case 4: /* ctrl-d */
history_len--;
free(history[history_len]);
return (len == 0 && c == 4) ? -1 : (int)len;
return (int)len;
case 3: /* ctrl-c */
errno = EAGAIN;
return -1;
@@ -337,6 +336,18 @@ static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt)
refreshLine(fd,prompt,buf,len,pos,cols);
}
break;
case 4: /* ctrl-d, remove char at right of cursor */
if (len > 1 && pos < (len-1)) {
memmove(buf+pos,buf+pos+1,len-pos);
len--;
buf[len] = '\0';
refreshLine(fd,prompt,buf,len,pos,cols);
} else if (len == 0) {
history_len--;
free(history[history_len]);
return -1;
}
break;
case 20: /* ctrl-t */
if (pos > 0 && pos < len) {
int aux = buf[pos-1];
+1
View File
@@ -21,6 +21,7 @@ daemonize no
pidfile /var/run/redis.pid
# Accept connections on the specified port, default is 6379.
# If port 0 is specified Redis will not listen on a TCP socket.
port 6379
# If you want you can bind a single interface, if the bind option is not
+8 -4
View File
@@ -220,8 +220,10 @@ int loadAppendOnlyFile(char *filename) {
int appendonly = server.appendonly;
long loops = 0;
if (redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0)
if (fp && redis_fstat(fileno(fp),&sb) != -1 && sb.st_size == 0) {
fclose(fp);
return REDIS_ERR;
}
if (fp == NULL) {
redisLog(REDIS_WARNING,"Fatal error: can't open the append log file for reading: %s",strerror(errno));
@@ -283,9 +285,11 @@ int loadAppendOnlyFile(char *filename) {
/* The fake client should not have a reply */
redisAssert(fakeClient->bufpos == 0 && listLength(fakeClient->reply) == 0);
/* Clean up, ready for the next command */
for (j = 0; j < argc; j++) decrRefCount(argv[j]);
zfree(argv);
/* Clean up. Command code may have changed argv/argc so we use the
* argv/argc of the client instead of the local variables. */
for (j = 0; j < fakeClient->argc; j++)
decrRefCount(fakeClient->argv[j]);
zfree(fakeClient->argv);
/* Handle swapping while loading big datasets when VM is on */
force_swapout = 0;
+57 -1
View File
@@ -66,7 +66,7 @@ void loadServerConfig(char *filename) {
}
} else if (!strcasecmp(argv[0],"port") && argc == 2) {
server.port = atoi(argv[1]);
if (server.port < 1 || server.port > 65535) {
if (server.port < 0 || server.port > 65535) {
err = "Invalid port"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"bind") && argc == 2) {
@@ -426,6 +426,26 @@ void configSetCommand(redisClient *c) {
if (yn == -1) goto badfmt;
server.repl_serve_stale_data = yn;
} else if (!strcasecmp(c->argv[2]->ptr,"dir")) {
if (chdir((char*)o->ptr) == -1) {
addReplyErrorFormat(c,"Changing directory: %s", strerror(errno));
return;
}
} else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.hash_max_zipmap_entries = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"hash-max-zipmap-value")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.hash_max_zipmap_value = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.list_max_ziplist_entries = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"list-max-ziplist-value")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.list_max_ziplist_value = ll;
} else if (!strcasecmp(c->argv[2]->ptr,"set-max-intset-entries")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.set_max_intset_entries = ll;
} else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr);
@@ -448,6 +468,17 @@ void configGetCommand(redisClient *c) {
int matches = 0;
redisAssert(o->encoding == REDIS_ENCODING_RAW);
if (stringmatch(pattern,"dir",0)) {
char buf[1024];
addReplyBulkCString(c,"dir");
if (getcwd(buf,sizeof(buf)) == NULL) {
buf[0] = '\0';
} else {
addReplyBulkCString(c,buf);
}
matches++;
}
if (stringmatch(pattern,"dbfilename",0)) {
addReplyBulkCString(c,"dbfilename");
addReplyBulkCString(c,server.dbfilename);
@@ -541,6 +572,31 @@ void configGetCommand(redisClient *c) {
addReplyBulkCString(c,server.repl_serve_stale_data ? "yes" : "no");
matches++;
}
if (stringmatch(pattern,"hash-max-zipmap-entries",0)) {
addReplyBulkCString(c,"hash-max-zipmap-entries");
addReplyBulkLongLong(c,server.hash_max_zipmap_entries);
matches++;
}
if (stringmatch(pattern,"hash-max-zipmap-value",0)) {
addReplyBulkCString(c,"hash-max-zipmap-value");
addReplyBulkLongLong(c,server.hash_max_zipmap_value);
matches++;
}
if (stringmatch(pattern,"list-max-ziplist-entries",0)) {
addReplyBulkCString(c,"list-max-ziplist-entries");
addReplyBulkLongLong(c,server.list_max_ziplist_entries);
matches++;
}
if (stringmatch(pattern,"list-max-ziplist-value",0)) {
addReplyBulkCString(c,"list-max-ziplist-value");
addReplyBulkLongLong(c,server.list_max_ziplist_value);
matches++;
}
if (stringmatch(pattern,"set-max-intset-entries",0)) {
addReplyBulkCString(c,"set-max-intset-entries");
addReplyBulkLongLong(c,server.set_max_intset_entries);
matches++;
}
setDeferredMultiBulkLength(c,replylen,matches*2);
}
+6 -1
View File
@@ -80,7 +80,7 @@ void computeDatasetDigest(unsigned char *final) {
redisDb *db = server.db+j;
if (dictSize(db->dict) == 0) continue;
di = dictGetIterator(db->dict);
di = dictGetSafeIterator(db->dict);
/* hash the DB id, so the same dataset moved in a different
* DB will lead to a different digest */
@@ -101,6 +101,11 @@ void computeDatasetDigest(unsigned char *final) {
/* Make sure the key is loaded if VM is active */
o = lookupKeyRead(db,keyobj);
if (o == NULL) {
/* Key expired on lookup? Try the next one. */
decrRefCount(keyobj);
continue;
}
aux = htonl(o->type);
mixDigest(digest,&aux,sizeof(aux));
+15 -5
View File
@@ -244,9 +244,9 @@ int dictRehashMilliseconds(dict *d, int ms) {
}
/* This function performs just a step of rehashing, and only if there are
* not iterators bound to our hash table. When we have iterators in the middle
* of a rehashing we can't mess with the two hash tables otherwise some element
* can be missed or duplicated.
* no safe iterators bound to our hash table. When we have iterators in the
* middle of a rehashing we can't mess with the two hash tables otherwise
* some element can be missed or duplicated.
*
* This function is called by common lookup or update operations in the
* dictionary so that the hash table automatically migrates from H1 to H2
@@ -423,17 +423,26 @@ dictIterator *dictGetIterator(dict *d)
iter->d = d;
iter->table = 0;
iter->index = -1;
iter->safe = 0;
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
dictIterator *dictGetSafeIterator(dict *d) {
dictIterator *i = dictGetIterator(d);
i->safe = 1;
return i;
}
dictEntry *dictNext(dictIterator *iter)
{
while (1) {
if (iter->entry == NULL) {
dictht *ht = &iter->d->ht[iter->table];
if (iter->index == -1 && iter->table == 0) iter->d->iterators++;
if (iter->safe && iter->index == -1 && iter->table == 0)
iter->d->iterators++;
iter->index++;
if (iter->index >= (signed) ht->size) {
if (dictIsRehashing(iter->d) && iter->table == 0) {
@@ -460,7 +469,8 @@ dictEntry *dictNext(dictIterator *iter)
void dictReleaseIterator(dictIterator *iter)
{
if (!(iter->index == -1 && iter->table == 0)) iter->d->iterators--;
if (iter->safe && !(iter->index == -1 && iter->table == 0))
iter->d->iterators--;
zfree(iter);
}
+6 -2
View File
@@ -74,10 +74,13 @@ typedef struct dict {
int iterators; /* number of iterators currently running */
} dict;
/* If safe is set to 1 this is a safe iteartor, that means, you can call
* dictAdd, dictFind, and other functions against the dictionary even while
* iterating. Otherwise it is a non safe iterator, and only dictNext()
* should be called while iterating. */
typedef struct dictIterator {
dict *d;
int table;
int index;
int table, index, safe;
dictEntry *entry, *nextEntry;
} dictIterator;
@@ -132,6 +135,7 @@ dictEntry * dictFind(dict *d, const void *key);
void *dictFetchValue(dict *d, const void *key);
int dictResize(dict *d);
dictIterator *dictGetIterator(dict *d);
dictIterator *dictGetSafeIterator(dict *d);
dictEntry *dictNext(dictIterator *iter);
void dictReleaseIterator(dictIterator *iter);
dictEntry *dictGetRandomKey(dict *d);
+4
View File
@@ -110,6 +110,10 @@ void execCommand(redisClient *c) {
c->argc = c->mstate.commands[j].argc;
c->argv = c->mstate.commands[j].argv;
call(c,c->mstate.commands[j].cmd);
/* Commands may alter argc/argv, restore mstate. */
c->mstate.commands[j].argc = c->argc;
c->mstate.commands[j].argv = c->argv;
}
c->argv = orig_argv;
c->argc = orig_argc;
+35 -9
View File
@@ -60,9 +60,6 @@ redisClient *createClient(int fd) {
/* Set the event loop to listen for write events on the client's socket.
* Typically gets called every time a reply is built. */
int _installWriteEvent(redisClient *c) {
/* When CLOSE_AFTER_REPLY is set, no more replies may be added! */
redisAssert(!(c->flags & REDIS_CLOSE_AFTER_REPLY));
if (c->fd <= 0) return REDIS_ERR;
if (c->bufpos == 0 && listLength(c->reply) == 0 &&
(c->replstate == REDIS_REPL_NONE ||
@@ -88,9 +85,15 @@ robj *dupLastObjectIfNeeded(list *reply) {
return listNodeValue(ln);
}
/* -----------------------------------------------------------------------------
* Low level functions to add more data to output buffers.
* -------------------------------------------------------------------------- */
int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
size_t available = sizeof(c->buf)-c->bufpos;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return REDIS_OK;
/* If there already are entries in the reply list, we cannot
* add anything more to the static buffer. */
if (listLength(c->reply) > 0) return REDIS_ERR;
@@ -105,6 +108,9 @@ int _addReplyToBuffer(redisClient *c, char *s, size_t len) {
void _addReplyObjectToList(redisClient *c, robj *o) {
robj *tail;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
if (listLength(c->reply) == 0) {
incrRefCount(o);
listAddNodeTail(c->reply,o);
@@ -128,6 +134,12 @@ void _addReplyObjectToList(redisClient *c, robj *o) {
* needed it will be free'd, otherwise it ends up in a robj. */
void _addReplySdsToList(redisClient *c, sds s) {
robj *tail;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) {
sdsfree(s);
return;
}
if (listLength(c->reply) == 0) {
listAddNodeTail(c->reply,createObject(REDIS_STRING,s));
} else {
@@ -148,6 +160,9 @@ void _addReplySdsToList(redisClient *c, sds s) {
void _addReplyStringToList(redisClient *c, char *s, size_t len) {
robj *tail;
if (c->flags & REDIS_CLOSE_AFTER_REPLY) return;
if (listLength(c->reply) == 0) {
listAddNodeTail(c->reply,createStringObject(s,len));
} else {
@@ -165,6 +180,11 @@ void _addReplyStringToList(redisClient *c, char *s, size_t len) {
}
}
/* -----------------------------------------------------------------------------
* Higher level functions to queue data on the client output buffer.
* The following functions are the ones that commands implementations will call.
* -------------------------------------------------------------------------- */
void addReply(redisClient *c, robj *obj) {
if (_installWriteEvent(c) != REDIS_OK) return;
redisAssert(!server.vm_enabled || obj->storage == REDIS_VM_MEMORY);
@@ -396,7 +416,7 @@ void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
cfd = anetTcpAccept(server.neterr, fd, cip, &cport);
if (cfd == AE_ERR) {
redisLog(REDIS_VERBOSE,"Accepting client connection: %s", server.neterr);
redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
return;
}
redisLog(REDIS_VERBOSE,"Accepted %s:%d", cip, cport);
@@ -411,7 +431,7 @@ void acceptUnixHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
cfd = anetUnixAccept(server.neterr, fd);
if (cfd == AE_ERR) {
redisLog(REDIS_VERBOSE,"Accepting client connection: %s", server.neterr);
redisLog(REDIS_WARNING,"Accepting client connection: %s", server.neterr);
return;
}
redisLog(REDIS_VERBOSE,"Accepted connection to %s", server.unixsocket);
@@ -503,10 +523,16 @@ void freeClient(redisClient *c) {
* close the connection with all our slaves if we have any, so
* when we'll resync with the master the other slaves will sync again
* with us as well. Note that also when the slave is not connected
* to the master it will keep refusing connections by other slaves. */
while (listLength(server.slaves)) {
ln = listFirst(server.slaves);
freeClient((redisClient*)ln->value);
* to the master it will keep refusing connections by other slaves.
*
* We do this only if server.masterhost != NULL. If it is NULL this
* means the user called SLAVEOF NO ONE and we are freeing our
* link with the master, so no need to close link with slaves. */
if (server.masterhost != NULL) {
while (listLength(server.slaves)) {
ln = listFirst(server.slaves);
freeClient((redisClient*)ln->value);
}
}
}
/* Release memory */
+44 -1
View File
@@ -93,10 +93,13 @@ robj *createHashObject(void) {
robj *createZsetObject(void) {
zset *zs = zmalloc(sizeof(*zs));
robj *o;
zs->dict = dictCreate(&zsetDictType,NULL);
zs->zsl = zslCreate();
return createObject(REDIS_ZSET,zs);
o = createObject(REDIS_ZSET,zs);
o->encoding = REDIS_ENCODING_SKIPLIST;
return o;
}
void freeStringObject(robj *o) {
@@ -425,6 +428,7 @@ char *strEncoding(int encoding) {
case REDIS_ENCODING_LINKEDLIST: return "linkedlist";
case REDIS_ENCODING_ZIPLIST: return "ziplist";
case REDIS_ENCODING_INTSET: return "intset";
case REDIS_ENCODING_SKIPLIST: return "skiplist";
default: return "unknown";
}
}
@@ -439,3 +443,42 @@ unsigned long estimateObjectIdleTime(robj *o) {
REDIS_LRU_CLOCK_RESOLUTION;
}
}
/* This is an helper function for the DEBUG command. We need to lookup keys
* without any modification of LRU or other parameters. */
robj *objectCommandLookup(redisClient *c, robj *key) {
dictEntry *de;
if ((de = dictFind(c->db->dict,key->ptr)) == NULL) return NULL;
return (robj*) dictGetEntryVal(de);
}
robj *objectCommandLookupOrReply(redisClient *c, robj *key, robj *reply) {
robj *o = objectCommandLookup(c,key);
if (!o) addReply(c, reply);
return o;
}
/* Object command allows to inspect the internals of an Redis Object.
* Usage: OBJECT <verb> ... arguments ... */
void objectCommand(redisClient *c) {
robj *o;
if (!strcasecmp(c->argv[1]->ptr,"refcount") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyLongLong(c,o->refcount);
} else if (!strcasecmp(c->argv[1]->ptr,"encoding") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyBulkCString(c,strEncoding(o->encoding));
} else if (!strcasecmp(c->argv[1]->ptr,"idletime") && c->argc == 3) {
if ((o = objectCommandLookupOrReply(c,c->argv[2],shared.nullbulk))
== NULL) return;
addReplyLongLong(c,estimateObjectIdleTime(o));
} else {
addReplyError(c,"Syntax error. Try OBJECT (refcount|encoding|idletime)");
}
}
+22 -5
View File
@@ -65,6 +65,7 @@ static struct config {
char *historyfile;
int raw_output; /* output mode per command */
sds mb_delim;
char prompt[32];
} config;
static void usage();
@@ -85,6 +86,13 @@ static long long mstime(void) {
return mst;
}
static void cliRefreshPrompt(void) {
if (config.dbnum == 0)
snprintf(config.prompt,sizeof(config.prompt),"redis> ");
else
snprintf(config.prompt,sizeof(config.prompt),"redis:%d> ",config.dbnum);
}
/*------------------------------------------------------------------------------
* Help functions
*--------------------------------------------------------------------------- */
@@ -264,11 +272,9 @@ static int cliAuth() {
/* Send SELECT dbnum to the server */
static int cliSelect() {
redisReply *reply;
char dbnum[16];
if (config.dbnum == 0) return REDIS_OK;
snprintf(dbnum,sizeof(dbnum),"%d",config.dbnum);
reply = redisCommand(context,"SELECT %s",dbnum);
reply = redisCommand(context,"SELECT %d",config.dbnum);
if (reply != NULL) {
freeReplyObject(reply);
return REDIS_OK;
@@ -489,9 +495,19 @@ static int cliSendCommand(int argc, char **argv, int repeat) {
}
}
if (cliReadReply(output_raw) != REDIS_OK)
if (cliReadReply(output_raw) != REDIS_OK) {
free(argvlen);
return REDIS_ERR;
} else {
/* Store database number when SELECT was successfully executed. */
if (!strcasecmp(command,"select") && argc == 2) {
config.dbnum = atoi(argv[1]);
cliRefreshPrompt();
}
}
}
free(argvlen);
return REDIS_OK;
}
@@ -616,7 +632,8 @@ static void repl() {
config.interactive = 1;
linenoiseSetCompletionCallback(completionCallback);
while((line = linenoise(context ? "redis> " : "not connected> ")) != NULL) {
cliRefreshPrompt();
while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) {
if (line[0] != '\0') {
argv = sdssplitargs(line,&argc);
linenoiseHistoryAdd(line);
+32 -30
View File
@@ -187,7 +187,8 @@ struct redisCommand readonlyCommandTable[] = {
{"punsubscribe",punsubscribeCommand,-1,0,NULL,0,0,0},
{"publish",publishCommand,3,REDIS_CMD_FORCE_REPLICATION,NULL,0,0,0},
{"watch",watchCommand,-2,0,NULL,0,0,0},
{"unwatch",unwatchCommand,1,0,NULL,0,0,0}
{"unwatch",unwatchCommand,1,0,NULL,0,0,0},
{"object",objectCommand,-2,0,NULL,0,0,0}
};
/*============================ Utility functions ============================ */
@@ -837,7 +838,7 @@ void initServer() {
signal(SIGHUP, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
setupSigSegvAction();
setupSignalHandlers();
if (server.syslog_enabled) {
openlog(server.syslog_ident, LOG_PID | LOG_NDELAY | LOG_NOWAIT,
@@ -852,10 +853,13 @@ void initServer() {
createSharedObjects();
server.el = aeCreateEventLoop();
server.db = zmalloc(sizeof(redisDb)*server.dbnum);
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
if (server.ipfd == ANET_ERR) {
redisLog(REDIS_WARNING, "Opening port: %s", server.neterr);
exit(1);
if (server.port != 0) {
server.ipfd = anetTcpServer(server.neterr,server.port,server.bindaddr);
if (server.ipfd == ANET_ERR) {
redisLog(REDIS_WARNING, "Opening port: %s", server.neterr);
exit(1);
}
}
if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
@@ -913,6 +917,7 @@ void initServer() {
}
if (server.vm_enabled) vmInit();
srand(time(NULL)^getpid());
}
/* Populates the Redis Command Table starting from the hard coded list
@@ -1457,6 +1462,7 @@ void freeMemoryIfNeeded(void) {
/* Finally remove the selected key. */
if (bestkey) {
robj *keyobj = createStringObject(bestkey,sdslen(bestkey));
propagateExpire(db,keyobj);
dbDelete(db,keyobj);
server.stat_evictedkeys++;
decrRefCount(keyobj);
@@ -1569,10 +1575,8 @@ int main(int argc, char **argv) {
return 0;
}
/* ============================= Backtrace support ========================= */
#ifdef HAVE_BACKTRACE
void *getMcontextEip(ucontext_t *uc) {
static void *getMcontextEip(ucontext_t *uc) {
#if defined(__FreeBSD__)
return (void*) uc->uc_mcontext.mc_eip;
#elif defined(__dietlibc__)
@@ -1600,7 +1604,7 @@ void *getMcontextEip(ucontext_t *uc) {
#endif
}
void segvHandler(int sig, siginfo_t *info, void *secret) {
static void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
void *trace[100];
char **messages = NULL;
int i, trace_size = 0;
@@ -1639,37 +1643,35 @@ void segvHandler(int sig, siginfo_t *info, void *secret) {
sigaction (sig, &act, NULL);
kill(getpid(),sig);
}
#endif /* HAVE_BACKTRACE */
void sigtermHandler(int sig) {
static void sigtermHandler(int sig) {
REDIS_NOTUSED(sig);
redisLog(REDIS_WARNING,"SIGTERM received, scheduling shutting down...");
redisLog(REDIS_WARNING,"Received SIGTERM, scheduling shutdown...");
server.shutdown_asap = 1;
}
void setupSigSegvAction(void) {
void setupSignalHandlers(void) {
struct sigaction act;
sigemptyset (&act.sa_mask);
/* When the SA_SIGINFO flag is set in sa_flags then sa_sigaction
* is used. Otherwise, sa_handler is used */
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
act.sa_sigaction = segvHandler;
sigaction (SIGSEGV, &act, NULL);
sigaction (SIGBUS, &act, NULL);
sigaction (SIGFPE, &act, NULL);
sigaction (SIGILL, &act, NULL);
sigaction (SIGBUS, &act, 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_handler = sigtermHandler;
sigaction (SIGTERM, &act, NULL);
sigaction(SIGTERM, &act, NULL);
#ifdef HAVE_BACKTRACE
sigemptyset(&act.sa_mask);
act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND | SA_SIGINFO;
act.sa_sigaction = sigsegvHandler;
sigaction(SIGSEGV, &act, NULL);
sigaction(SIGBUS, &act, NULL);
sigaction(SIGFPE, &act, NULL);
sigaction(SIGILL, &act, NULL);
#endif
return;
}
#else /* HAVE_BACKTRACE */
void setupSigSegvAction(void) {
}
#endif /* HAVE_BACKTRACE */
/* The End */
+3 -1
View File
@@ -81,6 +81,7 @@
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */
/* Object types only used for dumping to disk */
#define REDIS_EXPIRETIME 253
@@ -777,7 +778,7 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj);
/* Core functions */
void freeMemoryIfNeeded(void);
int processCommand(redisClient *c);
void setupSigSegvAction(void);
void setupSignalHandlers(void);
struct redisCommand *lookupCommand(sds name);
struct redisCommand *lookupCommandByCString(char *s);
void call(redisClient *c, struct redisCommand *cmd);
@@ -1008,6 +1009,7 @@ void punsubscribeCommand(redisClient *c);
void publishCommand(redisClient *c);
void watchCommand(redisClient *c);
void unwatchCommand(redisClient *c);
void objectCommand(redisClient *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
+3 -1
View File
@@ -547,7 +547,9 @@ void replicationCron(void) {
* connection last interaction time, and at the same time
* we'll be sure that being a single char there are no
* short-write problems. */
write(slave->fd, "\n", 1);
if (write(slave->fd, "\n", 1) == -1) {
/* Don't worry, it's just a ping. */
}
}
}
}
+48 -1
View File
@@ -26,6 +26,12 @@
* 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.
*
* History:
*
* - 22 March 2011: History section created on top of sds.c
* - 22 March 2011: Fixed a problem with "\xab" escapes convertion in
* function sdssplitargs().
*/
#define SDS_ABORT_ON_OOM
@@ -412,6 +418,37 @@ sds sdscatrepr(sds s, char *p, size_t len) {
return sdscatlen(s,"\"",1);
}
/* Helper function for sdssplitargs() that returns non zero if 'c'
* is a valid hex digit. */
int is_hex_digit(char c) {
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') ||
(c >= 'A' && c <= 'F');
}
/* Helper function for sdssplitargs() that converts an hex digit into an
* integer from 0 to 15 */
int hex_digit_to_int(char c) {
switch(c) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
default: return 0;
}
}
/* Split a line into arguments, where every argument can be in the
* following programming-language REPL-alike form:
*
@@ -441,7 +478,17 @@ sds *sdssplitargs(char *line, int *argc) {
if (current == NULL) current = sdsempty();
while(!done) {
if (inq) {
if (*p == '\\' && *(p+1)) {
if (*p == '\\' && *(p+1) == 'x' &&
is_hex_digit(*(p+2)) &&
is_hex_digit(*(p+3)))
{
unsigned char byte;
byte = (hex_digit_to_int(*(p+2))*16)+
hex_digit_to_int(*(p+3));
current = sdscatlen(current,(char*)&byte,1);
p += 3;
} else if (*p == '\\' && *(p+1)) {
char c;
p++;
+2 -3
View File
@@ -817,7 +817,6 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
/* This should remove the first element of the "clients" list. */
unblockClientWaitingData(receiver);
redisAssert(ln != listFirst(clients));
if (dstkey == NULL) {
/* BRPOP/BLPOP */
@@ -826,7 +825,7 @@ int handleClientsWaitingListPush(redisClient *c, robj *key, robj *ele) {
addReplyBulk(receiver,ele);
return 1;
} else {
/* BRPOPLPUSH */
/* BRPOPLPUSH, note that receiver->db is always equal to c->db. */
dstobj = lookupKeyWrite(receiver->db,dstkey);
if (dstobj && checkType(receiver,dstobj,REDIS_LIST)) {
decrRefCount(dstkey);
@@ -941,7 +940,7 @@ void brpoplpushCommand(redisClient *c) {
/* Blocking against an empty list in a multi state
* returns immediately. */
addReply(c, shared.nullmultibulk);
addReply(c, shared.nullbulk);
} else {
/* The list is empty and the client blocks. */
blockForKeys(c, c->argv + 1, 1, timeout, c->argv[2]);
+16 -2
View File
@@ -334,12 +334,25 @@ void spopCommand(redisClient *c) {
encoding = setTypeRandomElement(set,&ele,&llele);
if (encoding == REDIS_ENCODING_INTSET) {
addReplyBulkLongLong(c,llele);
ele = createStringObjectFromLongLong(llele);
set->ptr = intsetRemove(set->ptr,llele,NULL);
} else {
addReplyBulk(c,ele);
incrRefCount(ele);
setTypeRemove(set,ele);
}
/* Change argv to replicate as SREM */
c->argc = 3;
c->argv = zrealloc(c->argv,sizeof(robj*)*(c->argc));
/* Overwrite SREM with SPOP (same length) */
redisAssert(sdslen(c->argv[0]->ptr) == 4);
memcpy(c->argv[0]->ptr, "SREM", 4);
/* Popped element already has incremented refcount */
c->argv[2] = ele;
addReplyBulk(c,ele);
if (setTypeSize(set) == 0) dbDelete(c->db,c->argv[1]);
touchWatchedKey(c->db,c->argv[1]);
server.dirty++;
@@ -420,6 +433,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
si = setTypeInitIterator(sets[0]);
while((encoding = setTypeNext(si,&eleobj,&intobj)) != -1) {
for (j = 1; j < setnum; j++) {
if (sets[j] == sets[0]) continue;
if (encoding == REDIS_ENCODING_INTSET) {
/* intset with intset is simple... and fast */
if (sets[j]->encoding == REDIS_ENCODING_INTSET &&
+2 -2
View File
@@ -259,7 +259,7 @@ void getrangeCommand(redisClient *c) {
return;
if (getLongFromObjectOrReply(c,c->argv[3],&end,NULL) != REDIS_OK)
return;
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.nullbulk)) == NULL ||
if ((o = lookupKeyReadOrReply(c,c->argv[1],shared.emptybulk)) == NULL ||
checkType(c,o,REDIS_STRING)) return;
if (o->encoding == REDIS_ENCODING_INT) {
@@ -280,7 +280,7 @@ void getrangeCommand(redisClient *c) {
/* Precondition: end >= 0 && end < strlen, so the only condition where
* nothing can be returned is: start > end. */
if (start > end) {
addReply(c,shared.nullbulk);
addReply(c,shared.emptybulk);
} else {
addReplyBulkCBuffer(c,(char*)str+start,end-start+1);
}
+28 -4
View File
@@ -615,6 +615,10 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
if (obj->type == REDIS_ZSET) {
src[i].dict = ((zset*)obj->ptr)->dict;
} else if (obj->type == REDIS_SET) {
if (obj->encoding == REDIS_ENCODING_INTSET)
setTypeConvert(obj, REDIS_ENCODING_HT);
redisAssert(obj->encoding == REDIS_ENCODING_HT);
src[i].dict = (obj->ptr);
} else {
zfree(src);
@@ -682,7 +686,18 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
score = src[0].weight * zunionInterDictValue(de);
for (j = 1; j < setnum; j++) {
dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de));
dictEntry *other;
/* If it's the same dictionary don't lookup as we are not
* in the context of a safe iterator. It's the same
* dictionary so we are sure the element is inside.
* This happens on SINTERSTORE dest 2 mykey mykey. */
if (src[j].dict == src[0].dict) {
other = de;
} else {
other = dictFind(src[j].dict,dictGetEntryKey(de));
}
if (other) {
value = src[j].weight * zunionInterDictValue(other);
zunionInterAggregate(&score,value,aggregate);
@@ -720,10 +735,19 @@ void zunionInterGenericCommand(redisClient *c, robj *dstkey, int op) {
/* because the zsets are sorted by size, its only possible
* for sets at larger indices to hold this entry */
for (j = (i+1); j < setnum; j++) {
dictEntry *other = dictFind(src[j].dict,dictGetEntryKey(de));
if (other) {
value = src[j].weight * zunionInterDictValue(other);
/* It is not safe to access the zset we are
* iterating, so explicitly check for equal object. */
if (src[j].dict == src[i].dict) {
value = src[i].weight * zunionInterDictValue(de);
zunionInterAggregate(&score,value,aggregate);
} else {
dictEntry *other;
other = dictFind(src[j].dict,dictGetEntryKey(de));
if (other) {
value = src[j].weight * zunionInterDictValue(other);
zunionInterAggregate(&score,value,aggregate);
}
}
}
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "2.1.12"
#define REDIS_VERSION "2.2.8"
+34 -31
View File
@@ -398,12 +398,17 @@ static unsigned char *__ziplistCascadeUpdate(unsigned char *zl, unsigned char *p
offset = p-zl;
extra = rawlensize-next.prevrawlensize;
zl = ziplistResize(zl,curlen+extra);
ZIPLIST_TAIL_OFFSET(zl) += extra;
p = zl+offset;
/* Move the tail to the back. */
/* Current pointer and offset for next element. */
np = p+rawlen;
noffset = np-zl;
/* Update tail offset when next element is not the tail element. */
if ((zl+ZIPLIST_TAIL_OFFSET(zl)) != np)
ZIPLIST_TAIL_OFFSET(zl) += extra;
/* Move the tail to the back. */
memmove(np+rawlensize,
np+next.prevrawlensize,
curlen-noffset-next.prevrawlensize-1);
@@ -877,7 +882,7 @@ void pop(unsigned char *zl, int where) {
}
}
void randstring(char *target, unsigned int min, unsigned int max) {
int randstring(char *target, unsigned int min, unsigned int max) {
int p, len = min+rand()%(max-min+1);
int minval, maxval;
switch(rand() % 3) {
@@ -899,10 +904,9 @@ void randstring(char *target, unsigned int min, unsigned int max) {
while(p < len)
target[p++] = minval+rand()%(maxval-minval+1);
return;
return len;
}
int main(int argc, char **argv) {
unsigned char *zl, *p;
unsigned char *entry;
@@ -1235,6 +1239,7 @@ int main(int argc, char **argv) {
int i,j,len,where;
unsigned char *p;
char buf[1024];
int buflen;
list *ref;
listNode *refnode;
@@ -1243,10 +1248,6 @@ int main(int argc, char **argv) {
unsigned int slen;
long long sval;
/* In the regression for the cascade bug, it was triggered
* with a random seed of 2. */
srand(2);
for (i = 0; i < 20000; i++) {
zl = ziplistNew();
ref = listCreate();
@@ -1256,31 +1257,32 @@ int main(int argc, char **argv) {
/* Create lists */
for (j = 0; j < len; j++) {
where = (rand() & 1) ? ZIPLIST_HEAD : ZIPLIST_TAIL;
switch(rand() % 4) {
case 0:
sprintf(buf,"%lld",(0LL + rand()) >> 20);
break;
case 1:
sprintf(buf,"%lld",(0LL + rand()));
break;
case 2:
sprintf(buf,"%lld",(0LL + rand()) << 20);
break;
case 3:
randstring(buf,0,256);
break;
default:
assert(NULL);
if (rand() % 2) {
buflen = randstring(buf,1,sizeof(buf)-1);
} else {
switch(rand() % 3) {
case 0:
buflen = sprintf(buf,"%lld",(0LL + rand()) >> 20);
break;
case 1:
buflen = sprintf(buf,"%lld",(0LL + rand()));
break;
case 2:
buflen = sprintf(buf,"%lld",(0LL + rand()) << 20);
break;
default:
assert(NULL);
}
}
/* Add to ziplist */
zl = ziplistPush(zl, (unsigned char*)buf, strlen(buf), where);
zl = ziplistPush(zl, (unsigned char*)buf, buflen, where);
/* Add to reference list */
if (where == ZIPLIST_HEAD) {
listAddNodeHead(ref,sdsnew(buf));
listAddNodeHead(ref,sdsnewlen(buf, buflen));
} else if (where == ZIPLIST_TAIL) {
listAddNodeTail(ref,sdsnew(buf));
listAddNodeTail(ref,sdsnewlen(buf, buflen));
} else {
assert(NULL);
}
@@ -1295,12 +1297,13 @@ int main(int argc, char **argv) {
assert(ziplistGet(p,&sstr,&slen,&sval));
if (sstr == NULL) {
sprintf(buf,"%lld",sval);
buflen = sprintf(buf,"%lld",sval);
} else {
memcpy(buf,sstr,slen);
buf[slen] = '\0';
buflen = slen;
memcpy(buf,sstr,buflen);
buf[buflen] = '\0';
}
assert(strcmp(buf,listNodeValue(refnode)) == 0);
assert(memcmp(buf,listNodeValue(refnode),buflen) == 0);
}
zfree(zl);
listRelease(ref);
+47 -25
View File
@@ -31,13 +31,14 @@ tags {"aof"} {
}
start_server_aof [list dir $server_path] {
test {Unfinished MULTI: Server should not have been started} {
is_alive $srv
} {0}
test "Unfinished MULTI: Server should not have been started" {
assert_equal 0 [is_alive $srv]
}
test {Unfinished MULTI: Server should have logged an error} {
exec cat [dict get $srv stdout] | tail -n1
} {*Unexpected end of file reading the append only file*}
test "Unfinished MULTI: Server should have logged an error" {
set result [exec cat [dict get $srv stdout] | tail -n1]
assert_match "*Unexpected end of file reading the append only file*" $result
}
}
## Test that the server exits when the AOF contains a short read
@@ -47,36 +48,57 @@ tags {"aof"} {
}
start_server_aof [list dir $server_path] {
test {Short read: Server should not have been started} {
is_alive $srv
} {0}
test "Short read: Server should not have been started" {
assert_equal 0 [is_alive $srv]
}
test {Short read: Server should have logged an error} {
exec cat [dict get $srv stdout] | tail -n1
} {*Bad file format reading the append only file*}
test "Short read: Server should have logged an error" {
set result [exec cat [dict get $srv stdout] | tail -n1]
assert_match "*Bad file format reading the append only file*" $result
}
}
## Test that redis-check-aof indeed sees this AOF is not valid
test {Short read: Utility should confirm the AOF is not valid} {
test "Short read: Utility should confirm the AOF is not valid" {
catch {
exec src/redis-check-aof $aof_path
} str
set _ $str
} {*not valid*}
} result
assert_match "*not valid*" $result
}
test {Short read: Utility should be able to fix the AOF} {
exec echo y | src/redis-check-aof --fix $aof_path
} {*Successfully truncated AOF*}
test "Short read: Utility should be able to fix the AOF" {
set result [exec echo y | src/redis-check-aof --fix $aof_path]
assert_match "*Successfully truncated AOF*" $result
}
## Test that the server can be started using the truncated AOF
start_server_aof [list dir $server_path] {
test {Fixed AOF: Server should have been started} {
is_alive $srv
} {1}
test "Fixed AOF: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
test {Fixed AOF: Keyspace should contain values that were parsable} {
test "Fixed AOF: Keyspace should contain values that were parsable" {
set client [redis [dict get $srv host] [dict get $srv port]]
list [$client get foo] [$client get bar]
} {hello {}}
assert_equal "hello" [$client get foo]
assert_equal "" [$client get bar]
}
}
## Test that SPOP (that modifies the client its argc/argv) is correctly free'd
create_aof {
append_to_aof [formatCommand sadd set foo]
append_to_aof [formatCommand sadd set bar]
append_to_aof [formatCommand spop set]
}
start_server_aof [list dir $server_path] {
test "AOF+SPOP: Server should have been started" {
assert_equal 1 [is_alive $srv]
}
test "AOF+SPOP: Set should have 1 member" {
set client [redis [dict get $srv host] [dict get $srv port]]
assert_equal 1 [$client scard set]
}
}
}
+2
View File
@@ -27,6 +27,8 @@ start_server {tags {"repl"}} {
test {MASTER and SLAVE consistency with expire} {
createComplexDataset r 50000 useexpire
after 4000 ;# Make sure everything expired before taking the digest
r keys * ;# Force DEL syntesizing to slave
after 1000 ;# Wait another second. Now everything should be fine.
if {[r debug digest] ne [r -1 debug digest]} {
set csv1 [csvdump r]
set csv2 [csvdump {r -1}]
+7 -2
View File
@@ -44,9 +44,14 @@ proc assert_type {type key} {
assert_equal $type [r type $key]
}
# Test if TERM looks like to support colors
proc color_term {} {
expr {[info exists ::env(TERM)] && [string match *xterm* $::env(TERM)]}
}
# This is called before starting the test
proc announce_test {s} {
if {[info exists ::env(TERM)] && [string match $::env(TERM) xterm]} {
if {[color_term]} {
puts -nonewline "$s\033\[0K"
flush stdout
set ::backward_count [string length $s]
@@ -55,7 +60,7 @@ proc announce_test {s} {
# This is called after the test finished
proc colored_dot {tags passed} {
if {[info exists ::env(TERM)] && [string match $::env(TERM) xterm]} {
if {[color_term]} {
# Go backward and delete what announc_test function printed.
puts -nonewline "\033\[${::backward_count}D\033\[0K\033\[J"
+1
View File
@@ -108,6 +108,7 @@ proc cleanup {} {
}
proc execute_everything {} {
execute_tests "unit/printver"
execute_tests "unit/auth"
execute_tests "unit/protocol"
execute_tests "unit/basic"
+7
View File
@@ -174,6 +174,13 @@ start_server {tags {"other"}} {
set _ $err
} {*ERR MULTI*}
test {MULTI where commands alter argc/argv} {
r sadd myset a
r multi
r spop myset
list [r exec] [r exists myset]
} {a 0}
test {WATCH inside MULTI is not allowed} {
set err {}
r multi
+6
View File
@@ -0,0 +1,6 @@
start_server {} {
set i [r info]
regexp {redis_version:(.*?)\r\n} $i - version
regexp {redis_git_sha1:(.*?)\r\n} $i - sha1
puts "Testing Redis version $version ($sha1)"
}
+7
View File
@@ -484,6 +484,13 @@ start_server {tags {"zset"}} {
test {ZINTERSTORE with AGGREGATE MAX} {
list [r zinterstore zsetc 2 zseta zsetb aggregate max] [r zrange zsetc 0 -1 withscores]
} {2 {b 2 c 3}}
test {ZINTERSTORE regression with two sets, intset+hashtable} {
r del seta setb setc
r sadd set1 a
r sadd set2 10
r zinterstore set3 2 set1 set2
} {0}
foreach cmd {ZUNIONSTORE ZINTERSTORE} {
test "$cmd with +inf/-inf scores" {