Compare commits

..
34 Commits
Author SHA1 Message Date
antirez 01888d1e58 Redis 3.0.2 2015-06-04 11:35:56 +02:00
Ben Murphy 30278061cc hide access to debug table 2015-06-03 13:36:00 +02:00
Ben Murphy 49efe300af disable loading lua bytecode 2015-06-03 13:35:59 +02:00
antirez 702f914771 Scripting: Lua cmsgpack lib updated to include str8 support 2015-06-03 08:45:58 +02:00
Itamar Haber e0b2e24830 Removed incorrect suggestion
DEL/INCR/DECR and others could be NTH but apparently never made it to the implementation of SORT
2015-05-29 12:27:59 +02:00
antirez 0da453160b Test: ZADD CH tests 2015-05-29 12:27:17 +02:00
antirez 1d8973c47d ZADD RETCH option renamed CH
From Twitter:

    "@antirez that’s an awfully-named command :(
     http://en.wikipedia.org/wiki/Retching"
2015-05-29 12:27:17 +02:00
antirez df7add9e70 Test: ZADD INCR test 2015-05-29 12:27:17 +02:00
antirez 9003483d43 Test: ZADD NX and XX options tests 2015-05-29 12:27:17 +02:00
antirez a13d6378c1 ZADD RETCH option: Return number of elements added or updated
Normally ZADD only returns the number of elements added to a sorted
set, using the RETCH option it returns the sum of elements added or
for which the score was updated.
2015-05-29 12:27:17 +02:00
antirez fa17e2daf0 ZADD NX and XX options 2015-05-29 12:27:17 +02:00
antirez ff7c1faa12 ZADD implemenation able to take options. 2015-05-29 12:27:16 +02:00
therealbill e252e9f231 adding a sentinel command: "flushconfig"
This new command triggers a config flush to save the in-memory config to
disk. This is useful for cases of a configuration management system or a
package manager wiping out your sentinel config while the process is
still running - and has not yet been restarted. It can also be useful
for scripting a backup and migrate or clone of a running sentinel.
2015-05-25 12:06:52 +02:00
antirez 9c0a68861e Sentinel: CKQUORUM tests 2015-05-19 12:27:19 +02:00
antirez 5844f5d0d1 Sentinel: SENTINEL CKQUORUM command
A way for monitoring systems to check that Sentinel is technically able
to reach the quorum and failover, using the currently visible Sentinels.
2015-05-19 12:27:19 +02:00
antirez a97658f293 Merge branch '3.0' of github.com:/antirez/redis into 3.0 2015-05-15 17:40:03 +02:00
antirez 138e7af57b Rewrite smoveCommand test with ternary operator 2015-05-15 17:39:37 +02:00
Glenn Nethercutt 3a9f41ad86 uphold the smove contract to return 0 when the element is not a member of the source set, even if source=dest 2015-05-15 17:39:37 +02:00
Salvatore Sanfilippo 71fba427c2 Merge pull request #2567 from ryanschwartz/patch-1
Update 00-RELEASENOTES
2015-05-15 17:22:21 +02:00
Jungtaek Lim 08d4df8d31 protocol error log should be seen debug/verbose level 2015-05-15 17:06:42 +02:00
Ryan Schwartz e7422ef166 Update 00-RELEASENOTES
Fix typo in 00-RELEASENOTES.
2015-05-07 14:28:21 -05:00
antirez 362032e43a Release notes: Sentinel upgrade urgency moved to moderate 2015-05-05 11:16:20 +02:00
antirez 10323dc5fe Redis 3.0.1 2015-05-05 11:01:19 +02:00
FuGangqiang e213c408fa sdsfree x and y 2015-05-04 13:03:01 +02:00
FuGangqiang 5674656db7 fix doc example 2015-05-04 13:03:01 +02:00
FuGangqiang 35d71b1ffc fix typo 2015-05-04 13:03:01 +02:00
Itamar Haber 6637862838 update copyright year 2015-05-04 12:56:50 +02:00
therealbill b065f4441b Making sentinel flush config on +slave
Originally, only the +slave event which occurs when a slave is
reconfigured during sentinelResetMasterAndChangeAddress triggers a flush
of the config to disk.  However, newly discovered slaves don't
apparently trigger this flush but do trigger the +slave event issuance.

So if you start up a sentinel, add a master, then add a slave to the
master (as a way to reproduce it) you'll see the +slave event issued,
but the sentinel config won't be updated with the known-slave entry.

This change makes sentinel do the flush of the config if a new slave is
deteted in sentinelRefreshInstanceInfo.
2015-05-04 12:55:27 +02:00
antirez bd99b26bc5 Sentinel: remove useless sentinelFlushConfig() call
To rewrite the config in the loop that adds slaves back after a master
reset, in order to handle switching to another master, is useless: it
just adds latency since there is an fsync call in the inner loop,
without providing any additional guarantee, but the contrary, since if
after the first loop iteration the server crashes we end with just a
single slave entry losing all the other informations.

