Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
97ddfbbfc3 | ||
|
|
50e50d6a25 | ||
|
|
9d665825d9 | ||
|
|
19b55f8ebc | ||
|
|
bf3692797d | ||
|
|
da06854477 | ||
|
|
38de9362b2 | ||
|
|
7600994937 | ||
|
|
6a9764d183 | ||
|
|
c138dc7da4 | ||
|
|
1b905277bb | ||
|
|
2b886275e9 | ||
|
|
93db956e06 | ||
|
|
27fc6199ca | ||
|
|
0b01578d2e | ||
|
|
920c45b818 | ||
|
|
cf6c3f4b04 | ||
|
|
fb90934c47 | ||
|
|
207ca3cedd | ||
|
|
97d3b7dc8d | ||
|
|
abdbfc14c0 | ||
|
|
de5d4be994 | ||
|
|
69bfdcf7d4 | ||
|
|
699465c31e | ||
|
|
45332cc47b | ||
|
|
2d2c17b075 | ||
|
|
048c0f0f37 | ||
|
|
3eb3cc9da4 | ||
|
|
74f6ee8dd5 | ||
|
|
7a55d72f28 | ||
|
|
4e4b3b943c | ||
|
|
61e2547675 | ||
|
|
595b5974f8 | ||
|
|
58418d7c3e | ||
|
|
c02bda324f | ||
|
|
1afef16d88 | ||
|
|
7377aacd84 | ||
|
|
eece2d528c | ||
|
|
eb5aeaf138 | ||
|
|
1a6281f52f | ||
|
|
c2571b314a | ||
|
|
97aeda9828 | ||
|
|
ca9d961073 | ||
|
|
2446fbb3bb | ||
|
|
970e813be2 | ||
|
|
a36d524562 | ||
|
|
bbaf76ea2f | ||
|
|
669f302e16 | ||
|
|
71791e7a8e |
@@ -12,6 +12,65 @@ for 2.0.
|
||||
CHANGELOG
|
||||
---------
|
||||
|
||||
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,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.
|
||||
|
||||
|
||||
Vendored
+13
-2
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
+33
-7
@@ -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);
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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. */
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
-2
@@ -826,7 +826,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 +941,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]);
|
||||
|
||||
+15
-2
@@ -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++;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.1.12"
|
||||
#define REDIS_VERSION "2.2.6"
|
||||
|
||||
+34
-31
@@ -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
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user