Commit 819d291b authored by Oran Agra's avatar Oran Agra
Browse files

Merge remote-tracking branch 'origin/unstable' into 7.2

parents a51eb05b 936cfa46
...@@ -180,10 +180,17 @@ int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen) ...@@ -180,10 +180,17 @@ int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen)
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as /* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX * EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
* logic consistent. */ * logic consistent.
if (errno == EWOULDBLOCK) { * Additionally, WSAALREADY is can be reported as WSAEINVAL to and this is
* translated to EIO. Convert appropriately
*/
int err = errno;
if (err == EWOULDBLOCK) {
errno = EINPROGRESS; errno = EINPROGRESS;
} }
else if (err == EIO) {
errno = EALREADY;
}
return ret != SOCKET_ERROR ? ret : -1; return ret != SOCKET_ERROR ? ret : -1;
} }
...@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle ...@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
} else { } else {
ret = getsockopt(sockfd, level, optname, (char*)optval, optlen); ret = getsockopt(sockfd, level, optname, (char*)optval, optlen);
} }
if (ret != SOCKET_ERROR && level == SOL_SOCKET && optname == SO_ERROR) {
/* translate SO_ERROR codes, if non-zero */
int err = *(int*)optval;
if (err != 0) {
err = _wsaErrorToErrno(err);
*(int*)optval = err;
}
}
_updateErrno(ret != SOCKET_ERROR); _updateErrno(ret != SOCKET_ERROR);
return ret != SOCKET_ERROR ? ret : -1; return ret != SOCKET_ERROR ? ret : -1;
} }
...@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) { ...@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
_updateErrno(ret != SOCKET_ERROR); _updateErrno(ret != SOCKET_ERROR);
return ret != SOCKET_ERROR ? ret : -1; return ret != SOCKET_ERROR ? ret : -1;
} }
int win32_redisKeepAlive(SOCKET sockfd, int interval_ms) {
struct tcp_keepalive cfg;
DWORD bytes_in;
int res;
cfg.onoff = 1;
cfg.keepaliveinterval = interval_ms;
cfg.keepalivetime = interval_ms;
res = WSAIoctl(sockfd, SIO_KEEPALIVE_VALS, &cfg,
sizeof(struct tcp_keepalive), NULL, 0,
&bytes_in, NULL, NULL);
return res == 0 ? 0 : _wsaErrorToErrno(res);
}
#endif /* _WIN32 */ #endif /* _WIN32 */
...@@ -50,6 +50,7 @@ ...@@ -50,6 +50,7 @@
#include <ws2tcpip.h> #include <ws2tcpip.h>
#include <stddef.h> #include <stddef.h>
#include <errno.h> #include <errno.h>
#include <mstcpip.h>
#ifdef _MSC_VER #ifdef _MSC_VER
typedef long long ssize_t; typedef long long ssize_t;
...@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags); ...@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
typedef ULONG nfds_t; typedef ULONG nfds_t;
int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout); int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout);
int win32_redisKeepAlive(SOCKET sockfd, int interval_ms);
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION #ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res) #define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
#undef gai_strerror #undef gai_strerror
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "hiredis.h" #include "hiredis.h"
#include "async.h" #include "async.h"
#include "net.h"
#include <assert.h> #include <assert.h>
#include <errno.h> #include <errno.h>
...@@ -39,6 +40,14 @@ ...@@ -39,6 +40,14 @@
#ifdef _WIN32 #ifdef _WIN32
#include <windows.h> #include <windows.h>
#include <wincrypt.h> #include <wincrypt.h>
#ifdef OPENSSL_IS_BORINGSSL
#undef X509_NAME
#undef X509_EXTENSIONS
#undef PKCS7_ISSUER_AND_SERIAL
#undef PKCS7_SIGNER_INFO
#undef OCSP_REQUEST
#undef OCSP_RESPONSE
#endif
#else #else
#include <pthread.h> #include <pthread.h>
#endif #endif
...@@ -219,6 +228,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * ...@@ -219,6 +228,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
const char *cert_filename, const char *private_key_filename, const char *cert_filename, const char *private_key_filename,
const char *server_name, redisSSLContextError *error) const char *server_name, redisSSLContextError *error)
{ {
redisSSLOptions options = {
.cacert_filename = cacert_filename,
.capath = capath,
.cert_filename = cert_filename,
.private_key_filename = private_key_filename,
.server_name = server_name,
.verify_mode = REDIS_SSL_VERIFY_PEER,
};
return redisCreateSSLContextWithOptions(&options, error);
}
redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) {
const char *cacert_filename = options->cacert_filename;
const char *capath = options->capath;
const char *cert_filename = options->cert_filename;
const char *private_key_filename = options->private_key_filename;
const char *server_name = options->server_name;
#ifdef _WIN32 #ifdef _WIN32
HCERTSTORE win_store = NULL; HCERTSTORE win_store = NULL;
PCCERT_CONTEXT win_ctx = NULL; PCCERT_CONTEXT win_ctx = NULL;
...@@ -235,7 +263,7 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * ...@@ -235,7 +263,7 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
} }
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);
if ((cert_filename != NULL && private_key_filename == NULL) || if ((cert_filename != NULL && private_key_filename == NULL) ||
(private_key_filename != NULL && cert_filename == NULL)) { (private_key_filename != NULL && cert_filename == NULL)) {
...@@ -273,6 +301,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * ...@@ -273,6 +301,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED; if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
goto error; goto error;
} }
} else {
if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
if (error) *error = REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED;
goto error;
}
} }
if (cert_filename) { if (cert_filename) {
...@@ -560,6 +593,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) { ...@@ -560,6 +593,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
} }
redisContextFuncs redisContextSSLFuncs = { redisContextFuncs redisContextSSLFuncs = {
.close = redisNetClose,
.free_privctx = redisSSLFree, .free_privctx = redisSSLFree,
.async_read = redisSSLAsyncRead, .async_read = redisSSLAsyncRead,
.async_write = redisSSLAsyncWrite, .async_write = redisSSLAsyncWrite,
......
This diff is collapsed.
...@@ -4,9 +4,17 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server} ...@@ -4,9 +4,17 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
REDIS_PORT=${REDIS_PORT:-56379} REDIS_PORT=${REDIS_PORT:-56379}
REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443} REDIS_SSL_PORT=${REDIS_SSL_PORT:-56443}
TEST_SSL=${TEST_SSL:-0} TEST_SSL=${TEST_SSL:-0}
SKIPS_AS_FAILS=${SKIPS_AS_FAILS-:0} SKIPS_AS_FAILS=${SKIPS_AS_FAILS:-0}
ENABLE_DEBUG_CMD=
SSL_TEST_ARGS= SSL_TEST_ARGS=
SKIPS_ARG= SKIPS_ARG=${SKIPS_ARG:-}
REDIS_DOCKER=${REDIS_DOCKER:-}
# We need to enable the DEBUG command for redis-server >= 7.0.0
REDIS_MAJOR_VERSION="$(redis-server --version|awk -F'[^0-9]+' '{ print $2 }')"
if [ "$REDIS_MAJOR_VERSION" -gt "6" ]; then
ENABLE_DEBUG_CMD="enable-debug-command local"
fi
tmpdir=$(mktemp -d) tmpdir=$(mktemp -d)
PID_FILE=${tmpdir}/hiredis-test-redis.pid PID_FILE=${tmpdir}/hiredis-test-redis.pid
...@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then ...@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then
fi fi
cleanup() { cleanup() {
set +e if [ -n "${REDIS_DOCKER}" ] ; then
kill $(cat ${PID_FILE}) docker kill redis-test-server
else
set +e
kill $(cat ${PID_FILE})
fi
rm -rf ${tmpdir} rm -rf ${tmpdir}
} }
trap cleanup INT TERM EXIT trap cleanup INT TERM EXIT
# base config
cat > ${tmpdir}/redis.conf <<EOF cat > ${tmpdir}/redis.conf <<EOF
daemonize yes
pidfile ${PID_FILE} pidfile ${PID_FILE}
port ${REDIS_PORT} port ${REDIS_PORT}
bind 127.0.0.1
unixsocket ${SOCK_FILE} unixsocket ${SOCK_FILE}
unixsocketperm 777
EOF EOF
# if not running in docker add these:
if [ ! -n "${REDIS_DOCKER}" ]; then
cat >> ${tmpdir}/redis.conf <<EOF
daemonize yes
${ENABLE_DEBUG_CMD}
bind 127.0.0.1
EOF
fi
# if doing ssl, add these
if [ "$TEST_SSL" = "1" ]; then if [ "$TEST_SSL" = "1" ]; then
cat >> ${tmpdir}/redis.conf <<EOF cat >> ${tmpdir}/redis.conf <<EOF
tls-port ${REDIS_SSL_PORT} tls-port ${REDIS_SSL_PORT}
...@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY} ...@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY}
EOF EOF
fi fi
echo ${tmpdir}
cat ${tmpdir}/redis.conf cat ${tmpdir}/redis.conf
${REDIS_SERVER} ${tmpdir}/redis.conf if [ -n "${REDIS_DOCKER}" ] ; then
chmod a+wx ${tmpdir}
chmod a+r ${tmpdir}/*
docker run -d --rm --name redis-test-server \
-p ${REDIS_PORT}:${REDIS_PORT} \
-p ${REDIS_SSL_PORT}:${REDIS_SSL_PORT} \
-v ${tmpdir}:${tmpdir} \
${REDIS_DOCKER} \
redis-server ${tmpdir}/redis.conf
else
${REDIS_SERVER} ${tmpdir}/redis.conf
fi
# Wait until we detect the unix socket # Wait until we detect the unix socket
echo waiting for server
while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done while [ ! -S "${SOCK_FILE}" ]; do sleep 1; done
# Treat skips as failures if directed # Treat skips as failures if directed
[ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="--skips-as-fails" [ "$SKIPS_AS_FAILS" = 1 ] && SKIPS_ARG="${SKIPS_ARG} --skips-as-fails"
${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG} ${TEST_PREFIX:-} ./hiredis-test -h 127.0.0.1 -p ${REDIS_PORT} -s ${SOCK_FILE} ${SSL_TEST_ARGS} ${SKIPS_ARG}
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
#include <assert.h> #include <assert.h>
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <stdint.h>
#include <limits.h> #include <limits.h>
#include "lua.h" #include "lua.h"
#include "lauxlib.h" #include "lauxlib.h"
...@@ -141,13 +142,13 @@ typedef struct { ...@@ -141,13 +142,13 @@ typedef struct {
typedef struct { typedef struct {
json_token_type_t type; json_token_type_t type;
int index; size_t index;
union { union {
const char *string; const char *string;
double number; double number;
int boolean; int boolean;
} value; } value;
int string_len; size_t string_len;
} json_token_t; } json_token_t;
static const char *char2escape[256] = { static const char *char2escape[256] = {
...@@ -473,6 +474,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex) ...@@ -473,6 +474,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
* This buffer is reused constantly for small strings * This buffer is reused constantly for small strings
* If there are any excess pages, they won't be hit anyway. * If there are any excess pages, they won't be hit anyway.
* This gains ~5% speedup. */ * This gains ~5% speedup. */
if (len > SIZE_MAX / 6 - 3)
abort(); /* Overflow check */
strbuf_ensure_empty_length(json, len * 6 + 2); strbuf_ensure_empty_length(json, len * 6 + 2);
strbuf_append_char_unsafe(json, '\"'); strbuf_append_char_unsafe(json, '\"');
...@@ -706,7 +709,7 @@ static int json_encode(lua_State *l) ...@@ -706,7 +709,7 @@ static int json_encode(lua_State *l)
strbuf_t local_encode_buf; strbuf_t local_encode_buf;
strbuf_t *encode_buf; strbuf_t *encode_buf;
char *json; char *json;
int len; size_t len;
luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument");
......
...@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) { ...@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) {
void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) { void mp_buf_append(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
if (buf->free < len) { if (buf->free < len) {
size_t newsize = (buf->len+len)*2; size_t newsize = buf->len+len;
if (newsize < buf->len || newsize >= SIZE_MAX/2) abort();
newsize *= 2;
buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize); buf->b = (unsigned char*)mp_realloc(L, buf->b, buf->len + buf->free, newsize);
buf->free = newsize - buf->len; buf->free = newsize - buf->len;
...@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) { ...@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
void mp_encode_bytes(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) { void mp_encode_bytes(lua_State *L, mp_buf *buf, const unsigned char *s, size_t len) {
unsigned char hdr[5]; unsigned char hdr[5];
int hdrlen; size_t hdrlen;
if (len < 32) { if (len < 32) {
hdr[0] = 0xa0 | (len&0xff); /* fix raw */ hdr[0] = 0xa0 | (len&0xff); /* fix raw */
...@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) { ...@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) {
void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) { void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
unsigned char b[9]; unsigned char b[9];
int enclen; size_t enclen;
if (n >= 0) { if (n >= 0) {
if (n <= 127) { if (n <= 127) {
...@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) { ...@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
mp_buf_append(L,buf,b,enclen); mp_buf_append(L,buf,b,enclen);
} }
void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) { void mp_encode_array(lua_State *L, mp_buf *buf, uint64_t n) {
unsigned char b[5]; unsigned char b[5];
int enclen; size_t enclen;
if (n <= 15) { if (n <= 15) {
b[0] = 0x90 | (n & 0xf); /* fix array */ b[0] = 0x90 | (n & 0xf); /* fix array */
...@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) { ...@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
mp_buf_append(L,buf,b,enclen); mp_buf_append(L,buf,b,enclen);
} }
void mp_encode_map(lua_State *L, mp_buf *buf, int64_t n) { void mp_encode_map(lua_State *L, mp_buf *buf, uint64_t n) {
unsigned char b[5]; unsigned char b[5];
int enclen; int enclen;
...@@ -791,7 +793,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) { ...@@ -791,7 +793,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
} }
} }
int mp_unpack_full(lua_State *L, int limit, int offset) { int mp_unpack_full(lua_State *L, lua_Integer limit, lua_Integer offset) {
size_t len; size_t len;
const char *s; const char *s;
mp_cur c; mp_cur c;
...@@ -803,10 +805,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) { ...@@ -803,10 +805,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */ if (offset < 0 || limit < 0) /* requesting negative off or lim is invalid */
return luaL_error(L, return luaL_error(L,
"Invalid request to unpack with offset of %d and limit of %d.", "Invalid request to unpack with offset of %d and limit of %d.",
offset, len); (int) offset, (int) len);
else if (offset > len) else if (offset > len)
return luaL_error(L, return luaL_error(L,
"Start offset %d greater than input length %d.", offset, len); "Start offset %d greater than input length %d.", (int) offset, (int) len);
if (decode_all) limit = INT_MAX; if (decode_all) limit = INT_MAX;
...@@ -828,12 +830,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) { ...@@ -828,12 +830,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
/* c->left is the remaining size of the input buffer. /* c->left is the remaining size of the input buffer.
* subtract the entire buffer size from the unprocessed size * subtract the entire buffer size from the unprocessed size
* to get our next start offset */ * to get our next start offset */
int offset = len - c.left; size_t new_offset = len - c.left;
if (new_offset > LONG_MAX) abort();
luaL_checkstack(L, 1, "in function mp_unpack_full"); luaL_checkstack(L, 1, "in function mp_unpack_full");
/* Return offset -1 when we have have processed the entire buffer. */ /* Return offset -1 when we have have processed the entire buffer. */
lua_pushinteger(L, c.left == 0 ? -1 : offset); lua_pushinteger(L, c.left == 0 ? -1 : (lua_Integer) new_offset);
/* Results are returned with the arg elements still /* Results are returned with the arg elements still
* in place. Lua takes care of only returning * in place. Lua takes care of only returning
* elements above the args for us. * elements above the args for us.
...@@ -852,15 +855,15 @@ int mp_unpack(lua_State *L) { ...@@ -852,15 +855,15 @@ int mp_unpack(lua_State *L) {
} }
int mp_unpack_one(lua_State *L) { int mp_unpack_one(lua_State *L) {
int offset = luaL_optinteger(L, 2, 0); lua_Integer offset = luaL_optinteger(L, 2, 0);
/* Variable pop because offset may not exist */ /* Variable pop because offset may not exist */
lua_pop(L, lua_gettop(L)-1); lua_pop(L, lua_gettop(L)-1);
return mp_unpack_full(L, 1, offset); return mp_unpack_full(L, 1, offset);
} }
int mp_unpack_limit(lua_State *L) { int mp_unpack_limit(lua_State *L) {
int limit = luaL_checkinteger(L, 2); lua_Integer limit = luaL_checkinteger(L, 2);
int offset = luaL_optinteger(L, 3, 0); lua_Integer offset = luaL_optinteger(L, 3, 0);
/* Variable pop because offset may not exist */ /* Variable pop because offset may not exist */
lua_pop(L, lua_gettop(L)-1); lua_pop(L, lua_gettop(L)-1);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <stdint.h>
#include "strbuf.h" #include "strbuf.h"
...@@ -38,22 +39,22 @@ static void die(const char *fmt, ...) ...@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
va_end(arg); va_end(arg);
fprintf(stderr, "\n"); fprintf(stderr, "\n");
exit(-1); abort();
} }
void strbuf_init(strbuf_t *s, int len) void strbuf_init(strbuf_t *s, size_t len)
{ {
int size; size_t size;
if (len <= 0) if (!len)
size = STRBUF_DEFAULT_SIZE; size = STRBUF_DEFAULT_SIZE;
else else
size = len + 1; /* \0 terminator */ size = len + 1;
if (size < len)
die("Overflow, len: %zu", len);
s->buf = NULL; s->buf = NULL;
s->size = size; s->size = size;
s->length = 0; s->length = 0;
s->increment = STRBUF_DEFAULT_INCREMENT;
s->dynamic = 0; s->dynamic = 0;
s->reallocs = 0; s->reallocs = 0;
s->debug = 0; s->debug = 0;
...@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len) ...@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
strbuf_ensure_null(s); strbuf_ensure_null(s);
} }
strbuf_t *strbuf_new(int len) strbuf_t *strbuf_new(size_t len)
{ {
strbuf_t *s; strbuf_t *s;
...@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len) ...@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
return s; return s;
} }
void strbuf_set_increment(strbuf_t *s, int increment)
{
/* Increment > 0: Linear buffer growth rate
* Increment < -1: Exponential buffer growth rate */
if (increment == 0 || increment == -1)
die("BUG: Invalid string increment");
s->increment = increment;
}
static inline void debug_stats(strbuf_t *s) static inline void debug_stats(strbuf_t *s)
{ {
if (s->debug) { if (s->debug) {
fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %zd, size: %zd\n",
(long)s, s->reallocs, s->length, s->size); (long)s, s->reallocs, s->length, s->size);
} }
} }
...@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s) ...@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
free(s); free(s);
} }
char *strbuf_free_to_string(strbuf_t *s, int *len) char *strbuf_free_to_string(strbuf_t *s, size_t *len)
{ {
char *buf; char *buf;
...@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len) ...@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
return buf; return buf;
} }
static int calculate_new_size(strbuf_t *s, int len) static size_t calculate_new_size(strbuf_t *s, size_t len)
{ {
int reqsize, newsize; size_t reqsize, newsize;
if (len <= 0) if (len <= 0)
die("BUG: Invalid strbuf length requested"); die("BUG: Invalid strbuf length requested");
/* Ensure there is room for optional NULL termination */ /* Ensure there is room for optional NULL termination */
reqsize = len + 1; reqsize = len + 1;
if (reqsize < len)
die("Overflow, len: %zu", len);
/* If the user has requested to shrink the buffer, do it exactly */ /* If the user has requested to shrink the buffer, do it exactly */
if (s->size > reqsize) if (s->size > reqsize)
return reqsize; return reqsize;
newsize = s->size; newsize = s->size;
if (s->increment < 0) { if (reqsize >= SIZE_MAX / 2) {
newsize = reqsize;
} else {
/* Exponential sizing */ /* Exponential sizing */
while (newsize < reqsize) while (newsize < reqsize)
newsize *= -s->increment; newsize *= 2;
} else {
/* Linear sizing */
newsize = ((newsize + s->increment - 1) / s->increment) * s->increment;
} }
if (newsize < reqsize)
die("BUG: strbuf length would overflow, len: %zu", len);
return newsize; return newsize;
} }
/* Ensure strbuf can handle a string length bytes long (ignoring NULL /* Ensure strbuf can handle a string length bytes long (ignoring NULL
* optional termination). */ * optional termination). */
void strbuf_resize(strbuf_t *s, int len) void strbuf_resize(strbuf_t *s, size_t len)
{ {
int newsize; size_t newsize;
newsize = calculate_new_size(s, len); newsize = calculate_new_size(s, len);
if (s->debug > 1) { if (s->debug > 1) {
fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", fprintf(stderr, "strbuf(%lx) resize: %zd => %zd\n",
(long)s, s->size, newsize); (long)s, s->size, newsize);
} }
s->size = newsize; s->size = newsize;
s->buf = realloc(s->buf, s->size); s->buf = realloc(s->buf, s->size);
if (!s->buf) if (!s->buf)
die("Out of memory"); die("Out of memory, len: %zu", len);
s->reallocs++; s->reallocs++;
} }
void strbuf_append_string(strbuf_t *s, const char *str) void strbuf_append_string(strbuf_t *s, const char *str)
{ {
int space, i; int i;
size_t space;
space = strbuf_empty_length(s); space = strbuf_empty_length(s);
...@@ -197,55 +193,6 @@ void strbuf_append_string(strbuf_t *s, const char *str) ...@@ -197,55 +193,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
} }
} }
/* strbuf_append_fmt() should only be used when an upper bound
* is known for the output string. */
void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...)
{
va_list arg;
int fmt_len;
strbuf_ensure_empty_length(s, len);
va_start(arg, fmt);
fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg);
va_end(arg);
if (fmt_len < 0)
die("BUG: Unable to convert number"); /* This should never happen.. */
s->length += fmt_len;
}
/* strbuf_append_fmt_retry() can be used when the there is no known
* upper bound for the output string. */
void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...)
{
va_list arg;
int fmt_len, try;
int empty_len;
/* If the first attempt to append fails, resize the buffer appropriately
* and try again */
for (try = 0; ; try++) {
va_start(arg, fmt);
/* Append the new formatted string */
/* fmt_len is the length of the string required, excluding the
* trailing NULL */
empty_len = strbuf_empty_length(s);
/* Add 1 since there is also space to store the terminating NULL. */
fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg);
va_end(arg);
if (fmt_len <= empty_len)
break; /* SUCCESS */
if (try > 0)
die("BUG: length of formatted string changed");
strbuf_resize(s, s->length + fmt_len);
}
s->length += fmt_len;
}
/* vi:ai et sw=4 ts=4: /* vi:ai et sw=4 ts=4:
*/ */
...@@ -27,15 +27,13 @@ ...@@ -27,15 +27,13 @@
/* Size: Total bytes allocated to *buf /* Size: Total bytes allocated to *buf
* Length: String length, excluding optional NULL terminator. * Length: String length, excluding optional NULL terminator.
* Increment: Allocation increments when resizing the string buffer.
* Dynamic: True if created via strbuf_new() * Dynamic: True if created via strbuf_new()
*/ */
typedef struct { typedef struct {
char *buf; char *buf;
int size; size_t size;
int length; size_t length;
int increment;
int dynamic; int dynamic;
int reallocs; int reallocs;
int debug; int debug;
...@@ -44,32 +42,26 @@ typedef struct { ...@@ -44,32 +42,26 @@ typedef struct {
#ifndef STRBUF_DEFAULT_SIZE #ifndef STRBUF_DEFAULT_SIZE
#define STRBUF_DEFAULT_SIZE 1023 #define STRBUF_DEFAULT_SIZE 1023
#endif #endif
#ifndef STRBUF_DEFAULT_INCREMENT
#define STRBUF_DEFAULT_INCREMENT -2
#endif
/* Initialise */ /* Initialise */
extern strbuf_t *strbuf_new(int len); extern strbuf_t *strbuf_new(size_t len);
extern void strbuf_init(strbuf_t *s, int len); extern void strbuf_init(strbuf_t *s, size_t len);
extern void strbuf_set_increment(strbuf_t *s, int increment);
/* Release */ /* Release */
extern void strbuf_free(strbuf_t *s); extern void strbuf_free(strbuf_t *s);
extern char *strbuf_free_to_string(strbuf_t *s, int *len); extern char *strbuf_free_to_string(strbuf_t *s, size_t *len);
/* Management */ /* Management */
extern void strbuf_resize(strbuf_t *s, int len); extern void strbuf_resize(strbuf_t *s, size_t len);
static int strbuf_empty_length(strbuf_t *s); static size_t strbuf_empty_length(strbuf_t *s);
static int strbuf_length(strbuf_t *s); static size_t strbuf_length(strbuf_t *s);
static char *strbuf_string(strbuf_t *s, int *len); static char *strbuf_string(strbuf_t *s, size_t *len);
static void strbuf_ensure_empty_length(strbuf_t *s, int len); static void strbuf_ensure_empty_length(strbuf_t *s, size_t len);
static char *strbuf_empty_ptr(strbuf_t *s); static char *strbuf_empty_ptr(strbuf_t *s);
static void strbuf_extend_length(strbuf_t *s, int len); static void strbuf_extend_length(strbuf_t *s, size_t len);
/* Update */ /* Update */
extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); static void strbuf_append_mem(strbuf_t *s, const char *c, size_t len);
extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...);
static void strbuf_append_mem(strbuf_t *s, const char *c, int len);
extern void strbuf_append_string(strbuf_t *s, const char *str); extern void strbuf_append_string(strbuf_t *s, const char *str);
static void strbuf_append_char(strbuf_t *s, const char c); static void strbuf_append_char(strbuf_t *s, const char c);
static void strbuf_ensure_null(strbuf_t *s); static void strbuf_ensure_null(strbuf_t *s);
...@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s) ...@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
/* Return bytes remaining in the string buffer /* Return bytes remaining in the string buffer
* Ensure there is space for a NULL terminator. */ * Ensure there is space for a NULL terminator. */
static inline int strbuf_empty_length(strbuf_t *s) static inline size_t strbuf_empty_length(strbuf_t *s)
{ {
return s->size - s->length - 1; return s->size - s->length - 1;
} }
static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) static inline void strbuf_ensure_empty_length(strbuf_t *s, size_t len)
{ {
if (len > strbuf_empty_length(s)) if (len > strbuf_empty_length(s))
strbuf_resize(s, s->length + len); strbuf_resize(s, s->length + len);
...@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s) ...@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
return s->buf + s->length; return s->buf + s->length;
} }
static inline void strbuf_extend_length(strbuf_t *s, int len) static inline void strbuf_extend_length(strbuf_t *s, size_t len)
{ {
s->length += len; s->length += len;
} }
static inline int strbuf_length(strbuf_t *s) static inline size_t strbuf_length(strbuf_t *s)
{ {
return s->length; return s->length;
} }
...@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) ...@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
s->buf[s->length++] = c; s->buf[s->length++] = c;
} }
static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) static inline void strbuf_append_mem(strbuf_t *s, const char *c, size_t len)
{ {
strbuf_ensure_empty_length(s, len); strbuf_ensure_empty_length(s, len);
memcpy(s->buf + s->length, c, len); memcpy(s->buf + s->length, c, len);
s->length += len; s->length += len;
} }
static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, size_t len)
{ {
memcpy(s->buf + s->length, c, len); memcpy(s->buf + s->length, c, len);
s->length += len; s->length += len;
...@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s) ...@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
s->buf[s->length] = 0; s->buf[s->length] = 0;
} }
static inline char *strbuf_string(strbuf_t *s, int *len) static inline char *strbuf_string(strbuf_t *s, size_t *len)
{ {
if (len) if (len)
*len = s->length; *len = s->length;
......
...@@ -346,6 +346,7 @@ pidfile /var/run/redis_6379.pid ...@@ -346,6 +346,7 @@ pidfile /var/run/redis_6379.pid
# verbose (many rarely useful info, but not a mess like the debug level) # verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) # notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged) # warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel notice loglevel notice
# Specify the log file name. Also the empty string can be used to force # Specify the log file name. Also the empty string can be used to force
...@@ -1742,6 +1743,11 @@ aof-timestamp-enabled no ...@@ -1742,6 +1743,11 @@ aof-timestamp-enabled no
# #
# cluster-announce-hostname "" # cluster-announce-hostname ""
# Clusters can configure an optional nodename to be used in addition to the node ID for
# debugging and admin information. This name is broadcasted between nodes, so will be used
# in addition to the node ID when reporting cross node events such as node failures.
# cluster-announce-human-nodename ""
# Clusters can advertise how clients should connect to them using either their IP address, # Clusters can advertise how clients should connect to them using either their IP address,
# a user defined hostname, or by declaring they have no endpoint. Which endpoint is # a user defined hostname, or by declaring they have no endpoint. Which endpoint is
# shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type # shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type
......
...@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid ...@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid
# verbose (many rarely useful info, but not a mess like the debug level) # verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably) # notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged) # warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel notice loglevel notice
# Specify the log file name. Also the empty string can be used to force # Specify the log file name. Also the empty string can be used to force
......
...@@ -23,7 +23,7 @@ ifeq ($(OPTIMIZATION),-O3) ...@@ -23,7 +23,7 @@ ifeq ($(OPTIMIZATION),-O3)
else else
REDIS_CFLAGS+=-flto=auto REDIS_CFLAGS+=-flto=auto
endif endif
REDIS_LDFLAGS+=-flto REDIS_LDFLAGS+=-O3 -flto
endif endif
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv
NODEPS:=clean distclean NODEPS:=clean distclean
...@@ -47,10 +47,10 @@ OPT=$(OPTIMIZATION) ...@@ -47,10 +47,10 @@ OPT=$(OPTIMIZATION)
# NUMBER_SIGN_CHAR is a workaround to support both GNU Make 4.3 and older versions. # NUMBER_SIGN_CHAR is a workaround to support both GNU Make 4.3 and older versions.
NUMBER_SIGN_CHAR := \# NUMBER_SIGN_CHAR := \#
C11_ATOMIC := $(shell sh -c 'echo "$(NUMBER_SIGN_CHAR)include <stdatomic.h>" > foo.c; \ C11_ATOMIC := $(shell sh -c 'echo "$(NUMBER_SIGN_CHAR)include <stdatomic.h>" > foo.c; \
$(CC) -std=c11 -c foo.c -o foo.o > /dev/null 2>&1; \ $(CC) -std=gnu11 -c foo.c -o foo.o > /dev/null 2>&1; \
if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c') if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c')
ifeq ($(C11_ATOMIC),yes) ifeq ($(C11_ATOMIC),yes)
STD+=-std=c11 STD+=-std=gnu11
else else
STD+=-std=c99 STD+=-std=c99
endif endif
......
...@@ -1923,26 +1923,28 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) { ...@@ -1923,26 +1923,28 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
if (c->user == original && getClientType(c) == CLIENT_TYPE_PUBSUB) { if (c->user == original && getClientType(c) == CLIENT_TYPE_PUBSUB) {
/* Check for pattern violations. */ /* Check for pattern violations. */
listRewind(c->pubsub_patterns,&lpi); dictIterator *di = dictGetIterator(c->pubsub_patterns);
while (!kill && ((lpn = listNext(&lpi)) != NULL)) { dictEntry *de;
while (!kill && ((de = dictNext(di)) != NULL)) {
o = lpn->value; o = dictGetKey(de);
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1); int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 1);
kill = (res == ACL_DENIED_CHANNEL); kill = (res == ACL_DENIED_CHANNEL);
} }
dictReleaseIterator(di);
/* Check for channel violations. */ /* Check for channel violations. */
if (!kill) { if (!kill) {
/* Check for global channels violation. */ /* Check for global channels violation. */
dictIterator *di = dictGetIterator(c->pubsub_channels); di = dictGetIterator(c->pubsub_channels);
dictEntry *de;
while (!kill && ((de = dictNext(di)) != NULL)) { while (!kill && ((de = dictNext(di)) != NULL)) {
o = dictGetKey(de); o = dictGetKey(de);
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0); int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
kill = (res == ACL_DENIED_CHANNEL); kill = (res == ACL_DENIED_CHANNEL);
} }
dictReleaseIterator(di); dictReleaseIterator(di);
}
if (!kill) {
/* Check for shard channels violation. */ /* Check for shard channels violation. */
di = dictGetIterator(c->pubsubshard_channels); di = dictGetIterator(c->pubsubshard_channels);
while (!kill && ((de = dictNext(di)) != NULL)) { while (!kill && ((de = dictNext(di)) != NULL)) {
...@@ -1950,7 +1952,6 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) { ...@@ -1950,7 +1952,6 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0); int res = ACLCheckChannelAgainstList(upcoming, o->ptr, sdslen(o->ptr), 0);
kill = (res == ACL_DENIED_CHANNEL); kill = (res == ACL_DENIED_CHANNEL);
} }
dictReleaseIterator(di); dictReleaseIterator(di);
} }
...@@ -2112,10 +2113,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) { ...@@ -2112,10 +2113,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
return C_ERR; return C_ERR;
} }
/* Try to apply the user rules in a fake user to see if they
* are actually valid. */
user *fakeuser = ACLCreateUnlinkedUser();
/* Merged selectors before trying to process */ /* Merged selectors before trying to process */
int merged_argc; int merged_argc;
sds *acl_args = ACLMergeSelectorArguments(argv + 2, argc - 2, &merged_argc, argc_err); sds *acl_args = ACLMergeSelectorArguments(argv + 2, argc - 2, &merged_argc, argc_err);
...@@ -2124,6 +2121,10 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) { ...@@ -2124,6 +2121,10 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
return C_ERR; return C_ERR;
} }
/* Try to apply the user rules in a fake user to see if they
* are actually valid. */
user *fakeuser = ACLCreateUnlinkedUser();
for (int j = 0; j < merged_argc; j++) { for (int j = 0; j < merged_argc; j++) {
if (ACLSetUser(fakeuser,acl_args[j],sdslen(acl_args[j])) == C_ERR) { if (ACLSetUser(fakeuser,acl_args[j],sdslen(acl_args[j])) == C_ERR) {
if (errno != ENOENT) { if (errno != ENOENT) {
......
...@@ -142,7 +142,7 @@ void bioInit(void) { ...@@ -142,7 +142,7 @@ void bioInit(void) {
for (j = 0; j < BIO_WORKER_NUM; j++) { for (j = 0; j < BIO_WORKER_NUM; j++) {
void *arg = (void*)(unsigned long) j; void *arg = (void*)(unsigned long) j;
if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) { if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {
serverLog(LL_WARNING,"Fatal: Can't initialize Background Jobs."); serverLog(LL_WARNING, "Fatal: Can't initialize Background Jobs. Error message: %s", strerror(errno));
exit(1); exit(1);
} }
bio_threads[j] = thread; bio_threads[j] = thread;
......
...@@ -325,6 +325,9 @@ void handleClientsBlockedOnKeys(void) { ...@@ -325,6 +325,9 @@ void handleClientsBlockedOnKeys(void) {
* (i.e. not from call(), module context, etc.) */ * (i.e. not from call(), module context, etc.) */
serverAssert(server.also_propagate.numops == 0); serverAssert(server.also_propagate.numops == 0);
/* If a command being unblocked causes another command to get unblocked,
* like a BLMOVE would do, then the new unblocked command will get processed
* right away rather than wait for later. */
while(listLength(server.ready_keys) != 0) { while(listLength(server.ready_keys) != 0) {
list *l; list *l;
...@@ -564,7 +567,10 @@ static void handleClientsBlockedOnKey(readyList *rl) { ...@@ -564,7 +567,10 @@ static void handleClientsBlockedOnKey(readyList *rl) {
listIter li; listIter li;
listRewind(clients,&li); listRewind(clients,&li);
while((ln = listNext(&li))) { /* Avoid processing more than the initial count so that we're not stuck
* in an endless loop in case the reprocessing of the command blocks again. */
long count = listLength(clients);
while ((ln = listNext(&li)) && count--) {
client *receiver = listNodeValue(ln); client *receiver = listNodeValue(ln);
robj *o = lookupKeyReadWithFlags(rl->db, rl->key, LOOKUP_NOEFFECTS); robj *o = lookupKeyReadWithFlags(rl->db, rl->key, LOOKUP_NOEFFECTS);
/* 1. In case new key was added/touched we need to verify it satisfy the /* 1. In case new key was added/touched we need to verify it satisfy the
...@@ -728,3 +734,30 @@ void totalNumberOfBlockingKeys(unsigned long *blocking_keys, unsigned long *blok ...@@ -728,3 +734,30 @@ void totalNumberOfBlockingKeys(unsigned long *blocking_keys, unsigned long *blok
if (bloking_keys_on_nokey) if (bloking_keys_on_nokey)
*bloking_keys_on_nokey = bkeys_on_nokey; *bloking_keys_on_nokey = bkeys_on_nokey;
} }
void blockedBeforeSleep(void) {
/* Handle precise timeouts of blocked clients. */
handleBlockedClientsTimeout();
/* Unblock all the clients blocked for synchronous replication
* in WAIT or WAITAOF. */
if (listLength(server.clients_waiting_acks))
processClientsWaitingReplicas();
/* Try to process blocked clients every once in while.
*
* Example: A module calls RM_SignalKeyAsReady from within a timer callback
* (So we don't visit processCommand() at all).
*
* This may unblock clients, so must be done before processUnblockedClients */
handleClientsBlockedOnKeys();
/* Check if there are clients unblocked by modules that implement
* blocking commands. */
if (moduleCount())
moduleHandleBlockedClients();
/* Try to process pending commands for clients that were just unblocked. */
if (listLength(server.unblocked_clients))
processUnblockedClients();
}
...@@ -352,9 +352,19 @@ void parseRedisUri(const char *uri, const char* tool_name, cliConnInfo *connInfo ...@@ -352,9 +352,19 @@ void parseRedisUri(const char *uri, const char* tool_name, cliConnInfo *connInfo
path = strchr(curr, '/'); path = strchr(curr, '/');
if (*curr != '/') { if (*curr != '/') {
host = path ? path - 1 : end; host = path ? path - 1 : end;
if ((port = strchr(curr, ':'))) { if (*curr == '[') {
connInfo->hostport = atoi(port + 1); curr += 1;
host = port - 1; if ((port = strchr(curr, ']'))) {
if (*(port+1) == ':') {
connInfo->hostport = atoi(port + 2);
}
host = port - 1;
}
} else {
if ((port = strchr(curr, ':'))) {
connInfo->hostport = atoi(port + 1);
host = port - 1;
}
} }
sdsfree(connInfo->hostip); sdsfree(connInfo->hostip);
connInfo->hostip = sdsnewlen(curr, host - curr + 1); connInfo->hostip = sdsnewlen(curr, host - curr + 1);
......
This diff is collapsed.
...@@ -141,9 +141,9 @@ typedef struct clusterNode { ...@@ -141,9 +141,9 @@ typedef struct clusterNode {
long long repl_offset; /* Last known repl offset for this node. */ long long repl_offset; /* Last known repl offset for this node. */
char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */ char ip[NET_IP_STR_LEN]; /* Latest known IP address of this node */
sds hostname; /* The known hostname for this node */ sds hostname; /* The known hostname for this node */
int port; /* Latest known clients port (TLS or plain). */ sds human_nodename; /* The known human readable nodename for this node */
int pport; /* Latest known clients plaintext port. Only used int tcp_port; /* Latest known clients TCP port. */
if the main clients port is for TLS. */ int tls_port; /* Latest known clients TLS port */
int cport; /* Latest known cluster port of this node. */ int cport; /* Latest known cluster port of this node. */
clusterLink *link; /* TCP/IP link established toward this node */ clusterLink *link; /* TCP/IP link established toward this node */
clusterLink *inbound_link; /* TCP/IP link accepted from this node */ clusterLink *inbound_link; /* TCP/IP link accepted from this node */
...@@ -213,6 +213,13 @@ typedef struct clusterState { ...@@ -213,6 +213,13 @@ typedef struct clusterState {
long long stats_pfail_nodes; /* Number of nodes in PFAIL status, long long stats_pfail_nodes; /* Number of nodes in PFAIL status,
excluding nodes without address. */ excluding nodes without address. */
unsigned long long stat_cluster_links_buffer_limit_exceeded; /* Total number of cluster links freed due to exceeding buffer limit */ unsigned long long stat_cluster_links_buffer_limit_exceeded; /* Total number of cluster links freed due to exceeding buffer limit */
/* Bit map for slots that are no longer claimed by the owner in cluster PING
* messages. During slot migration, the owner will stop claiming the slot after
* the ownership transfer. Set the bit corresponding to the slot when a node
* stops claiming the slot. This prevents spreading incorrect information (that
* source still owns the slot) using UPDATE messages. */
unsigned char owner_not_claiming_slot[CLUSTER_SLOTS / 8];
} clusterState; } clusterState;
/* Redis cluster messages header */ /* Redis cluster messages header */
...@@ -225,10 +232,10 @@ typedef struct { ...@@ -225,10 +232,10 @@ typedef struct {
uint32_t ping_sent; uint32_t ping_sent;
uint32_t pong_received; uint32_t pong_received;
char ip[NET_IP_STR_LEN]; /* IP address last time it was seen */ char ip[NET_IP_STR_LEN]; /* IP address last time it was seen */
uint16_t port; /* base port last time it was seen */ uint16_t port; /* primary port last time it was seen */
uint16_t cport; /* cluster port last time it was seen */ uint16_t cport; /* cluster port last time it was seen */
uint16_t flags; /* node->flags copy */ uint16_t flags; /* node->flags copy */
uint16_t pport; /* plaintext-port, when base port is TLS */ uint16_t pport; /* secondary port last time it was seen */
uint16_t notused1; uint16_t notused1;
} clusterMsgDataGossip; } clusterMsgDataGossip;
...@@ -260,6 +267,7 @@ typedef struct { ...@@ -260,6 +267,7 @@ typedef struct {
* consistent manner. */ * consistent manner. */
typedef enum { typedef enum {
CLUSTERMSG_EXT_TYPE_HOSTNAME, CLUSTERMSG_EXT_TYPE_HOSTNAME,
CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME,
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE, CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE,
CLUSTERMSG_EXT_TYPE_SHARDID, CLUSTERMSG_EXT_TYPE_SHARDID,
} clusterMsgPingtypes; } clusterMsgPingtypes;
...@@ -271,6 +279,10 @@ typedef struct { ...@@ -271,6 +279,10 @@ typedef struct {
char hostname[1]; /* The announced hostname, ends with \0. */ char hostname[1]; /* The announced hostname, ends with \0. */
} clusterMsgPingExtHostname; } clusterMsgPingExtHostname;
typedef struct {
char human_nodename[1]; /* The announced nodename, ends with \0. */
} clusterMsgPingExtHumanNodename;
typedef struct { typedef struct {
char name[CLUSTER_NAMELEN]; /* Node name. */ char name[CLUSTER_NAMELEN]; /* Node name. */
uint64_t ttl; /* Remaining time to blacklist the node, in seconds. */ uint64_t ttl; /* Remaining time to blacklist the node, in seconds. */
...@@ -288,6 +300,7 @@ typedef struct { ...@@ -288,6 +300,7 @@ typedef struct {
uint16_t unused; /* 16 bits of padding to make this structure 8 byte aligned. */ uint16_t unused; /* 16 bits of padding to make this structure 8 byte aligned. */
union { union {
clusterMsgPingExtHostname hostname; clusterMsgPingExtHostname hostname;
clusterMsgPingExtHumanNodename human_nodename;
clusterMsgPingExtForgottenNode forgotten_node; clusterMsgPingExtForgottenNode forgotten_node;
clusterMsgPingExtShardId shard_id; clusterMsgPingExtShardId shard_id;
} ext[]; /* Actual extension information, formatted so that the data is 8 } ext[]; /* Actual extension information, formatted so that the data is 8
...@@ -331,7 +344,7 @@ typedef struct { ...@@ -331,7 +344,7 @@ typedef struct {
char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */ char sig[4]; /* Signature "RCmb" (Redis Cluster message bus). */
uint32_t totlen; /* Total length of this message */ uint32_t totlen; /* Total length of this message */
uint16_t ver; /* Protocol version, currently set to 1. */ uint16_t ver; /* Protocol version, currently set to 1. */
uint16_t port; /* TCP base port number. */ uint16_t port; /* Primary port number (TCP or TLS). */
uint16_t type; /* Message type */ uint16_t type; /* Message type */
uint16_t count; /* Only used for some kind of messages. */ uint16_t count; /* Only used for some kind of messages. */
uint64_t currentEpoch; /* The epoch accordingly to the sending node. */ uint64_t currentEpoch; /* The epoch accordingly to the sending node. */
...@@ -346,7 +359,8 @@ typedef struct { ...@@ -346,7 +359,8 @@ typedef struct {
char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */ char myip[NET_IP_STR_LEN]; /* Sender IP, if not all zeroed. */
uint16_t extensions; /* Number of extensions sent along with this packet. */ uint16_t extensions; /* Number of extensions sent along with this packet. */
char notused1[30]; /* 30 bytes reserved for future usage. */ char notused1[30]; /* 30 bytes reserved for future usage. */
uint16_t pport; /* Sender TCP plaintext port, if base port is TLS */ uint16_t pport; /* Secondary port number: if primary port is TCP port, this is
TLS port, and if primary port is TLS port, this is TCP port.*/
uint16_t cport; /* Sender TCP cluster bus port */ uint16_t cport; /* Sender TCP cluster bus port */
uint16_t flags; /* Sender node flags */ uint16_t flags; /* Sender node flags */
unsigned char state; /* Cluster state from the POV of the sender */ unsigned char state; /* Cluster state from the POV of the sender */
...@@ -422,8 +436,11 @@ void slotToChannelAdd(sds channel); ...@@ -422,8 +436,11 @@ void slotToChannelAdd(sds channel);
void slotToChannelDel(sds channel); void slotToChannelDel(sds channel);
void clusterUpdateMyselfHostname(void); void clusterUpdateMyselfHostname(void);
void clusterUpdateMyselfAnnouncedPorts(void); void clusterUpdateMyselfAnnouncedPorts(void);
sds clusterGenNodesDescription(int filter, int use_pport); sds clusterGenNodesDescription(client *c, int filter, int tls_primary);
sds genClusterInfoString(void); sds genClusterInfoString(void);
void freeClusterLink(clusterLink *link); void freeClusterLink(clusterLink *link);
void clusterUpdateMyselfHumanNodename(void);
int isValidAuxString(char *s, unsigned int length);
int getNodeDefaultClientPort(clusterNode *n);
#endif /* __CLUSTER_H */ #endif /* __CLUSTER_H */
...@@ -970,9 +970,9 @@ struct COMMAND_STRUCT CLUSTER_Subcommands[] = { ...@@ -970,9 +970,9 @@ struct COMMAND_STRUCT CLUSTER_Subcommands[] = {
{MAKE_CMD("saveconfig","Forces a node to save the cluster configuration to disk.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,0,CLUSTER_SAVECONFIG_Tips,0,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SAVECONFIG_Keyspecs,0,NULL,0)}, {MAKE_CMD("saveconfig","Forces a node to save the cluster configuration to disk.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,0,CLUSTER_SAVECONFIG_Tips,0,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SAVECONFIG_Keyspecs,0,NULL,0)},
{MAKE_CMD("set-config-epoch","Sets the configuration epoch for a new node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,0,CLUSTER_SET_CONFIG_EPOCH_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SET_CONFIG_EPOCH_Keyspecs,0,NULL,1),.args=CLUSTER_SET_CONFIG_EPOCH_Args}, {MAKE_CMD("set-config-epoch","Sets the configuration epoch for a new node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,0,CLUSTER_SET_CONFIG_EPOCH_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SET_CONFIG_EPOCH_Keyspecs,0,NULL,1),.args=CLUSTER_SET_CONFIG_EPOCH_Args},
{MAKE_CMD("setslot","Binds a hash slot to a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,0,CLUSTER_SETSLOT_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SETSLOT_Keyspecs,0,NULL,2),.args=CLUSTER_SETSLOT_Args}, {MAKE_CMD("setslot","Binds a hash slot to a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,0,CLUSTER_SETSLOT_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SETSLOT_Keyspecs,0,NULL,2),.args=CLUSTER_SETSLOT_Args},
{MAKE_CMD("shards","Returns the mapping of cluster slots to shards.","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,0,CLUSTER_SHARDS_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_SHARDS_Keyspecs,0,NULL,0)}, {MAKE_CMD("shards","Returns the mapping of cluster slots to shards.","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,0,CLUSTER_SHARDS_Tips,1,clusterCommand,2,CMD_LOADING|CMD_STALE,0,CLUSTER_SHARDS_Keyspecs,0,NULL,0)},
{MAKE_CMD("slaves","Lists the replica nodes of a master node.","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,0,CLUSTER_SLAVES_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_SLAVES_Keyspecs,0,NULL,1),.args=CLUSTER_SLAVES_Args}, {MAKE_CMD("slaves","Lists the replica nodes of a master node.","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,0,CLUSTER_SLAVES_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_SLAVES_Keyspecs,0,NULL,1),.args=CLUSTER_SLAVES_Args},
{MAKE_CMD("slots","Returns the mapping of cluster slots to nodes.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,2,CLUSTER_SLOTS_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_SLOTS_Keyspecs,0,NULL,0)}, {MAKE_CMD("slots","Returns the mapping of cluster slots to nodes.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,2,CLUSTER_SLOTS_Tips,1,clusterCommand,2,CMD_LOADING|CMD_STALE,0,CLUSTER_SLOTS_Keyspecs,0,NULL,0)},
{0} {0}
}; };
...@@ -5359,7 +5359,9 @@ struct COMMAND_ARG SENTINEL_CKQUORUM_Args[] = { ...@@ -5359,7 +5359,9 @@ struct COMMAND_ARG SENTINEL_CKQUORUM_Args[] = {
#ifndef SKIP_CMD_HISTORY_TABLE #ifndef SKIP_CMD_HISTORY_TABLE
/* SENTINEL CONFIG history */ /* SENTINEL CONFIG history */
#define SENTINEL_CONFIG_History NULL commandHistory SENTINEL_CONFIG_History[] = {
{"7.2.0","Added the ability to set and get multiple parameters in one call."},
};
#endif #endif
#ifndef SKIP_CMD_TIPS_TABLE #ifndef SKIP_CMD_TIPS_TABLE
...@@ -5380,8 +5382,8 @@ struct COMMAND_ARG SENTINEL_CONFIG_action_set_Subargs[] = { ...@@ -5380,8 +5382,8 @@ struct COMMAND_ARG SENTINEL_CONFIG_action_set_Subargs[] = {
/* SENTINEL CONFIG action argument table */ /* SENTINEL CONFIG action argument table */
struct COMMAND_ARG SENTINEL_CONFIG_action_Subargs[] = { struct COMMAND_ARG SENTINEL_CONFIG_action_Subargs[] = {
{MAKE_ARG("set",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SENTINEL_CONFIG_action_set_Subargs}, {MAKE_ARG("set",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=SENTINEL_CONFIG_action_set_Subargs},
{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,"GET",NULL,NULL,CMD_ARG_NONE,0,NULL)}, {MAKE_ARG("parameter",ARG_TYPE_STRING,-1,"GET",NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
}; };
/* SENTINEL CONFIG argument table */ /* SENTINEL CONFIG argument table */
...@@ -5811,7 +5813,7 @@ struct COMMAND_ARG SENTINEL_SLAVES_Args[] = { ...@@ -5811,7 +5813,7 @@ struct COMMAND_ARG SENTINEL_SLAVES_Args[] = {
/* SENTINEL command table */ /* SENTINEL command table */
struct COMMAND_STRUCT SENTINEL_Subcommands[] = { struct COMMAND_STRUCT SENTINEL_Subcommands[] = {
{MAKE_CMD("ckquorum","Checks for a Redis Sentinel quorum.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,0,SENTINEL_CKQUORUM_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CKQUORUM_Keyspecs,0,NULL,1),.args=SENTINEL_CKQUORUM_Args}, {MAKE_CMD("ckquorum","Checks for a Redis Sentinel quorum.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,0,SENTINEL_CKQUORUM_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CKQUORUM_Keyspecs,0,NULL,1),.args=SENTINEL_CKQUORUM_Args},
{MAKE_CMD("config","Configures Redis Sentinel.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,0,SENTINEL_CONFIG_Tips,0,sentinelCommand,-4,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CONFIG_Keyspecs,0,NULL,1),.args=SENTINEL_CONFIG_Args}, {MAKE_CMD("config","Configures Redis Sentinel.","O(N) when N is the number of configuration parameters provided","6.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,1,SENTINEL_CONFIG_Tips,0,sentinelCommand,-4,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CONFIG_Keyspecs,0,NULL,1),.args=SENTINEL_CONFIG_Args},
{MAKE_CMD("debug","Lists or updates the current configurable parameters of Redis Sentinel.","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,0,SENTINEL_DEBUG_Tips,0,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_DEBUG_Keyspecs,0,NULL,1),.args=SENTINEL_DEBUG_Args}, {MAKE_CMD("debug","Lists or updates the current configurable parameters of Redis Sentinel.","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,0,SENTINEL_DEBUG_Tips,0,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_DEBUG_Keyspecs,0,NULL,1),.args=SENTINEL_DEBUG_Args},
{MAKE_CMD("failover","Forces a Redis Sentinel failover.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,0,SENTINEL_FAILOVER_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FAILOVER_Keyspecs,0,NULL,1),.args=SENTINEL_FAILOVER_Args}, {MAKE_CMD("failover","Forces a Redis Sentinel failover.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,0,SENTINEL_FAILOVER_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FAILOVER_Keyspecs,0,NULL,1),.args=SENTINEL_FAILOVER_Args},
{MAKE_CMD("flushconfig","Rewrites the Redis Sentinel configuration file.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FLUSHCONFIG_History,0,SENTINEL_FLUSHCONFIG_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FLUSHCONFIG_Keyspecs,0,NULL,0)}, {MAKE_CMD("flushconfig","Rewrites the Redis Sentinel configuration file.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FLUSHCONFIG_History,0,SENTINEL_FLUSHCONFIG_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FLUSHCONFIG_Keyspecs,0,NULL,0)},
...@@ -10690,15 +10692,15 @@ struct COMMAND_STRUCT redisCommandTable[] = { ...@@ -10690,15 +10692,15 @@ struct COMMAND_STRUCT redisCommandTable[] = {
{MAKE_CMD("rpush","Appends one or more elements to a list. Creates the key if it doesn't exist.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSH_History,1,RPUSH_Tips,0,rpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSH_Keyspecs,1,NULL,2),.args=RPUSH_Args}, {MAKE_CMD("rpush","Appends one or more elements to a list. Creates the key if it doesn't exist.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSH_History,1,RPUSH_Tips,0,rpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSH_Keyspecs,1,NULL,2),.args=RPUSH_Args},
{MAKE_CMD("rpushx","Appends an element to a list only when the list exists.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSHX_History,1,RPUSHX_Tips,0,rpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSHX_Keyspecs,1,NULL,2),.args=RPUSHX_Args}, {MAKE_CMD("rpushx","Appends an element to a list only when the list exists.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSHX_History,1,RPUSHX_Tips,0,rpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSHX_Keyspecs,1,NULL,2),.args=RPUSHX_Args},
/* pubsub */ /* pubsub */
{MAKE_CMD("psubscribe","Listens for messages published to channels that match one or more patterns.","O(N) where N is the number of patterns the client is already subscribed to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PSUBSCRIBE_History,0,PSUBSCRIBE_Tips,0,psubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PSUBSCRIBE_Keyspecs,0,NULL,1),.args=PSUBSCRIBE_Args}, {MAKE_CMD("psubscribe","Listens for messages published to channels that match one or more patterns.","O(N) where N is the number of patterns to subscribe to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PSUBSCRIBE_History,0,PSUBSCRIBE_Tips,0,psubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PSUBSCRIBE_Keyspecs,0,NULL,1),.args=PSUBSCRIBE_Args},
{MAKE_CMD("publish","Posts a message to a channel.","O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBLISH_History,0,PUBLISH_Tips,0,publishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE|CMD_SENTINEL,0,PUBLISH_Keyspecs,0,NULL,2),.args=PUBLISH_Args}, {MAKE_CMD("publish","Posts a message to a channel.","O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBLISH_History,0,PUBLISH_Tips,0,publishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE|CMD_SENTINEL,0,PUBLISH_Keyspecs,0,NULL,2),.args=PUBLISH_Args},
{MAKE_CMD("pubsub","A container for Pub/Sub commands.","Depends on subcommand.","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_History,0,PUBSUB_Tips,0,NULL,-2,0,0,PUBSUB_Keyspecs,0,NULL,0),.subcommands=PUBSUB_Subcommands}, {MAKE_CMD("pubsub","A container for Pub/Sub commands.","Depends on subcommand.","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_History,0,PUBSUB_Tips,0,NULL,-2,0,0,PUBSUB_Keyspecs,0,NULL,0),.subcommands=PUBSUB_Subcommands},
{MAKE_CMD("punsubscribe","Stops listening to messages published to channels that match one or more patterns.","O(N+M) where N is the number of patterns the client is already subscribed and M is the number of total patterns subscribed in the system (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUNSUBSCRIBE_History,0,PUNSUBSCRIBE_Tips,0,punsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PUNSUBSCRIBE_Keyspecs,0,NULL,1),.args=PUNSUBSCRIBE_Args}, {MAKE_CMD("punsubscribe","Stops listening to messages published to channels that match one or more patterns.","O(N) where N is the number of patterns to unsubscribe.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUNSUBSCRIBE_History,0,PUNSUBSCRIBE_Tips,0,punsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PUNSUBSCRIBE_Keyspecs,0,NULL,1),.args=PUNSUBSCRIBE_Args},
{MAKE_CMD("spublish","Post a message to a shard channel","O(N) where N is the number of clients subscribed to the receiving shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SPUBLISH_History,0,SPUBLISH_Tips,0,spublishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE,0,SPUBLISH_Keyspecs,1,NULL,2),.args=SPUBLISH_Args}, {MAKE_CMD("spublish","Post a message to a shard channel","O(N) where N is the number of clients subscribed to the receiving shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SPUBLISH_History,0,SPUBLISH_Tips,0,spublishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE,0,SPUBLISH_Keyspecs,1,NULL,2),.args=SPUBLISH_Args},
{MAKE_CMD("ssubscribe","Listens for messages published to shard channels.","O(N) where N is the number of shard channels to subscribe to.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SSUBSCRIBE_History,0,SSUBSCRIBE_Tips,0,ssubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SSUBSCRIBE_Keyspecs,1,NULL,1),.args=SSUBSCRIBE_Args}, {MAKE_CMD("ssubscribe","Listens for messages published to shard channels.","O(N) where N is the number of shard channels to subscribe to.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SSUBSCRIBE_History,0,SSUBSCRIBE_Tips,0,ssubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SSUBSCRIBE_Keyspecs,1,NULL,1),.args=SSUBSCRIBE_Args},
{MAKE_CMD("subscribe","Listens for messages published to channels.","O(N) where N is the number of channels to subscribe to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUBSCRIBE_History,0,SUBSCRIBE_Tips,0,subscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,SUBSCRIBE_Keyspecs,0,NULL,1),.args=SUBSCRIBE_Args}, {MAKE_CMD("subscribe","Listens for messages published to channels.","O(N) where N is the number of channels to subscribe to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUBSCRIBE_History,0,SUBSCRIBE_Tips,0,subscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,SUBSCRIBE_Keyspecs,0,NULL,1),.args=SUBSCRIBE_Args},
{MAKE_CMD("sunsubscribe","Stops listening to messages posted to shard channels.","O(N) where N is the number of clients already subscribed to a shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUNSUBSCRIBE_History,0,SUNSUBSCRIBE_Tips,0,sunsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SUNSUBSCRIBE_Keyspecs,1,NULL,1),.args=SUNSUBSCRIBE_Args}, {MAKE_CMD("sunsubscribe","Stops listening to messages posted to shard channels.","O(N) where N is the number of shard channels to unsubscribe.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUNSUBSCRIBE_History,0,SUNSUBSCRIBE_Tips,0,sunsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SUNSUBSCRIBE_Keyspecs,1,NULL,1),.args=SUNSUBSCRIBE_Args},
{MAKE_CMD("unsubscribe","Stops listening to messages posted to channels.","O(N) where N is the number of clients already subscribed to a channel.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,UNSUBSCRIBE_History,0,UNSUBSCRIBE_Tips,0,unsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,UNSUBSCRIBE_Keyspecs,0,NULL,1),.args=UNSUBSCRIBE_Args}, {MAKE_CMD("unsubscribe","Stops listening to messages posted to channels.","O(N) where N is the number of channels to unsubscribe.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,UNSUBSCRIBE_History,0,UNSUBSCRIBE_Tips,0,unsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,UNSUBSCRIBE_Keyspecs,0,NULL,1),.args=UNSUBSCRIBE_Args},
/* scripting */ /* scripting */
{MAKE_CMD("eval","Executes a server-side Lua script.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVAL_History,0,EVAL_Tips,0,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVAL_Keyspecs,1,evalGetKeys,4),.args=EVAL_Args}, {MAKE_CMD("eval","Executes a server-side Lua script.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVAL_History,0,EVAL_Tips,0,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVAL_Keyspecs,1,evalGetKeys,4),.args=EVAL_Args},
{MAKE_CMD("evalsha","Executes a server-side Lua script by SHA1 digest.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVALSHA_History,0,EVALSHA_Tips,0,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVALSHA_Keyspecs,1,evalGetKeys,4),.args=EVALSHA_Args}, {MAKE_CMD("evalsha","Executes a server-side Lua script by SHA1 digest.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVALSHA_History,0,EVALSHA_Tips,0,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVALSHA_Keyspecs,1,evalGetKeys,4),.args=EVALSHA_Args},
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
"function": "clusterCommand", "function": "clusterCommand",
"history": [], "history": [],
"command_flags": [ "command_flags": [
"LOADING",
"STALE" "STALE"
], ],
"command_tips": [ "command_tips": [
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment