Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
990cd2c835 | ||
|
|
635119ded1 | ||
|
|
f11666847a | ||
|
|
3983dac12b | ||
|
|
befcbfbe7f | ||
|
|
590f537420 | ||
|
|
37578f2ecf | ||
|
|
299d5a4b2c | ||
|
|
3dcf42697b | ||
|
|
cd13249b55 | ||
|
|
75d66a7afd | ||
|
|
1565106673 | ||
|
|
2717be6392 | ||
|
|
9d797fe128 | ||
|
|
50571f5738 | ||
|
|
9176f4b945 | ||
|
|
e6043981f5 | ||
|
|
fd5f229ff0 | ||
|
|
bab437bef3 | ||
|
|
0aa0cdf7cc | ||
|
|
5727d7ec2f | ||
|
|
1cb6effdc8 | ||
|
|
13e8e53806 | ||
|
|
c9ab43ef7c | ||
|
|
bf50875353 | ||
|
|
8774228cc5 | ||
|
|
ab900303b8 | ||
|
|
7fcca9baef | ||
|
|
41963fe676 | ||
|
|
97d50344c4 | ||
|
|
e39849808c | ||
|
|
84a4f20278 | ||
|
|
5aa2525006 | ||
|
|
7018d27dfe | ||
|
|
d557144ebb | ||
|
|
a309388dff | ||
|
|
55442262e1 | ||
|
|
e3641c8dee | ||
|
|
9ce105add5 | ||
|
|
e084a394a6 | ||
|
|
61c78a5215 | ||
|
|
d067e3342f | ||
|
|
2b0f03e4f2 | ||
|
|
ad949f2329 | ||
|
|
351663bd7b | ||
|
|
d70ac1d105 | ||
|
|
8da9a167e0 | ||
|
|
13546adcb9 | ||
|
|
373facf81c | ||
|
|
29e3ff9bec | ||
|
|
5116130d42 | ||
|
|
3b46cf97de | ||
|
|
f59b4b93d9 | ||
|
|
dd80fedf6f | ||
|
|
697d3abeff | ||
|
|
c9c04b11b9 | ||
|
|
d3b4992450 | ||
|
|
6a33952b8a | ||
|
|
bacb04f89f | ||
|
|
b83f9fea81 | ||
|
|
10dbb5cd33 |
+213
@@ -10,6 +10,219 @@ 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.2.13 Released Mon Mar 18 17:24:10 CEST 2019
|
||||
================================================================================
|
||||
|
||||
Ciritcal fixes backported from Redis 5.
|
||||
|
||||
================================================================================
|
||||
Redis 3.2.12 Released Wed Jun 13 12:43:01 CEST 2018
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency CRITICAL:
|
||||
|
||||
* Multilple security issues fixed.
|
||||
* Backport of an older AOF fsync=always fix. Check 4.x release notes.
|
||||
* Backport of a *SCAN bug. Sometimes elements could be missing from the scan.
|
||||
* Other minor things.
|
||||
|
||||
================================================================================
|
||||
Redis 3.2.11 Released Thu Sep 21 15:47:53 CEST 2017
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency HIGH: Potentially critical bugs fixed.
|
||||
|
||||
AOF flush on SHUTDOWN did not cared to really write the AOF buffers
|
||||
(not in the kernel but in the Redis process memory) to disk before exiting.
|
||||
Calling SHUTDOWN during traffic resulted into not every operation to be
|
||||
persisted on disk.
|
||||
|
||||
Oran Agra (and also Buğra Gedik) in commit 8651e5d5:
|
||||
Flush append only buffers before existing.
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
================================================================================
|
||||
Redis 3.2.10 Released Fri Jul 28 17:49:07 CEST 2017
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency MODERATE: This release contains a number of fixes that are not
|
||||
critical in the general case, but quite important
|
||||
in certain use cases. Upgrading is suggested but not
|
||||
mandatory.
|
||||
|
||||
Dear Redis users,
|
||||
|
||||
while 4.0.1 is already out, this is a maintenance release of Redis 3.2.
|
||||
It fixes several bugs, the most interesting ones are the following:
|
||||
|
||||
1. INFO server.stat_net_output_bytes field was computed in a wrong way, now
|
||||
the output is correct.
|
||||
2. SET with EX/PX options are now propagated correctly in the AOF, using
|
||||
the absolute times.
|
||||
3. CLIENT PAUSE was fixed to prevent eviction of keys, so that master and
|
||||
slave continue to be consistent for the time the pause is active.
|
||||
4. GEORADIUS(BYMEMBER)_RO variants for read-only operations added.
|
||||
5. HyperLogLog sanity detection fixed in an edge case.
|
||||
6. Redis Cluster crash due to mis-handling of hidden node flags fixed.
|
||||
|
||||
If you think you may be affected by the above problems, upgrading is a
|
||||
good idea.
|
||||
|
||||
Have a nice day,
|
||||
Salvatore
|
||||
|
||||
Full history of commits in this release:
|
||||
|
||||
Jan-Erik Rediger in commit bf508753:
|
||||
Check that the whole first argument is a number
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
WuYunlong in commit 8774228c:
|
||||
fix rewrite config: auto-aof-rewrite-min-size
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
Byron Grobe in commit ab900303:
|
||||
Fixed issue #1996 (Missing '-' in help message for redis-benchmark)
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
Jan-Erik Rediger in commit 7fcca9ba:
|
||||
Don't use extended Regexp Syntax
|
||||
1 file changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
Leon Chen in commit 41963fe6:
|
||||
fix return wrong value of clusterDelNodeSlots
|
||||
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||
|
||||
Leon Chen in commit 97d50344:
|
||||
fix mismatch argument
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
liangsijian in commit e3984980:
|
||||
Fix lua ldb command log
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
antirez in commit 84a4f202:
|
||||
Make representClusterNodeFlags() more robust.
|
||||
1 file changed, 17 insertions(+), 16 deletions(-)
|
||||
|
||||
antirez in commit 5aa25250:
|
||||
Fix isHLLObjectOrReply() to handle integer encoded strings.
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
antirez in commit 7018d27d:
|
||||
Fix abort typo in Lua debugger help screen.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
antirez in commit d557144e:
|
||||
Added GEORADIUS(BYMEMBER)_RO variants for read-only operations.
|
||||
3 files changed, 32 insertions(+), 11 deletions(-)
|
||||
|
||||
Suraj Narkhede in commit a309388d:
|
||||
Fix following issues in blocking commands: 1. brpop last key index, thus checking all keys for slots. 2. Memory leak in clusterRedirectBlockedClientIfNeeded. 3. Remove while loop in clusterRedirectBlockedClientIfNeeded.
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
Suraj Narkhede in commit 55442262:
|
||||
Fix brpop command table entry and redirect blocked clients.
|
||||
2 files changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
antirez in commit e3641c8d:
|
||||
Fix PERSIST expired key resuscitation issue #4048.
|
||||
1 file changed, 4 insertions(+), 7 deletions(-)
|
||||
|
||||
Antonio Mallia in commit 9ce105ad:
|
||||
Removed duplicate 'sys/socket.h' include
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
Zachary Marquez in commit e084a394:
|
||||
Prevent expirations and evictions while paused
|
||||
1 file changed, 10 insertions(+)
|
||||
|
||||
antirez in commit 61c78a52:
|
||||
Collect fork() timing info only if fork succeeded.
|
||||
1 file changed, 4 insertions(+), 3 deletions(-)
|
||||
|
||||
antirez in commit d067e334:
|
||||
Aesthetic changes to #4068 PR to conform to Redis coding standard.
|
||||
1 file changed, 6 insertions(+), 7 deletions(-)
|
||||
|
||||
xuzhou in commit 2b0f03e4:
|
||||
Optimize set command with ex/px when updating aof.
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
antirez in commit ad949f23:
|
||||
redis-benchmark: add -t hset target.
|
||||
1 file changed, 7 insertions(+)
|
||||
|
||||
xuzhou in commit 351663bd:
|
||||
Fix set with ex/px option when propagated to aof
|
||||
4 files changed, 36 insertions(+), 1 deletion(-)
|
||||
|
||||
minghang.zmh in commit d70ac1d1:
|
||||
fix server.stat_net_output_bytes calc bug
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
xuchengxuan in commit 8da9a167:
|
||||
Fixed comments of slowlog duration
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
cbgbt in commit 13546adc:
|
||||
cli: Only print elapsed time on OUTPUT_STANDARD
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
Aric Huang in commit 373facf8:
|
||||
(fix) Update create-cluster README
|
||||
1 file changed, 4 insertions(+), 4 deletions(-)
|
||||
|
||||
Salvatore Sanfilippo in commit 29e3ff9b:
|
||||
Merge pull request #3926 from QuChen88/3.2
|
||||
|
||||
================================================================================
|
||||
Redis 3.2.9 Released Mon May 17 17:35:38 CEST 2017
|
||||
================================================================================
|
||||
|
||||
Upgrade urgency LOW: A few rarely harmful bugs were fixed.
|
||||
|
||||
This release just fixes bugs that are unlikely to cause serious problems
|
||||
so there is no need to update ASAP. Please, see the list of commits
|
||||
for the details on the bugs fixed and credits:
|
||||
|
||||
antirez in commit 3b46cf97:
|
||||
redis-cli --bigkeys: show error when TYPE fails.
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
antirez in commit f59b4b93:
|
||||
Fix preprocessor if/else chain broken in order to fix #3927.
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
antirez in commit dd80fedf:
|
||||
Fix zmalloc_get_memory_size() ifdefs to actually use the else branch.
|
||||
1 file changed, 2 deletions(-)
|
||||
|
||||
antirez in commit 697d3abe:
|
||||
Set lua-time-limit default value at safe place.
|
||||
2 files changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
antirez in commit c9c04b11:
|
||||
Fix #3848 by closing the descriptor on error.
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
张文康 in commit d3b49924:
|
||||
update block->free after some diff data are written to the child process
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
antirez in commit 6a33952b:
|
||||
Test: fix, hopefully, false PSYNC failure like in issue #2715.
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
John.Koepi in commit b83f9fea:
|
||||
fix #2883, #2857 pipe fds leak when fork() failed on bg aof rw
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
antirez in commit 10dbb5cd:
|
||||
Don't leak file descriptor on syncWithMaster().
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
================================================================================
|
||||
Redis 3.2.8 Released Sun Feb 12 16:11:18 CET 2017
|
||||
================================================================================
|
||||
|
||||
Vendored
+10
@@ -385,6 +385,7 @@ void mp_encode_lua_table_as_array(lua_State *L, mp_buf *buf, int level) {
|
||||
#endif
|
||||
|
||||
mp_encode_array(L,buf,len);
|
||||
luaL_checkstack(L, 1, "in function mp_encode_lua_table_as_array");
|
||||
for (j = 1; j <= len; j++) {
|
||||
lua_pushnumber(L,j);
|
||||
lua_gettable(L,-2);
|
||||
@@ -400,6 +401,7 @@ void mp_encode_lua_table_as_map(lua_State *L, mp_buf *buf, int level) {
|
||||
* Lua API, we need to iterate a first time. Note that an alternative
|
||||
* would be to do a single run, and then hack the buffer to insert the
|
||||
* map opcodes for message pack. Too hackish for this lib. */
|
||||
luaL_checkstack(L, 3, "in function mp_encode_lua_table_as_map");
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L,-2)) {
|
||||
lua_pop(L,1); /* remove value, keep key for next iteration. */
|
||||
@@ -515,10 +517,14 @@ int mp_pack(lua_State *L) {
|
||||
if (nargs == 0)
|
||||
return luaL_argerror(L, 0, "MessagePack pack needs input.");
|
||||
|
||||
if (!lua_checkstack(L, nargs))
|
||||
return luaL_argerror(L, 0, "Too many arguments for MessagePack pack.");
|
||||
|
||||
buf = mp_buf_new(L);
|
||||
for(i = 1; i <= nargs; i++) {
|
||||
/* Copy argument i to top of stack for _encode processing;
|
||||
* the encode function pops it from the stack when complete. */
|
||||
luaL_checkstack(L, 1, "in function mp_check");
|
||||
lua_pushvalue(L, i);
|
||||
|
||||
mp_encode_lua_type(L,buf,0);
|
||||
@@ -547,6 +553,7 @@ void mp_decode_to_lua_array(lua_State *L, mp_cur *c, size_t len) {
|
||||
int index = 1;
|
||||
|
||||
lua_newtable(L);
|
||||
luaL_checkstack(L, 1, "in function mp_decode_to_lua_array");
|
||||
while(len--) {
|
||||
lua_pushnumber(L,index++);
|
||||
mp_decode_to_lua_type(L,c);
|
||||
@@ -821,6 +828,9 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
|
||||
* subtract the entire buffer size from the unprocessed size
|
||||
* to get our next start offset */
|
||||
int offset = len - c.left;
|
||||
|
||||
luaL_checkstack(L, 1, "in function mp_unpack_full");
|
||||
|
||||
/* Return offset -1 when we have have processed the entire buffer. */
|
||||
lua_pushinteger(L, c.left == 0 ? -1 : offset);
|
||||
/* Results are returned with the arg elements still
|
||||
|
||||
Vendored
+29
-25
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
** {======================================================
|
||||
** Library for packing/unpacking structures.
|
||||
** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
|
||||
** $Id: struct.c,v 1.7 2018/05/11 22:04:31 roberto Exp $
|
||||
** See Copyright Notice at the end of this file
|
||||
** =======================================================
|
||||
*/
|
||||
@@ -15,8 +15,8 @@
|
||||
** h/H - signed/unsigned short
|
||||
** l/L - signed/unsigned long
|
||||
** T - size_t
|
||||
** i/In - signed/unsigned integer with size `n' (default is size of int)
|
||||
** cn - sequence of `n' chars (from/to a string); when packing, n==0 means
|
||||
** i/In - signed/unsigned integer with size 'n' (default is size of int)
|
||||
** cn - sequence of 'n' chars (from/to a string); when packing, n==0 means
|
||||
the whole string; when unpacking, n==0 means use the previous
|
||||
read number as the string length
|
||||
** s - zero-terminated string
|
||||
@@ -89,14 +89,12 @@ typedef struct Header {
|
||||
} Header;
|
||||
|
||||
|
||||
static int getnum (lua_State *L, const char **fmt, int df) {
|
||||
static int getnum (const char **fmt, int df) {
|
||||
if (!isdigit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
int a = 0;
|
||||
do {
|
||||
if (a > (INT_MAX / 10) || a * 10 > (INT_MAX - (**fmt - '0')))
|
||||
luaL_error(L, "integral size overflow");
|
||||
a = a*10 + *((*fmt)++) - '0';
|
||||
} while (isdigit(**fmt));
|
||||
return a;
|
||||
@@ -117,9 +115,9 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
|
||||
case 'f': return sizeof(float);
|
||||
case 'd': return sizeof(double);
|
||||
case 'x': return 1;
|
||||
case 'c': return getnum(L, fmt, 1);
|
||||
case 'c': return getnum(fmt, 1);
|
||||
case 'i': case 'I': {
|
||||
int sz = getnum(L, fmt, sizeof(int));
|
||||
int sz = getnum(fmt, sizeof(int));
|
||||
if (sz > MAXINTSIZE)
|
||||
luaL_error(L, "integral size %d is larger than limit of %d",
|
||||
sz, MAXINTSIZE);
|
||||
@@ -152,7 +150,7 @@ static void controloptions (lua_State *L, int opt, const char **fmt,
|
||||
case '>': h->endian = BIG; return;
|
||||
case '<': h->endian = LITTLE; return;
|
||||
case '!': {
|
||||
int a = getnum(L, fmt, MAXALIGN);
|
||||
int a = getnum(fmt, MAXALIGN);
|
||||
if (!isp2(a))
|
||||
luaL_error(L, "alignment %d is not a power of 2", a);
|
||||
h->align = a;
|
||||
@@ -295,21 +293,26 @@ static int b_unpack (lua_State *L) {
|
||||
const char *fmt = luaL_checkstring(L, 1);
|
||||
size_t ld;
|
||||
const char *data = luaL_checklstring(L, 2, &ld);
|
||||
size_t pos = luaL_optinteger(L, 3, 1) - 1;
|
||||
size_t pos = luaL_optinteger(L, 3, 1);
|
||||
luaL_argcheck(L, pos > 0, 3, "offset must be 1 or greater");
|
||||
pos--; /* Lua indexes are 1-based, but here we want 0-based for C
|
||||
* pointer math. */
|
||||
int n = 0; /* number of results */
|
||||
defaultoptions(&h);
|
||||
lua_settop(L, 2);
|
||||
while (*fmt) {
|
||||
int opt = *fmt++;
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
pos += gettoalign(pos, &h, opt, size);
|
||||
luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
|
||||
luaL_checkstack(L, 1, "too many results");
|
||||
luaL_argcheck(L, size <= ld && pos <= ld - size,
|
||||
2, "data string too short");
|
||||
/* stack space for item + next position */
|
||||
luaL_checkstack(L, 2, "too many results");
|
||||
switch (opt) {
|
||||
case 'b': case 'B': case 'h': case 'H':
|
||||
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
|
||||
int issigned = islower(opt);
|
||||
lua_Number res = getinteger(data+pos, h.endian, issigned, size);
|
||||
lua_pushnumber(L, res);
|
||||
lua_pushnumber(L, res); n++;
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
@@ -319,25 +322,26 @@ static int b_unpack (lua_State *L) {
|
||||
float f;
|
||||
memcpy(&f, data+pos, size);
|
||||
correctbytes((char *)&f, sizeof(f), h.endian);
|
||||
lua_pushnumber(L, f);
|
||||
lua_pushnumber(L, f); n++;
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
double d;
|
||||
memcpy(&d, data+pos, size);
|
||||
correctbytes((char *)&d, sizeof(d), h.endian);
|
||||
lua_pushnumber(L, d);
|
||||
lua_pushnumber(L, d); n++;
|
||||
break;
|
||||
}
|
||||
case 'c': {
|
||||
if (size == 0) {
|
||||
if (!lua_isnumber(L, -1))
|
||||
luaL_error(L, "format `c0' needs a previous size");
|
||||
if (n == 0 || !lua_isnumber(L, -1))
|
||||
luaL_error(L, "format 'c0' needs a previous size");
|
||||
size = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
luaL_argcheck(L, pos+size <= ld, 2, "data string too short");
|
||||
lua_pop(L, 1); n--;
|
||||
luaL_argcheck(L, size <= ld && pos <= ld - size,
|
||||
2, "data string too short");
|
||||
}
|
||||
lua_pushlstring(L, data+pos, size);
|
||||
lua_pushlstring(L, data+pos, size); n++;
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
@@ -345,15 +349,15 @@ static int b_unpack (lua_State *L) {
|
||||
if (e == NULL)
|
||||
luaL_error(L, "unfinished string in data");
|
||||
size = (e - (data+pos)) + 1;
|
||||
lua_pushlstring(L, data+pos, size - 1);
|
||||
lua_pushlstring(L, data+pos, size - 1); n++;
|
||||
break;
|
||||
}
|
||||
default: controloptions(L, opt, &fmt, &h);
|
||||
}
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos + 1);
|
||||
return lua_gettop(L) - 2;
|
||||
lua_pushinteger(L, pos + 1); /* next position */
|
||||
return n + 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -399,7 +403,7 @@ LUALIB_API int luaopen_struct (lua_State *L) {
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2010-2012 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 2010-2018 Lua.org, PUC-Rio. All rights reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -194,3 +194,12 @@ sentinel failover-timeout mymaster 180000
|
||||
#
|
||||
# sentinel client-reconfig-script mymaster /var/redis/reconfig.sh
|
||||
|
||||
# SECURITY
|
||||
#
|
||||
# By default SENTINEL SET will not be able to change the notification-script
|
||||
# and client-reconfig-script at runtime. This avoids a trivial security issue
|
||||
# where clients can set the script to anything and trigger a failover in order
|
||||
# to get the program executed.
|
||||
|
||||
sentinel deny-scripts-reconfig yes
|
||||
|
||||
|
||||
@@ -158,6 +158,10 @@ void aeDeleteFileEvent(aeEventLoop *eventLoop, int fd, int mask)
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
if (fe->mask == AE_NONE) return;
|
||||
|
||||
/* We want to always remove AE_BARRIER if set when AE_WRITABLE
|
||||
* is removed. */
|
||||
if (mask & AE_WRITABLE) mask |= AE_BARRIER;
|
||||
|
||||
aeApiDelEvent(eventLoop, fd, mask);
|
||||
fe->mask = fe->mask & (~mask);
|
||||
if (fd == eventLoop->maxfd && fe->mask == AE_NONE) {
|
||||
@@ -402,19 +406,49 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
|
||||
int mask = eventLoop->fired[j].mask;
|
||||
int fd = eventLoop->fired[j].fd;
|
||||
int rfired = 0;
|
||||
int fired = 0; /* Number of events fired for current fd. */
|
||||
|
||||
/* note the fe->mask & mask & ... code: maybe an already processed
|
||||
* event removed an element that fired and we still didn't
|
||||
* processed, so we check if the event is still valid. */
|
||||
if (fe->mask & mask & AE_READABLE) {
|
||||
rfired = 1;
|
||||
/* Normally we execute the readable event first, and the writable
|
||||
* event laster. This is useful as sometimes we may be able
|
||||
* to serve the reply of a query immediately after processing the
|
||||
* query.
|
||||
*
|
||||
* However if AE_BARRIER is set in the mask, our application is
|
||||
* asking us to do the reverse: never fire the writable event
|
||||
* after the readable. In such a case, we invert the calls.
|
||||
* This is useful when, for instance, we want to do things
|
||||
* in the beforeSleep() hook, like fsynching a file to disk,
|
||||
* before replying to a client. */
|
||||
int invert = fe->mask & AE_BARRIER;
|
||||
|
||||
/* Note the "fe->mask & mask & ..." code: maybe an already
|
||||
* processed event removed an element that fired and we still
|
||||
* didn't processed, so we check if the event is still valid.
|
||||
*
|
||||
* Fire the readable event if the call sequence is not
|
||||
* inverted. */
|
||||
if (!invert && fe->mask & mask & AE_READABLE) {
|
||||
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
fired++;
|
||||
}
|
||||
|
||||
/* Fire the writable event. */
|
||||
if (fe->mask & mask & AE_WRITABLE) {
|
||||
if (!rfired || fe->wfileProc != fe->rfileProc)
|
||||
if (!fired || fe->wfileProc != fe->rfileProc) {
|
||||
fe->wfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
fired++;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we have to invert the call, fire the readable event now
|
||||
* after the writable one. */
|
||||
if (invert && fe->mask & mask & AE_READABLE) {
|
||||
if (!fired || fe->wfileProc != fe->rfileProc) {
|
||||
fe->rfileProc(eventLoop,fd,fe->clientData,mask);
|
||||
fired++;
|
||||
}
|
||||
}
|
||||
|
||||
processed++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,9 +38,14 @@
|
||||
#define AE_OK 0
|
||||
#define AE_ERR -1
|
||||
|
||||
#define AE_NONE 0
|
||||
#define AE_READABLE 1
|
||||
#define AE_WRITABLE 2
|
||||
#define AE_NONE 0 /* No events registered. */
|
||||
#define AE_READABLE 1 /* Fire when descriptor is readable. */
|
||||
#define AE_WRITABLE 2 /* Fire when descriptor is writable. */
|
||||
#define AE_BARRIER 4 /* With WRITABLE, never fire the event if the
|
||||
READABLE event already fired in the same event
|
||||
loop iteration. Useful when you want to persist
|
||||
things to disk before sending replies, and want
|
||||
to do that in a group fashion. */
|
||||
|
||||
#define AE_FILE_EVENTS 1
|
||||
#define AE_TIME_EVENTS 2
|
||||
@@ -63,7 +68,7 @@ typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
|
||||
|
||||
/* File event structure */
|
||||
typedef struct aeFileEvent {
|
||||
int mask; /* one of AE_(READABLE|WRITABLE) */
|
||||
int mask; /* one of AE_(READABLE|WRITABLE|BARRIER) */
|
||||
aeFileProc *rfileProc;
|
||||
aeFileProc *wfileProc;
|
||||
void *clientData;
|
||||
|
||||
+2
-1
@@ -462,7 +462,7 @@ static int anetV6Only(char *err, int s) {
|
||||
|
||||
static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backlog)
|
||||
{
|
||||
int s, rv;
|
||||
int s = -1, rv;
|
||||
char _port[6]; /* strlen("65535") */
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
|
||||
@@ -491,6 +491,7 @@ static int _anetTcpServer(char *err, int port, char *bindaddr, int af, int backl
|
||||
}
|
||||
|
||||
error:
|
||||
if (s != -1) close(s);
|
||||
s = ANET_ERR;
|
||||
end:
|
||||
freeaddrinfo(servinfo);
|
||||
|
||||
@@ -115,6 +115,7 @@ void aofChildWriteDiffData(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
if (nwritten <= 0) return;
|
||||
memmove(block->buf,block->buf+nwritten,block->used-nwritten);
|
||||
block->used -= nwritten;
|
||||
block->free += nwritten;
|
||||
}
|
||||
if (block->used == 0) listDelNode(server.aof_rewrite_buf_blocks,ln);
|
||||
}
|
||||
@@ -535,6 +536,22 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
buf = catAppendOnlyGenericCommand(buf,3,tmpargv);
|
||||
decrRefCount(tmpargv[0]);
|
||||
buf = catAppendOnlyExpireAtCommand(buf,cmd,argv[1],argv[2]);
|
||||
} else if (cmd->proc == setCommand && argc > 3) {
|
||||
int i;
|
||||
robj *exarg = NULL, *pxarg = NULL;
|
||||
/* Translate SET [EX seconds][PX milliseconds] to SET and PEXPIREAT */
|
||||
buf = catAppendOnlyGenericCommand(buf,3,argv);
|
||||
for (i = 3; i < argc; i ++) {
|
||||
if (!strcasecmp(argv[i]->ptr, "ex")) exarg = argv[i+1];
|
||||
if (!strcasecmp(argv[i]->ptr, "px")) pxarg = argv[i+1];
|
||||
}
|
||||
serverAssert(!(exarg && pxarg));
|
||||
if (exarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,server.expireCommand,argv[1],
|
||||
exarg);
|
||||
if (pxarg)
|
||||
buf = catAppendOnlyExpireAtCommand(buf,server.pexpireCommand,argv[1],
|
||||
pxarg);
|
||||
} else {
|
||||
/* All the other commands don't need translation or need the
|
||||
* same translation already operated in the command vector
|
||||
@@ -1295,6 +1312,7 @@ int rewriteAppendOnlyFileBackground(void) {
|
||||
serverLog(LL_WARNING,
|
||||
"Can't rewrite append only file in background: fork: %s",
|
||||
strerror(errno));
|
||||
aofClosePipes();
|
||||
return C_ERR;
|
||||
}
|
||||
serverLog(LL_NOTICE,
|
||||
|
||||
+30
-22
@@ -37,7 +37,6 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/file.h>
|
||||
#include <math.h>
|
||||
@@ -234,6 +233,7 @@ int clusterLoadConfig(char *filename) {
|
||||
*p = '\0';
|
||||
direction = p[1]; /* Either '>' or '<' */
|
||||
slot = atoi(argv[j]+1);
|
||||
if (slot < 0 || slot >= CLUSTER_SLOTS) goto fmterr;
|
||||
p += 3;
|
||||
cn = clusterLookupNode(p);
|
||||
if (!cn) {
|
||||
@@ -253,6 +253,8 @@ int clusterLoadConfig(char *filename) {
|
||||
} else {
|
||||
start = stop = atoi(argv[j]);
|
||||
}
|
||||
if (start < 0 || start >= CLUSTER_SLOTS) goto fmterr;
|
||||
if (stop < 0 || stop >= CLUSTER_SLOTS) goto fmterr;
|
||||
while(start <= stop) clusterAddSlot(n, start++);
|
||||
}
|
||||
|
||||
@@ -1300,13 +1302,15 @@ void clusterProcessGossipSection(clusterMsg *hdr, clusterLink *link) {
|
||||
clusterNode *node;
|
||||
sds ci;
|
||||
|
||||
ci = representClusterNodeFlags(sdsempty(), flags);
|
||||
serverLog(LL_DEBUG,"GOSSIP %.40s %s:%d %s",
|
||||
g->nodename,
|
||||
g->ip,
|
||||
ntohs(g->port),
|
||||
ci);
|
||||
sdsfree(ci);
|
||||
if (server.verbosity == LL_DEBUG) {
|
||||
ci = representClusterNodeFlags(sdsempty(), flags);
|
||||
serverLog(LL_DEBUG,"GOSSIP %.40s %s:%d %s",
|
||||
g->nodename,
|
||||
g->ip,
|
||||
ntohs(g->port),
|
||||
ci);
|
||||
sdsfree(ci);
|
||||
}
|
||||
|
||||
/* Update our state accordingly to the gossip sections */
|
||||
node = clusterLookupNode(g->nodename);
|
||||
@@ -2082,7 +2086,7 @@ void clusterReadHandler(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
* from event handlers that will do stuff with the same link later. */
|
||||
void clusterSendMessage(clusterLink *link, unsigned char *msg, size_t msglen) {
|
||||
if (sdslen(link->sndbuf) == 0 && msglen != 0)
|
||||
aeCreateFileEvent(server.el,link->fd,AE_WRITABLE,
|
||||
aeCreateFileEvent(server.el,link->fd,AE_WRITABLE|AE_BARRIER,
|
||||
clusterWriteHandler,link);
|
||||
|
||||
link->sndbuf = sdscatlen(link->sndbuf, msg, msglen);
|
||||
@@ -2558,9 +2562,10 @@ void clusterSendFailoverAuthIfNeeded(clusterNode *node, clusterMsg *request) {
|
||||
}
|
||||
|
||||
/* We can vote for this slave. */
|
||||
clusterSendFailoverAuth(node);
|
||||
server.cluster->lastVoteEpoch = server.cluster->currentEpoch;
|
||||
node->slaveof->voted_time = mstime();
|
||||
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|CLUSTER_TODO_FSYNC_CONFIG);
|
||||
clusterSendFailoverAuth(node);
|
||||
serverLog(LL_WARNING, "Failover auth granted to %.40s for epoch %llu",
|
||||
node->name, (unsigned long long) server.cluster->currentEpoch);
|
||||
}
|
||||
@@ -3440,8 +3445,10 @@ int clusterDelNodeSlots(clusterNode *node) {
|
||||
int deleted = 0, j;
|
||||
|
||||
for (j = 0; j < CLUSTER_SLOTS; j++) {
|
||||
if (clusterNodeGetSlotBit(node,j)) clusterDelSlot(j);
|
||||
deleted++;
|
||||
if (clusterNodeGetSlotBit(node,j)) {
|
||||
clusterDelSlot(j);
|
||||
deleted++;
|
||||
}
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
@@ -3675,15 +3682,14 @@ static struct redisNodeFlags redisNodeFlagsTable[] = {
|
||||
/* Concatenate the comma separated list of node flags to the given SDS
|
||||
* string 'ci'. */
|
||||
sds representClusterNodeFlags(sds ci, uint16_t flags) {
|
||||
if (flags == 0) {
|
||||
ci = sdscat(ci,"noflags,");
|
||||
} else {
|
||||
int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);
|
||||
for (i = 0; i < size; i++) {
|
||||
struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;
|
||||
if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);
|
||||
}
|
||||
size_t orig_len = sdslen(ci);
|
||||
int i, size = sizeof(redisNodeFlagsTable)/sizeof(struct redisNodeFlags);
|
||||
for (i = 0; i < size; i++) {
|
||||
struct redisNodeFlags *nodeflag = redisNodeFlagsTable + i;
|
||||
if (flags & nodeflag->flag) ci = sdscat(ci, nodeflag->name);
|
||||
}
|
||||
/* If no flag was added, add the "noflags" special flag. */
|
||||
if (sdslen(ci) == orig_len) ci = sdscat(ci,"noflags,");
|
||||
sdsIncrLen(ci,-1); /* Remove trailing comma. */
|
||||
return ci;
|
||||
}
|
||||
@@ -4007,7 +4013,7 @@ void clusterCommand(client *c) {
|
||||
}
|
||||
if ((n = clusterLookupNode(c->argv[4]->ptr)) == NULL) {
|
||||
addReplyErrorFormat(c,"I don't know about node %s",
|
||||
(char*)c->argv[3]->ptr);
|
||||
(char*)c->argv[4]->ptr);
|
||||
return;
|
||||
}
|
||||
server.cluster->importing_slots_from[slot] = n;
|
||||
@@ -5204,8 +5210,9 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* All keys must belong to the same slot, so check first key only. */
|
||||
di = dictGetIterator(c->bpop.keys);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
if ((de = dictNext(di)) != NULL) {
|
||||
robj *key = dictGetKey(de);
|
||||
int slot = keyHashSlot((char*)key->ptr, sdslen(key->ptr));
|
||||
clusterNode *node = server.cluster->slots[slot];
|
||||
@@ -5223,6 +5230,7 @@ int clusterRedirectBlockedClientIfNeeded(client *c) {
|
||||
clusterRedirectClient(c,node,slot,
|
||||
CLUSTER_REDIR_MOVED);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -910,8 +910,6 @@ void configSetCommand(client *c) {
|
||||
"timeout",server.maxidletime,0,LONG_MAX) {
|
||||
} config_set_numerical_field(
|
||||
"auto-aof-rewrite-percentage",server.aof_rewrite_perc,0,LLONG_MAX){
|
||||
} config_set_numerical_field(
|
||||
"auto-aof-rewrite-min-size",server.aof_rewrite_min_size,0,LLONG_MAX) {
|
||||
} config_set_numerical_field(
|
||||
"hash-max-ziplist-entries",server.hash_max_ziplist_entries,0,LLONG_MAX) {
|
||||
} config_set_numerical_field(
|
||||
@@ -986,6 +984,8 @@ void configSetCommand(client *c) {
|
||||
}
|
||||
} config_set_memory_field("repl-backlog-size",ll) {
|
||||
resizeReplicationBacklog(ll);
|
||||
} config_set_memory_field("auto-aof-rewrite-min-size",ll) {
|
||||
server.aof_rewrite_min_size = ll;
|
||||
|
||||
/* Enumeration fields.
|
||||
* config_set_enum_field(name,var,enum_var) */
|
||||
|
||||
@@ -90,7 +90,7 @@ robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags) {
|
||||
|
||||
if (expireIfNeeded(db,key) == 1) {
|
||||
/* Key expired. If we are in the context of a master, expireIfNeeded()
|
||||
* returns 0 only when the key does not exist at all, so it's save
|
||||
* returns 0 only when the key does not exist at all, so it's safe
|
||||
* to return NULL ASAP. */
|
||||
if (server.masterhost == NULL) return NULL;
|
||||
|
||||
@@ -1033,18 +1033,15 @@ void pttlCommand(client *c) {
|
||||
}
|
||||
|
||||
void persistCommand(client *c) {
|
||||
dictEntry *de;
|
||||
|
||||
de = dictFind(c->db->dict,c->argv[1]->ptr);
|
||||
if (de == NULL) {
|
||||
addReply(c,shared.czero);
|
||||
} else {
|
||||
if (lookupKeyWrite(c->db,c->argv[1])) {
|
||||
if (removeExpire(c->db,c->argv[1])) {
|
||||
addReply(c,shared.cone);
|
||||
server.dirty++;
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
} else {
|
||||
addReply(c,shared.czero);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,6 +1231,44 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
|
||||
return keys;
|
||||
}
|
||||
|
||||
/* Helper function to extract keys from following commands:
|
||||
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
|
||||
* [COUNT count] [STORE key] [STOREDIST key]
|
||||
* GEORADIUSBYMEMBER key member radius unit ... options ... */
|
||||
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
|
||||
int i, num, *keys;
|
||||
UNUSED(cmd);
|
||||
|
||||
/* Check for the presence of the stored key in the command */
|
||||
int stored_key = -1;
|
||||
for (i = 5; i < argc; i++) {
|
||||
char *arg = argv[i]->ptr;
|
||||
/* For the case when user specifies both "store" and "storedist" options, the
|
||||
* second key specified would override the first key. This behavior is kept
|
||||
* the same as in georadiusCommand method.
|
||||
*/
|
||||
if ((!strcasecmp(arg, "store") || !strcasecmp(arg, "storedist")) && ((i+1) < argc)) {
|
||||
stored_key = i+1;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
num = 1 + (stored_key == -1 ? 0 : 1);
|
||||
|
||||
/* Keys in the command come from two places:
|
||||
* argv[1] = key,
|
||||
* argv[5...n] = stored key if present
|
||||
*/
|
||||
keys = zmalloc(sizeof(int) * num);
|
||||
|
||||
/* Add all key positions to keys[] */
|
||||
keys[0] = 1;
|
||||
if(num > 1) {
|
||||
keys[1] = stored_key;
|
||||
}
|
||||
*numkeys = num;
|
||||
return keys;
|
||||
}
|
||||
|
||||
/* Slot to Key API. This is used by Redis Cluster in order to obtain in
|
||||
* a fast way a key that belongs to a specified hash slot. This is useful
|
||||
* while rehashing the cluster. */
|
||||
|
||||
+15
-12
@@ -871,6 +871,15 @@ unsigned long dictScan(dict *d,
|
||||
de = de->next;
|
||||
}
|
||||
|
||||
/* Set unmasked bits so incrementing the reversed cursor
|
||||
* operates on the masked bits */
|
||||
v |= ~m0;
|
||||
|
||||
/* Increment the reverse cursor */
|
||||
v = rev(v);
|
||||
v++;
|
||||
v = rev(v);
|
||||
|
||||
} else {
|
||||
t0 = &d->ht[0];
|
||||
t1 = &d->ht[1];
|
||||
@@ -901,22 +910,16 @@ unsigned long dictScan(dict *d,
|
||||
de = de->next;
|
||||
}
|
||||
|
||||
/* Increment bits not covered by the smaller mask */
|
||||
v = (((v | m0) + 1) & ~m0) | (v & m0);
|
||||
/* Increment the reverse cursor not covered by the smaller mask.*/
|
||||
v |= ~m1;
|
||||
v = rev(v);
|
||||
v++;
|
||||
v = rev(v);
|
||||
|
||||
/* Continue while bits covered by mask difference is non-zero */
|
||||
} while (v & (m0 ^ m1));
|
||||
}
|
||||
|
||||
/* Set unmasked bits so incrementing the reversed cursor
|
||||
* operates on the masked bits of the smaller table */
|
||||
v |= ~m0;
|
||||
|
||||
/* Increment the reverse cursor */
|
||||
v = rev(v);
|
||||
v++;
|
||||
v = rev(v);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -949,7 +952,7 @@ static unsigned long _dictNextPower(unsigned long size)
|
||||
{
|
||||
unsigned long i = DICT_HT_INITIAL_SIZE;
|
||||
|
||||
if (size >= LONG_MAX) return LONG_MAX;
|
||||
if (size >= LONG_MAX) return LONG_MAX + 1LU;
|
||||
while(1) {
|
||||
if (i >= size)
|
||||
return i;
|
||||
|
||||
@@ -454,13 +454,14 @@ void geoaddCommand(client *c) {
|
||||
#define SORT_ASC 1
|
||||
#define SORT_DESC 2
|
||||
|
||||
#define RADIUS_COORDS 1
|
||||
#define RADIUS_MEMBER 2
|
||||
#define RADIUS_COORDS (1<<0) /* Search around coordinates. */
|
||||
#define RADIUS_MEMBER (1<<1) /* Search around member. */
|
||||
#define RADIUS_NOSTORE (1<<2) /* Do not acceot STORE/STOREDIST option. */
|
||||
|
||||
/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
|
||||
* [COUNT count] [STORE key] [STOREDIST key]
|
||||
* GEORADIUSBYMEMBER key member radius unit ... options ... */
|
||||
void georadiusGeneric(client *c, int type) {
|
||||
void georadiusGeneric(client *c, int flags) {
|
||||
robj *key = c->argv[1];
|
||||
robj *storekey = NULL;
|
||||
int storedist = 0; /* 0 for STORE, 1 for STOREDIST. */
|
||||
@@ -475,11 +476,11 @@ void georadiusGeneric(client *c, int type) {
|
||||
/* Find long/lat to use for radius search based on inquiry type */
|
||||
int base_args;
|
||||
double xy[2] = { 0 };
|
||||
if (type == RADIUS_COORDS) {
|
||||
if (flags & RADIUS_COORDS) {
|
||||
base_args = 6;
|
||||
if (extractLongLatOrReply(c, c->argv + 2, xy) == C_ERR)
|
||||
return;
|
||||
} else if (type == RADIUS_MEMBER) {
|
||||
} else if (flags & RADIUS_MEMBER) {
|
||||
base_args = 5;
|
||||
robj *member = c->argv[2];
|
||||
if (longLatFromMember(zobj, member, xy) == C_ERR) {
|
||||
@@ -487,7 +488,7 @@ void georadiusGeneric(client *c, int type) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
addReplyError(c, "unknown georadius search type");
|
||||
addReplyError(c, "Unknown georadius search type");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -524,11 +525,17 @@ void georadiusGeneric(client *c, int type) {
|
||||
return;
|
||||
}
|
||||
i++;
|
||||
} else if (!strcasecmp(arg, "store") && (i+1) < remaining) {
|
||||
} else if (!strcasecmp(arg, "store") &&
|
||||
(i+1) < remaining &&
|
||||
!(flags & RADIUS_NOSTORE))
|
||||
{
|
||||
storekey = c->argv[base_args+i+1];
|
||||
storedist = 0;
|
||||
i++;
|
||||
} else if (!strcasecmp(arg, "storedist") && (i+1) < remaining) {
|
||||
} else if (!strcasecmp(arg, "storedist") &&
|
||||
(i+1) < remaining &&
|
||||
!(flags & RADIUS_NOSTORE))
|
||||
{
|
||||
storekey = c->argv[base_args+i+1];
|
||||
storedist = 1;
|
||||
i++;
|
||||
@@ -676,10 +683,20 @@ void georadiusCommand(client *c) {
|
||||
}
|
||||
|
||||
/* GEORADIUSBYMEMBER wrapper function. */
|
||||
void georadiusByMemberCommand(client *c) {
|
||||
void georadiusbymemberCommand(client *c) {
|
||||
georadiusGeneric(c, RADIUS_MEMBER);
|
||||
}
|
||||
|
||||
/* GEORADIUS_RO wrapper function. */
|
||||
void georadiusroCommand(client *c) {
|
||||
georadiusGeneric(c, RADIUS_COORDS|RADIUS_NOSTORE);
|
||||
}
|
||||
|
||||
/* GEORADIUSBYMEMBER_RO wrapper function. */
|
||||
void georadiusbymemberroCommand(client *c) {
|
||||
georadiusGeneric(c, RADIUS_MEMBER|RADIUS_NOSTORE);
|
||||
}
|
||||
|
||||
/* GEOHASH key ele1 ele2 ... eleN
|
||||
*
|
||||
* Returns an array with an 11 characters geohash representation of the
|
||||
|
||||
@@ -598,6 +598,7 @@ int hllSparseToDense(robj *o) {
|
||||
} else {
|
||||
runlen = HLL_SPARSE_VAL_LEN(p);
|
||||
regval = HLL_SPARSE_VAL_VALUE(p);
|
||||
if ((runlen + idx) > HLL_REGISTERS) break; /* Overflow. */
|
||||
while(runlen--) {
|
||||
HLL_DENSE_SET_REGISTER(hdr->registers,idx,regval);
|
||||
idx++;
|
||||
@@ -1070,6 +1071,7 @@ int hllMerge(uint8_t *max, robj *hll) {
|
||||
} else {
|
||||
runlen = HLL_SPARSE_VAL_LEN(p);
|
||||
regval = HLL_SPARSE_VAL_VALUE(p);
|
||||
if ((runlen + i) > HLL_REGISTERS) break; /* Overflow. */
|
||||
while(runlen--) {
|
||||
if (regval > max[i]) max[i] = regval;
|
||||
i++;
|
||||
@@ -1128,6 +1130,7 @@ int isHLLObjectOrReply(client *c, robj *o) {
|
||||
if (checkType(c,o,OBJ_STRING))
|
||||
return C_ERR; /* Error already sent. */
|
||||
|
||||
if (!sdsEncodedObject(o)) goto invalid;
|
||||
if (stringObjectLen(o) < sizeof(*hdr)) goto invalid;
|
||||
hdr = o->ptr;
|
||||
|
||||
|
||||
+19
-7
@@ -949,11 +949,11 @@ int writeToClient(int fd, client *c, int handler_installed) {
|
||||
*
|
||||
* However if we are over the maxmemory limit we ignore that and
|
||||
* just deliver as much data as it is possible to deliver. */
|
||||
server.stat_net_output_bytes += totwritten;
|
||||
if (totwritten > NET_MAX_WRITES_PER_EVENT &&
|
||||
(server.maxmemory == 0 ||
|
||||
zmalloc_used_memory() < server.maxmemory)) break;
|
||||
}
|
||||
server.stat_net_output_bytes += totwritten;
|
||||
if (nwritten == -1) {
|
||||
if (errno == EAGAIN) {
|
||||
nwritten = 0;
|
||||
@@ -1009,13 +1009,25 @@ int handleClientsWithPendingWrites(void) {
|
||||
/* Try to write buffers to the client socket. */
|
||||
if (writeToClient(c->fd,c,0) == C_ERR) continue;
|
||||
|
||||
/* If there is nothing left, do nothing. Otherwise install
|
||||
* the write handler. */
|
||||
if (clientHasPendingReplies(c) &&
|
||||
aeCreateFileEvent(server.el, c->fd, AE_WRITABLE,
|
||||
/* If after the synchronous writes above we still have data to
|
||||
* output to the client, we need to install the writable handler. */
|
||||
if (clientHasPendingReplies(c)) {
|
||||
int ae_flags = AE_WRITABLE;
|
||||
/* For the fsync=always policy, we want that a given FD is never
|
||||
* served for reading and writing in the same event loop iteration,
|
||||
* so that in the middle of receiving the query, and serving it
|
||||
* to the client, we'll call beforeSleep() that will do the
|
||||
* actual fsync of AOF to disk. AE_BARRIER ensures that. */
|
||||
if (server.aof_state == AOF_ON &&
|
||||
server.aof_fsync == AOF_FSYNC_ALWAYS)
|
||||
{
|
||||
ae_flags |= AE_BARRIER;
|
||||
}
|
||||
if (aeCreateFileEvent(server.el, c->fd, ae_flags,
|
||||
sendReplyToClient, c) == AE_ERR)
|
||||
{
|
||||
freeClientAsync(c);
|
||||
{
|
||||
freeClientAsync(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed;
|
||||
|
||||
@@ -584,7 +584,7 @@ ssize_t rdbSaveObject(rio *rdb, robj *o) {
|
||||
if ((n = rdbSaveLen(rdb,ql->len)) == -1) return -1;
|
||||
nwritten += n;
|
||||
|
||||
do {
|
||||
while(node) {
|
||||
if (quicklistNodeIsCompressed(node)) {
|
||||
void *data;
|
||||
size_t compress_len = quicklistGetLzf(node, &data);
|
||||
@@ -594,7 +594,8 @@ ssize_t rdbSaveObject(rio *rdb, robj *o) {
|
||||
if ((n = rdbSaveRawString(rdb,node->zl,node->sz)) == -1) return -1;
|
||||
nwritten += n;
|
||||
}
|
||||
} while ((node = node->next));
|
||||
node = node->next;
|
||||
}
|
||||
} else {
|
||||
serverPanic("Unknown list encoding");
|
||||
}
|
||||
@@ -1690,9 +1691,6 @@ int rdbSaveToSlavesSockets(void) {
|
||||
exitFromChild((retval == C_OK) ? 0 : 1);
|
||||
} else {
|
||||
/* Parent */
|
||||
server.stat_fork_time = ustime()-start;
|
||||
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
|
||||
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
|
||||
if (childpid == -1) {
|
||||
serverLog(LL_WARNING,"Can't save in background: fork: %s",
|
||||
strerror(errno));
|
||||
@@ -1715,6 +1713,10 @@ int rdbSaveToSlavesSockets(void) {
|
||||
close(pipefds[0]);
|
||||
close(pipefds[1]);
|
||||
} else {
|
||||
server.stat_fork_time = ustime()-start;
|
||||
server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
|
||||
latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
|
||||
|
||||
serverLog(LL_NOTICE,"Background RDB transfer started by pid %d",
|
||||
childpid);
|
||||
server.rdb_save_time_start = time(NULL);
|
||||
|
||||
@@ -573,7 +573,7 @@ usage:
|
||||
" -c <clients> Number of parallel connections (default 50)\n"
|
||||
" -n <requests> Total number of requests (default 100000)\n"
|
||||
" -d <size> Data size of SET/GET value in bytes (default 2)\n"
|
||||
" -dbnum <db> SELECT the specified db number (default 0)\n"
|
||||
" --dbnum <db> SELECT the specified db number (default 0)\n"
|
||||
" -k <boolean> 1=keep alive 0=reconnect (default 1)\n"
|
||||
" -r <keyspacelen> Use random keys for SET/GET/INCR, random values for SADD\n"
|
||||
" Using this option the benchmark will expand the string __rand_int__\n"
|
||||
@@ -779,6 +779,13 @@ int main(int argc, const char **argv) {
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("hset")) {
|
||||
len = redisFormatCommand(&cmd,
|
||||
"HSET myset:__rand_int__ element:__rand_int__ %s",data);
|
||||
benchmark("HSET",cmd,len);
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
if (test_is_selected("spop")) {
|
||||
len = redisFormatCommand(&cmd,"SPOP myset");
|
||||
benchmark("SPOP",cmd,len);
|
||||
|
||||
+29
-16
@@ -151,20 +151,25 @@ static long long mstime(void) {
|
||||
}
|
||||
|
||||
static void cliRefreshPrompt(void) {
|
||||
int len;
|
||||
|
||||
if (config.eval_ldb) return;
|
||||
if (config.hostsocket != NULL)
|
||||
len = snprintf(config.prompt,sizeof(config.prompt),"redis %s",
|
||||
config.hostsocket);
|
||||
else
|
||||
len = anetFormatAddr(config.prompt, sizeof(config.prompt),
|
||||
config.hostip, config.hostport);
|
||||
|
||||
sds prompt = sdsempty();
|
||||
if (config.hostsocket != NULL) {
|
||||
prompt = sdscatfmt(prompt,"redis %s",config.hostsocket);
|
||||
} else {
|
||||
char addr[256];
|
||||
anetFormatAddr(addr, sizeof(addr), config.hostip, config.hostport);
|
||||
prompt = sdscatlen(prompt,addr,strlen(addr));
|
||||
}
|
||||
|
||||
/* Add [dbnum] if needed */
|
||||
if (config.dbnum != 0)
|
||||
len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
|
||||
config.dbnum);
|
||||
snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
|
||||
prompt = sdscatfmt(prompt,"[%i]",config.dbnum);
|
||||
|
||||
/* Copy the prompt in the static buffer. */
|
||||
prompt = sdscatlen(prompt,"> ",2);
|
||||
snprintf(config.prompt,sizeof(config.prompt),"%s",prompt);
|
||||
sdsfree(prompt);
|
||||
}
|
||||
|
||||
/* Return the name of the dotfile for the specified 'dotfilename'.
|
||||
@@ -1330,9 +1335,10 @@ static void repl(void) {
|
||||
} else {
|
||||
long long start_time = mstime(), elapsed;
|
||||
int repeat, skipargs = 0;
|
||||
char *endptr;
|
||||
|
||||
repeat = atoi(argv[0]);
|
||||
if (argc > 1 && repeat) {
|
||||
repeat = strtol(argv[0], &endptr, 10);
|
||||
if (argc > 1 && *endptr == '\0' && repeat) {
|
||||
skipargs = 1;
|
||||
} else {
|
||||
repeat = 1;
|
||||
@@ -1351,7 +1357,9 @@ static void repl(void) {
|
||||
}
|
||||
|
||||
elapsed = mstime()-start_time;
|
||||
if (elapsed >= 500) {
|
||||
if (elapsed >= 500 &&
|
||||
config.output == OUTPUT_STANDARD)
|
||||
{
|
||||
printf("(%.2fs)\n",(double)elapsed/1000);
|
||||
}
|
||||
}
|
||||
@@ -2022,8 +2030,13 @@ static void getKeyTypes(redisReply *keys, int *types) {
|
||||
keys->element[i]->str, context->err, context->errstr);
|
||||
exit(1);
|
||||
} else if(reply->type != REDIS_REPLY_STATUS) {
|
||||
fprintf(stderr, "Invalid reply type (%d) for TYPE on key '%s'!\n",
|
||||
reply->type, keys->element[i]->str);
|
||||
if(reply->type == REDIS_REPLY_ERROR) {
|
||||
fprintf(stderr, "TYPE returned an error: %s\n", reply->str);
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"Invalid reply type (%d) for TYPE on key '%s'!\n",
|
||||
reply->type, keys->element[i]->str);
|
||||
}
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
+10
-1
@@ -1385,7 +1385,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
|
||||
|
||||
void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
char tmpfile[256], *err = NULL;
|
||||
int dfd, maxtries = 5;
|
||||
int dfd = -1, maxtries = 5;
|
||||
int sockerr = 0, psync_result;
|
||||
socklen_t errlen = sizeof(sockerr);
|
||||
UNUSED(el);
|
||||
@@ -1640,6 +1640,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
|
||||
error:
|
||||
aeDeleteFileEvent(server.el,fd,AE_READABLE|AE_WRITABLE);
|
||||
if (dfd != -1) close(dfd);
|
||||
close(fd);
|
||||
server.repl_transfer_s = -1;
|
||||
server.repl_state = REPL_STATE_CONNECT;
|
||||
@@ -1792,6 +1793,14 @@ void slaveofCommand(client *c) {
|
||||
} else {
|
||||
long port;
|
||||
|
||||
if (c->flags & CLIENT_SLAVE)
|
||||
{
|
||||
/* If a client is already a replica they cannot run this command,
|
||||
* because it involves flushing all replicas (including this client) */
|
||||
addReplyError(c, "Command is not valid when client is a replica.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((getLongFromObjectOrReply(c, c->argv[2], &port, NULL) != C_OK))
|
||||
return;
|
||||
|
||||
|
||||
+2
-2
@@ -443,6 +443,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
|
||||
if (j == 10) {
|
||||
cmdlog = sdscatprintf(cmdlog," ... (%d more)",
|
||||
c->argc-j-1);
|
||||
break;
|
||||
} else {
|
||||
cmdlog = sdscatlen(cmdlog," ",1);
|
||||
cmdlog = sdscatsds(cmdlog,c->argv[j]->ptr);
|
||||
@@ -900,7 +901,6 @@ void scriptingInit(int setup) {
|
||||
server.lua_caller = NULL;
|
||||
server.lua_timedout = 0;
|
||||
server.lua_always_replicate_commands = 0; /* Only DEBUG can change it.*/
|
||||
server.lua_time_limit = LUA_SCRIPT_TIME_LIMIT;
|
||||
ldbInit();
|
||||
}
|
||||
|
||||
@@ -2272,7 +2272,7 @@ ldbLog(sdsnew("[e]eval <code> Execute some Lua code (in a different callfr
|
||||
ldbLog(sdsnew("[r]edis <cmd> Execute a Redis command."));
|
||||
ldbLog(sdsnew("[m]axlen [len] Trim logged Redis replies and Lua var dumps to len."));
|
||||
ldbLog(sdsnew(" Specifying zero as <len> means unlimited."));
|
||||
ldbLog(sdsnew("[a]abort Stop the execution of the script. In sync"));
|
||||
ldbLog(sdsnew("[a]bort Stop the execution of the script. In sync"));
|
||||
ldbLog(sdsnew(" mode dataset changes will be retained."));
|
||||
ldbLog(sdsnew(""));
|
||||
ldbLog(sdsnew("Debugger functions you can call from Lua scripts:"));
|
||||
|
||||
@@ -84,6 +84,7 @@ typedef struct sentinelAddr {
|
||||
#define SENTINEL_MAX_PENDING_COMMANDS 100
|
||||
#define SENTINEL_ELECTION_TIMEOUT 10000
|
||||
#define SENTINEL_MAX_DESYNC 1000
|
||||
#define SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG 1
|
||||
|
||||
/* Failover machine different states. */
|
||||
#define SENTINEL_FAILOVER_STATE_NONE 0 /* No failover in progress. */
|
||||
@@ -241,6 +242,8 @@ struct sentinelState {
|
||||
int announce_port; /* Port that is gossiped to other sentinels if
|
||||
non zero. */
|
||||
unsigned long simfailure_flags; /* Failures simulation. */
|
||||
int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script
|
||||
paths at runtime? */
|
||||
} sentinel;
|
||||
|
||||
/* A script execution job. */
|
||||
@@ -468,6 +471,7 @@ void initSentinel(void) {
|
||||
sentinel.announce_ip = NULL;
|
||||
sentinel.announce_port = 0;
|
||||
sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
|
||||
sentinel.deny_scripts_reconfig = SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG;
|
||||
memset(sentinel.myid,0,sizeof(sentinel.myid));
|
||||
}
|
||||
|
||||
@@ -1684,6 +1688,12 @@ char *sentinelHandleConfiguration(char **argv, int argc) {
|
||||
} else if (!strcasecmp(argv[0],"announce-port") && argc == 2) {
|
||||
/* announce-port <port> */
|
||||
sentinel.announce_port = atoi(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"deny-scripts-reconfig") && argc == 2) {
|
||||
/* deny-scripts-reconfig <yes|no> */
|
||||
if ((sentinel.deny_scripts_reconfig = yesnotoi(argv[1])) == -1) {
|
||||
return "Please specify yes or no for the "
|
||||
"deny-scripts-reconfig options.";
|
||||
}
|
||||
} else {
|
||||
return "Unrecognized sentinel configuration statement.";
|
||||
}
|
||||
@@ -1704,6 +1714,12 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
|
||||
line = sdscatprintf(sdsempty(), "sentinel myid %s", sentinel.myid);
|
||||
rewriteConfigRewriteLine(state,"sentinel",line,1);
|
||||
|
||||
/* sentinel deny-scripts-reconfig. */
|
||||
line = sdscatprintf(sdsempty(), "sentinel deny-scripts-reconfig %s",
|
||||
sentinel.deny_scripts_reconfig ? "yes" : "no");
|
||||
rewriteConfigRewriteLine(state,"sentinel",line,
|
||||
sentinel.deny_scripts_reconfig != SENTINEL_DEFAULT_DENY_SCRIPTS_RECONFIG);
|
||||
|
||||
/* For every master emit a "sentinel monitor" config entry. */
|
||||
di = dictGetIterator(sentinel.masters);
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
@@ -3327,6 +3343,14 @@ void sentinelSetCommand(client *c) {
|
||||
changes++;
|
||||
} else if (!strcasecmp(option,"notification-script")) {
|
||||
/* notification-script <path> */
|
||||
if (sentinel.deny_scripts_reconfig) {
|
||||
addReplyError(c,
|
||||
"Reconfiguration of scripts path is denied for "
|
||||
"security reasons. Check the deny-scripts-reconfig "
|
||||
"configuration directive in your Sentinel configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(value) && access(value,X_OK) == -1) {
|
||||
addReplyError(c,
|
||||
"Notification script seems non existing or non executable");
|
||||
@@ -3338,6 +3362,14 @@ void sentinelSetCommand(client *c) {
|
||||
changes++;
|
||||
} else if (!strcasecmp(option,"client-reconfig-script")) {
|
||||
/* client-reconfig-script <path> */
|
||||
if (sentinel.deny_scripts_reconfig) {
|
||||
addReplyError(c,
|
||||
"Reconfiguration of scripts path is denied for "
|
||||
"security reasons. Check the deny-scripts-reconfig "
|
||||
"configuration directive in your Sentinel configuration");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(value) && access(value,X_OK) == -1) {
|
||||
addReplyError(c,
|
||||
"Client reconfiguration script seems non existing or "
|
||||
|
||||
+25
-6
@@ -148,7 +148,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"linsert",linsertCommand,5,"wm",0,NULL,1,1,1,0,0},
|
||||
{"rpop",rpopCommand,2,"wF",0,NULL,1,1,1,0,0},
|
||||
{"lpop",lpopCommand,2,"wF",0,NULL,1,1,1,0,0},
|
||||
{"brpop",brpopCommand,-3,"ws",0,NULL,1,1,1,0,0},
|
||||
{"brpop",brpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
|
||||
{"brpoplpush",brpoplpushCommand,4,"wms",0,NULL,1,2,1,0,0},
|
||||
{"blpop",blpopCommand,-3,"ws",0,NULL,1,-2,1,0,0},
|
||||
{"llen",llenCommand,2,"rF",0,NULL,1,1,1,0,0},
|
||||
@@ -284,8 +284,10 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"wait",waitCommand,3,"s",0,NULL,0,0,0,0,0},
|
||||
{"command",commandCommand,0,"lt",0,NULL,0,0,0,0,0},
|
||||
{"geoadd",geoaddCommand,-5,"wm",0,NULL,1,1,1,0,0},
|
||||
{"georadius",georadiusCommand,-6,"w",0,NULL,1,1,1,0,0},
|
||||
{"georadiusbymember",georadiusByMemberCommand,-5,"w",0,NULL,1,1,1,0,0},
|
||||
{"georadius",georadiusCommand,-6,"w",0,georadiusGetKeys,1,1,1,0,0},
|
||||
{"georadius_ro",georadiusroCommand,-6,"r",0,georadiusGetKeys,1,1,1,0,0},
|
||||
{"georadiusbymember",georadiusbymemberCommand,-5,"w",0,georadiusGetKeys,1,1,1,0,0},
|
||||
{"georadiusbymember_ro",georadiusbymemberroCommand,-5,"r",0,georadiusGetKeys,1,1,1,0,0},
|
||||
{"geohash",geohashCommand,-2,"r",0,NULL,1,1,1,0,0},
|
||||
{"geopos",geoposCommand,-2,"r",0,NULL,1,1,1,0,0},
|
||||
{"geodist",geodistCommand,-4,"r",0,NULL,1,1,1,0,0},
|
||||
@@ -777,6 +779,11 @@ void activeExpireCycle(int type) {
|
||||
int dbs_per_call = CRON_DBS_PER_CALL;
|
||||
long long start = ustime(), timelimit;
|
||||
|
||||
/* When clients are paused the dataset should be static not just from the
|
||||
* POV of clients not being able to write, but also from the POV of
|
||||
* expires and evictions of keys not being performed. */
|
||||
if (clientsArePaused()) return;
|
||||
|
||||
if (type == ACTIVE_EXPIRE_CYCLE_FAST) {
|
||||
/* Don't start a fast cycle if the previous cycle did not exited
|
||||
* for time limt. Also don't repeat a fast cycle for the same period
|
||||
@@ -1049,12 +1056,15 @@ void databasesCron(void) {
|
||||
/* Rehash */
|
||||
if (server.activerehashing) {
|
||||
for (j = 0; j < dbs_per_call; j++) {
|
||||
int work_done = incrementallyRehash(rehash_db % server.dbnum);
|
||||
rehash_db++;
|
||||
int work_done = incrementallyRehash(rehash_db);
|
||||
if (work_done) {
|
||||
/* If the function did some work, stop here, we'll do
|
||||
* more at the next cron loop. */
|
||||
break;
|
||||
} else {
|
||||
/* If this db didn't need rehash, we'll try the next one. */
|
||||
rehash_db++;
|
||||
rehash_db %= server.dbnum;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1533,6 +1543,7 @@ void initServerConfig(void) {
|
||||
server.migrate_cached_sockets = dictCreate(&migrateCacheDictType,NULL);
|
||||
server.next_client_id = 1; /* Client IDs, start from 1 .*/
|
||||
server.loading_process_events_interval_bytes = (1024*1024*2);
|
||||
server.lua_time_limit = LUA_SCRIPT_TIME_LIMIT;
|
||||
|
||||
server.lruclock = getLRUClock();
|
||||
resetServerSaveParams();
|
||||
@@ -1592,6 +1603,8 @@ void initServerConfig(void) {
|
||||
server.rpopCommand = lookupCommandByCString("rpop");
|
||||
server.sremCommand = lookupCommandByCString("srem");
|
||||
server.execCommand = lookupCommandByCString("exec");
|
||||
server.expireCommand = lookupCommandByCString("expire");
|
||||
server.pexpireCommand = lookupCommandByCString("pexpire");
|
||||
|
||||
/* Slow log */
|
||||
server.slowlog_log_slower_than = CONFIG_DEFAULT_SLOWLOG_LOG_SLOWER_THAN;
|
||||
@@ -2585,8 +2598,9 @@ int prepareForShutdown(int flags) {
|
||||
"There is a child rewriting the AOF. Killing it!");
|
||||
kill(server.aof_child_pid,SIGUSR1);
|
||||
}
|
||||
/* Append only file: fsync() the AOF and exit */
|
||||
/* Append only file: flush buffers and fsync() the AOF at exit */
|
||||
serverLog(LL_NOTICE,"Calling fsync() on the AOF file.");
|
||||
flushAppendOnlyFile(1);
|
||||
aof_fsync(server.aof_fd);
|
||||
}
|
||||
|
||||
@@ -3461,6 +3475,11 @@ int freeMemoryIfNeeded(void) {
|
||||
int slaves = listLength(server.slaves);
|
||||
mstime_t latency, eviction_latency;
|
||||
|
||||
/* When clients are paused the dataset should be static not just from the
|
||||
* POV of clients not being able to write, but also from the POV of
|
||||
* expires and evictions of keys not being performed. */
|
||||
if (clientsArePaused()) return C_OK;
|
||||
|
||||
/* Remove the size of slaves output buffers and AOF buffer from the
|
||||
* count of used memory. */
|
||||
mem_used = zmalloc_used_memory();
|
||||
|
||||
+6
-2
@@ -752,7 +752,8 @@ struct redisServer {
|
||||
off_t loading_process_events_interval_bytes;
|
||||
/* Fast pointers to often looked up command */
|
||||
struct redisCommand *delCommand, *multiCommand, *lpushCommand, *lpopCommand,
|
||||
*rpopCommand, *sremCommand, *execCommand;
|
||||
*rpopCommand, *sremCommand, *execCommand, *expireCommand,
|
||||
*pexpireCommand;
|
||||
/* Fields used only for stats */
|
||||
time_t stat_starttime; /* Server start time */
|
||||
long long stat_numcommands; /* Number of processed commands */
|
||||
@@ -1435,6 +1436,7 @@ int *zunionInterGetKeys(struct redisCommand *cmd,robj **argv, int argc, int *num
|
||||
int *evalGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *sortGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
|
||||
|
||||
/* Cluster */
|
||||
void clusterInit(void);
|
||||
@@ -1633,8 +1635,10 @@ void replconfCommand(client *c);
|
||||
void waitCommand(client *c);
|
||||
void geoencodeCommand(client *c);
|
||||
void geodecodeCommand(client *c);
|
||||
void georadiusByMemberCommand(client *c);
|
||||
void georadiusbymemberCommand(client *c);
|
||||
void georadiusbymemberroCommand(client *c);
|
||||
void georadiusCommand(client *c);
|
||||
void georadiusroCommand(client *c);
|
||||
void geoaddCommand(client *c);
|
||||
void geohashCommand(client *c);
|
||||
void geoposCommand(client *c);
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ typedef struct slowlogEntry {
|
||||
robj **argv;
|
||||
int argc;
|
||||
long long id; /* Unique entry identifier. */
|
||||
long long duration; /* Time spent by the query, in nanoseconds. */
|
||||
long long duration; /* Time spent by the query, in microseconds. */
|
||||
time_t time; /* Unix time at which the query was executed. */
|
||||
} slowlogEntry;
|
||||
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "3.2.8"
|
||||
#define REDIS_VERSION "3.2.13"
|
||||
|
||||
+3
-2
@@ -413,8 +413,9 @@ size_t zmalloc_get_memory_size(void) {
|
||||
if (sysctl(mib, 2, &size, &len, NULL, 0) == 0)
|
||||
return (size_t)size;
|
||||
return 0L; /* Failed? */
|
||||
#endif /* sysctl and sysconf variants */
|
||||
|
||||
#else
|
||||
return 0L; /* Unknown method to get the data. */
|
||||
#endif
|
||||
#else
|
||||
return 0L; /* Unknown OS. */
|
||||
#endif
|
||||
|
||||
@@ -110,7 +110,7 @@ foreach diskless {no yes} {
|
||||
test_psync {no reconnection, just sync} 6 1000000 3600 0 {
|
||||
} $diskless 0
|
||||
|
||||
test_psync {ok psync} 6 1000000 3600 0 {
|
||||
test_psync {ok psync} 6 100000000 3600 0 {
|
||||
assert {[s -1 sync_partial_ok] > 0}
|
||||
} $diskless 1
|
||||
|
||||
|
||||
@@ -204,4 +204,19 @@ start_server {tags {"expire"}} {
|
||||
catch {r expire foo ""} e
|
||||
set e
|
||||
} {*not an integer*}
|
||||
|
||||
test {SET - use EX/PX option, TTL should not be reseted after loadaof} {
|
||||
r config set appendonly yes
|
||||
r set foo bar EX 100
|
||||
after 2000
|
||||
r debug loadaof
|
||||
set ttl [r ttl foo]
|
||||
assert {$ttl <= 98 && $ttl > 90}
|
||||
|
||||
r set foo bar PX 100000
|
||||
after 2000
|
||||
r debug loadaof
|
||||
set ttl [r ttl foo]
|
||||
assert {$ttl <= 98 && $ttl > 90}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ Create-custer is a small script used to easily start a big number of Redis
|
||||
instances configured to run in cluster mode. Its main goal is to allow manual
|
||||
testing in a condition which is not easy to replicate with the Redis cluster
|
||||
unit tests, for example when a lot of instances are needed in order to trigger
|
||||
a give bug.
|
||||
a given bug.
|
||||
|
||||
The tool can also be used just to easily create a number of instances in a
|
||||
Redis Cluster in order to experiment a bit with the system.
|
||||
@@ -10,7 +10,7 @@ Redis Cluster in order to experiment a bit with the system.
|
||||
USAGE
|
||||
---
|
||||
|
||||
To create a cluster, follow this steps:
|
||||
To create a cluster, follow these steps:
|
||||
|
||||
1. Edit create-cluster and change the start / end port, depending on the
|
||||
number of instances you want to create.
|
||||
@@ -21,7 +21,7 @@ an actual Redis cluster will be created.
|
||||
|
||||
In order to stop a cluster:
|
||||
|
||||
1. Use "./craete-cluster stop" to stop all the instances. After you stopped the instances you can use "./create-cluster start" to restart them if you change ideas.
|
||||
2. Use "./create-cluster clean" to remove all the AOF / log files to restat with a clean environment.
|
||||
1. Use "./create-cluster stop" to stop all the instances. After you stopped the instances you can use "./create-cluster start" to restart them if you change your mind.
|
||||
2. Use "./create-cluster clean" to remove all the AOF / log files to restart with a clean environment.
|
||||
|
||||
Use the command "./create-cluster help" to get the full list of features.
|
||||
|
||||
@@ -135,13 +135,13 @@ fi
|
||||
echo "## Generated by install_server.sh ##" > $TMP_FILE
|
||||
|
||||
read -r SED_EXPR <<-EOF
|
||||
s#^port [0-9]{4}\$#port ${REDIS_PORT}#; \
|
||||
s#^logfile .+\$#logfile ${REDIS_LOG_FILE}#; \
|
||||
s#^dir .+\$#dir ${REDIS_DATA_DIR}#; \
|
||||
s#^pidfile .+\$#pidfile ${PIDFILE}#; \
|
||||
s#^daemonize no\$#daemonize yes#;
|
||||
s#^port .\+#port ${REDIS_PORT}#; \
|
||||
s#^logfile .\+#logfile ${REDIS_LOG_FILE}#; \
|
||||
s#^dir .\+#dir ${REDIS_DATA_DIR}#; \
|
||||
s#^pidfile .\+#pidfile ${PIDFILE}#; \
|
||||
s#^daemonize no#daemonize yes#;
|
||||
EOF
|
||||
sed -r "$SED_EXPR" $DEFAULT_CONFIG >> $TMP_FILE
|
||||
sed "$SED_EXPR" $DEFAULT_CONFIG >> $TMP_FILE
|
||||
|
||||
#cat $TPL_FILE | while read line; do eval "echo \"$line\"" >> $TMP_FILE; done
|
||||
cp $TMP_FILE $REDIS_CONFIG_FILE || die "Could not write redis config file $REDIS_CONFIG_FILE"
|
||||
|
||||
Reference in New Issue
Block a user