Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
948fdb5f7d | ||
|
|
fe97d7b1db | ||
|
|
e88517ac30 | ||
|
|
2d89c53d0d | ||
|
|
0fe052efaa | ||
|
|
e1e8b1cd29 | ||
|
|
609a3ec1be | ||
|
|
0b50d1ce9a | ||
|
|
c7b9a57fb2 | ||
|
|
63ad0e8867 | ||
|
|
d5d0b467db | ||
|
|
cf0191dc3c | ||
|
|
45b1b2f7ad | ||
|
|
eb07793b59 | ||
|
|
22ccc6db0f | ||
|
|
f9a8b0eced | ||
|
|
6da090e916 | ||
|
|
8c0b9f778e | ||
|
|
63dc34bd01 | ||
|
|
9ddd0f7796 | ||
|
|
c17a7f6fbc | ||
|
|
bfad381f57 | ||
|
|
742e580f40 | ||
|
|
1b0f244509 | ||
|
|
580f65a50b | ||
|
|
a154908a8f | ||
|
|
da540228d8 | ||
|
|
9e5b70162c | ||
|
|
dbde1d85ea | ||
|
|
560e049947 | ||
|
|
c2b43eac4f | ||
|
|
5e2aa2cafb | ||
|
|
a732d86fe0 | ||
|
|
0f4dbd9a11 | ||
|
|
21159d53f5 | ||
|
|
52f6a1dc16 | ||
|
|
f9c1263f31 | ||
|
|
1b2ac3b206 | ||
|
|
c5f23ca79b | ||
|
|
27abaa238f | ||
|
|
78b175a241 | ||
|
|
14cc0045dc | ||
|
|
023cbc3787 | ||
|
|
b70b9c6027 | ||
|
|
46dc3fbb53 | ||
|
|
c1c3e9b756 | ||
|
|
1f8ad7ae55 | ||
|
|
4468ba2317 | ||
|
|
d8a0fddd51 | ||
|
|
886c9ecb7c | ||
|
|
8e0a9f6027 | ||
|
|
d64a9cf514 | ||
|
|
d9301f05e2 | ||
|
|
6c9897f6cf | ||
|
|
984f6edf0a | ||
|
|
a2b3fff235 | ||
|
|
54b08c86a6 | ||
|
|
6bdcd50b56 | ||
|
|
cac49a9031 | ||
|
|
ed70955510 |
@@ -14,6 +14,59 @@ 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 2.6.10 ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE, this release contains many non-critical fixes
|
||||
and many small improvements.
|
||||
|
||||
* [BUGFIX] redis-cli --rdb, fixed when the server sends newlines to ping.
|
||||
* [BUGFIX] redis-cli, minor fixes on connection handling, prompt.
|
||||
* [BUGFIX] Slow log: don't log EXEC, just executed commands.
|
||||
* [BUGFIX] On failed shutdown don't try again and again compulsively.
|
||||
* [BUGFIX] Fix build on sunos without backtrace().
|
||||
* [BUGFIX] UNSUBSCRIBE and PUNSUBSCRIBE: always provide a reply (see 742e580)
|
||||
* [BUGFIX] Lua struct library was broken, upgraded.
|
||||
* [BUGFIX] Fix a bug in srandmemberWithCountCommand() with count argument.
|
||||
* [BUGFIX] Test: disable clients timeout to prevent issues on slow systems.
|
||||
* [BUGFIX] Sentinel: don't advertise the promoted slave as master too early.
|
||||
* [IMPROVED] Whitelist SIGUSR1, see http://redis.io/topics/signals.
|
||||
* [IMPROVED] Simpler to understand redis-cli --bigkeys output.
|
||||
* [IMPROVED] Test now works with tclsh > 8.5.
|
||||
* [IMPROVED] Added option to turn of the Nagle algorithm in slave socket.
|
||||
* [IMPROVED] Optionally use SO_KEEPALIVE to detect dead peers.
|
||||
|
||||
--[ Redis 2.6.9 ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE if you use replication.
|
||||
|
||||
* [BUGFIX] Changing master at runtime (SLAVEOF command) in presence of
|
||||
network problems, or in very rapid succession, could result
|
||||
in non-critical problems (GitHub Issue #828).
|
||||
* [IMPROVED] CLINGET GETNAME and SETNAME to set and query connection names
|
||||
reported by CLIENT LIST. Very useful for debugging of
|
||||
problems.
|
||||
* [IMPROVED] redis-cli is now able to transfer an RDB file from a remote
|
||||
server to a local file using the --rdb <filename> command
|
||||
line option.
|
||||
|
||||
--[ Redis 2.6.8 ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE if you use Lua scripting. Otherwise LOW.
|
||||
|
||||
* [BUGFIX] Multiple fixes for EVAL (issue #872).
|
||||
* [BUGFIX] Fix overflow in mstime() in redis-cli and benchmark.
|
||||
* [BUGFIX] Fix Linux / PPC64 behavior by correcting endianess detection.
|
||||
* [BUGFIX] Fix NetBSD build by defining _XOPEN_SOURCE appropriately.
|
||||
* [BUGFIX] Added missing license and copyright in a few places.
|
||||
* [BUGFIX] Better error reporting when fd event creation fails.
|
||||
|
||||
--[ Redis 2.6.7 ]
|
||||
|
||||
UPGRADE URGENCY: MODERATE (unless you BLPOP using the same key multiple times).
|
||||
|
||||
* [BUGFIX] Don't crash if BLPOP & co are called with the same key repeated
|
||||
multiple times (Issue #801).
|
||||
|
||||
--[ Redis 2.6.6 ]
|
||||
|
||||
UPGRADE URGENCY: CRITICAL if you experienced one more more crashes.
|
||||
|
||||
+2
-1
@@ -2,7 +2,8 @@ Note: by contributing code to the Redis project in any form, including sending
|
||||
a pull request via Github, a code fragment or patch via private email or
|
||||
public discussion groups, you agree to release your code under the terms
|
||||
of the BSD license that you can find in the COPYING file included in the Redis
|
||||
source distribution.
|
||||
source distribution. You will include BSD license in the COPYING file within
|
||||
each source file that you contribute.
|
||||
|
||||
# IMPORTANT: HOW TO USE REDIS GITHUB ISSUES
|
||||
|
||||
|
||||
Vendored
+30
@@ -1,3 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_AE_H__
|
||||
#define __HIREDIS_AE_H__
|
||||
#include <sys/types.h>
|
||||
|
||||
Vendored
+30
@@ -1,3 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_LIBEV_H__
|
||||
#define __HIREDIS_LIBEV_H__
|
||||
#include <stdlib.h>
|
||||
|
||||
Vendored
+30
@@ -1,3 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Redis nor the names of its contributors may be used
|
||||
* to endorse or promote products derived from this software without
|
||||
* specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef __HIREDIS_LIBEVENT_H__
|
||||
#define __HIREDIS_LIBEVENT_H__
|
||||
#include <event.h>
|
||||
|
||||
Vendored
+1
-1
@@ -7,7 +7,7 @@
|
||||
|
||||
#if defined(__sun__)
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
#elif defined(__linux__)
|
||||
#elif defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#define _XOPEN_SOURCE 600
|
||||
#else
|
||||
#define _XOPEN_SOURCE
|
||||
|
||||
Vendored
+119
-52
@@ -1,18 +1,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
/*
|
||||
** {======================================================
|
||||
** Library for packing/unpacking structures.
|
||||
** $Id: struct.c,v 1.2 2008/04/18 20:06:01 roberto Exp $
|
||||
** $Id: struct.c,v 1.4 2012/07/04 18:54:29 roberto Exp $
|
||||
** See Copyright Notice at the end of this file
|
||||
** =======================================================
|
||||
*/
|
||||
@@ -25,6 +14,7 @@
|
||||
** b/B - signed/unsigned byte
|
||||
** 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
|
||||
the whole string; when unpacking, n==0 means use the previous
|
||||
@@ -36,6 +26,38 @@
|
||||
*/
|
||||
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
|
||||
|
||||
#if (LUA_VERSION_NUM >= 502)
|
||||
|
||||
#define luaL_register(L,n,f) luaL_newlib(L,f)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/* basic integer type */
|
||||
#if !defined(STRUCT_INT)
|
||||
#define STRUCT_INT long
|
||||
#endif
|
||||
|
||||
typedef STRUCT_INT Inttype;
|
||||
|
||||
/* corresponding unsigned version */
|
||||
typedef unsigned STRUCT_INT Uinttype;
|
||||
|
||||
|
||||
/* maximum size (in bytes) for integral types */
|
||||
#define MAXINTSIZE 32
|
||||
|
||||
/* is 'x' a power of 2? */
|
||||
#define isp2(x) ((x) > 0 && ((x) & ((x) - 1)) == 0)
|
||||
|
||||
@@ -67,11 +89,11 @@ typedef struct Header {
|
||||
} Header;
|
||||
|
||||
|
||||
static size_t getnum (const char **fmt, size_t df) {
|
||||
static int getnum (const char **fmt, int df) {
|
||||
if (!isdigit(**fmt)) /* no number? */
|
||||
return df; /* return default value */
|
||||
else {
|
||||
size_t a = 0;
|
||||
int a = 0;
|
||||
do {
|
||||
a = a*10 + *((*fmt)++) - '0';
|
||||
} while (isdigit(**fmt));
|
||||
@@ -89,33 +111,40 @@ static size_t optsize (lua_State *L, char opt, const char **fmt) {
|
||||
case 'B': case 'b': return sizeof(char);
|
||||
case 'H': case 'h': return sizeof(short);
|
||||
case 'L': case 'l': return sizeof(long);
|
||||
case 'T': return sizeof(size_t);
|
||||
case 'f': return sizeof(float);
|
||||
case 'd': return sizeof(double);
|
||||
case 'x': return 1;
|
||||
case 'c': return getnum(fmt, 1);
|
||||
case 's': case ' ': case '<': case '>': case '!': return 0;
|
||||
case 'i': case 'I': {
|
||||
int sz = getnum(fmt, sizeof(int));
|
||||
if (!isp2(sz))
|
||||
luaL_error(L, "integral size %d is not a power of 2", sz);
|
||||
if (sz > MAXINTSIZE)
|
||||
luaL_error(L, "integral size %d is larger than limit of %d",
|
||||
sz, MAXINTSIZE);
|
||||
return sz;
|
||||
}
|
||||
default: {
|
||||
const char *msg = lua_pushfstring(L, "invalid format option [%c]", opt);
|
||||
return luaL_argerror(L, 1, msg);
|
||||
}
|
||||
default: return 0; /* other cases do not need alignment */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** return number of bytes needed to align an element of size 'size'
|
||||
** at current position 'len'
|
||||
*/
|
||||
static int gettoalign (size_t len, Header *h, int opt, size_t size) {
|
||||
if (size == 0 || opt == 'c') return 0;
|
||||
if (size > (size_t)h->align) size = h->align; /* respect max. alignment */
|
||||
return (size - (len & (size - 1))) & (size - 1);
|
||||
if (size > (size_t)h->align)
|
||||
size = h->align; /* respect max. alignment */
|
||||
return (size - (len & (size - 1))) & (size - 1);
|
||||
}
|
||||
|
||||
|
||||
static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
|
||||
/*
|
||||
** options to control endianess and alignment
|
||||
*/
|
||||
static void controloptions (lua_State *L, int opt, const char **fmt,
|
||||
Header *h) {
|
||||
switch (opt) {
|
||||
case ' ': return; /* ignore white spaces */
|
||||
case '>': h->endian = BIG; return;
|
||||
@@ -127,7 +156,10 @@ static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
|
||||
h->align = a;
|
||||
return;
|
||||
}
|
||||
default: assert(0);
|
||||
default: {
|
||||
const char *msg = lua_pushfstring(L, "invalid format option '%c'", opt);
|
||||
luaL_argerror(L, 1, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,21 +167,27 @@ static void commoncases (lua_State *L, int opt, const char **fmt, Header *h) {
|
||||
static void putinteger (lua_State *L, luaL_Buffer *b, int arg, int endian,
|
||||
int size) {
|
||||
lua_Number n = luaL_checknumber(L, arg);
|
||||
unsigned long value;
|
||||
if (n < (lua_Number)LONG_MAX)
|
||||
value = (long)n;
|
||||
Uinttype value;
|
||||
char buff[MAXINTSIZE];
|
||||
if (n < 0)
|
||||
value = (Uinttype)(Inttype)n;
|
||||
else
|
||||
value = (unsigned long)n;
|
||||
value = (Uinttype)n;
|
||||
if (endian == LITTLE) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
luaL_addchar(b, (value >> 8*i) & 0xff);
|
||||
for (i = 0; i < size; i++) {
|
||||
buff[i] = (value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = size - 1; i >= 0; i--)
|
||||
luaL_addchar(b, (value >> 8*i) & 0xff);
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
buff[i] = (value & 0xff);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
luaL_addlstring(b, buff, size);
|
||||
}
|
||||
|
||||
|
||||
@@ -179,15 +217,15 @@ static int b_pack (lua_State *L) {
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
int toalign = gettoalign(totalsize, &h, opt, size);
|
||||
totalsize += toalign;
|
||||
while (toalign-- > 0) luaL_putchar(&b, '\0');
|
||||
while (toalign-- > 0) luaL_addchar(&b, '\0');
|
||||
switch (opt) {
|
||||
case 'b': case 'B': case 'h': case 'H':
|
||||
case 'l': case 'L': case 'i': case 'I': { /* integer types */
|
||||
case 'l': case 'L': case 'T': case 'i': case 'I': { /* integer types */
|
||||
putinteger(L, &b, arg++, h.endian, size);
|
||||
break;
|
||||
}
|
||||
case 'x': {
|
||||
luaL_putchar(&b, '\0');
|
||||
luaL_addchar(&b, '\0');
|
||||
break;
|
||||
}
|
||||
case 'f': {
|
||||
@@ -209,12 +247,12 @@ static int b_pack (lua_State *L) {
|
||||
luaL_argcheck(L, l >= (size_t)size, arg, "string too short");
|
||||
luaL_addlstring(&b, s, size);
|
||||
if (opt == 's') {
|
||||
luaL_putchar(&b, '\0'); /* add zero at the end */
|
||||
luaL_addchar(&b, '\0'); /* add zero at the end */
|
||||
size++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: commoncases(L, opt, &fmt, &h);
|
||||
default: controloptions(L, opt, &fmt, &h);
|
||||
}
|
||||
totalsize += size;
|
||||
}
|
||||
@@ -225,24 +263,27 @@ static int b_pack (lua_State *L) {
|
||||
|
||||
static lua_Number getinteger (const char *buff, int endian,
|
||||
int issigned, int size) {
|
||||
unsigned long l = 0;
|
||||
Uinttype l = 0;
|
||||
int i;
|
||||
if (endian == BIG) {
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
l |= (unsigned long)(unsigned char)buff[size - i - 1] << (i*8);
|
||||
for (i = 0; i < size; i++) {
|
||||
l <<= 8;
|
||||
l |= (Uinttype)(unsigned char)buff[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
for (i = 0; i < size; i++)
|
||||
l |= (unsigned long)(unsigned char)buff[i] << (i*8);
|
||||
for (i = size - 1; i >= 0; i--) {
|
||||
l <<= 8;
|
||||
l |= (Uinttype)(unsigned char)buff[i];
|
||||
}
|
||||
}
|
||||
if (!issigned)
|
||||
return (lua_Number)l;
|
||||
else { /* signed format */
|
||||
unsigned long mask = ~(0UL) << (size*8 - 1);
|
||||
Uinttype mask = (Uinttype)(~((Uinttype)0)) << (size*8 - 1);
|
||||
if (l & mask) /* negative value? */
|
||||
l |= mask; /* signal extension */
|
||||
return (lua_Number)(long)l;
|
||||
return (lua_Number)(Inttype)l;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +301,10 @@ static int b_unpack (lua_State *L) {
|
||||
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");
|
||||
switch (opt) {
|
||||
case 'b': case 'B': case 'h': case 'H':
|
||||
case 'l': case 'L': case 'i': case 'I': { /* integer types */
|
||||
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);
|
||||
@@ -304,7 +346,7 @@ static int b_unpack (lua_State *L) {
|
||||
lua_pushlstring(L, data+pos, size - 1);
|
||||
break;
|
||||
}
|
||||
default: commoncases(L, opt, &fmt, &h);
|
||||
default: controloptions(L, opt, &fmt, &h);
|
||||
}
|
||||
pos += size;
|
||||
}
|
||||
@@ -312,26 +354,50 @@ static int b_unpack (lua_State *L) {
|
||||
return lua_gettop(L) - 2;
|
||||
}
|
||||
|
||||
|
||||
static int b_size (lua_State *L) {
|
||||
Header h;
|
||||
const char *fmt = luaL_checkstring(L, 1);
|
||||
size_t pos = 0;
|
||||
defaultoptions(&h);
|
||||
while (*fmt) {
|
||||
int opt = *fmt++;
|
||||
size_t size = optsize(L, opt, &fmt);
|
||||
pos += gettoalign(pos, &h, opt, size);
|
||||
if (opt == 's')
|
||||
luaL_argerror(L, 1, "option 's' has no fixed size");
|
||||
else if (opt == 'c' && size == 0)
|
||||
luaL_argerror(L, 1, "option 'c0' has no fixed size");
|
||||
if (!isalnum(opt))
|
||||
controloptions(L, opt, &fmt, &h);
|
||||
pos += size;
|
||||
}
|
||||
lua_pushinteger(L, pos);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* }====================================================== */
|
||||
|
||||
|
||||
|
||||
static const struct luaL_reg thislib[] = {
|
||||
static const struct luaL_Reg thislib[] = {
|
||||
{"pack", b_pack},
|
||||
{"unpack", b_unpack},
|
||||
{"size", b_size},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
LUALIB_API int luaopen_struct (lua_State *L);
|
||||
|
||||
LUALIB_API int luaopen_struct (lua_State *L) {
|
||||
luaL_register(L, "struct", thislib);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Copyright (C) 2010 Lua.org, PUC-Rio. All rights reserved.
|
||||
* Copyright (C) 2010-2012 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
|
||||
@@ -352,3 +418,4 @@ LUALIB_API int luaopen_struct (lua_State *L) {
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
******************************************************************************/
|
||||
|
||||
|
||||
+50
-19
@@ -39,8 +39,24 @@ port 6379
|
||||
# Close the connection after a client is idle for N seconds (0 to disable)
|
||||
timeout 0
|
||||
|
||||
# Set server verbosity to 'debug'
|
||||
# it can be one of:
|
||||
# TCP keepalive.
|
||||
#
|
||||
# If non-zero, use SO_KEEPALIVE to send TCP ACKs to clients in absence
|
||||
# of communication. This is useful for two reasons:
|
||||
#
|
||||
# 1) Detect dead peers.
|
||||
# 2) Take the connection alive from the point of view of network
|
||||
# equipment in the middle.
|
||||
#
|
||||
# On Linux, the specified value (in seconds) is the period used to send ACKs.
|
||||
# Note that to close the connection the double of the time is needed.
|
||||
# On other kernels the period depends on the kernel configuration.
|
||||
#
|
||||
# A reasonable value for this option is 60 seconds.
|
||||
tcp-keepalive 0
|
||||
|
||||
# Specify the server verbosity level.
|
||||
# This can be one of:
|
||||
# debug (a lot of information, useful for development/testing)
|
||||
# verbose (many rarely useful info, but not a mess like the debug level)
|
||||
# notice (moderately verbose, what you want in production probably)
|
||||
@@ -59,7 +75,7 @@ logfile stdout
|
||||
# Specify the syslog identity.
|
||||
# syslog-ident redis
|
||||
|
||||
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
|
||||
# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
|
||||
# syslog-facility local0
|
||||
|
||||
# Set the number of databases. The default database is DB 0, you can select
|
||||
@@ -114,7 +130,7 @@ stop-writes-on-bgsave-error yes
|
||||
# the dataset will likely be bigger if you have compressible values or keys.
|
||||
rdbcompression yes
|
||||
|
||||
# Since verison 5 of RDB a CRC64 checksum is placed at the end of the file.
|
||||
# Since version 5 of RDB a CRC64 checksum is placed at the end of the file.
|
||||
# This makes the format more resistant to corruption but there is a performance
|
||||
# hit to pay (around 10%) when saving and loading RDB files, so you can disable it
|
||||
# for maximum performances.
|
||||
@@ -131,7 +147,7 @@ dbfilename dump.rdb
|
||||
# The DB will be written inside this directory, with the filename specified
|
||||
# above using the 'dbfilename' configuration directive.
|
||||
#
|
||||
# Also the Append Only File will be created inside this directory.
|
||||
# The Append Only File will also be created inside this directory.
|
||||
#
|
||||
# Note that you must specify a directory here, not a file name.
|
||||
dir ./
|
||||
@@ -152,7 +168,7 @@ dir ./
|
||||
#
|
||||
# masterauth <master-password>
|
||||
|
||||
# When a slave lost the connection with the master, or when the replication
|
||||
# When a slave loses its connection with the master, or when the replication
|
||||
# is still in progress, the slave can act in two different ways:
|
||||
#
|
||||
# 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will
|
||||
@@ -196,6 +212,21 @@ slave-read-only yes
|
||||
#
|
||||
# repl-timeout 60
|
||||
|
||||
# Disable TCP_NODELAY on the slave socket after SYNC?
|
||||
#
|
||||
# If you select "yes" Redis will use a smaller number of TCP packets and
|
||||
# less bandwidth to send data to slaves. But this can add a delay for
|
||||
# the data to appear on the slave side, up to 40 milliseconds with
|
||||
# Linux kernels using a default configuration.
|
||||
#
|
||||
# If you select "no" the delay for data to appear on the slave side will
|
||||
# be reduced but more bandwidth will be used for replication.
|
||||
#
|
||||
# By default we optimize for low latency, but in very high traffic conditions
|
||||
# or when the master and slaves are many hops away, turning this to "yes" may
|
||||
# be a good idea.
|
||||
repl-disable-tcp-nodelay no
|
||||
|
||||
# The slave priority is an integer number published by Redis in the INFO output.
|
||||
# It is used by Redis Sentinel in order to select a slave to promote into a
|
||||
# master if the master is no longer working correctly.
|
||||
@@ -230,14 +261,14 @@ slave-priority 100
|
||||
#
|
||||
# It is possible to change the name of dangerous commands in a shared
|
||||
# environment. For instance the CONFIG command may be renamed into something
|
||||
# of hard to guess so that it will be still available for internal-use
|
||||
# tools but not available for general clients.
|
||||
# hard to guess so that it will still be available for internal-use tools
|
||||
# but not available for general clients.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52
|
||||
#
|
||||
# It is also possible to completely kill a command renaming it into
|
||||
# It is also possible to completely kill a command by renaming it into
|
||||
# an empty string:
|
||||
#
|
||||
# rename-command CONFIG ""
|
||||
@@ -246,7 +277,7 @@ slave-priority 100
|
||||
|
||||
# Set the max number of connected clients at the same time. By default
|
||||
# this limit is set to 10000 clients, however if the Redis server is not
|
||||
# able ot configure the process file limit to allow for the specified limit
|
||||
# able to configure the process file limit to allow for the specified limit
|
||||
# the max number of allowed clients is set to the current file limit
|
||||
# minus 32 (as Redis reserves a few file descriptors for internal uses).
|
||||
#
|
||||
@@ -281,7 +312,7 @@ slave-priority 100
|
||||
# maxmemory <bytes>
|
||||
|
||||
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory
|
||||
# is reached? You can select among five behavior:
|
||||
# is reached. You can select among five behaviors:
|
||||
#
|
||||
# volatile-lru -> remove the key with an expire set using an LRU algorithm
|
||||
# allkeys-lru -> remove any key accordingly to the LRU algorithm
|
||||
@@ -290,7 +321,7 @@ slave-priority 100
|
||||
# volatile-ttl -> remove the key with the nearest expire time (minor TTL)
|
||||
# noeviction -> don't expire at all, just return an error on write operations
|
||||
#
|
||||
# Note: with all the kind of policies, Redis will return an error on write
|
||||
# Note: with any of the above policies, Redis will return an error on write
|
||||
# operations, when there are not suitable keys for eviction.
|
||||
#
|
||||
# At the date of writing this commands are: set setnx setex append
|
||||
@@ -346,7 +377,7 @@ appendonly no
|
||||
# always: fsync after every write to the append only log . Slow, Safest.
|
||||
# everysec: fsync only one time every second. Compromise.
|
||||
#
|
||||
# The default is "everysec" that's usually the right compromise between
|
||||
# The default is "everysec", as that's usually the right compromise between
|
||||
# speed and data safety. It's up to you to understand if you can relax this to
|
||||
# "no" that will let the operating system flush the output buffer when
|
||||
# it wants, for better performances (but if you can live with the idea of
|
||||
@@ -374,9 +405,9 @@ appendfsync everysec
|
||||
# that will prevent fsync() from being called in the main process while a
|
||||
# BGSAVE or BGREWRITEAOF is in progress.
|
||||
#
|
||||
# This means that while another child is saving the durability of Redis is
|
||||
# the same as "appendfsync none", that in practical terms means that it is
|
||||
# possible to lost up to 30 seconds of log in the worst scenario (with the
|
||||
# This means that while another child is saving, the durability of Redis is
|
||||
# the same as "appendfsync none". In practical terms, this means that it is
|
||||
# possible to lose up to 30 seconds of log in the worst scenario (with the
|
||||
# default Linux settings).
|
||||
#
|
||||
# If you have latency problems turn this to "yes". Otherwise leave it as
|
||||
@@ -385,10 +416,10 @@ no-appendfsync-on-rewrite no
|
||||
|
||||
# Automatic rewrite of the append only file.
|
||||
# Redis is able to automatically rewrite the log file implicitly calling
|
||||
# BGREWRITEAOF when the AOF log size will growth by the specified percentage.
|
||||
# BGREWRITEAOF when the AOF log size grows by the specified percentage.
|
||||
#
|
||||
# This is how it works: Redis remembers the size of the AOF file after the
|
||||
# latest rewrite (or if no rewrite happened since the restart, the size of
|
||||
# latest rewrite (if no rewrite has happened since the restart, the size of
|
||||
# the AOF at startup is used).
|
||||
#
|
||||
# This base size is compared to the current size. If the current size is
|
||||
@@ -524,7 +555,7 @@ activerehashing yes
|
||||
# Instead there is a default limit for pubsub and slave clients, since
|
||||
# subscribers and slaves receive data in a push fashion.
|
||||
#
|
||||
# Both the hard or the soft limit can be disabled just setting it to zero.
|
||||
# Both the hard or the soft limit can be disabled by setting them to zero.
|
||||
client-output-buffer-limit normal 0 0 0
|
||||
client-output-buffer-limit slave 256mb 64mb 60
|
||||
client-output-buffer-limit pubsub 32mb 8mb 60
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/bin/sh
|
||||
TCL=tclsh8.5
|
||||
which $TCL
|
||||
if [ "$?" != "0" ]
|
||||
TCL_VERSIONS="8.5 8.6"
|
||||
TCLSH=""
|
||||
|
||||
for VERSION in $TCL_VERSIONS; do
|
||||
TCL=`which tclsh$VERSION 2>/dev/null` && TCLSH=$TCL
|
||||
done
|
||||
|
||||
if [ -z $TCLSH ]
|
||||
then
|
||||
echo "You need '$TCL' in order to run the Redis test"
|
||||
echo "You need tcl 8.5 or newer in order to run the Redis test"
|
||||
exit 1
|
||||
fi
|
||||
$TCL tests/test_helper.tcl $*
|
||||
$TCLSH tests/test_helper.tcl $*
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ sentinel parallel-syncs mymaster 1
|
||||
# Default is 15 minutes.
|
||||
sentinel failover-timeout mymaster 900000
|
||||
|
||||
# SCRIPTS EXECTION
|
||||
# SCRIPTS EXECUTION
|
||||
#
|
||||
# sentinel notification-script and sentinel reconfig-script are used in order
|
||||
# to configure scripts that are called to notify the system administrator
|
||||
|
||||
+4
-1
@@ -217,7 +217,10 @@ gcov:
|
||||
$(MAKE) REDIS_CFLAGS="-fprofile-arcs -ftest-coverage -DCOVERAGE_TEST" REDIS_LDFLAGS="-fprofile-arcs -ftest-coverage"
|
||||
|
||||
noopt:
|
||||
$(MAKE) OPT="-O0"
|
||||
$(MAKE) OPTIMIZATION="-O0"
|
||||
|
||||
valgrind:
|
||||
$(MAKE) OPTIMIZATION="-O0" MALLOC="libc"
|
||||
|
||||
src/help.h:
|
||||
@../utils/generate-command-help.rb > help.h
|
||||
|
||||
+13
-4
@@ -1,6 +1,7 @@
|
||||
adlist.o: adlist.c adlist.h zmalloc.h
|
||||
ae.o: ae.c ae.h zmalloc.h config.h ae_kqueue.c
|
||||
ae_epoll.o: ae_epoll.c
|
||||
ae_evport.o: ae_evport.c
|
||||
ae_kqueue.o: ae_kqueue.c
|
||||
ae_select.o: ae_select.c
|
||||
anet.o: anet.c fmacros.h anet.h
|
||||
@@ -10,6 +11,9 @@ aof.o: aof.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
bio.o: bio.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h bio.h
|
||||
bitops.o: bitops.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
config.o: config.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
@@ -22,7 +26,7 @@ debug.o: debug.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h sha1.h
|
||||
dict.o: dict.c fmacros.h dict.h zmalloc.h
|
||||
endianconv.o: endianconv.c
|
||||
intset.o: intset.c intset.h zmalloc.h endianconv.h
|
||||
intset.o: intset.c intset.h zmalloc.h endianconv.h config.h
|
||||
lzf_c.o: lzf_c.c lzfP.h
|
||||
lzf_d.o: lzf_d.c lzfP.h
|
||||
memtest.o: memtest.c
|
||||
@@ -53,7 +57,7 @@ redis-benchmark.o: redis-benchmark.c fmacros.h ae.h \
|
||||
redis-check-aof.o: redis-check-aof.c fmacros.h config.h
|
||||
redis-check-dump.o: redis-check-dump.c lzf.h
|
||||
redis-cli.o: redis-cli.c fmacros.h version.h ../deps/hiredis/hiredis.h \
|
||||
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h
|
||||
sds.h zmalloc.h ../deps/linenoise/linenoise.h help.h anet.h ae.h
|
||||
redis.o: redis.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h slowlog.h bio.h \
|
||||
@@ -70,6 +74,11 @@ scripting.o: scripting.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/lauxlib.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/lualib.h
|
||||
sds.o: sds.c sds.h zmalloc.h
|
||||
sentinel.o: sentinel.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h \
|
||||
../deps/hiredis/hiredis.h ../deps/hiredis/async.h \
|
||||
../deps/hiredis/hiredis.h
|
||||
sha1.o: sha1.c sha1.h config.h
|
||||
slowlog.o: slowlog.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
@@ -96,6 +105,6 @@ t_zset.o: t_zset.c redis.h fmacros.h config.h ../deps/lua/src/lua.h \
|
||||
../deps/lua/src/luaconf.h ae.h sds.h dict.h adlist.h zmalloc.h anet.h \
|
||||
ziplist.h intset.h version.h util.h rdb.h rio.h
|
||||
util.o: util.c fmacros.h util.h
|
||||
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h
|
||||
zipmap.o: zipmap.c zmalloc.h endianconv.h
|
||||
ziplist.o: ziplist.c zmalloc.h util.h ziplist.h endianconv.h config.h
|
||||
zipmap.o: zipmap.c zmalloc.h endianconv.h config.h
|
||||
zmalloc.o: zmalloc.c config.h zmalloc.h
|
||||
|
||||
+3
-3
@@ -97,7 +97,7 @@ list *listAddNodeHead(list *list, void *value)
|
||||
return list;
|
||||
}
|
||||
|
||||
/* Add a new node to the list, to tail, contaning the specified 'value'
|
||||
/* Add a new node to the list, to tail, containing the specified 'value'
|
||||
* pointer as value.
|
||||
*
|
||||
* On error, NULL is returned and no operation is performed (i.e. the
|
||||
@@ -308,7 +308,7 @@ listNode *listSearchKey(list *list, void *key)
|
||||
/* Return the element at the specified zero-based index
|
||||
* where 0 is the head, 1 is the element next to head
|
||||
* and so on. Negative integers are used in order to count
|
||||
* from the tail, -1 is the last element, -2 the penultimante
|
||||
* from the tail, -1 is the last element, -2 the penultimate
|
||||
* and so on. If the index is out of range NULL is returned. */
|
||||
listNode *listIndex(list *list, long index) {
|
||||
listNode *n;
|
||||
@@ -330,7 +330,7 @@ void listRotate(list *list) {
|
||||
|
||||
if (listLength(list) <= 1) return;
|
||||
|
||||
/* Detatch current tail */
|
||||
/* Detach current tail */
|
||||
list->tail = tail->prev;
|
||||
list->tail->next = NULL;
|
||||
/* Move it as head */
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "ae.h"
|
||||
#include "zmalloc.h"
|
||||
@@ -104,7 +105,10 @@ void aeStop(aeEventLoop *eventLoop) {
|
||||
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
|
||||
aeFileProc *proc, void *clientData)
|
||||
{
|
||||
if (fd >= eventLoop->setsize) return AE_ERR;
|
||||
if (fd >= eventLoop->setsize) {
|
||||
errno = ERANGE;
|
||||
return AE_ERR;
|
||||
}
|
||||
aeFileEvent *fe = &eventLoop->events[fd];
|
||||
|
||||
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
|
||||
@@ -305,7 +309,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
/* Process every pending time event, then every pending file event
|
||||
* (that may be registered by time event callbacks just processed).
|
||||
* Without special flags the function sleeps until some file event
|
||||
* fires, or when the next time event occurrs (if any).
|
||||
* fires, or when the next time event occurs (if any).
|
||||
*
|
||||
* If flags is 0, the function does nothing and returns.
|
||||
* if flags has AE_ALL_EVENTS set, all the kind of events are processed.
|
||||
@@ -352,7 +356,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
if (tvp->tv_usec < 0) tvp->tv_usec = 0;
|
||||
} else {
|
||||
/* If we have to check for events but need to return
|
||||
* ASAP because of AE_DONT_WAIT we need to se the timeout
|
||||
* ASAP because of AE_DONT_WAIT we need to set the timeout
|
||||
* to zero */
|
||||
if (flags & AE_DONT_WAIT) {
|
||||
tv.tv_sec = tv.tv_usec = 0;
|
||||
@@ -391,7 +395,7 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
|
||||
return processed; /* return the number of processed file/time events */
|
||||
}
|
||||
|
||||
/* Wait for millseconds until the given file descriptor becomes
|
||||
/* Wait for milliseconds until the given file descriptor becomes
|
||||
* writable/readable/exception */
|
||||
int aeWait(int fd, int mask, long long milliseconds) {
|
||||
struct pollfd pfd;
|
||||
|
||||
+6
-6
@@ -50,15 +50,15 @@ static int evport_debug = 0;
|
||||
* aeApiPoll, the corresponding file descriptors become dissociated from the
|
||||
* port. This is necessary because poll events are level-triggered, so if the
|
||||
* fd didn't become dissociated, it would immediately fire another event since
|
||||
* the underlying state hasn't changed yet. We must reassociate the file
|
||||
* the underlying state hasn't changed yet. We must re-associate the file
|
||||
* descriptor, but only after we know that our caller has actually read from it.
|
||||
* The ae API does not tell us exactly when that happens, but we do know that
|
||||
* it must happen by the time aeApiPoll is called again. Our solution is to
|
||||
* keep track of the last fds returned by aeApiPoll and reassociate them next
|
||||
* keep track of the last fds returned by aeApiPoll and re-associate them next
|
||||
* time aeApiPoll is invoked.
|
||||
*
|
||||
* To summarize, in this module, each fd association is EITHER (a) represented
|
||||
* only via the in-kernel assocation OR (b) represented by pending_fds and
|
||||
* only via the in-kernel association OR (b) represented by pending_fds and
|
||||
* pending_masks. (b) is only true for the last fds we returned from aeApiPoll,
|
||||
* and only until we enter aeApiPoll again (at which point we restore the
|
||||
* in-kernel association).
|
||||
@@ -164,7 +164,7 @@ static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
* This fd was recently returned from aeApiPoll. It should be safe to
|
||||
* assume that the consumer has processed that poll event, but we play
|
||||
* it safer by simply updating pending_mask. The fd will be
|
||||
* reassociated as usual when aeApiPoll is called again.
|
||||
* re-associated as usual when aeApiPoll is called again.
|
||||
*/
|
||||
if (evport_debug)
|
||||
fprintf(stderr, "aeApiAddEvent: adding to pending fd %d\n", fd);
|
||||
@@ -228,7 +228,7 @@ static void aeApiDelEvent(aeEventLoop *eventLoop, int fd, int mask) {
|
||||
* ENOMEM is a potentially transient condition, but the kernel won't
|
||||
* generally return it unless things are really bad. EAGAIN indicates
|
||||
* we've reached an resource limit, for which it doesn't make sense to
|
||||
* retry (counterintuitively). All other errors indicate a bug. In any
|
||||
* retry (counter-intuitively). All other errors indicate a bug. In any
|
||||
* of these cases, the best we can do is to abort.
|
||||
*/
|
||||
abort(); /* will not return */
|
||||
@@ -243,7 +243,7 @@ static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
|
||||
port_event_t event[MAX_EVENT_BATCHSZ];
|
||||
|
||||
/*
|
||||
* If we've returned fd events before, we must reassociate them with the
|
||||
* If we've returned fd events before, we must re-associate them with the
|
||||
* port now, before calling port_get(). See the block comment at the top of
|
||||
* this file for an explanation of why.
|
||||
*/
|
||||
|
||||
+62
-5
@@ -61,7 +61,7 @@ int anetNonBlock(char *err, int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
/* Set the socket nonblocking.
|
||||
/* Set the socket non-blocking.
|
||||
* Note that fcntl(2) for F_GETFL and F_SETFL can't be
|
||||
* interrupted by a signal. */
|
||||
if ((flags = fcntl(fd, F_GETFL)) == -1) {
|
||||
@@ -75,10 +75,56 @@ int anetNonBlock(char *err, int fd)
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetTcpNoDelay(char *err, int fd)
|
||||
/* Set TCP keep alive option to detect dead peers. The interval option
|
||||
* is only used for Linux as we are using Linux-specific APIs to set
|
||||
* the probe send time, interval, and count. */
|
||||
int anetKeepAlive(char *err, int fd, int interval)
|
||||
{
|
||||
int yes = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &yes, sizeof(yes)) == -1)
|
||||
int val = 1;
|
||||
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof(val)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt SO_KEEPALIVE: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
/* Default settings are more or less garbage, with the keepalive time
|
||||
* set to 7200 by default on Linux. Modify settings to make the feature
|
||||
* actually useful. */
|
||||
|
||||
/* Send first probe after interval. */
|
||||
val = interval;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Send next probes after the specified interval. Note that we set the
|
||||
* delay as interval / 3, as we send three probes before detecting
|
||||
* an error (see the next setsockopt call). */
|
||||
val = interval/3;
|
||||
if (val == 0) val = 1;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Consider the socket in error state after three we send three ACK
|
||||
* probes without getting a reply. */
|
||||
val = 3;
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof(val)) < 0) {
|
||||
anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n", strerror(errno));
|
||||
return ANET_ERR;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
static int anetSetTcpNoDelay(char *err, int fd, int val)
|
||||
{
|
||||
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) == -1)
|
||||
{
|
||||
anetSetError(err, "setsockopt TCP_NODELAY: %s", strerror(errno));
|
||||
return ANET_ERR;
|
||||
@@ -86,6 +132,17 @@ int anetTcpNoDelay(char *err, int fd)
|
||||
return ANET_OK;
|
||||
}
|
||||
|
||||
int anetEnableTcpNoDelay(char *err, int fd)
|
||||
{
|
||||
return anetSetTcpNoDelay(err, fd, 1);
|
||||
}
|
||||
|
||||
int anetDisableTcpNoDelay(char *err, int fd)
|
||||
{
|
||||
return anetSetTcpNoDelay(err, fd, 0);
|
||||
}
|
||||
|
||||
|
||||
int anetSetSendBuffer(char *err, int fd, int buffsize)
|
||||
{
|
||||
if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &buffsize, sizeof(buffsize)) == -1)
|
||||
@@ -132,7 +189,7 @@ static int anetCreateSocket(char *err, int domain) {
|
||||
return ANET_ERR;
|
||||
}
|
||||
|
||||
/* Make sure connection-intensive things like the redis benckmark
|
||||
/* Make sure connection-intensive things like the redis benchmark
|
||||
* will be able to close/open sockets a zillion of times */
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
|
||||
anetSetError(err, "setsockopt SO_REUSEADDR: %s", strerror(errno));
|
||||
|
||||
+3
-1
@@ -51,8 +51,10 @@ int anetTcpAccept(char *err, int serversock, char *ip, int *port);
|
||||
int anetUnixAccept(char *err, int serversock);
|
||||
int anetWrite(int fd, char *buf, int count);
|
||||
int anetNonBlock(char *err, int fd);
|
||||
int anetTcpNoDelay(char *err, int fd);
|
||||
int anetEnableTcpNoDelay(char *err, int fd);
|
||||
int anetDisableTcpNoDelay(char *err, int fd);
|
||||
int anetTcpKeepAlive(char *err, int fd);
|
||||
int anetPeerToString(int fd, char *ip, int *port);
|
||||
int anetKeepAlive(char *err, int fd, int interval);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -177,7 +177,7 @@ void stopAppendOnly(void) {
|
||||
|
||||
redisLog(REDIS_NOTICE,"Killing running AOF rewrite child: %ld",
|
||||
(long) server.aof_child_pid);
|
||||
if (kill(server.aof_child_pid,SIGKILL) != -1)
|
||||
if (kill(server.aof_child_pid,SIGUSR1) != -1)
|
||||
wait3(&statloc,0,NULL);
|
||||
/* reset the buffer accumulating changes while the child saves */
|
||||
aofRewriteBufferReset();
|
||||
@@ -385,7 +385,7 @@ void feedAppendOnlyFile(struct redisCommand *cmd, int dictid, robj **argv, int a
|
||||
sds buf = sdsempty();
|
||||
robj *tmpargv[3];
|
||||
|
||||
/* The DB this command was targetting is not the same as the last command
|
||||
/* The DB this command was targeting is not the same as the last command
|
||||
* we appendend. To issue a SELECT command is needed. */
|
||||
if (dictid != server.aof_selected_db) {
|
||||
char seldb[64];
|
||||
@@ -442,6 +442,7 @@ struct redisClient *createFakeClient(void) {
|
||||
|
||||
selectDb(c,0);
|
||||
c->fd = -1;
|
||||
c->name = NULL;
|
||||
c->querybuf = sdsempty();
|
||||
c->querybuf_peak = 0;
|
||||
c->argc = 0;
|
||||
|
||||
@@ -73,7 +73,7 @@ static list *bio_jobs[REDIS_BIO_NUM_OPS];
|
||||
static unsigned long long bio_pending[REDIS_BIO_NUM_OPS];
|
||||
|
||||
/* This structure represents a background Job. It is only used locally to this
|
||||
* file as the API deos not expose the internals at all. */
|
||||
* file as the API does not expose the internals at all. */
|
||||
struct bio_job {
|
||||
time_t time; /* Time at which the job was created. */
|
||||
/* Job specific arguments pointers. If we need to pass more than three
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@
|
||||
* Helpers and low level bit functions.
|
||||
* -------------------------------------------------------------------------- */
|
||||
|
||||
/* This helper function used by GETBIT / SETBIT parses the bit offset arguemnt
|
||||
/* This helper function used by GETBIT / SETBIT parses the bit offset argument
|
||||
* making sure an error is returned if it is negative or if it overflows
|
||||
* Redis 512 MB limit for the string value. */
|
||||
static int getBitOffsetFromArgument(redisClient *c, robj *o, size_t *offset) {
|
||||
@@ -189,7 +189,7 @@ void bitopCommand(redisClient *c) {
|
||||
char *opname = c->argv[1]->ptr;
|
||||
robj *o, *targetkey = c->argv[2];
|
||||
long op, j, numkeys;
|
||||
robj **objects; /* Array of soruce objects. */
|
||||
robj **objects; /* Array of source objects. */
|
||||
unsigned char **src; /* Array of source strings pointers. */
|
||||
long *len, maxlen = 0; /* Array of length of src strings, and max len. */
|
||||
long minlen = 0; /* Min len among the input keys. */
|
||||
|
||||
+24
-3
@@ -81,6 +81,11 @@ void loadServerConfigFromString(char *config) {
|
||||
if (server.maxidletime < 0) {
|
||||
err = "Invalid timeout value"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"tcp-keepalive") && argc == 2) {
|
||||
server.tcpkeepalive = atoi(argv[1]);
|
||||
if (server.tcpkeepalive < 0) {
|
||||
err = "Invalid tcp-keepalive value"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"port") && argc == 2) {
|
||||
server.port = atoi(argv[1]);
|
||||
if (server.port < 0 || server.port > 65535) {
|
||||
@@ -230,6 +235,10 @@ void loadServerConfigFromString(char *config) {
|
||||
err = "repl-timeout must be 1 or greater";
|
||||
goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"repl-disable-tcp-nodelay") && argc==2) {
|
||||
if ((server.repl_disable_tcp_nodelay = yesnotoi(argv[1])) == -1) {
|
||||
err = "argument must be 'yes' or 'no'"; goto loaderr;
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"masterauth") && argc == 2) {
|
||||
server.masterauth = zstrdup(argv[1]);
|
||||
} else if (!strcasecmp(argv[0],"slave-serve-stale-data") && argc == 2) {
|
||||
@@ -329,7 +338,7 @@ void loadServerConfigFromString(char *config) {
|
||||
goto loaderr;
|
||||
}
|
||||
|
||||
/* If the target command name is the emtpy string we just
|
||||
/* If the target command name is the empty string we just
|
||||
* remove it from the command table. */
|
||||
retval = dictDelete(server.commands, argv[1]);
|
||||
redisAssert(retval == DICT_OK);
|
||||
@@ -367,7 +376,7 @@ void loadServerConfigFromString(char *config) {
|
||||
soft = memtoll(argv[3],NULL);
|
||||
soft_seconds = atoi(argv[4]);
|
||||
if (soft_seconds < 0) {
|
||||
err = "Negative number of seconds in soft limt is invalid";
|
||||
err = "Negative number of seconds in soft limit is invalid";
|
||||
goto loaderr;
|
||||
}
|
||||
server.client_obuf_limits[class].hard_limit_bytes = hard;
|
||||
@@ -412,7 +421,7 @@ loaderr:
|
||||
* in the 'options' string to the config file before loading.
|
||||
*
|
||||
* Both filename and options can be NULL, in such a case are considered
|
||||
* emtpy. This way loadServerConfig can be used to just load a file or
|
||||
* empty. This way loadServerConfig can be used to just load a file or
|
||||
* just load a string. */
|
||||
void loadServerConfig(char *filename, char *options) {
|
||||
sds config = sdsempty();
|
||||
@@ -499,6 +508,10 @@ void configSetCommand(redisClient *c) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0 || ll > LONG_MAX) goto badfmt;
|
||||
server.maxidletime = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"tcp-keepalive")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0 || ll > INT_MAX) goto badfmt;
|
||||
server.tcpkeepalive = ll;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"appendfsync")) {
|
||||
if (!strcasecmp(o->ptr,"no")) {
|
||||
server.aof_fsync = AOF_FSYNC_NO;
|
||||
@@ -697,6 +710,11 @@ void configSetCommand(redisClient *c) {
|
||||
|
||||
if (yn == -1) goto badfmt;
|
||||
server.rdb_checksum = yn;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"repl-disable-tcp-nodelay")) {
|
||||
int yn = yesnotoi(o->ptr);
|
||||
|
||||
if (yn == -1) goto badfmt;
|
||||
server.repl_disable_tcp_nodelay = yn;
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"slave-priority")) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll <= 0) goto badfmt;
|
||||
@@ -761,6 +779,7 @@ void configGetCommand(redisClient *c) {
|
||||
config_get_numerical_field("maxmemory",server.maxmemory);
|
||||
config_get_numerical_field("maxmemory-samples",server.maxmemory_samples);
|
||||
config_get_numerical_field("timeout",server.maxidletime);
|
||||
config_get_numerical_field("tcp-keepalive",server.tcpkeepalive);
|
||||
config_get_numerical_field("auto-aof-rewrite-percentage",
|
||||
server.aof_rewrite_perc);
|
||||
config_get_numerical_field("auto-aof-rewrite-min-size",
|
||||
@@ -805,6 +824,8 @@ void configGetCommand(redisClient *c) {
|
||||
config_get_bool_field("rdbcompression", server.rdb_compression);
|
||||
config_get_bool_field("rdbchecksum", server.rdb_checksum);
|
||||
config_get_bool_field("activerehashing", server.activerehashing);
|
||||
config_get_bool_field("repl-disable-tcp-nodelay",
|
||||
server.repl_disable_tcp_nodelay);
|
||||
|
||||
/* Everything we can't handle with macros follows. */
|
||||
|
||||
|
||||
+16
-3
@@ -54,7 +54,7 @@
|
||||
#endif
|
||||
|
||||
/* Test for backtrace() */
|
||||
#if defined(__APPLE__) || defined(__linux__) || defined(__sun)
|
||||
#if defined(__APPLE__) || defined(__linux__)
|
||||
#define HAVE_BACKTRACE 1
|
||||
#endif
|
||||
|
||||
@@ -137,13 +137,27 @@
|
||||
#endif /* BSD */
|
||||
#endif /* BYTE_ORDER */
|
||||
|
||||
#if defined(__BYTE_ORDER) && !defined(BYTE_ORDER)
|
||||
/* Sometimes after including an OS-specific header that defines the
|
||||
* endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what
|
||||
* the Redis code uses. In this case let's define everything without the
|
||||
* underscores. */
|
||||
#ifndef BYTE_ORDER
|
||||
#ifdef __BYTE_ORDER
|
||||
#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
|
||||
#ifndef LITTLE_ENDIAN
|
||||
#define LITTLE_ENDIAN __LITTLE_ENDIAN
|
||||
#endif
|
||||
#ifndef BIG_ENDIAN
|
||||
#define BIG_ENDIAN __BIG_ENDIAN
|
||||
#endif
|
||||
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
|
||||
#define BYTE_ORDER LITTLE_ENDIAN
|
||||
#else
|
||||
#define BYTE_ORDER BIG_ENDIAN
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if !defined(BYTE_ORDER) || \
|
||||
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN)
|
||||
@@ -162,5 +176,4 @@
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -44,7 +44,7 @@ robj *lookupKey(redisDb *db, robj *key) {
|
||||
if (de) {
|
||||
robj *val = dictGetVal(de);
|
||||
|
||||
/* Update the access time for the aging algorithm.
|
||||
/* Update the access time for the ageing algorithm.
|
||||
* Don't do it if we have a saving child, as this will trigger
|
||||
* a copy on write madness. */
|
||||
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1)
|
||||
@@ -85,7 +85,7 @@ robj *lookupKeyWriteOrReply(redisClient *c, robj *key, robj *reply) {
|
||||
}
|
||||
|
||||
/* Add the key to the DB. It's up to the caller to increment the reference
|
||||
* counte of the value if needed.
|
||||
* counter of the value if needed.
|
||||
*
|
||||
* The program is aborted if the key already exists. */
|
||||
void dbAdd(redisDb *db, robj *key, robj *val) {
|
||||
@@ -219,7 +219,7 @@ void flushallCommand(redisClient *c) {
|
||||
server.dirty += emptyDb();
|
||||
addReply(c,shared.ok);
|
||||
if (server.rdb_child_pid != -1) {
|
||||
kill(server.rdb_child_pid,SIGKILL);
|
||||
kill(server.rdb_child_pid,SIGUSR1);
|
||||
rdbRemoveTempFile(server.rdb_child_pid);
|
||||
}
|
||||
if (server.saveparamslen > 0) {
|
||||
@@ -538,7 +538,7 @@ int expireIfNeeded(redisDb *db, robj *key) {
|
||||
* for *AT variants of the command, or the current time for relative expires).
|
||||
*
|
||||
* unit is either UNIT_SECONDS or UNIT_MILLISECONDS, and is only used for
|
||||
* the argv[2] parameter. The basetime is always specified in milliesconds. */
|
||||
* the argv[2] parameter. The basetime is always specified in milliseconds. */
|
||||
void expireGenericCommand(redisClient *c, long long basetime, int unit) {
|
||||
dictEntry *de;
|
||||
robj *key = c->argv[1], *param = c->argv[2];
|
||||
|
||||
+2
-2
@@ -42,7 +42,7 @@
|
||||
/* ================================= Debugging ============================== */
|
||||
|
||||
/* Compute the sha1 of string at 's' with 'len' bytes long.
|
||||
* The SHA1 is then xored againt the string pointed by digest.
|
||||
* The SHA1 is then xored against the string pointed by digest.
|
||||
* Since xor is commutative, this operation is used in order to
|
||||
* "add" digests relative to unordered elements.
|
||||
*
|
||||
@@ -67,7 +67,7 @@ void xorObjectDigest(unsigned char *digest, robj *o) {
|
||||
}
|
||||
|
||||
/* This function instead of just computing the SHA1 and xoring it
|
||||
* against diget, also perform the digest of "digest" itself and
|
||||
* against digest, also perform the digest of "digest" itself and
|
||||
* replace the old value with the new one.
|
||||
*
|
||||
* So the final digest will be:
|
||||
|
||||
+1
-1
@@ -610,7 +610,7 @@ static int _dictExpandIfNeeded(dict *d)
|
||||
/* Incremental rehashing already in progress. Return. */
|
||||
if (dictIsRehashing(d)) return DICT_OK;
|
||||
|
||||
/* If the hash table is empty expand it to the intial size. */
|
||||
/* If the hash table is empty expand it to the initial size. */
|
||||
if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);
|
||||
|
||||
/* If we reached the 1:1 ratio, and we are allowed to resize the hash
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#ifndef __ENDIANCONV_H
|
||||
#define __ENDIANCONV_H
|
||||
|
||||
#include "config.h"
|
||||
#include <stdint.h>
|
||||
|
||||
void memrev16(void *p);
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) || defined(__OpenBSD__)
|
||||
#if defined(__linux__) || defined(__OpenBSD__) || defined(__NetBSD__)
|
||||
#define _XOPEN_SOURCE 700
|
||||
#else
|
||||
#define _XOPEN_SOURCE
|
||||
|
||||
+4
-4
@@ -93,7 +93,7 @@
|
||||
|
||||
/*
|
||||
* Avoid assigning values to errno variable? for some embedding purposes
|
||||
* (linux kernel for example), this is neccessary. NOTE: this breaks
|
||||
* (linux kernel for example), this is necessary. NOTE: this breaks
|
||||
* the documentation in lzf.h.
|
||||
*/
|
||||
#ifndef AVOID_ERRNO
|
||||
@@ -101,7 +101,7 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Wether to pass the LZF_STATE variable as argument, or allocate it
|
||||
* Whether to pass the LZF_STATE variable as argument, or allocate it
|
||||
* on the stack. For small-stack environments, define this to 1.
|
||||
* NOTE: this breaks the prototype in lzf.h.
|
||||
*/
|
||||
@@ -110,11 +110,11 @@
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Wether to add extra checks for input validity in lzf_decompress
|
||||
* Whether to add extra checks for input validity in lzf_decompress
|
||||
* and return EINVAL if the input stream has been corrupted. This
|
||||
* only shields against overflowing the input buffer and will not
|
||||
* detect most corrupted streams.
|
||||
* This check is not normally noticable on modern hardware
|
||||
* This check is not normally noticeable on modern hardware
|
||||
* (<1% slowdown), but might slow down older cpus considerably.
|
||||
*/
|
||||
#ifndef CHECK_INPUT
|
||||
|
||||
+2
-4
@@ -78,10 +78,8 @@ void memtest_progress_end(void) {
|
||||
void memtest_progress_step(size_t curr, size_t size, char c) {
|
||||
size_t chars = ((unsigned long long)curr*progress_full)/size, j;
|
||||
|
||||
for (j = 0; j < chars-progress_printed; j++) {
|
||||
printf("%c",c);
|
||||
progress_printed++;
|
||||
}
|
||||
for (j = 0; j < chars-progress_printed; j++) printf("%c",c);
|
||||
progress_printed = chars;
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ GIT_SHA1=`(git show-ref --head --hash=8 2> /dev/null || echo 00000000) | head -n
|
||||
GIT_DIRTY=`git diff 2> /dev/null | wc -l`
|
||||
test -f release.h || touch release.h
|
||||
(cat release.h | grep SHA1 | grep $GIT_SHA1) && \
|
||||
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already uptodate
|
||||
(cat release.h | grep DIRTY | grep $GIT_DIRTY) && exit 0 # Already up-to-date
|
||||
echo "#define REDIS_GIT_SHA1 \"$GIT_SHA1\"" > release.h
|
||||
echo "#define REDIS_GIT_DIRTY \"$GIT_DIRTY\"" >> release.h
|
||||
touch release.c # Force recompile of release.c
|
||||
|
||||
+2
-2
@@ -102,7 +102,7 @@ void discardCommand(redisClient *c) {
|
||||
}
|
||||
|
||||
/* Send a MULTI command to all the slaves and AOF file. Check the execCommand
|
||||
* implememntation for more information. */
|
||||
* implementation for more information. */
|
||||
void execCommandReplicateMulti(redisClient *c) {
|
||||
robj *multistring = createStringObject("MULTI",5);
|
||||
|
||||
@@ -223,7 +223,7 @@ void watchForKey(redisClient *c, robj *key) {
|
||||
incrRefCount(key);
|
||||
}
|
||||
listAddNodeTail(clients,c);
|
||||
/* Add the new key to the lits of keys watched by this client */
|
||||
/* Add the new key to the list of keys watched by this client */
|
||||
wk = zmalloc(sizeof(*wk));
|
||||
wk->key = key;
|
||||
wk->db = c->db;
|
||||
|
||||
+52
-11
@@ -58,7 +58,9 @@ redisClient *createClient(int fd) {
|
||||
* contexts (for instance a Lua script) we need a non connected client. */
|
||||
if (fd != -1) {
|
||||
anetNonBlock(NULL,fd);
|
||||
anetTcpNoDelay(NULL,fd);
|
||||
anetEnableTcpNoDelay(NULL,fd);
|
||||
if (server.tcpkeepalive)
|
||||
anetKeepAlive(NULL,fd,server.tcpkeepalive);
|
||||
if (aeCreateFileEvent(server.el,fd,AE_READABLE,
|
||||
readQueryFromClient, c) == AE_ERR)
|
||||
{
|
||||
@@ -70,6 +72,7 @@ redisClient *createClient(int fd) {
|
||||
|
||||
selectDb(c,0);
|
||||
c->fd = fd;
|
||||
c->name = NULL;
|
||||
c->bufpos = 0;
|
||||
c->querybuf = sdsempty();
|
||||
c->querybuf_peak = 0;
|
||||
@@ -90,8 +93,7 @@ redisClient *createClient(int fd) {
|
||||
c->obuf_soft_limit_reached_time = 0;
|
||||
listSetFreeMethod(c->reply,decrRefCount);
|
||||
listSetDupMethod(c->reply,dupClientReplyValue);
|
||||
c->bpop.keys = NULL;
|
||||
c->bpop.count = 0;
|
||||
c->bpop.keys = dictCreate(&setDictType,NULL);
|
||||
c->bpop.timeout = 0;
|
||||
c->bpop.target = NULL;
|
||||
c->io_keys = listCreate();
|
||||
@@ -378,7 +380,7 @@ void *addDeferredMultiBulkLength(redisClient *c) {
|
||||
return listLast(c->reply);
|
||||
}
|
||||
|
||||
/* Populate the length object and try glueing it to the next chunk. */
|
||||
/* Populate the length object and try gluing it to the next chunk. */
|
||||
void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
|
||||
listNode *ln = (listNode*)node;
|
||||
robj *len, *next;
|
||||
@@ -404,7 +406,7 @@ void setDeferredMultiBulkLength(redisClient *c, void *node, long length) {
|
||||
asyncCloseClientOnOutputBufferLimitReached(c);
|
||||
}
|
||||
|
||||
/* Add a duble as a bulk reply */
|
||||
/* Add a double as a bulk reply */
|
||||
void addReplyDouble(redisClient *c, double d) {
|
||||
char dbuf[128], sbuf[128];
|
||||
int dlen, slen;
|
||||
@@ -518,13 +520,15 @@ void copyClientOutputBuffer(redisClient *dst, redisClient *src) {
|
||||
static void acceptCommonHandler(int fd, int flags) {
|
||||
redisClient *c;
|
||||
if ((c = createClient(fd)) == NULL) {
|
||||
redisLog(REDIS_WARNING,"Error allocating resources for the client");
|
||||
redisLog(REDIS_WARNING,
|
||||
"Error registering fd event for the new client: %s (fd=%d)",
|
||||
strerror(errno),fd);
|
||||
close(fd); /* May be already closed, just ignore errors */
|
||||
return;
|
||||
}
|
||||
/* If maxclient directive is set and this is one client more... close the
|
||||
* connection. Note that we create the client instead to check before
|
||||
* for this condition, since now the socket is already set in nonblocking
|
||||
* for this condition, since now the socket is already set in non-blocking
|
||||
* mode and we can send an error for free using the Kernel I/O */
|
||||
if (listLength(server.clients) > server.maxclients) {
|
||||
char *err = "-ERR max number of clients reached\r\n";
|
||||
@@ -606,6 +610,7 @@ void freeClient(redisClient *c) {
|
||||
c->querybuf = NULL;
|
||||
if (c->flags & REDIS_BLOCKED)
|
||||
unblockClientWaitingData(c);
|
||||
dictRelease(c->bpop.keys);
|
||||
|
||||
/* UNWATCH all the keys */
|
||||
unwatchAllKeys(c);
|
||||
@@ -666,6 +671,7 @@ void freeClient(redisClient *c) {
|
||||
}
|
||||
|
||||
/* Release memory */
|
||||
if (c->name) decrRefCount(c->name);
|
||||
zfree(c->argv);
|
||||
freeClientMultiState(c);
|
||||
zfree(c);
|
||||
@@ -937,7 +943,7 @@ int processMultibulkBuffer(redisClient *c) {
|
||||
/* Not enough data (+2 == trailing \r\n) */
|
||||
break;
|
||||
} else {
|
||||
/* Optimization: if the buffer contanins JUST our bulk element
|
||||
/* Optimization: if the buffer contains JUST our bulk element
|
||||
* instead of creating a new object by *copying* the sds we
|
||||
* just use the current sds string. */
|
||||
if (pos == 0 &&
|
||||
@@ -1121,9 +1127,11 @@ sds getClientInfoString(redisClient *client) {
|
||||
if (emask & AE_WRITABLE) *p++ = 'w';
|
||||
*p = '\0';
|
||||
return sdscatprintf(sdsempty(),
|
||||
"addr=%s:%d fd=%d age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
|
||||
"addr=%s:%d fd=%d name=%s age=%ld idle=%ld flags=%s db=%d sub=%d psub=%d multi=%d qbuf=%lu qbuf-free=%lu obl=%lu oll=%lu omem=%lu events=%s cmd=%s",
|
||||
(client->flags & REDIS_UNIX_SOCKET) ? server.unixsocket : ip,
|
||||
port,client->fd,
|
||||
port,
|
||||
client->fd,
|
||||
client->name ? (char*)client->name->ptr : "",
|
||||
(long)(server.unixtime - client->ctime),
|
||||
(long)(server.unixtime - client->lastinteraction),
|
||||
flags,
|
||||
@@ -1188,8 +1196,41 @@ void clientCommand(redisClient *c) {
|
||||
}
|
||||
}
|
||||
addReplyError(c,"No such client");
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"setname") && c->argc == 3) {
|
||||
int j, len = sdslen(c->argv[2]->ptr);
|
||||
char *p = c->argv[2]->ptr;
|
||||
|
||||
/* Setting the client name to an empty string actually removes
|
||||
* the current name. */
|
||||
if (len == 0) {
|
||||
if (c->name) decrRefCount(c->name);
|
||||
c->name = NULL;
|
||||
addReply(c,shared.ok);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Otherwise check if the charset is ok. We need to do this otherwise
|
||||
* CLIENT LIST format will break. You should always be able to
|
||||
* split by space to get the different fields. */
|
||||
for (j = 0; j < len; j++) {
|
||||
if (p[j] < '!' || p[j] > '~') { /* ASCII is assumed. */
|
||||
addReplyError(c,
|
||||
"Client names cannot contain spaces, "
|
||||
"newlines or special characters.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (c->name) decrRefCount(c->name);
|
||||
c->name = c->argv[2];
|
||||
incrRefCount(c->name);
|
||||
addReply(c,shared.ok);
|
||||
} else if (!strcasecmp(c->argv[1]->ptr,"getname") && c->argc == 2) {
|
||||
if (c->name)
|
||||
addReplyBulk(c,c->name);
|
||||
else
|
||||
addReply(c,shared.nullbulk);
|
||||
} else {
|
||||
addReplyError(c, "Syntax error, try CLIENT (LIST | KILL ip:port)");
|
||||
addReplyError(c, "Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -72,7 +72,7 @@ robj *createStringObjectFromLongDouble(long double value) {
|
||||
int len;
|
||||
|
||||
/* We use 17 digits precision since with 128 bit floats that precision
|
||||
* after rouding is able to represent most small decimal numbers in a way
|
||||
* after rounding is able to represent most small decimal numbers in a way
|
||||
* that is "non surprising" for the user (that is, most small decimal
|
||||
* numbers will be represented in a way that when converted back into
|
||||
* a string are exactly the same as what the user typed.) */
|
||||
|
||||
+17
-1
@@ -117,7 +117,7 @@ int pubsubUnsubscribeChannel(redisClient *c, robj *channel, int notify) {
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the clinet was already subscribed to that pattern. */
|
||||
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */
|
||||
int pubsubSubscribePattern(redisClient *c, robj *pattern) {
|
||||
int retval = 0;
|
||||
|
||||
@@ -179,6 +179,14 @@ int pubsubUnsubscribeAllChannels(redisClient *c, int notify) {
|
||||
|
||||
count += pubsubUnsubscribeChannel(c,channel,notify);
|
||||
}
|
||||
/* We were subscribed to nothing? Still reply to the client. */
|
||||
if (notify && count == 0) {
|
||||
addReply(c,shared.mbulkhdr[3]);
|
||||
addReply(c,shared.unsubscribebulk);
|
||||
addReply(c,shared.nullbulk);
|
||||
addReplyLongLong(c,dictSize(c->pubsub_channels)+
|
||||
listLength(c->pubsub_patterns));
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
return count;
|
||||
}
|
||||
@@ -196,6 +204,14 @@ int pubsubUnsubscribeAllPatterns(redisClient *c, int notify) {
|
||||
|
||||
count += pubsubUnsubscribePattern(c,pattern,notify);
|
||||
}
|
||||
if (notify && count == 0) {
|
||||
/* We were subscribed to nothing? Still reply to the client. */
|
||||
addReply(c,shared.mbulkhdr[3]);
|
||||
addReply(c,shared.punsubscribebulk);
|
||||
addReply(c,shared.nullbulk);
|
||||
addReplyLongLong(c,dictSize(c->pubsub_channels)+
|
||||
listLength(c->pubsub_patterns));
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Save a string objet as [len][data] on disk. If the object is a string
|
||||
/* Save a string object as [len][data] on disk. If the object is a string
|
||||
* representation of an integer value we try to save it in a special form */
|
||||
int rdbSaveRawString(rio *rdb, unsigned char *s, size_t len) {
|
||||
int enclen;
|
||||
@@ -321,7 +321,7 @@ int rdbSaveLongLongAsStringObject(rio *rdb, long long value) {
|
||||
/* Like rdbSaveStringObjectRaw() but handle encoded objects */
|
||||
int rdbSaveStringObject(rio *rdb, robj *obj) {
|
||||
/* Avoid to decode the object, then encode it again, if the
|
||||
* object is alrady integer encoded. */
|
||||
* object is already integer encoded. */
|
||||
if (obj->encoding == REDIS_ENCODING_INT) {
|
||||
return rdbSaveLongLongAsStringObject(rdb,(long)obj->ptr);
|
||||
} else {
|
||||
@@ -367,7 +367,7 @@ robj *rdbLoadEncodedStringObject(rio *rdb) {
|
||||
}
|
||||
|
||||
/* Save a double value. Doubles are saved as strings prefixed by an unsigned
|
||||
* 8 bit integer specifing the length of the representation.
|
||||
* 8 bit integer specifying the length of the representation.
|
||||
* This 8 bit integer has special values in order to specify the following
|
||||
* conditions:
|
||||
* 253: not a number
|
||||
@@ -606,7 +606,7 @@ off_t rdbSavedObjectLen(robj *o) {
|
||||
|
||||
/* Save a key-value pair, with expire time, type, key, value.
|
||||
* On error -1 is returned.
|
||||
* On success if the key was actaully saved 1 is returned, otherwise 0
|
||||
* On success if the key was actually saved 1 is returned, otherwise 0
|
||||
* is returned (the key was already expired). */
|
||||
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val,
|
||||
long long expiretime, long long now)
|
||||
@@ -1109,7 +1109,7 @@ int rdbLoad(char *filename) {
|
||||
/* We read the time so we need to read the object type again. */
|
||||
if ((type = rdbLoadType(&rdb)) == -1) goto eoferr;
|
||||
/* the EXPIRETIME opcode specifies time in seconds, so convert
|
||||
* into milliesconds. */
|
||||
* into milliseconds. */
|
||||
expiretime *= 1000;
|
||||
} else if (type == REDIS_RDB_OPCODE_EXPIRETIME_MS) {
|
||||
/* Milliseconds precision expire times introduced with RDB
|
||||
@@ -1194,7 +1194,10 @@ void backgroundSaveDoneHandler(int exitcode, int bysignal) {
|
||||
redisLog(REDIS_WARNING,
|
||||
"Background saving terminated by signal %d", bysignal);
|
||||
rdbRemoveTempFile(server.rdb_child_pid);
|
||||
server.lastbgsave_status = REDIS_ERR;
|
||||
/* SIGUSR1 is whitelisted, so we have a way to kill a child without
|
||||
* tirggering an error conditon. */
|
||||
if (bysignal != SIGUSR1)
|
||||
server.lastbgsave_status = REDIS_ERR;
|
||||
}
|
||||
server.rdb_child_pid = -1;
|
||||
server.rdb_save_time_last = time(NULL)-server.rdb_save_time_start;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
* number specify the kind of object that follows.
|
||||
* See the REDIS_RDB_ENC_* defines.
|
||||
*
|
||||
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
|
||||
* Lengths up to 63 are stored using a single byte, most DB keys, and may
|
||||
* values, will fit inside. */
|
||||
#define REDIS_RDB_6BITLEN 0
|
||||
#define REDIS_RDB_14BITLEN 1
|
||||
|
||||
@@ -106,7 +106,7 @@ static long long mstime(void) {
|
||||
long long mst;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mst = ((long)tv.tv_sec)*1000;
|
||||
mst = ((long long)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
* number specify the kind of object that follows.
|
||||
* See the REDIS_RDB_ENC_* defines.
|
||||
*
|
||||
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
|
||||
* Lengths up to 63 are stored using a single byte, most DB keys, and may
|
||||
* values, will fit inside. */
|
||||
#define REDIS_RDB_6BITLEN 0
|
||||
#define REDIS_RDB_14BITLEN 1
|
||||
@@ -132,7 +132,7 @@ typedef struct {
|
||||
char success;
|
||||
} entry;
|
||||
|
||||
/* Global vars that are actally used as constants. The following double
|
||||
/* Global vars that are actually used as constants. The following double
|
||||
* values are used for double on-disk serialization, and are initialized
|
||||
* at runtime to avoid strange compiler optimizations. */
|
||||
static double R_Zero, R_PosInf, R_NegInf, R_Nan;
|
||||
|
||||
+94
-18
@@ -41,6 +41,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "hiredis.h"
|
||||
#include "sds.h"
|
||||
@@ -73,6 +74,8 @@ static struct config {
|
||||
int cluster_reissue_command;
|
||||
int slave_mode;
|
||||
int pipe_mode;
|
||||
int getrdb_mode;
|
||||
char *rdb_filename;
|
||||
int bigkeys;
|
||||
int stdinarg; /* get last arg from stdin. (-x option) */
|
||||
char *auth;
|
||||
@@ -95,7 +98,7 @@ static long long mstime(void) {
|
||||
long long mst;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
mst = ((long)tv.tv_sec)*1000;
|
||||
mst = ((long long)tv.tv_sec)*1000;
|
||||
mst += tv.tv_usec/1000;
|
||||
return mst;
|
||||
}
|
||||
@@ -305,7 +308,7 @@ static int cliSelect() {
|
||||
return REDIS_ERR;
|
||||
}
|
||||
|
||||
/* Connect to the client. If force is not zero the connection is performed
|
||||
/* Connect to the server. If force is not zero the connection is performed
|
||||
* even if there is already a connected socket. */
|
||||
static int cliConnect(int force) {
|
||||
if (context == NULL || force) {
|
||||
@@ -660,6 +663,9 @@ static int parseOptions(int argc, char **argv) {
|
||||
config.latency_mode = 1;
|
||||
} else if (!strcmp(argv[i],"--slave")) {
|
||||
config.slave_mode = 1;
|
||||
} else if (!strcmp(argv[i],"--rdb") && !lastarg) {
|
||||
config.getrdb_mode = 1;
|
||||
config.rdb_filename = argv[++i];
|
||||
} else if (!strcmp(argv[i],"--pipe")) {
|
||||
config.pipe_mode = 1;
|
||||
} else if (!strcmp(argv[i],"--bigkeys")) {
|
||||
@@ -720,6 +726,7 @@ static void usage() {
|
||||
" --raw Use raw formatting for replies (default when STDOUT is not a tty)\n"
|
||||
" --latency Enter a special mode continuously sampling latency\n"
|
||||
" --slave Simulate a slave showing commands received from the master\n"
|
||||
" --rdb <filename> Transfer an RDB dump from remote server to local file.\n"
|
||||
" --pipe Transfer raw Redis protocol from stdin to server\n"
|
||||
" --bigkeys Sample Redis keys looking for big keys\n"
|
||||
" --eval <file> Send an EVAL command using the Lua script at <file>\n"
|
||||
@@ -794,6 +801,7 @@ static void repl() {
|
||||
sdsfree(config.hostip);
|
||||
config.hostip = sdsnew(argv[1]);
|
||||
config.hostport = atoi(argv[2]);
|
||||
cliRefreshPrompt();
|
||||
cliConnect(1);
|
||||
} else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
|
||||
linenoiseClearScreen();
|
||||
@@ -927,15 +935,15 @@ static void latencyMode(void) {
|
||||
}
|
||||
}
|
||||
|
||||
static void slaveMode(void) {
|
||||
/* Sends SYNC and reads the number of bytes in the payload. Used both by
|
||||
* slaveMode() and getRDB(). */
|
||||
unsigned long long sendSync(int fd) {
|
||||
/* To start we need to send the SYNC command and return the payload.
|
||||
* The hiredis client lib does not understand this part of the protocol
|
||||
* and we don't want to mess with its buffers, so everything is performed
|
||||
* using direct low-level I/O. */
|
||||
int fd = context->fd;
|
||||
char buf[1024], *p;
|
||||
char buf[4096], *p;
|
||||
ssize_t nread;
|
||||
unsigned long long payload;
|
||||
|
||||
/* Send the SYNC command. */
|
||||
if (write(fd,"SYNC\r\n",6) != 6) {
|
||||
@@ -951,16 +959,29 @@ static void slaveMode(void) {
|
||||
fprintf(stderr,"Error reading bulk length while SYNCing\n");
|
||||
exit(1);
|
||||
}
|
||||
if (*p == '\n') break;
|
||||
p++;
|
||||
if (*p == '\n' && p != buf) break;
|
||||
if (*p != '\n') p++;
|
||||
}
|
||||
*p = '\0';
|
||||
payload = strtoull(buf+1,NULL,10);
|
||||
fprintf(stderr,"SYNC with master, discarding %lld bytes of bulk tranfer...\n",
|
||||
payload);
|
||||
if (buf[0] == '-') {
|
||||
printf("SYNC with master failed: %s\n", buf);
|
||||
exit(1);
|
||||
}
|
||||
return strtoull(buf+1,NULL,10);
|
||||
}
|
||||
|
||||
static void slaveMode(void) {
|
||||
int fd = context->fd;
|
||||
unsigned long long payload = sendSync(fd);
|
||||
char buf[1024];
|
||||
|
||||
fprintf(stderr,"SYNC with master, discarding %llu "
|
||||
"bytes of bulk transfer...\n", payload);
|
||||
|
||||
/* Discard the payload. */
|
||||
while(payload) {
|
||||
ssize_t nread;
|
||||
|
||||
nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
if (nread <= 0) {
|
||||
fprintf(stderr,"Error reading RDB payload while SYNCing\n");
|
||||
@@ -970,11 +991,56 @@ static void slaveMode(void) {
|
||||
}
|
||||
fprintf(stderr,"SYNC done. Logging commands from master.\n");
|
||||
|
||||
/* Now we can use the hiredis to read the incoming protocol. */
|
||||
/* Now we can use hiredis to read the incoming protocol. */
|
||||
config.output = OUTPUT_CSV;
|
||||
while (cliReadReply(0) == REDIS_OK);
|
||||
}
|
||||
|
||||
/* This function implements --rdb, so it uses the replication protocol in order
|
||||
* to fetch the RDB file from a remote server. */
|
||||
static void getRDB(void) {
|
||||
int s = context->fd;
|
||||
int fd;
|
||||
unsigned long long payload = sendSync(s);
|
||||
char buf[4096];
|
||||
|
||||
fprintf(stderr,"SYNC sent to master, writing %llu bytes to '%s'\n",
|
||||
payload, config.rdb_filename);
|
||||
|
||||
/* Write to file. */
|
||||
if (!strcmp(config.rdb_filename,"-")) {
|
||||
fd = STDOUT_FILENO;
|
||||
} else {
|
||||
fd = open(config.rdb_filename, O_CREAT|O_WRONLY, 0644);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error opening '%s': %s\n", config.rdb_filename,
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
while(payload) {
|
||||
ssize_t nread, nwritten;
|
||||
|
||||
nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
|
||||
if (nread <= 0) {
|
||||
fprintf(stderr,"I/O Error reading RDB payload from socket\n");
|
||||
exit(1);
|
||||
}
|
||||
nwritten = write(fd, buf, nread);
|
||||
if (nwritten != nread) {
|
||||
fprintf(stderr,"Error writing data to file: %s\n",
|
||||
strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
payload -= nread;
|
||||
}
|
||||
close(s); /* Close the file descriptor ASAP as fsync() may take time. */
|
||||
fsync(fd);
|
||||
fprintf(stderr,"Transfer finished with success.\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static void pipeMode(void) {
|
||||
int fd = context->fd;
|
||||
long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
|
||||
@@ -1075,7 +1141,7 @@ static void pipeMode(void) {
|
||||
int j;
|
||||
|
||||
eof = 1;
|
||||
/* Everything transfered, so we queue a special
|
||||
/* Everything transferred, so we queue a special
|
||||
* ECHO command that we can match in the replies
|
||||
* to make sure everything was read from the server. */
|
||||
for (j = 0; j < 20; j++)
|
||||
@@ -1117,6 +1183,7 @@ static void findBigKeys(void) {
|
||||
unsigned long long samples = 0;
|
||||
redisReply *reply1, *reply2, *reply3 = NULL;
|
||||
char *sizecmd, *typename[] = {"string","list","set","hash","zset"};
|
||||
char *typeunit[] = {"bytes","items","members","fields","members"};
|
||||
int type;
|
||||
|
||||
printf("\n# Press ctrl+c when you have had enough of it... :)\n");
|
||||
@@ -1168,9 +1235,10 @@ static void findBigKeys(void) {
|
||||
reply3 = redisCommand(context,"%s %s", sizecmd, reply1->str);
|
||||
if (reply3 && reply3->type == REDIS_REPLY_INTEGER) {
|
||||
if (biggest[type] < reply3->integer) {
|
||||
printf("[%6s] %s | biggest so far with size %llu\n",
|
||||
printf("Biggest %-6s found so far '%s' with %llu %s.\n",
|
||||
typename[type], reply1->str,
|
||||
(unsigned long long) reply3->integer);
|
||||
(unsigned long long) reply3->integer,
|
||||
typeunit[type]);
|
||||
biggest[type] = reply3->integer;
|
||||
}
|
||||
}
|
||||
@@ -1203,6 +1271,8 @@ int main(int argc, char **argv) {
|
||||
config.latency_mode = 0;
|
||||
config.cluster_mode = 0;
|
||||
config.slave_mode = 0;
|
||||
config.getrdb_mode = 0;
|
||||
config.rdb_filename = NULL;
|
||||
config.pipe_mode = 0;
|
||||
config.bigkeys = 0;
|
||||
config.stdinarg = 0;
|
||||
@@ -1221,16 +1291,22 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* Latency mode */
|
||||
if (config.latency_mode) {
|
||||
cliConnect(0);
|
||||
if (cliConnect(0) == REDIS_ERR) exit(1);
|
||||
latencyMode();
|
||||
}
|
||||
|
||||
/* Slave mode */
|
||||
if (config.slave_mode) {
|
||||
cliConnect(0);
|
||||
if (cliConnect(0) == REDIS_ERR) exit(1);
|
||||
slaveMode();
|
||||
}
|
||||
|
||||
/* Get RDB mode. */
|
||||
if (config.getrdb_mode) {
|
||||
if (cliConnect(0) == REDIS_ERR) exit(1);
|
||||
getRDB();
|
||||
}
|
||||
|
||||
/* Pipe mode */
|
||||
if (config.pipe_mode) {
|
||||
if (cliConnect(0) == REDIS_ERR) exit(1);
|
||||
@@ -1239,7 +1315,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* Find big keys */
|
||||
if (config.bigkeys) {
|
||||
cliConnect(0);
|
||||
if (cliConnect(0) == REDIS_ERR) exit(1);
|
||||
findBigKeys();
|
||||
}
|
||||
|
||||
|
||||
+24
-20
@@ -99,7 +99,7 @@ struct redisCommand *commandTable;
|
||||
* m: may increase memory usage once called. Don't allow if out of memory.
|
||||
* a: admin command, like SAVE or SHUTDOWN.
|
||||
* p: Pub/Sub related command.
|
||||
* f: force replication of this command, regarless of server.dirty.
|
||||
* f: force replication of this command, regardless of server.dirty.
|
||||
* s: command not allowed in scripts.
|
||||
* R: random command. Command is not deterministic, that is, the same command
|
||||
* with the same arguments, with the same key space, may have different
|
||||
@@ -214,7 +214,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
{"bgsave",bgsaveCommand,1,"ar",0,NULL,0,0,0,0,0},
|
||||
{"bgrewriteaof",bgrewriteaofCommand,1,"ar",0,NULL,0,0,0,0,0},
|
||||
{"shutdown",shutdownCommand,-1,"ar",0,NULL,0,0,0,0,0},
|
||||
{"lastsave",lastsaveCommand,1,"r",0,NULL,0,0,0,0,0},
|
||||
{"lastsave",lastsaveCommand,1,"rR",0,NULL,0,0,0,0,0},
|
||||
{"type",typeCommand,2,"r",0,NULL,1,1,1,0,0},
|
||||
{"multi",multiCommand,1,"rs",0,NULL,0,0,0,0,0},
|
||||
{"exec",execCommand,1,"sM",0,NULL,0,0,0,0,0},
|
||||
@@ -288,7 +288,7 @@ void redisLogRaw(int level, const char *msg) {
|
||||
if (server.syslog_enabled) syslog(syslogLevelMap[level], "%s", msg);
|
||||
}
|
||||
|
||||
/* Like redisLogRaw() but with printf-alike support. This is the funciton that
|
||||
/* Like redisLogRaw() but with printf-alike support. This is the function that
|
||||
* is used across the code. The raw version is only used in order to dump
|
||||
* the INFO output on crash. */
|
||||
void redisLog(int level, const char *fmt, ...) {
|
||||
@@ -363,7 +363,7 @@ void exitFromChild(int retcode) {
|
||||
|
||||
/*====================== Hash table type implementation ==================== */
|
||||
|
||||
/* This is an hash table type that uses the SDS dynamic strings libary as
|
||||
/* This is an hash table type that uses the SDS dynamic strings library as
|
||||
* keys and radis objects as values (objects can hold SDS strings,
|
||||
* lists, sets). */
|
||||
|
||||
@@ -537,7 +537,7 @@ dictType commandTableDictType = {
|
||||
NULL /* val destructor */
|
||||
};
|
||||
|
||||
/* Hash type hash table (note that small hashes are represented with zimpaps) */
|
||||
/* Hash type hash table (note that small hashes are represented with ziplists) */
|
||||
dictType hashDictType = {
|
||||
dictEncObjHash, /* hash function */
|
||||
NULL, /* key dup */
|
||||
@@ -738,7 +738,7 @@ int clientsCronHandleTimeout(redisClient *c) {
|
||||
/* The client query buffer is an sds.c string that can end with a lot of
|
||||
* free space not used, this function reclaims space if needed.
|
||||
*
|
||||
* The funciton always returns 0 as it never terminates the client. */
|
||||
* The function always returns 0 as it never terminates the client. */
|
||||
int clientsCronResizeQueryBuffer(redisClient *c) {
|
||||
size_t querybuf_size = sdsAllocSize(c->querybuf);
|
||||
time_t idletime = server.unixtime - c->lastinteraction;
|
||||
@@ -796,11 +796,11 @@ void clientsCron(void) {
|
||||
*
|
||||
* - Active expired keys collection (it is also performed in a lazy way on
|
||||
* lookup).
|
||||
* - Software watchdong.
|
||||
* - Software watchdog.
|
||||
* - Update some statistic.
|
||||
* - Incremental rehashing of the DBs hash tables.
|
||||
* - Triggering BGSAVE / AOF rewrite, and handling of terminated children.
|
||||
* - Clients timeout of differnet kinds.
|
||||
* - Clients timeout of different kinds.
|
||||
* - Replication reconnection.
|
||||
* - Many more...
|
||||
*
|
||||
@@ -829,7 +829,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
|
||||
/* We have just 22 bits per object for LRU information.
|
||||
* So we use an (eventually wrapping) LRU clock with 10 seconds resolution.
|
||||
* 2^22 bits with 10 seconds resoluton is more or less 1.5 years.
|
||||
* 2^22 bits with 10 seconds resolution is more or less 1.5 years.
|
||||
*
|
||||
* Note that even if this will wrap after 1.5 years it's not a problem,
|
||||
* everything will still work but just some object will appear younger
|
||||
@@ -850,6 +850,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
if (server.shutdown_asap) {
|
||||
if (prepareForShutdown(0) == REDIS_OK) exit(0);
|
||||
redisLog(REDIS_WARNING,"SIGTERM received but errors trying to shut down the server, check the logs for more information");
|
||||
server.shutdown_asap = 0;
|
||||
}
|
||||
|
||||
/* Show some info about non-empty databases */
|
||||
@@ -867,7 +868,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
|
||||
}
|
||||
}
|
||||
|
||||
/* We don't want to resize the hash tables while a bacground saving
|
||||
/* We don't want to resize the hash tables while a background saving
|
||||
* is in progress: the saving child is created using fork() that is
|
||||
* implemented with a copy-on-write semantic in most modern systems, so
|
||||
* if we resize the HT while there is the saving child at work actually
|
||||
@@ -1093,6 +1094,7 @@ void initServerConfig() {
|
||||
server.dbnum = REDIS_DEFAULT_DBNUM;
|
||||
server.verbosity = REDIS_NOTICE;
|
||||
server.maxidletime = REDIS_MAXIDLETIME;
|
||||
server.tcpkeepalive = 0;
|
||||
server.client_max_querybuf_len = REDIS_MAX_QUERYBUF_LEN;
|
||||
server.saveparams = NULL;
|
||||
server.loading = 0;
|
||||
@@ -1159,6 +1161,7 @@ void initServerConfig() {
|
||||
server.repl_serve_stale_data = 1;
|
||||
server.repl_slave_ro = 1;
|
||||
server.repl_down_since = time(NULL);
|
||||
server.repl_disable_tcp_nodelay = 0;
|
||||
server.slave_priority = REDIS_DEFAULT_SLAVE_PRIORITY;
|
||||
|
||||
/* Client output buffer limits */
|
||||
@@ -1178,7 +1181,7 @@ void initServerConfig() {
|
||||
R_NegInf = -1.0/R_Zero;
|
||||
R_Nan = R_Zero/R_Zero;
|
||||
|
||||
/* Command table -- we intiialize it here as it is part of the
|
||||
/* Command table -- we initiialize it here as it is part of the
|
||||
* initial configuration, since command names may be changed via
|
||||
* redis.conf using the rename-command directive. */
|
||||
server.commands = dictCreate(&commandTableDictType,NULL);
|
||||
@@ -1488,7 +1491,7 @@ void call(redisClient *c, int flags) {
|
||||
long long dirty, start = ustime(), duration;
|
||||
|
||||
/* Sent the command to clients in MONITOR mode, only if the commands are
|
||||
* not geneated from reading an AOF. */
|
||||
* not generated from reading an AOF. */
|
||||
if (listLength(server.monitors) &&
|
||||
!server.loading &&
|
||||
!(c->cmd->flags & REDIS_CMD_SKIP_MONITOR))
|
||||
@@ -1510,7 +1513,7 @@ void call(redisClient *c, int flags) {
|
||||
|
||||
/* Log the command into the Slow log if needed, and populate the
|
||||
* per-command statistics that we show in INFO commandstats. */
|
||||
if (flags & REDIS_CALL_SLOWLOG)
|
||||
if (flags & REDIS_CALL_SLOWLOG && c->cmd->proc != execCommand)
|
||||
slowlogPushEntryIfNeeded(c->argv,c->argc,duration);
|
||||
if (flags & REDIS_CALL_STATS) {
|
||||
c->cmd->microseconds += duration;
|
||||
@@ -1528,8 +1531,9 @@ void call(redisClient *c, int flags) {
|
||||
if (flags != REDIS_PROPAGATE_NONE)
|
||||
propagate(c->cmd,c->db->id,c->argv,c->argc,flags);
|
||||
}
|
||||
/* Commands such as LPUSH or BRPOPLPUSH may propagate an additional
|
||||
* PUSH command. */
|
||||
|
||||
/* Handle the alsoPropagate() API to handle commands that want to propagate
|
||||
* multiple separated commands. */
|
||||
if (server.also_propagate.numops) {
|
||||
int j;
|
||||
redisOp *rop;
|
||||
@@ -1549,8 +1553,8 @@ void call(redisClient *c, int flags) {
|
||||
* server for a bulk read from the client.
|
||||
*
|
||||
* If 1 is returned the client is still alive and valid and
|
||||
* and other operations can be performed by the caller. Otherwise
|
||||
* if 0 is returned the client was destroied (i.e. after QUIT). */
|
||||
* other operations can be performed by the caller. Otherwise
|
||||
* if 0 is returned the client was destroyed (i.e. after QUIT). */
|
||||
int processCommand(redisClient *c) {
|
||||
/* The QUIT command is handled separately. Normal command procs will
|
||||
* go through checking for replication and QUIT will cause trouble
|
||||
@@ -1692,7 +1696,7 @@ int prepareForShutdown(int flags) {
|
||||
overwrite the synchronous saving did by SHUTDOWN. */
|
||||
if (server.rdb_child_pid != -1) {
|
||||
redisLog(REDIS_WARNING,"There is a child saving an .rdb. Killing it!");
|
||||
kill(server.rdb_child_pid,SIGKILL);
|
||||
kill(server.rdb_child_pid,SIGUSR1);
|
||||
rdbRemoveTempFile(server.rdb_child_pid);
|
||||
}
|
||||
if (server.aof_state != REDIS_AOF_OFF) {
|
||||
@@ -1701,7 +1705,7 @@ int prepareForShutdown(int flags) {
|
||||
if (server.aof_child_pid != -1) {
|
||||
redisLog(REDIS_WARNING,
|
||||
"There is a child rewriting the AOF. Killing it!");
|
||||
kill(server.aof_child_pid,SIGKILL);
|
||||
kill(server.aof_child_pid,SIGUSR1);
|
||||
}
|
||||
/* Append only file: fsync() the AOF and exit */
|
||||
redisLog(REDIS_NOTICE,"Calling fsync() on the AOF file.");
|
||||
@@ -1803,7 +1807,7 @@ void echoCommand(redisClient *c) {
|
||||
void timeCommand(redisClient *c) {
|
||||
struct timeval tv;
|
||||
|
||||
/* gettimeofday() can only fail if &tv is a bad addresss so we
|
||||
/* gettimeofday() can only fail if &tv is a bad address so we
|
||||
* don't check for errors. */
|
||||
gettimeofday(&tv,NULL);
|
||||
addReplyMultiBulkLen(c,2);
|
||||
|
||||
+15
-13
@@ -142,12 +142,12 @@
|
||||
*
|
||||
* 00|000000 => if the two MSB are 00 the len is the 6 bits of this byte
|
||||
* 01|000000 00000000 => 01, the len is 14 byes, 6 bits + 8 bits of next byte
|
||||
* 10|000000 [32 bit integer] => if it's 01, a full 32 bit len will follow
|
||||
* 10|000000 [32 bit integer] => if it's 10, a full 32 bit len will follow
|
||||
* 11|000000 this means: specially encoded object will follow. The six bits
|
||||
* number specify the kind of object that follows.
|
||||
* See the REDIS_RDB_ENC_* defines.
|
||||
*
|
||||
* Lenghts up to 63 are stored using a single byte, most DB keys, and may
|
||||
* Lengths up to 63 are stored using a single byte, most DB keys, and may
|
||||
* values, will fit inside. */
|
||||
#define REDIS_RDB_6BITLEN 0
|
||||
#define REDIS_RDB_14BITLEN 1
|
||||
@@ -317,7 +317,7 @@ typedef struct redisObject {
|
||||
void *ptr;
|
||||
} robj;
|
||||
|
||||
/* Macro used to initalize a Redis object allocated on the stack.
|
||||
/* Macro used to initialize a Redis object allocated on the stack.
|
||||
* Note that this macro is taken near the structure definition to make sure
|
||||
* we'll update it when the structure is changed, to avoid bugs like
|
||||
* bug #85 introduced exactly in this way. */
|
||||
@@ -350,9 +350,8 @@ typedef struct multiState {
|
||||
} multiState;
|
||||
|
||||
typedef struct blockingState {
|
||||
robj **keys; /* The key we are waiting to terminate a blocking
|
||||
dict *keys; /* The keys we are waiting to terminate a blocking
|
||||
* operation such as BLPOP. Otherwise NULL. */
|
||||
int count; /* Number of blocking keys */
|
||||
time_t timeout; /* Blocking operation timeout. If UNIX current time
|
||||
* is >= timeout then the operation timed out. */
|
||||
robj *target; /* The key that should receive the element,
|
||||
@@ -375,12 +374,13 @@ typedef struct readyList {
|
||||
robj *key;
|
||||
} readyList;
|
||||
|
||||
/* With multiplexing we need to take per-clinet state.
|
||||
/* With multiplexing we need to take per-client state.
|
||||
* Clients are taken in a liked list. */
|
||||
typedef struct redisClient {
|
||||
int fd;
|
||||
redisDb *db;
|
||||
int dictid;
|
||||
robj *name; /* As set by CLIENT SETNAME */
|
||||
sds querybuf;
|
||||
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size */
|
||||
int argc;
|
||||
@@ -536,7 +536,7 @@ struct redisServer {
|
||||
long long stat_keyspace_hits; /* Number of successful lookups of keys */
|
||||
long long stat_keyspace_misses; /* Number of failed lookups of keys */
|
||||
size_t stat_peak_memory; /* Max used memory record */
|
||||
long long stat_fork_time; /* Time needed to perform latets fork() */
|
||||
long long stat_fork_time; /* Time needed to perform latest fork() */
|
||||
long long stat_rejected_conn; /* Clients rejected because of maxclients */
|
||||
list *slowlog; /* SLOWLOG list of commands */
|
||||
long long slowlog_entry_id; /* SLOWLOG current entry ID */
|
||||
@@ -551,6 +551,7 @@ struct redisServer {
|
||||
/* Configuration */
|
||||
int verbosity; /* Loglevel in redis.conf */
|
||||
int maxidletime; /* Client timeout in seconds */
|
||||
int tcpkeepalive; /* Set SO_KEEPALIVE if non-zero. */
|
||||
size_t client_max_querybuf_len; /* Limit for client query buffer length */
|
||||
int dbnum; /* Total number of configured DBs */
|
||||
int daemonize; /* True if running as a daemon */
|
||||
@@ -585,7 +586,7 @@ struct redisServer {
|
||||
char *rdb_filename; /* Name of RDB file */
|
||||
int rdb_compression; /* Use compression in RDB? */
|
||||
int rdb_checksum; /* Use RDB checksum? */
|
||||
time_t lastsave; /* Unix time of last save succeeede */
|
||||
time_t lastsave; /* Unix time of last successful save */
|
||||
time_t rdb_save_time_last; /* Time used by last RDB save run. */
|
||||
time_t rdb_save_time_start; /* Current RDB save start time. */
|
||||
int lastbgsave_status; /* REDIS_OK or REDIS_ERR */
|
||||
@@ -616,11 +617,12 @@ struct redisServer {
|
||||
int repl_serve_stale_data; /* Serve stale data when link is down? */
|
||||
int repl_slave_ro; /* Slave is read only? */
|
||||
time_t repl_down_since; /* Unix time at which link with master went down */
|
||||
int repl_disable_tcp_nodelay; /* Disable TCP_NODELAY after SYNC? */
|
||||
int slave_priority; /* Reported in INFO and used by Sentinel. */
|
||||
/* Limits */
|
||||
unsigned int maxclients; /* Max number of simultaneous clients */
|
||||
unsigned long long maxmemory; /* Max number of memory bytes to use */
|
||||
int maxmemory_policy; /* Policy for key evition */
|
||||
int maxmemory_policy; /* Policy for key eviction */
|
||||
int maxmemory_samples; /* Pricision of random sampling */
|
||||
/* Blocked clients */
|
||||
unsigned int bpop_blocked_clients; /* Number of clients blocked by lists */
|
||||
@@ -657,7 +659,7 @@ struct redisServer {
|
||||
int lua_timedout; /* True if we reached the time limit for script
|
||||
execution. */
|
||||
int lua_kill; /* Kill the script if true. */
|
||||
/* Assert & bug reportign */
|
||||
/* Assert & bug reporting */
|
||||
char *assert_failed;
|
||||
char *assert_file;
|
||||
int assert_line;
|
||||
@@ -676,13 +678,13 @@ struct redisCommand {
|
||||
char *name;
|
||||
redisCommandProc *proc;
|
||||
int arity;
|
||||
char *sflags; /* Flags as string represenation, one char per flag. */
|
||||
char *sflags; /* Flags as string representation, one char per flag. */
|
||||
int flags; /* The actual flags, obtained from the 'sflags' field. */
|
||||
/* Use a function to determine keys arguments in a command line. */
|
||||
redisGetKeysProc *getkeys_proc;
|
||||
/* What keys should be loaded in background when calling this command? */
|
||||
int firstkey; /* The first argument that's a key (0 = no keys) */
|
||||
int lastkey; /* THe last argument that's a key */
|
||||
int lastkey; /* The last argument that's a key */
|
||||
int keystep; /* The step between first and last key */
|
||||
long long microseconds, calls;
|
||||
};
|
||||
@@ -729,7 +731,7 @@ typedef struct {
|
||||
dictIterator *di;
|
||||
} setTypeIterator;
|
||||
|
||||
/* Structure to hold hash iteration abstration. Note that iteration over
|
||||
/* Structure to hold hash iteration abstraction. Note that iteration over
|
||||
* hashes involves both fields and values. Because it is possible that
|
||||
* not both are required, store pointers in the iterator to avoid
|
||||
* unnecessary memory allocation for fields/values. */
|
||||
|
||||
+2
-2
@@ -27,8 +27,8 @@
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/* Every time the Redis Git SHA1 or Dirty status changes only this file
|
||||
* small file is recompiled, as we access this information in all the other
|
||||
/* Every time the Redis Git SHA1 or Dirty status changes only this small
|
||||
* file is recompiled, as we access this information in all the other
|
||||
* files using this functions. */
|
||||
|
||||
#include "release.h"
|
||||
|
||||
+35
-13
@@ -52,7 +52,7 @@ void replicationFeedSlaves(list *slaves, int dictid, robj **argv, int argc) {
|
||||
if (slave->replstate == REDIS_REPL_WAIT_BGSAVE_START) continue;
|
||||
|
||||
/* Feed slaves that are waiting for the initial SYNC (so these commands
|
||||
* are queued in the output buffer until the intial SYNC completes),
|
||||
* are queued in the output buffer until the initial SYNC completes),
|
||||
* or are already in sync with the master. */
|
||||
if (slave->slaveseldb != dictid) {
|
||||
robj *selectcmd;
|
||||
@@ -115,7 +115,7 @@ void replicationFeedMonitors(redisClient *c, list *monitors, int dictid, robj **
|
||||
}
|
||||
|
||||
void syncCommand(redisClient *c) {
|
||||
/* ignore SYNC if aleady slave or in monitor mode */
|
||||
/* ignore SYNC if already slave or in monitor mode */
|
||||
if (c->flags & REDIS_SLAVE) return;
|
||||
|
||||
/* Refuse SYNC requests if we are a slave but the link with our master
|
||||
@@ -172,6 +172,9 @@ void syncCommand(redisClient *c) {
|
||||
}
|
||||
c->replstate = REDIS_REPL_WAIT_BGSAVE_END;
|
||||
}
|
||||
|
||||
if (server.repl_disable_tcp_nodelay)
|
||||
anetDisableTcpNoDelay(NULL, c->fd); /* Non critical if it fails. */
|
||||
c->repldbfd = -1;
|
||||
c->flags |= REDIS_SLAVE;
|
||||
c->slaveseldb = 0;
|
||||
@@ -229,7 +232,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
if (slave->repldboff == 0) {
|
||||
/* Write the bulk write count before to transfer the DB. In theory here
|
||||
* we don't know how much room there is in the output buffer of the
|
||||
* socket, but in pratice SO_SNDLOWAT (the minimum count for output
|
||||
* socket, but in practice SO_SNDLOWAT (the minimum count for output
|
||||
* operations) will never be smaller than the few bytes we need. */
|
||||
sds bulkcount;
|
||||
|
||||
@@ -272,7 +275,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
}
|
||||
}
|
||||
|
||||
/* This function is called at the end of every backgrond saving.
|
||||
/* This function is called at the end of every background saving.
|
||||
* The argument bgsaveerr is REDIS_OK if the background saving succeeded
|
||||
* otherwise REDIS_ERR is passed to the function.
|
||||
*
|
||||
@@ -424,6 +427,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
return;
|
||||
}
|
||||
redisLog(REDIS_NOTICE, "MASTER <-> SLAVE sync: Loading DB in memory");
|
||||
signalFlushedDb(-1);
|
||||
emptyDb();
|
||||
/* Before loading the DB into memory we need to delete the readable
|
||||
* handler, otherwise it will get called recursively since
|
||||
@@ -451,7 +455,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
|
||||
stopAppendOnly();
|
||||
while (retry-- && startAppendOnly() == REDIS_ERR) {
|
||||
redisLog(REDIS_WARNING,"Failed enabling the AOF after successful master synchrnization! Trying it again in one second.");
|
||||
redisLog(REDIS_WARNING,"Failed enabling the AOF after successful master synchronization! Trying it again in one second.");
|
||||
sleep(1);
|
||||
}
|
||||
if (!retry) {
|
||||
@@ -636,7 +640,9 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
|
||||
if (aeCreateFileEvent(server.el,fd, AE_READABLE,readSyncBulkPayload,NULL)
|
||||
== AE_ERR)
|
||||
{
|
||||
redisLog(REDIS_WARNING,"Can't create readable event for SYNC");
|
||||
redisLog(REDIS_WARNING,
|
||||
"Can't create readable event for SYNC: %s (fd=%d)",
|
||||
strerror(errno),fd);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -693,6 +699,27 @@ void undoConnectWithMaster(void) {
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
}
|
||||
|
||||
/* This function aborts a non blocking replication attempt if there is one
|
||||
* in progress, by canceling the non-blocking connect attempt or
|
||||
* the initial bulk transfer.
|
||||
*
|
||||
* If there was a replication handshake in progress 1 is returned and
|
||||
* the replication state (server.repl_state) set to REDIS_REPL_CONNECT.
|
||||
*
|
||||
* Otherwise zero is returned and no operation is perforemd at all. */
|
||||
int cancelReplicationHandshake(void) {
|
||||
if (server.repl_state == REDIS_REPL_TRANSFER) {
|
||||
replicationAbortSyncTransfer();
|
||||
} else if (server.repl_state == REDIS_REPL_CONNECTING ||
|
||||
server.repl_state == REDIS_REPL_RECEIVE_PONG)
|
||||
{
|
||||
undoConnectWithMaster();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void slaveofCommand(redisClient *c) {
|
||||
if (!strcasecmp(c->argv[1]->ptr,"no") &&
|
||||
!strcasecmp(c->argv[2]->ptr,"one")) {
|
||||
@@ -700,11 +727,7 @@ void slaveofCommand(redisClient *c) {
|
||||
sdsfree(server.masterhost);
|
||||
server.masterhost = NULL;
|
||||
if (server.master) freeClient(server.master);
|
||||
if (server.repl_state == REDIS_REPL_TRANSFER)
|
||||
replicationAbortSyncTransfer();
|
||||
else if (server.repl_state == REDIS_REPL_CONNECTING ||
|
||||
server.repl_state == REDIS_REPL_RECEIVE_PONG)
|
||||
undoConnectWithMaster();
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_NONE;
|
||||
redisLog(REDIS_NOTICE,"MASTER MODE enabled (user request)");
|
||||
}
|
||||
@@ -728,8 +751,7 @@ void slaveofCommand(redisClient *c) {
|
||||
server.masterport = port;
|
||||
if (server.master) freeClient(server.master);
|
||||
disconnectSlaves(); /* Force our slaves to resync with us as well. */
|
||||
if (server.repl_state == REDIS_REPL_TRANSFER)
|
||||
replicationAbortSyncTransfer();
|
||||
cancelReplicationHandshake();
|
||||
server.repl_state = REDIS_REPL_CONNECT;
|
||||
redisLog(REDIS_NOTICE,"SLAVE OF %s:%d enabled (user request)",
|
||||
server.masterhost, server.masterport);
|
||||
|
||||
+27
-26
@@ -48,7 +48,7 @@ void sha1hex(char *digest, char *script, size_t len);
|
||||
|
||||
/* Take a Redis reply in the Redis protocol format and convert it into a
|
||||
* Lua type. Thanks to this function, and the introduction of not connected
|
||||
* clients, it is trvial to implement the redis() lua function.
|
||||
* clients, it is trivial to implement the redis() lua function.
|
||||
*
|
||||
* Basically we take the arguments, execute the Redis command in the context
|
||||
* of a non connected client, then take the generated reply and convert it
|
||||
@@ -58,7 +58,7 @@ void sha1hex(char *digest, char *script, size_t len);
|
||||
*
|
||||
* Note: in this function we do not do any sanity check as the reply is
|
||||
* generated by Redis directly. This allows us to go faster.
|
||||
* The reply string can be altered during the parsing as it is discared
|
||||
* The reply string can be altered during the parsing as it is discarded
|
||||
* after the conversion is completed.
|
||||
*
|
||||
* Errors are returned as a table with a single 'err' field set to the
|
||||
@@ -597,7 +597,7 @@ void scriptingInit(void) {
|
||||
|
||||
lua_setglobal(lua,"math");
|
||||
|
||||
/* Add a helper funciton that we use to sort the multi bulk output of non
|
||||
/* Add a helper function that we use to sort the multi bulk output of non
|
||||
* deterministic commands, when containing 'false' elements. */
|
||||
{
|
||||
char *compare_func = "function __redis__compare_helper(a,b)\n"
|
||||
@@ -638,7 +638,7 @@ void scriptingReset(void) {
|
||||
scriptingInit();
|
||||
}
|
||||
|
||||
/* Perform the SHA1 of the input string. We use this both for hasing script
|
||||
/* Perform the SHA1 of the input string. We use this both for hashing script
|
||||
* bodies in order to obtain the Lua function name, and in the implementation
|
||||
* of redis.sha1().
|
||||
*
|
||||
@@ -677,7 +677,7 @@ void luaReplyToRedisReply(redisClient *c, lua_State *lua) {
|
||||
case LUA_TTABLE:
|
||||
/* We need to check if it is an array, an error, or a status reply.
|
||||
* Error are returned as a single element table with 'err' field.
|
||||
* Status replies are returned as single elment table with 'ok' field */
|
||||
* Status replies are returned as single element table with 'ok' field */
|
||||
lua_pushstring(lua,"err");
|
||||
lua_gettable(lua,-2);
|
||||
t = lua_type(lua,-1);
|
||||
@@ -787,7 +787,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
lua_State *lua = server.lua;
|
||||
char funcname[43];
|
||||
long long numkeys;
|
||||
int delhook = 0;
|
||||
int delhook = 0, err;
|
||||
|
||||
/* We want the same PRNG sequence at every call so that our PRNG is
|
||||
* not affected by external state. */
|
||||
@@ -834,7 +834,7 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
if (lua_isnil(lua,1)) {
|
||||
lua_pop(lua,1); /* remove the nil from the stack */
|
||||
/* Function not defined... let's define it if we have the
|
||||
* body of the funciton. If this is an EVALSHA call we can just
|
||||
* body of the function. If this is an EVALSHA call we can just
|
||||
* return an error. */
|
||||
if (evalsha) {
|
||||
addReply(c, shared.noscripterr);
|
||||
@@ -869,30 +869,31 @@ void evalGenericCommand(redisClient *c, int evalsha) {
|
||||
/* At this point whatever this script was never seen before or if it was
|
||||
* already defined, we can call it. We have zero arguments and expect
|
||||
* a single return value. */
|
||||
if (lua_pcall(lua,0,1,0)) {
|
||||
if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
|
||||
if (server.lua_timedout) {
|
||||
server.lua_timedout = 0;
|
||||
/* Restore the readable handler that was unregistered when the
|
||||
* script timeout was detected. */
|
||||
aeCreateFileEvent(server.el,c->fd,AE_READABLE,
|
||||
readQueryFromClient,c);
|
||||
}
|
||||
server.lua_caller = NULL;
|
||||
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
||||
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
|
||||
funcname, lua_tostring(lua,-1));
|
||||
lua_pop(lua,1);
|
||||
lua_gc(lua,LUA_GCCOLLECT,0);
|
||||
return;
|
||||
}
|
||||
err = lua_pcall(lua,0,1,0);
|
||||
|
||||
/* Perform some cleanup that we need to do both on error and success. */
|
||||
if (delhook) lua_sethook(lua,luaMaskCountHook,0,0); /* Disable hook */
|
||||
server.lua_timedout = 0;
|
||||
if (server.lua_timedout) {
|
||||
server.lua_timedout = 0;
|
||||
/* Restore the readable handler that was unregistered when the
|
||||
* script timeout was detected. */
|
||||
aeCreateFileEvent(server.el,c->fd,AE_READABLE,
|
||||
readQueryFromClient,c);
|
||||
}
|
||||
server.lua_caller = NULL;
|
||||
selectDb(c,server.lua_client->db->id); /* set DB ID from Lua client */
|
||||
luaReplyToRedisReply(c,lua);
|
||||
lua_gc(lua,LUA_GCSTEP,1);
|
||||
|
||||
if (err) {
|
||||
addReplyErrorFormat(c,"Error running script (call to %s): %s\n",
|
||||
funcname, lua_tostring(lua,-1));
|
||||
lua_pop(lua,1); /* Consume the Lua reply. */
|
||||
} else {
|
||||
/* On success convert the Lua return value into Redis protocol, and
|
||||
* send it to * the client. */
|
||||
luaReplyToRedisReply(c,lua);
|
||||
}
|
||||
|
||||
/* If we have slaves attached we want to replicate this command as
|
||||
* EVAL instead of EVALSHA. We do this also in the AOF as currently there
|
||||
* is no easy way to propagate a command in a different way in the AOF
|
||||
|
||||
@@ -141,7 +141,7 @@ size_t sdsAllocSize(sds s) {
|
||||
* right-trim the string.
|
||||
*
|
||||
* Using sdsIncrLen() and sdsMakeRoomFor() it is possible to mount the
|
||||
* following schema to cat bytes coming from the kerenl to the end of an
|
||||
* following schema to cat bytes coming from the kernel to the end of an
|
||||
* sds string new things without copying into an intermediate buffer:
|
||||
*
|
||||
* oldlen = sdslen(s);
|
||||
@@ -596,7 +596,7 @@ void sdssplitargs_free(sds *argv, int argc) {
|
||||
}
|
||||
|
||||
/* Modify the string substituting all the occurrences of the set of
|
||||
* characters specifed in the 'from' string to the corresponding character
|
||||
* characters specified in the 'from' string to the corresponding character
|
||||
* in the 'to' array.
|
||||
*
|
||||
* For instance: sdsmapchars(mystring, "ho", "01", 2)
|
||||
|
||||
+19
-11
@@ -969,9 +969,9 @@ const char *sentinelRedisInstanceTypeStr(sentinelRedisInstance *ri) {
|
||||
* a master's Sentinels dictionary, we want to be very sure about not
|
||||
* having duplicated instances for any reason. This is so important because
|
||||
* we use those other sentinels in order to run our quorum protocol to
|
||||
* understand if it's time to proceeed with the fail over.
|
||||
* understand if it's time to proceed with the fail over.
|
||||
*
|
||||
* Making sure no duplication is possible we greately improve the robustness
|
||||
* Making sure no duplication is possible we greatly improve the robustness
|
||||
* of the quorum (otherwise we may end counting the same instance multiple
|
||||
* times for some reason).
|
||||
*
|
||||
@@ -1238,7 +1238,7 @@ void sentinelKillLink(sentinelRedisInstance *ri, redisAsyncContext *c) {
|
||||
* cleanup needed.
|
||||
*
|
||||
* Note: we don't free the hiredis context as hiredis will do it for us
|
||||
* for async conenctions. */
|
||||
* for async connections. */
|
||||
void sentinelDisconnectInstanceFromContext(const redisAsyncContext *c) {
|
||||
sentinelRedisInstance *ri = c->data;
|
||||
int pubsub;
|
||||
@@ -1647,7 +1647,7 @@ void sentinelReceiveHelloMessages(redisAsyncContext *c, void *reply, void *privd
|
||||
|
||||
/* Update the last activity in the pubsub channel. Note that since we
|
||||
* receive our messages as well this timestamp can be used to detect
|
||||
* if the link is probably diconnected even if it seems otherwise. */
|
||||
* if the link is probably disconnected even if it seems otherwise. */
|
||||
ri->pc_last_activity = mstime();
|
||||
|
||||
/* Sanity check in the reply we expect, so that the code that follows
|
||||
@@ -1939,7 +1939,7 @@ void addReplySentinelRedisInstance(redisClient *c, sentinelRedisInstance *ri) {
|
||||
setDeferredMultiBulkLength(c,mbl,fields*2);
|
||||
}
|
||||
|
||||
/* Output a number of instances contanined inside a dictionary as
|
||||
/* Output a number of instances contained inside a dictionary as
|
||||
* Redis protocol. */
|
||||
void addReplyDictOfRedisInstances(redisClient *c, dict *instances) {
|
||||
dictIterator *di;
|
||||
@@ -2035,8 +2035,16 @@ void sentinelCommand(redisClient *c) {
|
||||
} else {
|
||||
sentinelAddr *addr = ri->addr;
|
||||
|
||||
if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) && ri->promoted_slave)
|
||||
/* If we are in the middle of a failover, and the slave was
|
||||
* already successfully switched to master role, we can advertise
|
||||
* the new address as slave in order to allow clients to talk
|
||||
* with the new master ASAP. */
|
||||
if ((ri->flags & SRI_FAILOVER_IN_PROGRESS) &&
|
||||
ri->promoted_slave &&
|
||||
ri->failover_state >= SENTINEL_FAILOVER_STATE_RECONF_SLAVES)
|
||||
{
|
||||
addr = ri->promoted_slave->addr;
|
||||
}
|
||||
addReplyMultiBulkLen(c,2);
|
||||
addReplyBulkCString(c,addr->ip);
|
||||
addReplyBulkLongLong(c,addr->port);
|
||||
@@ -2535,7 +2543,7 @@ void sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) {
|
||||
* 3) info_refresh more recent than SENTINEL_INFO_VALIDITY_TIME.
|
||||
* 4) master_link_down_time no more than:
|
||||
* (now - master->s_down_since_time) + (master->down_after_period * 10).
|
||||
* 5) Slave priority can't be zero, otherwise the slave is discareded.
|
||||
* 5) Slave priority can't be zero, otherwise the slave is discarded.
|
||||
*
|
||||
* Among all the slaves matching the above conditions we select the slave
|
||||
* with lower slave_priority. If priority is the same we select the slave
|
||||
@@ -2611,10 +2619,10 @@ void sentinelFailoverWaitStart(sentinelRedisInstance *ri) {
|
||||
/* If we in "wait start" but the master is no longer in ODOWN nor in
|
||||
* SDOWN condition we abort the failover. This is important as it
|
||||
* prevents a useless failover in a a notable case of netsplit, where
|
||||
* the senitnels are split from the redis instances. In this case
|
||||
* the sentinels are split from the redis instances. In this case
|
||||
* the failover will not start while there is the split because no
|
||||
* good slave can be reached. However when the split is resolved, we
|
||||
* can go to waitstart if the slave is back rechable a few milliseconds
|
||||
* can go to waitstart if the slave is back reachable a few milliseconds
|
||||
* before the master is. In that case when the master is back online
|
||||
* we cancel the failover. */
|
||||
if ((ri->flags & (SRI_S_DOWN|SRI_O_DOWN|SRI_FORCE_FAILOVER)) == 0) {
|
||||
@@ -3026,13 +3034,13 @@ void sentinelHandleDictOfRedisInstances(dict *instances) {
|
||||
* following conditions happen:
|
||||
*
|
||||
* 1) The Sentiel process for some time is blocked, for every kind of
|
||||
* random reason: the load is huge, the computer was freezed for some time
|
||||
* random reason: the load is huge, the computer was frozen for some time
|
||||
* in I/O or alike, the process was stopped by a signal. Everything.
|
||||
* 2) The system clock was altered significantly.
|
||||
*
|
||||
* Under both this conditions we'll see everything as timed out and failing
|
||||
* without good reasons. Instead we enter the TILT mode and wait
|
||||
* for SENTIENL_TILT_PERIOD to elapse before starting to act again.
|
||||
* for SENTINEL_TILT_PERIOD to elapse before starting to act again.
|
||||
*
|
||||
* During TILT time we still collect information, we just do not act. */
|
||||
void sentinelCheckTiltCondition(void) {
|
||||
|
||||
+18
-18
@@ -57,13 +57,13 @@ A million repetitions of "a"
|
||||
|
||||
void SHA1Transform(u_int32_t state[5], const unsigned char buffer[64])
|
||||
{
|
||||
u_int32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
u_int32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
u_int32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
unsigned char c[64];
|
||||
u_int32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
#ifdef SHA1HANDSOFF
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
/* The following had better never be used because it causes the
|
||||
@@ -71,7 +71,7 @@ CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||
* And the result is written through. I threw a "const" in, hoping
|
||||
* this will cause a diagnostic.
|
||||
*/
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||
#endif
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
@@ -132,12 +132,11 @@ void SHA1Init(SHA1_CTX* context)
|
||||
|
||||
void SHA1Update(SHA1_CTX* context, const unsigned char* data, u_int32_t len)
|
||||
{
|
||||
u_int32_t i;
|
||||
u_int32_t j;
|
||||
u_int32_t i, j;
|
||||
|
||||
j = context->count[0];
|
||||
if ((context->count[0] += len << 3) < j)
|
||||
context->count[1]++;
|
||||
context->count[1]++;
|
||||
context->count[1] += (len>>29);
|
||||
j = (j >> 3) & 63;
|
||||
if ((j + len) > 63) {
|
||||
@@ -157,9 +156,9 @@ u_int32_t j;
|
||||
|
||||
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||
{
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
unsigned i;
|
||||
unsigned char finalcount[8];
|
||||
unsigned char c;
|
||||
|
||||
#if 0 /* untested "improvement" by DHR */
|
||||
/* Convert context->count to a sequence of bytes
|
||||
@@ -170,12 +169,12 @@ unsigned char c;
|
||||
unsigned char *fcp = &finalcount[8];
|
||||
|
||||
for (i = 0; i < 2; i++)
|
||||
{
|
||||
u_int32_t t = context->count[i];
|
||||
int j;
|
||||
{
|
||||
u_int32_t t = context->count[i];
|
||||
int j;
|
||||
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t
|
||||
for (j = 0; j < 4; t >>= 8, j++)
|
||||
*--fcp = (unsigned char) t;
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 8; i++) {
|
||||
@@ -226,3 +225,4 @@ main(int argc, char **argv)
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+3
-3
@@ -45,7 +45,7 @@ redisSortOperation *createSortOperation(int type, robj *pattern) {
|
||||
/* Return the value associated to the key with a name obtained using
|
||||
* the following rules:
|
||||
*
|
||||
* 1) The first occurence of '*' in 'pattern' is substituted with 'subst'.
|
||||
* 1) The first occurrence of '*' in 'pattern' is substituted with 'subst'.
|
||||
*
|
||||
* 2) If 'pattern' matches the "->" string, everything on the left of
|
||||
* the arrow is treated as the name of an hash field, and the part on the
|
||||
@@ -147,7 +147,7 @@ int sortCompare(const void *s1, const void *s2) {
|
||||
cmp = -1;
|
||||
} else {
|
||||
/* Objects have the same score, but we don't want the comparison
|
||||
* to be undefined, so we compare objects lexicographycally.
|
||||
* to be undefined, so we compare objects lexicographically.
|
||||
* This way the result of SORT is deterministic. */
|
||||
cmp = compareStringObjects(so1->obj,so2->obj);
|
||||
}
|
||||
@@ -205,7 +205,7 @@ void sortCommand(redisClient *c) {
|
||||
|
||||
/* Now we need to protect sortval incrementing its count, in the future
|
||||
* SORT may have options able to overwrite/delete keys during the sorting
|
||||
* and the sorted key itself may get destroied */
|
||||
* and the sorted key itself may get destroyed */
|
||||
if (sortval)
|
||||
incrRefCount(sortval);
|
||||
else
|
||||
|
||||
+24
-24
@@ -45,10 +45,10 @@ void listTypeTryConversion(robj *subject, robj *value) {
|
||||
listTypeConvert(subject,REDIS_ENCODING_LINKEDLIST);
|
||||
}
|
||||
|
||||
/* The function pushes an elmenet to the specified list object 'subject',
|
||||
/* The function pushes an element to the specified list object 'subject',
|
||||
* at head or tail position as specified by 'where'.
|
||||
*
|
||||
* There is no need for the caller to incremnet the refcount of 'value' as
|
||||
* There is no need for the caller to increment the refcount of 'value' as
|
||||
* the function takes care of it if needed. */
|
||||
void listTypePush(robj *subject, robj *value, int where) {
|
||||
/* Check if we need to convert the ziplist */
|
||||
@@ -757,18 +757,14 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
|
||||
list *l;
|
||||
int j;
|
||||
|
||||
c->bpop.keys = zmalloc(sizeof(robj*)*numkeys);
|
||||
c->bpop.count = numkeys;
|
||||
c->bpop.timeout = timeout;
|
||||
c->bpop.target = target;
|
||||
|
||||
if (target != NULL) {
|
||||
incrRefCount(target);
|
||||
}
|
||||
if (target != NULL) incrRefCount(target);
|
||||
|
||||
for (j = 0; j < numkeys; j++) {
|
||||
/* Add the key in the client structure, to map clients -> keys */
|
||||
c->bpop.keys[j] = keys[j];
|
||||
/* If the key already exists in the dict ignore it. */
|
||||
if (dictAdd(c->bpop.keys,keys[j],NULL) != DICT_OK) continue;
|
||||
incrRefCount(keys[j]);
|
||||
|
||||
/* And in the other "side", to map keys -> clients */
|
||||
@@ -786,6 +782,7 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
|
||||
}
|
||||
listAddNodeTail(l,c);
|
||||
}
|
||||
|
||||
/* Mark the client as a blocked client */
|
||||
c->flags |= REDIS_BLOCKED;
|
||||
server.bpop_blocked_clients++;
|
||||
@@ -794,28 +791,31 @@ void blockForKeys(redisClient *c, robj **keys, int numkeys, time_t timeout, robj
|
||||
/* Unblock a client that's waiting in a blocking operation such as BLPOP */
|
||||
void unblockClientWaitingData(redisClient *c) {
|
||||
dictEntry *de;
|
||||
dictIterator *di;
|
||||
list *l;
|
||||
int j;
|
||||
|
||||
redisAssertWithInfo(c,NULL,c->bpop.keys != NULL);
|
||||
redisAssertWithInfo(c,NULL,dictSize(c->bpop.keys) != 0);
|
||||
di = dictGetIterator(c->bpop.keys);
|
||||
/* The client may wait for multiple keys, so unblock it for every key. */
|
||||
for (j = 0; j < c->bpop.count; j++) {
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *key = dictGetKey(de);
|
||||
|
||||
/* Remove this client from the list of clients waiting for this key. */
|
||||
de = dictFind(c->db->blocking_keys,c->bpop.keys[j]);
|
||||
redisAssertWithInfo(c,c->bpop.keys[j],de != NULL);
|
||||
l = dictGetVal(de);
|
||||
l = dictFetchValue(c->db->blocking_keys,key);
|
||||
redisAssertWithInfo(c,key,l != NULL);
|
||||
listDelNode(l,listSearchKey(l,c));
|
||||
/* If the list is empty we need to remove it to avoid wasting memory */
|
||||
if (listLength(l) == 0)
|
||||
dictDelete(c->db->blocking_keys,c->bpop.keys[j]);
|
||||
decrRefCount(c->bpop.keys[j]);
|
||||
dictDelete(c->db->blocking_keys,key);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
|
||||
/* Cleanup the client structure */
|
||||
zfree(c->bpop.keys);
|
||||
c->bpop.keys = NULL;
|
||||
if (c->bpop.target) decrRefCount(c->bpop.target);
|
||||
c->bpop.target = NULL;
|
||||
dictEmpty(c->bpop.keys);
|
||||
if (c->bpop.target) {
|
||||
decrRefCount(c->bpop.target);
|
||||
c->bpop.target = NULL;
|
||||
}
|
||||
c->flags &= ~REDIS_BLOCKED;
|
||||
c->flags |= REDIS_UNBLOCKED;
|
||||
server.bpop_blocked_clients--;
|
||||
@@ -825,7 +825,7 @@ void unblockClientWaitingData(redisClient *c) {
|
||||
/* If the specified key has clients blocked waiting for list pushes, this
|
||||
* function will put the key reference into the server.ready_keys list.
|
||||
* Note that db->ready_keys is an hash table that allows us to avoid putting
|
||||
* the same key agains and again in the list in case of multiple pushes
|
||||
* the same key again and again in the list in case of multiple pushes
|
||||
* made by a script or in the context of MULTI/EXEC.
|
||||
*
|
||||
* The list will be finally processed by handleClientsBlockedOnLists() */
|
||||
@@ -858,7 +858,7 @@ void signalListAsReady(redisClient *c, robj *key) {
|
||||
*
|
||||
* 1) Provide the client with the 'value' element.
|
||||
* 2) If the dstkey is not NULL (we are serving a BRPOPLPUSH) also push the
|
||||
* 'value' element on the destionation list (the LPUSH side of the command).
|
||||
* 'value' element on the destination list (the LPUSH side of the command).
|
||||
* 3) Propagate the resulting BRPOP, BLPOP and additional LPUSH if any into
|
||||
* the AOF and replication channel.
|
||||
*
|
||||
@@ -868,7 +868,7 @@ void signalListAsReady(redisClient *c, robj *key) {
|
||||
*
|
||||
* The function returns REDIS_OK if we are able to serve the client, otherwise
|
||||
* REDIS_ERR is returned to signal the caller that the list POP operation
|
||||
* should be undoed as the client was not served: This only happens for
|
||||
* should be undone as the client was not served: This only happens for
|
||||
* BRPOPLPUSH that fails to push the value to the destination key as it is
|
||||
* of the wrong type. */
|
||||
int serveClientBlockedOnList(redisClient *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where)
|
||||
|
||||
+4
-4
@@ -188,7 +188,7 @@ robj *setTypeNextObject(setTypeIterator *si) {
|
||||
* The caller provides both pointers to be populated with the right
|
||||
* object. The return value of the function is the object->encoding
|
||||
* field of the object and is used by the caller to check if the
|
||||
* int64_t pointer or the redis object pointere was populated.
|
||||
* int64_t pointer or the redis object pointer was populated.
|
||||
*
|
||||
* When an object is returned (the set was a real set) the ref count
|
||||
* of the object is not incremented so this function can be considered
|
||||
@@ -450,7 +450,7 @@ void srandmemberWithCountCommand(redisClient *c) {
|
||||
* The number of requested elements is greater than the number of
|
||||
* elements inside the set: simply return the whole set. */
|
||||
if (count >= size) {
|
||||
sunionDiffGenericCommand(c,c->argv,c->argc-1,NULL,REDIS_OP_UNION);
|
||||
sunionDiffGenericCommand(c,c->argv+1,1,NULL,REDIS_OP_UNION);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ void srandmemberWithCountCommand(redisClient *c) {
|
||||
/* Add all the elements into the temporary dictionary. */
|
||||
si = setTypeInitIterator(set);
|
||||
while((encoding = setTypeNext(si,&ele,&llele)) != -1) {
|
||||
int retval;
|
||||
int retval = DICT_ERR;
|
||||
|
||||
if (encoding == REDIS_ENCODING_INTSET) {
|
||||
retval = dictAdd(d,createStringObjectFromLongLong(llele),NULL);
|
||||
@@ -598,7 +598,7 @@ void sinterGenericCommand(redisClient *c, robj **setkeys, unsigned long setnum,
|
||||
sets[j] = setobj;
|
||||
}
|
||||
/* Sort sets from the smallest to largest, this will improve our
|
||||
* algorithm's performace */
|
||||
* algorithm's performance */
|
||||
qsort(sets,setnum,sizeof(robj*),qsortCompareSetsByCardinality);
|
||||
|
||||
/* The first thing we should output is the total number of elements...
|
||||
|
||||
+2
-2
@@ -43,7 +43,7 @@ static int checkStringLength(redisClient *c, long long size) {
|
||||
}
|
||||
|
||||
void setGenericCommand(redisClient *c, int nx, robj *key, robj *val, robj *expire, int unit) {
|
||||
long long milliseconds = 0; /* initialized to avoid an harmness warning */
|
||||
long long milliseconds = 0; /* initialized to avoid any harmness warning */
|
||||
|
||||
if (expire) {
|
||||
if (getLongLongFromObjectOrReply(c, expire, &milliseconds, NULL) != REDIS_OK)
|
||||
@@ -340,7 +340,7 @@ void incrbyfloatCommand(redisClient *c) {
|
||||
addReplyBulk(c,new);
|
||||
|
||||
/* Always replicate INCRBYFLOAT as a SET command with the final value
|
||||
* in order to make sure that differences in float pricision or formatting
|
||||
* in order to make sure that differences in float precision or formatting
|
||||
* will not create differences in replicas or after an AOF restart. */
|
||||
aux = createStringObject("SET",3);
|
||||
rewriteClientCommandArgument(c,0,aux);
|
||||
|
||||
+2
-2
@@ -125,7 +125,7 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, robj *obj) {
|
||||
}
|
||||
/* we assume the key is not already inside, since we allow duplicated
|
||||
* scores, and the re-insertion of score and redis object should never
|
||||
* happpen since the caller of zslInsert() should test in the hash table
|
||||
* happen since the caller of zslInsert() should test in the hash table
|
||||
* if the element is already inside or not. */
|
||||
level = zslRandomLevel();
|
||||
if (level > zsl->level) {
|
||||
@@ -285,7 +285,7 @@ zskiplistNode *zslLastInRange(zskiplist *zsl, zrangespec range) {
|
||||
}
|
||||
|
||||
/* Delete all the elements with score between min and max from the skiplist.
|
||||
* Min and mx are inclusive, so a score >= min || score <= max is deleted.
|
||||
* Min and max are inclusive, so a score >= min || score <= max is deleted.
|
||||
* Note that this function takes the reference to the hash table view of the
|
||||
* sorted set, in order to remove the elements from the hash table too. */
|
||||
unsigned long zslDeleteRangeByScore(zskiplist *zsl, zrangespec range, dict *dict) {
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.6.6"
|
||||
#define REDIS_VERSION "2.6.10"
|
||||
|
||||
+2
-2
@@ -739,10 +739,10 @@ unsigned char *ziplistPrev(unsigned char *zl, unsigned char *p) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Get entry pointer to by 'p' and store in either 'e' or 'v' depending
|
||||
/* Get entry pointed to by 'p' and store in either 'e' or 'v' depending
|
||||
* on the encoding of the entry. 'e' is always set to NULL to be able
|
||||
* to find out whether the string pointer or the integer value was set.
|
||||
* Return 0 if 'p' points to the end of the zipmap, 1 otherwise. */
|
||||
* Return 0 if 'p' points to the end of the ziplist, 1 otherwise. */
|
||||
unsigned int ziplistGet(unsigned char *p, unsigned char **sstr, unsigned int *slen, long long *sval) {
|
||||
zlentry entry;
|
||||
if (p == NULL || p[0] == ZIP_END) return 0;
|
||||
|
||||
+1
-1
@@ -250,7 +250,7 @@ void zmalloc_set_oom_handler(void (*oom_handler)(size_t)) {
|
||||
*
|
||||
* For this kind of "fast RSS reporting" usages use instead the
|
||||
* function RedisEstimateRSS() that is a much faster (and less precise)
|
||||
* version of the funciton. */
|
||||
* version of the function. */
|
||||
|
||||
#if defined(HAVE_PROCFS)
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -35,7 +35,7 @@ port 6379
|
||||
# unixsocket /tmp/redis.sock
|
||||
|
||||
# Close the connection after a client is idle for N seconds (0 to disable)
|
||||
timeout 300
|
||||
timeout 0
|
||||
|
||||
# Set server verbosity to 'debug'
|
||||
# it can be one of:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
proc start_bg_complex_data {host port db ops} {
|
||||
exec tclsh8.5 tests/helpers/bg_complex_data.tcl $host $port $db $ops &
|
||||
set tclsh [info nameofexecutable]
|
||||
exec $tclsh tests/helpers/bg_complex_data.tcl $host $port $db $ops &
|
||||
}
|
||||
|
||||
proc stop_bg_complex_data {handle} {
|
||||
|
||||
@@ -78,7 +78,8 @@ start_server {tags {"repl"}} {
|
||||
}
|
||||
|
||||
proc start_write_load {host port seconds} {
|
||||
exec tclsh8.5 tests/helpers/gen_write_load.tcl $host $port $seconds &
|
||||
set tclsh [info nameofexecutable]
|
||||
exec $tclsh tests/helpers/gen_write_load.tcl $host $port $seconds &
|
||||
}
|
||||
|
||||
proc stop_write_load {handle} {
|
||||
|
||||
@@ -38,7 +38,9 @@ proc kill_server config {
|
||||
if {[string match {*Darwin*} [exec uname -a]]} {
|
||||
tags {"leaks"} {
|
||||
test "Check for memory leaks (pid $pid)" {
|
||||
exec leaks $pid
|
||||
set output {0 leaks}
|
||||
catch {exec leaks $pid} output
|
||||
set output
|
||||
} {*0 leaks*}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
# This softare is released under the BSD License. See the COPYING file for
|
||||
# more information.
|
||||
|
||||
package require Tcl 8.5
|
||||
|
||||
set tcl_precision 17
|
||||
source tests/support/redis.tcl
|
||||
source tests/support/server.tcl
|
||||
@@ -176,6 +178,7 @@ proc find_available_port start {
|
||||
|
||||
proc test_server_main {} {
|
||||
cleanup
|
||||
set tclsh [info nameofexecutable]
|
||||
# Open a listening socket, trying different ports in order to find a
|
||||
# non busy one.
|
||||
set port [find_available_port 11111]
|
||||
@@ -189,7 +192,7 @@ proc test_server_main {} {
|
||||
set start_port [expr {$::port+100}]
|
||||
for {set j 0} {$j < $::numclients} {incr j} {
|
||||
set start_port [find_available_port $start_port]
|
||||
set p [exec tclsh8.5 [info script] {*}$::argv \
|
||||
set p [exec $tclsh [info script] {*}$::argv \
|
||||
--client $port --port $start_port &]
|
||||
lappend ::clients_pids $p
|
||||
incr start_port 10
|
||||
|
||||
@@ -19,4 +19,37 @@ start_server {tags {"introspection"}} {
|
||||
assert_match {*eval*} [$rd read]
|
||||
assert_match {*lua*"set"*"foo"*"bar"*} [$rd read]
|
||||
}
|
||||
|
||||
test {CLIENT GETNAME should return NIL if name is not assigned} {
|
||||
r client getname
|
||||
} {}
|
||||
|
||||
test {CLIENT LIST shows empty fields for unassigned names} {
|
||||
r client list
|
||||
} {*name= *}
|
||||
|
||||
test {CLIENT SETNAME does not accept spaces} {
|
||||
catch {r client setname "foo bar"} e
|
||||
set e
|
||||
} {ERR*}
|
||||
|
||||
test {CLIENT SETNAME can assign a name to this connection} {
|
||||
assert_equal [r client setname myname] {OK}
|
||||
r client list
|
||||
} {*name=myname*}
|
||||
|
||||
test {CLIENT SETNAME can change the name of an existing connection} {
|
||||
assert_equal [r client setname someothername] {OK}
|
||||
r client list
|
||||
} {*name=someothername*}
|
||||
|
||||
test {After CLIENT SETNAME, connection can still be closed} {
|
||||
set rd [redis_deferring_client]
|
||||
$rd client setname foobar
|
||||
assert_equal [$rd read] "OK"
|
||||
assert_match {*foobar*} [r client list]
|
||||
$rd close
|
||||
# Now the client should no longer be listed
|
||||
string match {*foobar*} [r client list]
|
||||
} {0}
|
||||
}
|
||||
|
||||
@@ -63,13 +63,13 @@ start_server {tags {"multi"}} {
|
||||
r multi
|
||||
r set foo1 bar1
|
||||
$rd config set maxmemory 1
|
||||
assert {[$rd read] eq {OK}}
|
||||
catch {r lpush mylist myvalue}
|
||||
$rd config set maxmemory 0
|
||||
assert {[$rd read] eq {OK}}
|
||||
r set foo2 bar2
|
||||
catch {r exec} e
|
||||
assert_match {EXECABORT*} $e
|
||||
assert {[$rd read] eq {OK}}
|
||||
assert {[$rd read] eq {OK}}
|
||||
$rd close
|
||||
list [r exists foo1] [r exists foo2]
|
||||
} {0 0}
|
||||
|
||||
+11
-1
@@ -192,4 +192,14 @@ start_server {tags {"pubsub"}} {
|
||||
# clean up clients
|
||||
$rd1 close
|
||||
}
|
||||
}
|
||||
|
||||
test "PUNSUBSCRIBE and UNSUBSCRIBE should always reply." {
|
||||
# Make sure we are not subscribed to any channel at all.
|
||||
r punsubscribe
|
||||
r unsubscribe
|
||||
# Now check if the commands still reply correctly.
|
||||
set reply1 [r punsubscribe]
|
||||
set reply2 [r unsubscribe]
|
||||
concat $reply1 $reply2
|
||||
} {punsubscribe {} 0 unsubscribe {} 0}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ start_server {tags {"scripting"}} {
|
||||
r eval {return {1,2,3,'ciao',{1,2}}} 0
|
||||
} {1 2 3 ciao {1 2}}
|
||||
|
||||
test {EVAL - Are the KEYS and ARGS arrays populated correctly?} {
|
||||
test {EVAL - Are the KEYS and ARGV arrays populated correctly?} {
|
||||
r eval {return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}} 2 a b c d
|
||||
} {a b c d}
|
||||
|
||||
@@ -297,6 +297,12 @@ start_server {tags {"scripting"}} {
|
||||
assert_equal [r ping] "PONG"
|
||||
}
|
||||
|
||||
test {Timedout script link is still usable after Lua returns} {
|
||||
r config set lua-time-limit 10
|
||||
r eval {for i=1,100000 do redis.call('ping') end return 'ok'} 0
|
||||
r ping
|
||||
} {PONG}
|
||||
|
||||
test {Timedout scripts that modified data can't be killed by SCRIPT KILL} {
|
||||
set rd [redis_deferring_client]
|
||||
r config set lua-time-limit 10
|
||||
@@ -310,6 +316,8 @@ start_server {tags {"scripting"}} {
|
||||
assert_match {BUSY*} $e
|
||||
}
|
||||
|
||||
# Note: keep this test at the end of this server stanza because it
|
||||
# kills the server.
|
||||
test {SHUTDOWN NOSAVE can kill a timedout script anyway} {
|
||||
# The server sould be still unresponding to normal commands.
|
||||
catch {r ping} e
|
||||
@@ -323,9 +331,16 @@ start_server {tags {"scripting"}} {
|
||||
|
||||
start_server {tags {"scripting repl"}} {
|
||||
start_server {} {
|
||||
test {Before the slave connects we issue an EVAL command} {
|
||||
test {Before the slave connects we issue two EVAL commands} {
|
||||
# One with an error, but still executing a command.
|
||||
# SHA is: 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56
|
||||
catch {
|
||||
r eval {redis.call('incr','x'); redis.call('nonexisting')} 0
|
||||
}
|
||||
# One command is correct:
|
||||
# SHA is: ae3477e27be955de7e1bc9adfdca626b478d3cb2
|
||||
r eval {return redis.call('incr','x')} 0
|
||||
} {1}
|
||||
} {2}
|
||||
|
||||
test {Connect a slave to the main instance} {
|
||||
r -1 slaveof [srv 0 host] [srv 0 port]
|
||||
@@ -337,15 +352,20 @@ start_server {tags {"scripting repl"}} {
|
||||
}
|
||||
}
|
||||
|
||||
test {Now use EVALSHA against the master} {
|
||||
test {Now use EVALSHA against the master, with both SHAs} {
|
||||
# The server should replicate successful and unsuccessful
|
||||
# commands as EVAL instead of EVALSHA.
|
||||
catch {
|
||||
r evalsha 6e8bd6bdccbe78899e3cc06b31b6dbf4324c2e56 0
|
||||
}
|
||||
r evalsha ae3477e27be955de7e1bc9adfdca626b478d3cb2 0
|
||||
} {2}
|
||||
} {4}
|
||||
|
||||
test {If EVALSHA was replicated as EVAL the slave should be ok} {
|
||||
test {If EVALSHA was replicated as EVAL, 'x' should be '4'} {
|
||||
wait_for_condition 50 100 {
|
||||
[r -1 get x] eq {2}
|
||||
[r -1 get x] eq {4}
|
||||
} else {
|
||||
fail "Expected 2 in x, but value is '[r -1 get x]'"
|
||||
fail "Expected 4 in x, but value is '[r -1 get x]'"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,4 +55,16 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
|
||||
set e [lindex [r slowlog get] 0]
|
||||
lindex $e 3
|
||||
} {sadd set foo {AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA... (1 more bytes)}}
|
||||
|
||||
test {SLOWLOG - EXEC is not logged, just executed commands} {
|
||||
r config set slowlog-log-slower-than 100000
|
||||
r slowlog reset
|
||||
assert_equal [r slowlog len] 0
|
||||
r multi
|
||||
r debug sleep 0.2
|
||||
r exec
|
||||
assert_equal [r slowlog len] 1
|
||||
set e [lindex [r slowlog get] 0]
|
||||
assert_equal [lindex $e 3] {debug sleep 0.2}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,6 +190,27 @@ start_server {
|
||||
$rd read
|
||||
} {list b}
|
||||
|
||||
test "BLPOP with same key multiple times should work (issue #801)" {
|
||||
set rd [redis_deferring_client]
|
||||
r del list1 list2
|
||||
|
||||
# Data arriving after the BLPOP.
|
||||
$rd blpop list1 list2 list2 list1 0
|
||||
r lpush list1 a
|
||||
assert_equal [$rd read] {list1 a}
|
||||
$rd blpop list1 list2 list2 list1 0
|
||||
r lpush list2 b
|
||||
assert_equal [$rd read] {list2 b}
|
||||
|
||||
# Data already there.
|
||||
r lpush list1 a
|
||||
r lpush list2 b
|
||||
$rd blpop list1 list2 list2 list1 0
|
||||
assert_equal [$rd read] {list1 a}
|
||||
$rd blpop list1 list2 list2 list1 0
|
||||
assert_equal [$rd read] {list2 b}
|
||||
}
|
||||
|
||||
test "MULTI/EXEC is isolated from the point of view of BLPOP" {
|
||||
set rd [redis_deferring_client]
|
||||
r del list
|
||||
|
||||
@@ -151,6 +151,8 @@ REDIS_CHKCONFIG_INFO=\
|
||||
# Provides: redis_6379\n
|
||||
# Required-Start: $network $local_fs $remote_fs\n
|
||||
# Required-Stop: $network $local_fs $remote_fs\n
|
||||
# Default-Start: 2 3 4 5\n
|
||||
# Default-Stop: 0 1 6\n
|
||||
# Should-Start: $syslog $named\n
|
||||
# Should-Stop: $syslog $named\n
|
||||
# Short-Description: start and stop redis_6379\n
|
||||
|
||||
Reference in New Issue
Block a user