Compare commits

...

21 Commits

Author SHA1 Message Date
antirez
ab648d973d 2.4.1 release notes. 2011-10-17 10:46:00 +02:00
antirez
5ab72bedb7 version bumped to 2.4.1 2011-10-17 10:43:02 +02:00
antirez
9a83d13fb5 revert version bumping to 2.4.1, put in the previous commit for an error. 2011-10-17 10:42:47 +02:00
antirez
6662806a26 Regression test for issue #142 added 2011-10-17 10:40:11 +02:00
antirez
6e3f1a9e47 FLUSHALL now prevents rdbSave() from resetting the dirty counter, so that the command will get replicated and put inside the AOF. This fixes issue #142 2011-10-17 10:28:57 +02:00
antirez
d12f26c231 FLUSHALL will only perform a blocking SAVE if RDB persistence is configured. 2011-10-17 10:25:35 +02:00
antirez
a502f48320 2.4.0 Release notes. 2011-10-14 17:54:25 +02:00
antirez
0c71939c05 Version bumped to 2.4.0 2011-10-14 17:49:20 +02:00
antirez
a4f46f211c Exit with Fatal error at startup if the RDB file signature or version is wrong.
Ref: issue #103
2011-10-14 17:04:24 +02:00
antirez
8185c2e9f1 redis-check-dump: RDB version 2 now supported. 2011-10-14 16:52:11 +02:00
antirez
71bb31bf16 More informative error when DEBUG RELOAD fails. 2011-10-14 14:30:41 +02:00
antirez
c7d23f3ed1 Fix for issue #132. Now AUTH raises an error if no server password is configured. 2011-10-10 22:22:27 +02:00
Nathan Florea
09cefcfbb3 Added a config directive for a Unix socket mask
Added a configuration directive to allow a user to specify the
permissions to be granted to the Unix socket file.  I followed
the format Pieter and Salvatore discusses in issue #85 (
https://github.com/antirez/redis/issues/85).
2011-10-10 22:08:11 +02:00
Nathan Florea
1ca8deebef Unlink Unix socket file on shutdown 2011-10-10 22:08:03 +02:00
antirez
b7f667b553 Issue #131. stime/utime reported in INFO was inverted. Fixed thanks to Didier Spezia. 2011-10-10 15:29:36 +02:00
antirez
067ad37f96 Fix for bug #128 about the RENAME command. 2011-10-10 15:25:28 +02:00
antirez
a6e312bf9a Added two new tests for RENAME, currently both will fail because of bug #128. 2011-10-10 15:25:22 +02:00
antirez
0e841b998a CONFIG SET/GET support for loglevel 2011-10-06 15:25:53 +02:00
antirez
cd0458470e Redis test ports selection made more robust. This prevents the test from hanging if an already bound port is selected but the TCP server listening to it does not cause a protocol error with a Redis client PING. Also base port moved away from the range near to the Redis Cluster gossip ports. 2011-10-04 10:07:29 +02:00
antirez
678dbc48fd Compilation fixed on OpenBSD making sure that _XOPEN_SOURCE is set to 700 in fmacros.h 2011-09-29 10:20:26 +02:00
Juri M. Vainonen
3a91aca033 fixed a small bug that caused redis-cli to segfault when given single numeric parameter greater that zero. 2011-09-21 22:33:01 +02:00
19 changed files with 178 additions and 44 deletions

View File

@@ -18,6 +18,29 @@ to modify your program in order to use Redis 2.4.
CHANGELOG
---------
What's new in Redis 2.4.1
=========================
* [BUGFIX] FLUSHALL was not replicated nor written into the Append Only File.
* [BUGFIX] FLUSHALL now only performs a sync SAVE if there is at least
one save point configured.
What's new in Redis 2.4.0
=========================
* [BUGFIX] redis-cli segfault with single numerical argument fixed.
* [BUGFIX] OpenBSD compilation problem fixed.
* [BUGFIX] More robust Redis test, with better random port selection.
* [BUGFIX] Fix for bug #128 about the RENAME command.
* [BUGFIX] Fixed Issue #131. stime/utime reported in INFO was inverted.
* [BUGFIX] Unlink Unix socket file on shutdown.
* [BUGFIX] AUTH now returns error if no password is set on the server.
* [BUGFIX] Exit with Fatal error at startup on RDB loading errors.
* redis-check-dump: RDB version 2 now supported.
* More informative error when DEBUG RELOAD fails.
* Added a config directive for a Unix socket mask.
* CONFIG SET/GET for loglevel.
What's new in Redis 2.3.11 (2.4 Release Candidate 8)
====================================================

View File

@@ -34,6 +34,7 @@ port 6379
# on a unix socket when not specified.
#
# unixsocket /tmp/redis.sock
# unixsocketperm 755
# Close the connection after a client is idle for N seconds (0 to disable)
timeout 300

View File

@@ -32,6 +32,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
@@ -291,7 +292,7 @@ int anetTcpServer(char *err, int port, char *bindaddr)
return s;
}
int anetUnixServer(char *err, char *path)
int anetUnixServer(char *err, char *path, mode_t perm)
{
int s;
struct sockaddr_un sa;
@@ -304,6 +305,8 @@ int anetUnixServer(char *err, char *path)
strncpy(sa.sun_path,path,sizeof(sa.sun_path)-1);
if (anetListen(err,s,(struct sockaddr*)&sa,sizeof(sa)) == ANET_ERR)
return ANET_ERR;
if (perm)
chmod(sa.sun_path, perm);
return s;
}

View File

@@ -46,7 +46,7 @@ int anetUnixNonBlockConnect(char *err, char *path);
int anetRead(int fd, char *buf, int count);
int anetResolve(char *err, char *host, char *ipbuf);
int anetTcpServer(char *err, int port, char *bindaddr);
int anetUnixServer(char *err, char *path);
int anetUnixServer(char *err, char *path, mode_t perm);
int anetTcpAccept(char *err, int serversock, char *ip, int *port);
int anetUnixAccept(char *err, int serversock);
int anetWrite(int fd, char *buf, int count);

View File

@@ -74,6 +74,11 @@ void loadServerConfig(char *filename) {
server.bindaddr = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"unixsocket") && argc == 2) {
server.unixsocket = zstrdup(argv[1]);
} else if (!strcasecmp(argv[0],"unixsocketperm") && argc == 2) {
server.unixsocketperm = (mode_t)strtol(argv[1], NULL, 8);
if (errno || server.unixsocketperm > 0777) {
err = "Invalid socket file permissions"; goto loaderr;
}
} else if (!strcasecmp(argv[0],"save") && argc == 3) {
int seconds = atoi(argv[1]);
int changes = atoi(argv[2]);
@@ -501,6 +506,18 @@ void configSetCommand(redisClient *c) {
} else if (!strcasecmp(c->argv[2]->ptr,"slowlog-max-len")) {
if (getLongLongFromObject(o,&ll) == REDIS_ERR || ll < 0) goto badfmt;
server.slowlog_max_len = (unsigned)ll;
} else if (!strcasecmp(c->argv[2]->ptr,"loglevel")) {
if (!strcasecmp(o->ptr,"warning")) {
server.verbosity = REDIS_WARNING;
} else if (!strcasecmp(o->ptr,"notice")) {
server.verbosity = REDIS_NOTICE;
} else if (!strcasecmp(o->ptr,"verbose")) {
server.verbosity = REDIS_VERBOSE;
} else if (!strcasecmp(o->ptr,"debug")) {
server.verbosity = REDIS_DEBUG;
} else {
goto badfmt;
}
} else {
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
(char*)c->argv[2]->ptr);
@@ -681,6 +698,20 @@ void configGetCommand(redisClient *c) {
addReplyBulkLongLong(c,server.slowlog_max_len);
matches++;
}
if (stringmatch(pattern,"loglevel",0)) {
char *s;
switch(server.verbosity) {
case REDIS_WARNING: s = "warning"; break;
case REDIS_VERBOSE: s = "verbose"; break;
case REDIS_NOTICE: s = "notice"; break;
case REDIS_DEBUG: s = "debug"; break;
default: s = "unknown"; break; /* too harmless to panic */
}
addReplyBulkCString(c,"loglevel");
addReplyBulkCString(c,s);
matches++;
}
setDeferredMultiBulkLength(c,replylen,matches*2);
}

View File

@@ -206,7 +206,13 @@ void flushallCommand(redisClient *c) {
kill(server.bgsavechildpid,SIGKILL);
rdbRemoveTempFile(server.bgsavechildpid);
}
rdbSave(server.dbfilename);
if (server.saveparamslen > 0) {
/* Normally rdbSave() will reset dirty, but we don't want this here
* as otherwise FLUSHALL will not be replicated nor put into the AOF. */
int saved_dirty = server.dirty;
rdbSave(server.dbfilename);
server.dirty = saved_dirty;
}
server.dirty++;
}
@@ -341,6 +347,7 @@ void shutdownCommand(redisClient *c) {
void renameGenericCommand(redisClient *c, int nx) {
robj *o;
time_t expire;
/* To use the same key as src and dst is probably an error */
if (sdscmp(c->argv[1]->ptr,c->argv[2]->ptr) == 0) {
@@ -352,16 +359,18 @@ void renameGenericCommand(redisClient *c, int nx) {
return;
incrRefCount(o);
expire = getExpire(c->db,c->argv[1]);
if (lookupKeyWrite(c->db,c->argv[2]) != NULL) {
if (nx) {
decrRefCount(o);
addReply(c,shared.czero);
return;
}
dbOverwrite(c->db,c->argv[2],o);
} else {
dbAdd(c->db,c->argv[2],o);
/* Overwrite: delete the old key before creating the new one with the same name. */
dbDelete(c->db,c->argv[2]);
}
dbAdd(c->db,c->argv[2],o);
if (expire != -1) setExpire(c->db,c->argv[2],expire);
dbDelete(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);

View File

@@ -224,7 +224,7 @@ void debugCommand(redisClient *c) {
}
emptyDb();
if (rdbLoad(server.dbfilename) != REDIS_OK) {
addReply(c,shared.err);
addReplyError(c,"Error trying to load the RDB dump");
return;
}
redisLog(REDIS_WARNING,"DB reloaded by DEBUG RELOAD");

View File

@@ -3,7 +3,7 @@
#define _BSD_SOURCE
#ifdef __linux__
#if defined(__linux__) || defined(__OpenBSD__)
#define _XOPEN_SOURCE 700
#else
#define _XOPEN_SOURCE

View File

@@ -937,18 +937,23 @@ int rdbLoad(char *filename) {
long loops = 0;
fp = fopen(filename,"r");
if (!fp) return REDIS_ERR;
if (!fp) {
errno = ENOENT;
return REDIS_ERR;
}
if (fread(buf,9,1,fp) == 0) goto eoferr;
buf[9] = '\0';
if (memcmp(buf,"REDIS",5) != 0) {
fclose(fp);
redisLog(REDIS_WARNING,"Wrong signature trying to load DB from file");
errno = EINVAL;
return REDIS_ERR;
}
rdbver = atoi(buf+5);
if (rdbver < 1 || rdbver > 2) {
fclose(fp);
redisLog(REDIS_WARNING,"Can't handle RDB format version %d",rdbver);
errno = EINVAL;
return REDIS_ERR;
}

View File

@@ -16,6 +16,10 @@
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
#define REDIS_HASH_ZIPMAP 9
#define REDIS_LIST_ZIPLIST 10
#define REDIS_SET_INTSET 11
#define REDIS_ZSET_ZIPLIST 12
/* Objects encoding. Some kind of objects like Strings and Hashes can be
* internally represented in multiple ways. The 'encoding' field of the object
@@ -132,7 +136,7 @@ int processHeader() {
}
dump_version = (int)strtol(buf + 5, NULL, 10);
if (dump_version != 1) {
if (dump_version < 1 || dump_version > 2) {
ERROR("Unknown RDB format version: %d\n", dump_version);
}
return 1;
@@ -144,7 +148,7 @@ int loadType(entry *e) {
/* this byte needs to qualify as type */
unsigned char t;
if (readBytes(&t, 1)) {
if (t <= 4 || t >= 253) {
if (t <= 4 || (t >=9 && t <= 12) || t >= 253) {
e->type = t;
return 1;
} else {
@@ -160,7 +164,8 @@ int loadType(entry *e) {
int peekType() {
unsigned char t;
if (readBytes(&t, -1) && (t <= 4 || t >= 253)) return t;
if (readBytes(&t, -1) && (t <= 4 || (t >=9 && t <= 12) || t >= 253))
return t;
return -1;
}
@@ -375,6 +380,10 @@ int loadPair(entry *e) {
switch(e->type) {
case REDIS_STRING:
case REDIS_HASH_ZIPMAP:
case REDIS_LIST_ZIPLIST:
case REDIS_SET_INTSET:
case REDIS_ZSET_ZIPLIST:
if (!processStringObject(NULL)) {
SHIFT_ERROR(offset, "Error reading entry value");
return 0;

View File

@@ -694,7 +694,7 @@ static void repl() {
int repeat, skipargs = 0;
repeat = atoi(argv[0]);
if (repeat) {
if (argc > 1 && repeat) {
skipargs = 1;
} else {
repeat = 1;

View File

@@ -798,6 +798,7 @@ void initServerConfig() {
server.port = REDIS_SERVERPORT;
server.bindaddr = NULL;
server.unixsocket = NULL;
server.unixsocketperm = 0;
server.ipfd = -1;
server.sofd = -1;
server.dbnum = REDIS_DEFAULT_DBNUM;
@@ -915,7 +916,7 @@ void initServer() {
}
if (server.unixsocket != NULL) {
unlink(server.unixsocket); /* don't care if this fails */
server.sofd = anetUnixServer(server.neterr,server.unixsocket);
server.sofd = anetUnixServer(server.neterr,server.unixsocket,server.unixsocketperm);
if (server.sofd == ANET_ERR) {
redisLog(REDIS_WARNING, "Opening socket: %s", server.neterr);
exit(1);
@@ -1170,6 +1171,10 @@ int prepareForShutdown() {
/* Close the listening sockets. Apparently this allows faster restarts. */
if (server.ipfd != -1) close(server.ipfd);
if (server.sofd != -1) close(server.sofd);
if (server.unixsocket) {
redisLog(REDIS_NOTICE,"Removing the unix socket file.");
unlink(server.unixsocket); /* don't care if this fails */
}
redisLog(REDIS_WARNING,"Redis is now ready to exit, bye bye...");
return REDIS_OK;
@@ -1178,7 +1183,9 @@ int prepareForShutdown() {
/*================================== Commands =============================== */
void authCommand(redisClient *c) {
if (!server.requirepass || !strcmp(c->argv[1]->ptr, server.requirepass)) {
if (!server.requirepass) {
addReplyError(c,"Client sent AUTH, but no password is set");
} else if (!strcmp(c->argv[1]->ptr, server.requirepass)) {
c->authenticated = 1;
addReply(c,shared.ok);
} else {
@@ -1285,10 +1292,10 @@ sds genRedisInfoString(void) {
uptime,
uptime/(3600*24),
(unsigned long) server.lruclock,
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)self_ru.ru_stime.tv_sec+(float)self_ru.ru_stime.tv_usec/1000000,
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
(float)self_ru.ru_utime.tv_sec+(float)self_ru.ru_utime.tv_usec/1000000,
(float)c_ru.ru_stime.tv_sec+(float)c_ru.ru_stime.tv_usec/1000000,
(float)c_ru.ru_utime.tv_sec+(float)c_ru.ru_utime.tv_usec/1000000,
listLength(server.clients)-listLength(server.slaves),
listLength(server.slaves),
lol, bib,
@@ -1644,8 +1651,13 @@ int main(int argc, char **argv) {
if (loadAppendOnlyFile(server.appendfilename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from append only file: %ld seconds",time(NULL)-start);
} else {
if (rdbLoad(server.dbfilename) == REDIS_OK)
redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds",time(NULL)-start);
if (rdbLoad(server.dbfilename) == REDIS_OK) {
redisLog(REDIS_NOTICE,"DB loaded from disk: %ld seconds",
time(NULL)-start);
} else if (errno != ENOENT) {
redisLog(REDIS_WARNING,"Fatal error loading the DB. Exiting.");
exit(1);
}
}
if (server.ipfd > 0)
redisLog(REDIS_NOTICE,"The server is now ready to accept connections on port %d", server.port);

View File

@@ -378,6 +378,7 @@ struct redisServer {
int port;
char *bindaddr;
char *unixsocket;
mode_t unixsocketperm;
int ipfd;
int sofd;
redisDb *db;

View File

@@ -1 +1 @@
#define REDIS_VERSION "2.3.11"
#define REDIS_VERSION "2.4.1"

View File

@@ -57,5 +57,11 @@ start_server {tags {"repl"}} {
if {$::valgrind} {after 2000}
r 0 get mykey
} {bar}
test {FLUSHALL should replicate} {
r -1 flushall
if {$::valgrind} {after 2000}
list [r -1 dbsize] [r 0 dbsize]
} {0 0}
}
}

View File

@@ -154,7 +154,8 @@ proc start_server {options {code undefined}} {
dict set config dir [tmpdir server]
# start every server on a different port
dict set config port [incr ::port]
set ::port [find_available_port [expr {$::port+1}]]
dict set config port $::port
# apply overrides from global space and arguments
foreach {directive arguments} [concat $::global_overrides $overrides] {

View File

@@ -37,7 +37,7 @@ set ::all_tests {
set ::next_test 0
set ::host 127.0.0.1
set ::port 16379
set ::port 21111
set ::traceleaks 0
set ::valgrind 0
set ::verbose 0
@@ -148,36 +148,38 @@ proc cleanup {} {
puts "OK"
}
proc find_available_port start {
for {set j $start} {$j < $start+1024} {incr j} {
if {[catch {
set fd [socket 127.0.0.1 $start]
}]} {
return $start
} else {
close $fd
}
}
if {$j == $start+1024} {
error "Can't find a non busy port in the $start-[expr {$start+1023}] range."
}
}
proc test_server_main {} {
cleanup
# Open a listening socket, trying different ports in order to find a
# non busy one.
set port 11111
while 1 {
puts "Starting test server at port $port"
if {[catch {socket -server accept_test_clients $port} e]} {
if {[string match {*address already in use*} $e]} {
if {$port == 20000} {
puts "Can't find an available TCP port for test server."
exit 1
} else {
incr port
}
} else {
puts "Fatal error starting test server: $e"
exit 1
}
} else {
break
}
}
set port [find_available_port 11111]
puts "Starting test server at port $port"
socket -server accept_test_clients $port
# Start the client instances
set ::clients_pids {}
set start_port [expr {$::port+100}]
for {set j 0} {$j < $::numclients} {incr j} {
set start_port [find_available_port $start_port]
set p [exec tclsh8.5 [info script] {*}$::argv \
--client $port --port [expr {$::port+($j*10)}] &]
--client $port --port $start_port &]
lappend ::clients_pids $p
incr start_port 10
}
# Setup global state for the test server

View File

@@ -1,15 +1,27 @@
start_server {tags {"auth"}} {
test {AUTH fails if there is no password configured server side} {
catch {r auth foo} err
set _ $err
} {ERR*no password*}
}
start_server {tags {"auth"} overrides {requirepass foobar}} {
test {AUTH fails when a wrong password is given} {
catch {r auth wrong!} err
format $err
set _ $err
} {ERR*invalid password}
test {Arbitrary command gives an error when AUTH is required} {
catch {r set foo bar} err
format $err
set _ $err
} {ERR*operation not permitted}
test {AUTH succeeds when the right password is given} {
r auth foobar
} {OK}
test {Once AUTH succeeded we can actually send commands to the server} {
r set foo 100
r incr foo
} {101}
}

View File

@@ -262,6 +262,25 @@ start_server {tags {"basic"}} {
format $err
} {ERR*}
test {RENAME with volatile key, should move the TTL as well} {
r del mykey mykey2
r set mykey foo
r expire mykey 100
assert {[r ttl mykey] > 95 && [r ttl mykey] <= 100}
r rename mykey mykey2
assert {[r ttl mykey2] > 95 && [r ttl mykey2] <= 100}
}
test {RENAME with volatile key, should not inherit TTL of target key} {
r del mykey mykey2
r set mykey foo
r set mykey2 bar
r expire mykey2 100
assert {[r ttl mykey] == -1 && [r ttl mykey2] > 0}
r rename mykey mykey2
r ttl mykey2
} {-1}
test {DEL all keys again (DB 0)} {
foreach key [r keys *] {
r del $key