It is wiser to rewrite the config at the end when the full new
state is configured.
2015-05-04 12:55:27 +02:00
Yossi Gottlieb 0560738f6b Fix Redis server crash when Lua command exceeds client output buffer
limit.
2015-05-04 12:20:24 +02:00
clark.kang 88d58661db fix sentinel memory leak 2015-05-04 12:18:05 +02:00
antirez 315e3b14ef Fix Sentinel memory leak (hiredis bug)
This fixes issue #2535, that was actually an hiredis library bug (I
submitted an issue and fix to the redis/hiredis repo as well).

When an asynchronous hiredis connection subscribes to a Pub/Sub channel
and gets an error, and in other related conditions, the function
redisProcessCallbacks() enters a code path where the link is
disconnected, however the function returns before freeing the allocated
reply object. This causes a memory leak. The memory leak was trivial to
trigger in Redis Sentinel, which uses hiredis, every time we tried to
subscribe to an instance that required a password, in case the Sentinel
was configured either with the wrong password or without password at
all. In this case, the -AUTH error caused the leaking code path to be
executed.

It was verified with Valgrind that after this change the leak no longer
happens in Sentinel with a misconfigured authentication password.
2015-04-28 22:15:09 +02:00
antirez 7ff051f6c1 sha1.c: use standard uint32_t. 2015-04-27 12:07:59 +02:00
antirez f387a5acf8 Old warning removed from release notes. 2015-04-01 17:34:22 +02:00
18 changed files with 349 additions and 77 deletions
+24 -3
View File
@@ -1,8 +1,6 @@
Redis 3.0 release notes
=======================
WARNING: Redis 3.0 is currently a BETA not suitable for production environments.
--------------------------------------------------------------------------------
Upgrade urgency levels:
@@ -12,6 +10,29 @@ HIGH: There is a critical bug that may affect a subset of users. Upgrade!
CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
--------------------------------------------------------------------------------
--[ Redis 3.0.2 ] Release date: 4 Jun 2015
Upgrade urgency: HIGH for Redis because of a security issue.
LOW for Sentinel.
* [FIX] Critical security issue fix by Ben Murphy: http://t.co/LpGTyZmfS7
* [FIX] SMOVE reply fixed when src and dst keys are the same. (Glenn Nethercutt)
* [FIX] Lua cmsgpack lib updated to support str8 type. (Sebastian Waisbrot)
* [NEW] ZADD support for options: NX, XX, CH. See new doc at redis.io.
(Salvatore Sanfilippo)
* [NEW] Senitnel: CKQUORUM and FLUSHCONFIG commands back ported.
(Salvatore Sanfilippo)
--[ Redis 3.0.1 ] Release date: 5 May 2015
Upgrade urgency: LOW for Redis and Cluster, MODERATE for Sentinel.
* [FIX] Sentinel memory leak due to hiredis fixed. (Salvatore Sanfilippo)
* [FIX] Sentinel memory leak on duplicated instance. (Charsyam)
* [FIX] Redis crash on Lua reaching output buffer limits. (Yossi Gottlieb)
* [FIX] Sentinel flushes config on +slave events. (Bill Anderson)
--[ Redis 3.0.0 ] Release date: 1 Apr 2015
>> What's new in Redis 3.0 compared to Redis 2.8?
@@ -25,7 +46,7 @@ CRITICAL: There is a critical bug affecting MOST USERS. Upgrade ASAP.
* WAIT command to block waiting for a write to be transmitted to
the specified number of slaves.
* MIGRATE connection caching. Much faster keys migraitons.
* MIGARTE new options COPY and REPLACE.
* MIGRATE new options COPY and REPLACE.
* CLIENT PAUSE command: stop processing client requests for a
specified amount of time.
* BITCOUNT performance improvements.
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2006-2014, Salvatore Sanfilippo
Copyright (c) 2006-2015, Salvatore Sanfilippo
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+1
View File
@@ -443,6 +443,7 @@ void redisProcessCallbacks(redisAsyncContext *ac) {
if (((redisReply*)reply)->type == REDIS_REPLY_ERROR) {
c->err = REDIS_ERR_OTHER;
snprintf(c->errstr,sizeof(c->errstr),"%s",((redisReply*)reply)->str);
c->reader->fn->freeObject(reply);
__redisAsyncDisconnect(ac);
return;
}
+1 -1
View File
@@ -495,7 +495,7 @@ static void f_parser (lua_State *L, void *ud) {
struct SParser *p = cast(struct SParser *, ud);
int c = luaZ_lookahead(p->z);
luaC_checkGC(L);
tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z,
tf = (luaY_parser)(L, p->z,
&p->buff, p->name);
cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L)));
cl->l.p = tf;
+42 -29
View File
@@ -66,7 +66,7 @@
/* Reverse memory bytes if arch is little endian. Given the conceptual
* simplicity of the Lua build system we prefer check for endianess at runtime.
* The performance difference should be acceptable. */
static void memrevifle(void *ptr, size_t len) {
void memrevifle(void *ptr, size_t len) {
unsigned char *p = (unsigned char *)ptr,
*e = (unsigned char *)p+len-1,
aux;
@@ -96,7 +96,7 @@ typedef struct mp_buf {
size_t len, free;
} mp_buf;
static void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
void *(*local_realloc) (void *, void *, size_t osize, size_t nsize) = NULL;
void *ud;
@@ -105,7 +105,7 @@ static void *mp_realloc(lua_State *L, void *target, size_t osize,size_t nsize) {
return local_realloc(ud, target, osize, nsize);
}
static mp_buf *mp_buf_new(lua_State *L) {
mp_buf *mp_buf_new(lua_State *L) {
mp_buf *buf = NULL;
/* Old size = 0; new size = sizeof(*buf) */
@@ -117,7 +117,7 @@ static mp_buf *mp_buf_new(lua_State *L) {
return buf;
}
static void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
void mp_buf_append(mp_buf *buf, const unsigned char *s, size_t len) {
if (buf->free < len) {
size_t newlen = buf->len+len;
@@ -153,7 +153,7 @@ typedef struct mp_cur {
int err;
} mp_cur;
static void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
cursor->p = s;
cursor->left = len;
cursor->err = MP_CUR_ERROR_NONE;
@@ -173,13 +173,17 @@ static void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
/* ------------------------- Low level MP encoding -------------------------- */
static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
unsigned char hdr[5];
int hdrlen;
if (len < 32) {
hdr[0] = 0xa0 | (len&0xff); /* fix raw */
hdrlen = 1;
} else if (len <= 0xff) {
hdr[0] = 0xd9;
hdr[1] = len;
hdrlen = 2;
} else if (len <= 0xffff) {
hdr[0] = 0xda;
hdr[1] = (len&0xff00)>>8;
@@ -198,7 +202,7 @@ static void mp_encode_bytes(mp_buf *buf, const unsigned char *s, size_t len) {
}
/* we assume IEEE 754 internal format for single and double precision floats. */
static void mp_encode_double(mp_buf *buf, double d) {
void mp_encode_double(mp_buf *buf, double d) {
unsigned char b[9];
float f = d;
@@ -216,7 +220,7 @@ static void mp_encode_double(mp_buf *buf, double d) {
}
}
static void mp_encode_int(mp_buf *buf, int64_t n) {
void mp_encode_int(mp_buf *buf, int64_t n) {
unsigned char b[9];
int enclen;
@@ -288,7 +292,7 @@ static void mp_encode_int(mp_buf *buf, int64_t n) {
mp_buf_append(buf,b,enclen);
}
static void mp_encode_array(mp_buf *buf, int64_t n) {
void mp_encode_array(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
@@ -311,7 +315,7 @@ static void mp_encode_array(mp_buf *buf, int64_t n) {
mp_buf_append(buf,b,enclen);
}
static void mp_encode_map(mp_buf *buf, int64_t n) {
void mp_encode_map(mp_buf *buf, int64_t n) {
unsigned char b[5];
int enclen;
@@ -336,7 +340,7 @@ static void mp_encode_map(mp_buf *buf, int64_t n) {
/* --------------------------- Lua types encoding --------------------------- */
static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
size_t len;
const char *s;
@@ -344,13 +348,13 @@ static void mp_encode_lua_string(lua_State *L, mp_buf *buf) {
mp_encode_bytes(buf,(const unsigned char*)s,len);
}
static void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
void mp_encode_lua_bool(lua_State *L, mp_buf *buf) {
unsigned char b = lua_toboolean(L,-1) ? 0xc3 : 0xc2;
mp_buf_append(buf,&b,1);
}
/* Lua 5.3 has a built in 64-bit integer type */
static void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
#if (LUA_VERSION_NUM < 503) && BITS_32
lua_Number i = lua_tonumber(L,-1);
#else
@@ -362,7 +366,7 @@ static void mp_encode_lua_integer(lua_State *L, mp_buf *buf) {
/* Lua 5.2 and lower only has 64-bit doubles, so we need to
* detect if the double may be representable as an int
* for Lua < 5.3 */
static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
lua_Number n = lua_tonumber(L,-1);
if (IS_INT64_EQUIVALENT(n)) {
@@ -372,10 +376,10 @@ static void mp_encode_lua_number(lua_State *L, mp_buf *buf) {
}
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level);
/* Convert a lua table into a message pack list. */
static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
#if LUA_VERSION_NUM < 502
size_t len = lua_objlen(L,-1), j;
#else
@@ -391,7 +395,7 @@ static void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
}
/* Convert a lua table into a message pack key-value map. */
static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
size_t len = 0;
/* First step: count keys into table. No other way to do it with the
@@ -418,7 +422,7 @@ static void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
/* Returns true if the Lua table on top of the stack is exclusively composed
* of keys from numerical keys from 1 up to N, with N being the total number
* of elements, without any hole in the middle. */
static int table_is_an_array(lua_State *L) {
int table_is_an_array(lua_State *L) {
int count = 0, max = 0;
#if LUA_VERSION_NUM < 503
lua_Number n;
@@ -461,14 +465,14 @@ static int table_is_an_array(lua_State *L) {
/* If the length operator returns non-zero, that is, there is at least
* an object at key '1', we serialize to message pack list. Otherwise
* we use a map. */
static void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_table(lua_State *L, mp_buf *buf, int level) {
if (table_is_an_array(L))
mp_encode_lua_table_as_array(L,buf,level);
else
mp_encode_lua_table_as_map(L,buf,level);
}
static void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
unsigned char b[1];
(void)L;
@@ -476,7 +480,7 @@ static void mp_encode_lua_null(lua_State *L, mp_buf *buf) {
mp_buf_append(buf,b,1);
}
static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
int t = lua_type(L,-1);
/* Limit the encoding of nested tables to a specified maximum depth, so that
@@ -506,7 +510,7 @@ static void mp_encode_lua_type(lua_State *L, mp_buf *buf, int level) {
* Packs all arguments as a stream for multiple upacking later.
* Returns error if no arguments provided.
*/
static int mp_pack(lua_State *L) {
int mp_pack(lua_State *L) {
int nargs = lua_gettop(L);
int i;
mp_buf *buf;
@@ -687,6 +691,15 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
mp_cur_consume(c,9);
}
break;
case 0xd9: /* raw 8 */
mp_cur_need(c,2);
{
size_t l = c->p[1];
mp_cur_need(c,2+l);
lua_pushlstring(L,(char*)c->p+2,l);
mp_cur_consume(c,2+l);
}
break;
case 0xda: /* raw 16 */
mp_cur_need(c,3);
{
@@ -773,7 +786,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
}
}
static int mp_unpack_full(lua_State *L, int limit, int offset) {
int mp_unpack_full(lua_State *L, int limit, int offset) {
size_t len;
const char *s;
mp_cur c;
@@ -826,18 +839,18 @@ static int mp_unpack_full(lua_State *L, int limit, int offset) {
return cnt;
}
static int mp_unpack(lua_State *L) {
int mp_unpack(lua_State *L) {
return mp_unpack_full(L, 0, 0);
}
static int mp_unpack_one(lua_State *L) {
int mp_unpack_one(lua_State *L) {
int offset = luaL_optinteger(L, 2, 0);
/* Variable pop because offset may not exist */
lua_pop(L, lua_gettop(L)-1);
return mp_unpack_full(L, 1, offset);
}
static int mp_unpack_limit(lua_State *L) {
int mp_unpack_limit(lua_State *L) {
int limit = luaL_checkinteger(L, 2);
int offset = luaL_optinteger(L, 3, 0);
/* Variable pop because offset may not exist */
@@ -846,7 +859,7 @@ static int mp_unpack_limit(lua_State *L) {
return mp_unpack_full(L, limit, offset);
}
static int mp_safe(lua_State *L) {
int mp_safe(lua_State *L) {
int argc, err, total_results;
argc = lua_gettop(L);
@@ -869,7 +882,7 @@ static int mp_safe(lua_State *L) {
}
/* -------------------------------------------------------------------------- */
static const struct luaL_Reg cmds[] = {
const struct luaL_Reg cmds[] = {
{"pack", mp_pack},
{"unpack", mp_unpack},
{"unpack_one", mp_unpack_one},
@@ -877,7 +890,7 @@ static const struct luaL_Reg cmds[] = {
{0}
};
static int luaopen_create(lua_State *L) {
int luaopen_create(lua_State *L) {
int i;
/* Manually construct our module table instead of
* relying on _register or _newlib */
+2 -2
View File
@@ -797,7 +797,7 @@ void freeClient(redisClient *c) {
* a context where calling freeClient() is not possible, because the client
* should be valid for the continuation of the flow of the program. */
void freeClientAsync(redisClient *c) {
if (c->flags & REDIS_CLOSE_ASAP) return;
if (c->flags & REDIS_CLOSE_ASAP || c->flags & REDIS_LUA_CLIENT) return;
c->flags |= REDIS_CLOSE_ASAP;
listAddNodeTail(server.clients_to_close,c);
}
@@ -974,7 +974,7 @@ int processInlineBuffer(redisClient *c) {
/* Helper function. Trims query buffer to make the function that processes
* multi bulk requests idempotent. */
static void setProtocolError(redisClient *c, int pos) {
if (server.verbosity >= REDIS_VERBOSE) {
if (server.verbosity <= REDIS_VERBOSE) {
sds client = catClientInfoString(sdsempty(),c);
redisLog(REDIS_VERBOSE,
"Protocol error from client: %s", client);
+9 -6
View File
@@ -612,11 +612,12 @@ void scriptingEnableGlobalsProtection(lua_State *lua) {
/* strict.lua from: http://metalua.luaforge.net/src/lib/strict.lua.html.
* Modified to be adapted to Redis. */
s[j++]="local dbg=debug\n";
s[j++]="local mt = {}\n";
s[j++]="setmetatable(_G, mt)\n";
s[j++]="mt.__newindex = function (t, n, v)\n";
s[j++]=" if debug.getinfo(2) then\n";
s[j++]=" local w = debug.getinfo(2, \"S\").what\n";
s[j++]=" if dbg.getinfo(2) then\n";
s[j++]=" local w = dbg.getinfo(2, \"S\").what\n";
s[j++]=" if w ~= \"main\" and w ~= \"C\" then\n";
s[j++]=" error(\"Script attempted to create global variable '\"..tostring(n)..\"'\", 2)\n";
s[j++]=" end\n";
@@ -624,11 +625,12 @@ void scriptingEnableGlobalsProtection(lua_State *lua) {
s[j++]=" rawset(t, n, v)\n";
s[j++]="end\n";
s[j++]="mt.__index = function (t, n)\n";
s[j++]=" if debug.getinfo(2) and debug.getinfo(2, \"S\").what ~= \"C\" then\n";
s[j++]=" if dbg.getinfo(2) and dbg.getinfo(2, \"S\").what ~= \"C\" then\n";
s[j++]=" error(\"Script attempted to access unexisting global variable '\"..tostring(n)..\"'\", 2)\n";
s[j++]=" end\n";
s[j++]=" return rawget(t, n)\n";
s[j++]="end\n";
s[j++]="debug = nil\n";
s[j++]=NULL;
for (j = 0; s[j] != NULL; j++) code = sdscatlen(code,s[j],strlen(s[j]));
@@ -732,10 +734,11 @@ void scriptingInit(void) {
* information about the caller, that's what makes sense from the point
* of view of the user debugging a script. */
{
char *errh_func = "function __redis__err__handler(err)\n"
" local i = debug.getinfo(2,'nSl')\n"
char *errh_func = "local dbg = debug\n"
"function __redis__err__handler(err)\n"
" local i = dbg.getinfo(2,'nSl')\n"
" if i and i.what == 'C' then\n"
" i = debug.getinfo(3,'nSl')\n"
" i = dbg.getinfo(3,'nSl')\n"
" end\n"
" if i then\n"
" return i.source .. ':' .. i.currentline .. ': ' .. err\n"
+5 -2
View File
@@ -71,7 +71,7 @@ sds sdsempty(void) {
return sdsnewlen("",0);
}
/* Create a new sds string starting from a null termined C string. */
/* Create a new sds string starting from a null terminated C string. */
sds sdsnew(const char *init) {
size_t initlen = (init == NULL) ? 0 : strlen(init);
return sdsnewlen(init, initlen);
@@ -557,7 +557,7 @@ sds sdscatfmt(sds s, char const *fmt, ...) {
* Example:
*
* s = sdsnew("AA...AA.a.aa.aHelloWorld :::");
* s = sdstrim(s,"A. :");
* s = sdstrim(s,"Aa. :");
* printf("%s\n", s);
*
* Output will be just "Hello World".
@@ -1083,6 +1083,7 @@ int main(void) {
int oldfree;
sdsfree(x);
sdsfree(y);
x = sdsnew("0");
sh = (void*) (x-(sizeof(struct sdshdr)));
test_cond("sdsnew() free/len buffers", sh->len == 1 && sh->free == 0);
@@ -1095,6 +1096,8 @@ int main(void) {
test_cond("sdsIncrLen() -- content", x[0] == '0' && x[1] == '1');
test_cond("sdsIncrLen() -- len", sh->len == 2);
test_cond("sdsIncrLen() -- free", sh->free == oldfree-1);
sdsfree(x);
}
}
test_report()
+58 -4
View File
@@ -923,6 +923,7 @@ sentinelRedisInstance *createSentinelRedisInstance(char *name, int flags, char *
else if (flags & SRI_SENTINEL) table = master->sentinels;
sdsname = sdsnew(name);
if (dictFind(table,sdsname)) {
releaseSentinelAddr(addr);
sdsfree(sdsname);
errno = EBUSY;
return NULL;
@@ -1269,10 +1270,7 @@ int sentinelResetMasterAndChangeAddress(sentinelRedisInstance *master, char *ip,
slave = createSentinelRedisInstance(NULL,SRI_SLAVE,slaves[j]->ip,
slaves[j]->port, master->quorum, master);
releaseSentinelAddr(slaves[j]);
if (slave) {
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
if (slave) sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
}
zfree(slaves);
@@ -1844,6 +1842,7 @@ void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info) {
atoi(port), ri->quorum, ri)) != NULL)
{
sentinelEvent(REDIS_NOTICE,"+slave",slave,"%@");
sentinelFlushConfig();
}
}
}
@@ -2615,6 +2614,31 @@ sentinelRedisInstance *sentinelGetMasterByNameOrReplyError(redisClient *c,
return ri;
}
#define SENTINEL_ISQR_OK 0
#define SENTINEL_ISQR_NOQUORUM (1<<0)
#define SENTINEL_ISQR_NOAUTH (1<<1)
int sentinelIsQuorumReachable(sentinelRedisInstance *master, int *usableptr) {
dictIterator *di;
dictEntry *de;
int usable = 1; /* Number of usable Sentinels. Init to 1 to count myself. */
int result = SENTINEL_ISQR_OK;
int voters = dictSize(master->sentinels)+1; /* Known Sentinels + myself. */
di = dictGetIterator(master->sentinels);
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
if (ri->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;
usable++;
}
dictReleaseIterator(di);
if (usable < (int)master->quorum) result |= SENTINEL_ISQR_NOQUORUM;
if (usable < voters/2+1) result |= SENTINEL_ISQR_NOAUTH;
if (usableptr) *usableptr = usable;
return result;
}
void sentinelCommand(redisClient *c) {
if (!strcasecmp(c->argv[1]->ptr,"masters")) {
/* SENTINEL MASTERS */
@@ -2765,6 +2789,10 @@ void sentinelCommand(redisClient *c) {
sentinelEvent(REDIS_WARNING,"+monitor",ri,"%@ quorum %d",ri->quorum);
addReply(c,shared.ok);
}
} else if (!strcasecmp(c->argv[1]->ptr,"flushconfig")) {
sentinelFlushConfig();
addReply(c,shared.ok);
return;
} else if (!strcasecmp(c->argv[1]->ptr,"remove")) {
/* SENTINEL REMOVE <name> */
sentinelRedisInstance *ri;
@@ -2775,6 +2803,32 @@ void sentinelCommand(redisClient *c) {
dictDelete(sentinel.masters,c->argv[2]->ptr);
sentinelFlushConfig();
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"ckquorum")) {
/* SENTINEL CKQUORUM <name> */
sentinelRedisInstance *ri;
int usable;
if ((ri = sentinelGetMasterByNameOrReplyError(c,c->argv[2]))
== NULL) return;
int result = sentinelIsQuorumReachable(ri,&usable);
if (result == SENTINEL_ISQR_OK) {
addReplySds(c, sdscatfmt(sdsempty(),
"+OK %i usable Sentinels. Quorum and failover authorization "
"can be reached\r\n",usable));
} else {
sds e = sdscatfmt(sdsempty(),
"-NOQUORUM %i usable Sentinels. ",usable);
if (result & SENTINEL_ISQR_NOQUORUM)
e = sdscat(e,"Not enough available Sentinels to reach the"
" specified quorum for this master");
if (result & SENTINEL_ISQR_NOAUTH) {
if (result & SENTINEL_ISQR_NOQUORUM) e = sdscat(e,". ");
e = sdscat(e, "Not enough available Sentinels to reach the"
" majority and authorize a failover");
}
e = sdscat(e,"\r\n");
addReplySds(c,e);
}
} else if (!strcasecmp(c->argv[1]->ptr,"set")) {
if (c->argc < 3 || c->argc % 2 == 0) goto numargserr;
sentinelSetCommand(c);
+7 -7
View File
@@ -23,7 +23,7 @@ A million repetitions of "a"
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* for u_int*_t */
#include <stdint.h>
#include "solarisfixes.h"
#include "sha1.h"
#include "config.h"
@@ -53,12 +53,12 @@ A million repetitions of "a"
/* Hash a single 512-bit block. This is the core of the algorithm. */
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
{
u_int32_t a, b, c, d, e;
uint32_t a, b, c, d, e;
typedef union {
unsigned char c[64];
u_int32_t l[16];
uint32_t l[16];
} CHAR64LONG16;
#ifdef SHA1HANDSOFF
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
@@ -128,9 +128,9 @@ void SHA1Init(SHA1_CTX* context)
/* Run your data through this. */
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
{
u_int32_t i, j;
uint32_t i, j;
j = context->count[0];
if ((context->count[0] += len << 3) < j)
@@ -168,7 +168,7 @@ void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
for (i = 0; i < 2; i++)
{
u_int32_t t = context->count[i];
uint32_t t = context->count[i];
int j;
for (j = 0; j < 4; t >>= 8, j++)
+4 -4
View File
@@ -6,12 +6,12 @@ By Steve Reid <steve@edmweb.com>
*/
typedef struct {
u_int32_t state[5];
u_int32_t count[2];
uint32_t state[5];
uint32_t count[2];
unsigned char buffer[64];
} SHA1_CTX;
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64]);
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len);
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+1 -1
View File
@@ -209,7 +209,7 @@ void sortCommand(redisClient *c) {
}
/* Create a list of operations to perform for every sorted element.
* Operations can be GET/DEL/INCR/DECR */
* Operations can be GET */
operations = listCreate();
listSetFreeMethod(operations,zfree);
j = 2; /* options start at argv[2] */
+1 -1
View File
@@ -321,7 +321,7 @@ void smoveCommand(redisClient *c) {
/* If srcset and dstset are equal, SMOVE is a no-op */
if (srcset == dstset) {
addReply(c,shared.cone);
addReply(c,setTypeIsMember(srcset,ele) ? shared.cone : shared.czero);
return;
}
+77 -15
View File
@@ -1171,33 +1171,82 @@ void zsetConvert(robj *zobj, int encoding) {
*----------------------------------------------------------------------------*/
/* This generic command implements both ZADD and ZINCRBY. */
void zaddGenericCommand(redisClient *c, int incr) {
#define ZADD_NONE 0
#define ZADD_INCR (1<<0) /* Increment the score instead of setting it. */
#define ZADD_NX (1<<1) /* Don't touch elements not already existing. */
#define ZADD_XX (1<<2) /* Only touch elements already exisitng. */
#define ZADD_CH (1<<3) /* Return num of elements added or updated. */
void zaddGenericCommand(redisClient *c, int flags) {
static char *nanerr = "resulting score is not a number (NaN)";
robj *key = c->argv[1];
robj *ele;
robj *zobj;
robj *curobj;
double score = 0, *scores = NULL, curscore = 0.0;
int j, elements = (c->argc-2)/2;
int added = 0, updated = 0;
int j, elements;
int scoreidx = 0;
/* The following vars are used in order to track what the command actually
* did during the execution, to reply to the client and to trigger the
* notification of keyspace change. */
int added = 0; /* Number of new elements added. */
int updated = 0; /* Number of elements with updated score. */
int processed = 0; /* Number of elements processed, may remain zero with
options like XX. */
if (c->argc % 2) {
/* Parse options. At the end 'scoreidx' is set to the argument position
* of the score of the first score-element pair. */
scoreidx = 2;
while(scoreidx < c->argc) {
char *opt = c->argv[scoreidx]->ptr;
if (!strcasecmp(opt,"nx")) flags |= ZADD_NX;
else if (!strcasecmp(opt,"xx")) flags |= ZADD_XX;
else if (!strcasecmp(opt,"ch")) flags |= ZADD_CH;
else if (!strcasecmp(opt,"incr")) flags |= ZADD_INCR;
else break;
scoreidx++;
}
/* Turn options into simple to check vars. */
int incr = (flags & ZADD_INCR) != 0;
int nx = (flags & ZADD_NX) != 0;
int xx = (flags & ZADD_XX) != 0;
int ch = (flags & ZADD_CH) != 0;
/* After the options, we expect to have an even number of args, since
* we expect any number of score-element pairs. */
elements = c->argc-scoreidx;
if (elements % 2) {
addReply(c,shared.syntaxerr);
return;
}
elements /= 2; /* Now this holds the number of score-element pairs. */
/* Check for incompatible options. */
if (nx && xx) {
addReplyError(c,
"XX and NX options at the same time are not compatible");
return;
}
if (incr && elements > 1) {
addReplyError(c,
"INCR option supports a single increment-element pair");
return;
}
/* Start parsing all the scores, we need to emit any syntax error
* before executing additions to the sorted set, as the command should
* either execute fully or nothing at all. */
scores = zmalloc(sizeof(double)*elements);
for (j = 0; j < elements; j++) {
if (getDoubleFromObjectOrReply(c,c->argv[2+j*2],&scores[j],NULL)
if (getDoubleFromObjectOrReply(c,c->argv[scoreidx+j*2],&scores[j],NULL)
!= REDIS_OK) goto cleanup;
}
/* Lookup the key and create the sorted set if does not exist. */
zobj = lookupKeyWrite(c->db,key);
if (zobj == NULL) {
if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */
if (server.zset_max_ziplist_entries == 0 ||
server.zset_max_ziplist_value < sdslen(c->argv[3]->ptr))
{
@@ -1220,8 +1269,9 @@ void zaddGenericCommand(redisClient *c, int incr) {
unsigned char *eptr;
/* Prefer non-encoded element when dealing with ziplists. */
ele = c->argv[3+j*2];
ele = c->argv[scoreidx+1+j*2];
if ((eptr = zzlFind(zobj->ptr,ele,&curscore)) != NULL) {
if (nx) continue;
if (incr) {
score += curscore;
if (isnan(score)) {
@@ -1237,7 +1287,8 @@ void zaddGenericCommand(redisClient *c, int incr) {
server.dirty++;
updated++;
}
} else {
processed++;
} else if (!xx) {
/* Optimize: check if the element is too large or the list
* becomes too long *before* executing zzlInsert. */
zobj->ptr = zzlInsert(zobj->ptr,ele,score);
@@ -1247,15 +1298,18 @@ void zaddGenericCommand(redisClient *c, int incr) {
zsetConvert(zobj,REDIS_ENCODING_SKIPLIST);
server.dirty++;
added++;
processed++;
}
} else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {
zset *zs = zobj->ptr;
zskiplistNode *znode;
dictEntry *de;
ele = c->argv[3+j*2] = tryObjectEncoding(c->argv[3+j*2]);
ele = c->argv[scoreidx+1+j*2] =
tryObjectEncoding(c->argv[scoreidx+1+j*2]);
de = dictFind(zs->dict,ele);
if (de != NULL) {
if (nx) continue;
curobj = dictGetKey(de);
curscore = *(double*)dictGetVal(de);
@@ -1280,22 +1334,30 @@ void zaddGenericCommand(redisClient *c, int incr) {
server.dirty++;
updated++;
}
} else {
processed++;
} else if (!xx) {
znode = zslInsert(zs->zsl,score,ele);
incrRefCount(ele); /* Inserted in skiplist. */
redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele,&znode->score) == DICT_OK);
incrRefCount(ele); /* Added to dictionary. */
server.dirty++;
added++;
processed++;
}
} else {
redisPanic("Unknown sorted set encoding");
}
}
if (incr) /* ZINCRBY */
addReplyDouble(c,score);
else /* ZADD */
addReplyLongLong(c,added);
reply_to_client:
if (incr) { /* ZINCRBY or INCR option. */
if (processed)
addReplyDouble(c,score);
else
addReply(c,shared.nullbulk);
} else { /* ZADD. */
addReplyLongLong(c,ch ? added+updated : added);
}
cleanup:
zfree(scores);
@@ -1307,11 +1369,11 @@ cleanup:
}
void zaddCommand(redisClient *c) {
zaddGenericCommand(c,0);
zaddGenericCommand(c,ZADD_NONE);
}
void zincrbyCommand(redisClient *c) {
zaddGenericCommand(c,1);
zaddGenericCommand(c,ZADD_INCR);
}
void zremCommand(redisClient *c) {
+1 -1
View File
@@ -1 +1 @@
#define REDIS_VERSION "3.0.0"
#define REDIS_VERSION "3.0.2"
+34
View File
@@ -0,0 +1,34 @@
# Test for the SENTINEL CKQUORUM command
source "../tests/includes/init-tests.tcl"
set num_sentinels [llength $::sentinel_instances]
test "CKQUORUM reports OK and the right amount of Sentinels" {
foreach_sentinel_id id {
assert_match "*OK $num_sentinels usable*" [S $id SENTINEL CKQUORUM mymaster]
}
}
test "CKQUORUM detects quorum cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum [expr {$num_sentinels+1}]
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
}
test "CKQUORUM detects failover authorization cannot be reached" {
set orig_quorum [expr {$num_sentinels/2+1}]
S 0 SENTINEL SET mymaster quorum 1
kill_instance sentinel 1
kill_instance sentinel 2
kill_instance sentinel 3
after 5000
catch {[S 0 SENTINEL CKQUORUM mymaster]} err
assert_match "*NOQUORUM*" $err
S 0 SENTINEL SET mymaster quorum $orig_quorum
restart_instance sentinel 1
restart_instance sentinel 2
restart_instance sentinel 3
}
+1
View File
@@ -450,6 +450,7 @@ start_server {
test "SMOVE non existing key" {
setup_move
assert_equal 0 [r smove myset1 myset2 foo]
assert_equal 0 [r smove myset1 myset1 foo]
assert_equal {1 a b} [lsort [r smembers myset1]]
assert_equal {2 3 4} [lsort [r smembers myset2]]
}
+80
View File
@@ -43,6 +43,84 @@ start_server {tags {"zset"}} {
assert_error "*not*float*" {r zadd myzset nan abc}
}
test "ZADD with options syntax error with incomplete pair" {
r del ztmp
catch {r zadd ztmp xx 10 x 20} err
set err
} {ERR*}
test "ZADD XX option without key - $encoding" {
r del ztmp
assert {[r zadd ztmp xx 10 x] == 0}
assert {[r type ztmp] eq {none}}
}
test "ZADD XX existing key - $encoding" {
r del ztmp
r zadd ztmp 10 x
assert {[r zadd ztmp xx 20 y] == 0}
assert {[r zcard ztmp] == 1}
}
test "ZADD XX returns the number of elements actually added" {
r del ztmp
r zadd ztmp 10 x
set retval [r zadd ztmp 10 x 20 y 30 z]
assert {$retval == 2}
}
test "ZADD XX updates existing elements score" {
r del ztmp
r zadd ztmp 10 x 20 y 30 z
r zadd ztmp xx 5 foo 11 x 21 y 40 zap
assert {[r zcard ztmp] == 3}
assert {[r zscore ztmp x] == 11}
assert {[r zscore ztmp y] == 21}
}
test "ZADD XX and NX are not compatible" {
r del ztmp
catch {r zadd ztmp xx nx 10 x} err
set err
} {ERR*}
test "ZADD NX with non exisitng key" {
r del ztmp
r zadd ztmp nx 10 x 20 y 30 z
assert {[r zcard ztmp] == 3}
}
test "ZADD NX only add new elements without updating old ones" {
r del ztmp
r zadd ztmp 10 x 20 y 30 z
assert {[r zadd ztmp nx 11 x 21 y 100 a 200 b] == 2}
assert {[r zscore ztmp x] == 10}
assert {[r zscore ztmp y] == 20}
assert {[r zscore ztmp a] == 100}
assert {[r zscore ztmp b] == 200}
}
test "ZADD INCR works like ZINCRBY" {
r del ztmp
r zadd ztmp 10 x 20 y 30 z
r zadd ztmp INCR 15 x
assert {[r zscore ztmp x] == 25}
}
test "ZADD INCR works with a single score-elemenet pair" {
r del ztmp
r zadd ztmp 10 x 20 y 30 z
catch {r zadd ztmp INCR 15 x 10 y} err
set err
} {ERR*}
test "ZADD CH option changes return value to all changed elements" {
r del ztmp
r zadd ztmp 10 x 20 y 30 z
assert {[r zadd ztmp 11 x 21 y 30 z] == 0}
assert {[r zadd ztmp ch 12 x 22 y 30 z] == 2}
}
test "ZINCRBY calls leading to NaN result in error" {
r zincrby myzset +inf abc
assert_error "*NaN*" {r zincrby myzset -inf abc}
@@ -77,6 +155,8 @@ start_server {tags {"zset"}} {
}
test "ZCARD basics - $encoding" {
r del ztmp
r zadd ztmp 10 a 20 b 30 c
assert_equal 3 [r zcard ztmp]
assert_equal 0 [r zcard zdoesntexist]
}