Unverified Commit 76b9c13d authored by Oran Agra's avatar Oran Agra Committed by GitHub
Browse files

Merge pull request #10962 from oranagra/release-7.0.3

Release 7.0.3
parents 05833959 85abb7cf
...@@ -150,7 +150,7 @@ int checkOvercommit(sds *error_msg) { ...@@ -150,7 +150,7 @@ int checkOvercommit(sds *error_msg) {
} }
fclose(fp); fclose(fp);
if (atoi(buf)) { if (strtol(buf, NULL, 10) == 0) {
*error_msg = sdsnew( *error_msg = sdsnew(
"overcommit_memory is set to 0! Background save may fail under low memory condition. " "overcommit_memory is set to 0! Background save may fail under low memory condition. "
"To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the " "To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the "
......
...@@ -2952,8 +2952,10 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler, ...@@ -2952,8 +2952,10 @@ static void zrangeResultFinalizeClient(zrange_result_handler *handler,
/* Result handler methods for storing the ZRANGESTORE to a zset. */ /* Result handler methods for storing the ZRANGESTORE to a zset. */
static void zrangeResultBeginStore(zrange_result_handler *handler, long length) static void zrangeResultBeginStore(zrange_result_handler *handler, long length)
{ {
UNUSED(length); if (length > (long)server.zset_max_listpack_entries)
handler->dstobj = createZsetListpackObject(); handler->dstobj = createZsetObject();
else
handler->dstobj = createZsetListpackObject();
} }
static void zrangeResultEmitCBufferForStore(zrange_result_handler *handler, static void zrangeResultEmitCBufferForStore(zrange_result_handler *handler,
......
...@@ -722,6 +722,8 @@ static void connTLSClose(connection *conn_) { ...@@ -722,6 +722,8 @@ static void connTLSClose(connection *conn_) {
tls_connection *conn = (tls_connection *) conn_; tls_connection *conn = (tls_connection *) conn_;
if (conn->ssl) { if (conn->ssl) {
if (conn->c.state == CONN_STATE_CONNECTED)
SSL_shutdown(conn->ssl);
SSL_free(conn->ssl); SSL_free(conn->ssl);
conn->ssl = NULL; conn->ssl = NULL;
} }
...@@ -834,12 +836,12 @@ static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt) ...@@ -834,12 +836,12 @@ static int connTLSWritev(connection *conn_, const struct iovec *iov, int iovcnt)
* which is not worth doing so much memory copying to reduce system calls, * which is not worth doing so much memory copying to reduce system calls,
* therefore, invoke connTLSWrite() multiple times to avoid memory copies. */ * therefore, invoke connTLSWrite() multiple times to avoid memory copies. */
if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT) { if (iov_bytes_len > NET_MAX_WRITES_PER_EVENT) {
size_t tot_sent = 0; ssize_t tot_sent = 0;
for (int i = 0; i < iovcnt; i++) { for (int i = 0; i < iovcnt; i++) {
size_t sent = connTLSWrite(conn_, iov[i].iov_base, iov[i].iov_len); ssize_t sent = connTLSWrite(conn_, iov[i].iov_base, iov[i].iov_len);
if (sent <= 0) return tot_sent > 0 ? tot_sent : sent; if (sent <= 0) return tot_sent > 0 ? tot_sent : sent;
tot_sent += sent; tot_sent += sent;
if (sent != iov[i].iov_len) break; if ((size_t) sent != iov[i].iov_len) break;
} }
return tot_sent; return tot_sent;
} }
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h> #include <dirent.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include "util.h" #include "util.h"
#include "sha256.h" #include "sha256.h"
...@@ -923,6 +924,54 @@ sds makePath(char *path, char *filename) { ...@@ -923,6 +924,54 @@ sds makePath(char *path, char *filename) {
return sdscatfmt(sdsempty(), "%s/%s", path, filename); return sdscatfmt(sdsempty(), "%s/%s", path, filename);
} }
/* Given the filename, sync the corresponding directory.
*
* Usually a portable and safe pattern to overwrite existing files would be like:
* 1. create a new temp file (on the same file system!)
* 2. write data to the temp file
* 3. fsync() the temp file
* 4. rename the temp file to the appropriate name
* 5. fsync() the containing directory */
int fsyncFileDir(const char *filename) {
#ifdef _AIX
/* AIX is unable to fsync a directory */
return 0;
#endif
char temp_filename[PATH_MAX + 1];
char *dname;
int dir_fd;
if (strlen(filename) > PATH_MAX) {
errno = ENAMETOOLONG;
return -1;
}
/* In the glibc implementation dirname may modify their argument. */
memcpy(temp_filename, filename, strlen(filename) + 1);
dname = dirname(temp_filename);
dir_fd = open(dname, O_RDONLY);
if (dir_fd == -1) {
/* Some OSs don't allow us to open directories at all, just
* ignore the error in that case */
if (errno == EISDIR) {
return 0;
}
return -1;
}
/* Some OSs don't allow us to fsync directories at all, so we can ignore
* those errors. */
if (redis_fsync(dir_fd) == -1 && !(errno == EBADF || errno == EINVAL)) {
int save_errno = errno;
close(dir_fd);
errno = save_errno;
return -1;
}
close(dir_fd);
return 0;
}
#ifdef REDIS_TEST #ifdef REDIS_TEST
#include <assert.h> #include <assert.h>
......
...@@ -85,6 +85,7 @@ int dirExists(char *dname); ...@@ -85,6 +85,7 @@ int dirExists(char *dname);
int dirRemove(char *dname); int dirRemove(char *dname);
int fileExist(char *filename); int fileExist(char *filename);
sds makePath(char *path, char *filename); sds makePath(char *path, char *filename);
int fsyncFileDir(const char *filename);
#ifdef REDIS_TEST #ifdef REDIS_TEST
int utilTest(int argc, char **argv, int flags); int utilTest(int argc, char **argv, int flags);
......
#define REDIS_VERSION "7.0.2" #define REDIS_VERSION "7.0.3"
#define REDIS_VERSION_NUM 0x00070002 #define REDIS_VERSION_NUM 0x00070003
...@@ -80,3 +80,16 @@ test "Script no-cluster flag" { ...@@ -80,3 +80,16 @@ test "Script no-cluster flag" {
assert_match {*Can not run script on cluster, 'no-cluster' flag is set*} $e assert_match {*Can not run script on cluster, 'no-cluster' flag is set*} $e
} }
test "CLUSTER RESET SOFT test" {
set last_epoch_node0 [get_info_field [R 0 cluster info] cluster_current_epoch]
R 0 FLUSHALL
R 0 CLUSTER RESET
assert {[get_info_field [R 0 cluster info] cluster_current_epoch] eq $last_epoch_node0}
set last_epoch_node1 [get_info_field [R 1 cluster info] cluster_current_epoch]
R 1 FLUSHALL
R 1 CLUSTER RESET SOFT
assert {[get_info_field [R 1 cluster info] cluster_current_epoch] eq $last_epoch_node1}
}
...@@ -182,4 +182,21 @@ test "Test the replica reports a loading state while it's loading" { ...@@ -182,4 +182,21 @@ test "Test the replica reports a loading state while it's loading" {
# Final sanity, the replica agrees it is online. # Final sanity, the replica agrees it is online.
assert_equal "online" [dict get [get_node_info_from_shard $replica_cluster_id $replica_id "node"] health] assert_equal "online" [dict get [get_node_info_from_shard $replica_cluster_id $replica_id "node"] health]
} }
\ No newline at end of file
test "Regression test for a crash when calling SHARDS during handshake" {
# Reset forget a node, so we can use it to establish handshaking connections
set id [R 19 CLUSTER MYID]
R 19 CLUSTER RESET HARD
for {set i 0} {$i < 19} {incr i} {
R $i CLUSTER FORGET $id
}
R 19 cluster meet 127.0.0.1 [get_instance_attrib redis 0 port]
# This should line would previously crash, since all the outbound
# connections were in handshake state.
R 19 CLUSTER SHARDS
}
test "Cluster is up" {
assert_cluster_state ok
}
# Tests for the response of slot migrations.
source "../tests/includes/init-tests.tcl"
source "../tests/includes/utils.tcl"
test "Create a 2 nodes cluster" {
create_cluster 2 0
config_set_all_nodes cluster-allow-replica-migration no
}
test "Cluster is up" {
assert_cluster_state ok
}
set cluster [redis_cluster 127.0.0.1:[get_instance_attrib redis 0 port]]
catch {unset nodefrom}
catch {unset nodeto}
$cluster refresh_nodes_map
test "Set many keys in the cluster" {
for {set i 0} {$i < 5000} {incr i} {
$cluster set $i $i
assert { [$cluster get $i] eq $i }
}
}
test "Test cluster responses during migration of slot x" {
set slot 10
array set nodefrom [$cluster masternode_for_slot $slot]
array set nodeto [$cluster masternode_notfor_slot $slot]
$nodeto(link) cluster setslot $slot importing $nodefrom(id)
$nodefrom(link) cluster setslot $slot migrating $nodeto(id)
# Get a key from that slot
set key [$nodefrom(link) cluster GETKEYSINSLOT $slot "1"]
# MOVED REPLY
assert_error "*MOVED*" {$nodeto(link) set $key "newVal"}
# ASK REPLY
assert_error "*ASK*" {$nodefrom(link) set "abc{$key}" "newVal"}
# UNSTABLE REPLY
assert_error "*TRYAGAIN*" {$nodefrom(link) mset "a{$key}" "newVal" $key "newVal2"}
}
config_set_all_nodes cluster-allow-replica-migration yes
...@@ -116,6 +116,15 @@ start_server {tags {"benchmark network external:skip"}} { ...@@ -116,6 +116,15 @@ start_server {tags {"benchmark network external:skip"}} {
# ensure the keyspace has the desired size # ensure the keyspace has the desired size
assert_match {50} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d] assert_match {50} [scan [regexp -inline {keys\=([\d]*)} [r info keyspace]] keys=%d]
} }
test {benchmark: clients idle mode should return error when reached maxclients limit} {
set cmd [redisbenchmark $master_host $master_port "-c 10 -I"]
set original_maxclients [lindex [r config get maxclients] 1]
r config set maxclients 5
catch { exec {*}$cmd } error
assert_match "*Error*" $error
r config set maxclients $original_maxclients
}
# tls specific tests # tls specific tests
if {$::tls} { if {$::tls} {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <pthread.h> #include <pthread.h>
#include <strings.h>
#define UNUSED(V) ((void) V) #define UNUSED(V) ((void) V)
...@@ -119,8 +120,20 @@ void *bg_call_worker(void *arg) { ...@@ -119,8 +120,20 @@ void *bg_call_worker(void *arg) {
} }
// Call the command // Call the command
const char* cmd = RedisModule_StringPtrLen(bg->argv[1], NULL); const char *module_cmd = RedisModule_StringPtrLen(bg->argv[0], NULL);
RedisModuleCallReply* rep = RedisModule_Call(ctx, cmd, "v", bg->argv + 2, bg->argc - 2); int cmd_pos = 1;
RedisModuleString *format_redis_str = RedisModule_CreateString(NULL, "v", 1);
if (!strcasecmp(module_cmd, "do_bg_rm_call_format")) {
cmd_pos = 2;
size_t format_len;
const char *format = RedisModule_StringPtrLen(bg->argv[1], &format_len);
RedisModule_StringAppendBuffer(NULL, format_redis_str, format, format_len);
RedisModule_StringAppendBuffer(NULL, format_redis_str, "E", 1);
}
const char *format = RedisModule_StringPtrLen(format_redis_str, NULL);
const char *cmd = RedisModule_StringPtrLen(bg->argv[cmd_pos], NULL);
RedisModuleCallReply *rep = RedisModule_Call(ctx, cmd, format, bg->argv + cmd_pos + 1, bg->argc - cmd_pos - 1);
RedisModule_FreeString(NULL, format_redis_str);
// Release GIL // Release GIL
RedisModule_ThreadSafeContextUnlock(ctx); RedisModule_ThreadSafeContextUnlock(ctx);
...@@ -306,6 +319,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -306,6 +319,9 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_CreateCommand(ctx, "do_bg_rm_call", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx, "do_bg_rm_call", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "do_bg_rm_call_format", do_bg_rm_call, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx, "do_fake_bg_true", do_fake_bg_true, "", 0, 0, 0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx, "do_fake_bg_true", do_fake_bg_true, "", 0, 0, 0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <assert.h> #include <assert.h>
#include <unistd.h> #include <unistd.h>
#include <errno.h> #include <errno.h>
#include <limits.h>
#define UNUSED(x) (void)(x) #define UNUSED(x) (void)(x)
...@@ -239,9 +240,17 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -239,9 +240,17 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
(void) argv; (void) argv;
(void) argc; (void) argc;
RedisModuleClientInfo ci = { .version = REDISMODULE_CLIENTINFO_VERSION }; RedisModuleClientInfoV1 ci = REDISMODULE_CLIENTINFO_INITIALIZER_V1;
uint64_t client_id = RedisModule_GetClientId(ctx);
if (RedisModule_GetClientInfoById(&ci, RedisModule_GetClientId(ctx)) == REDISMODULE_ERR) { /* Check expected result from the V1 initializer. */
assert(ci.version == 1);
/* Trying to populate a future version of the struct should fail. */
ci.version = REDISMODULE_CLIENTINFO_VERSION + 1;
assert(RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR);
ci.version = 1;
if (RedisModule_GetClientInfoById(&ci, client_id) == REDISMODULE_ERR) {
RedisModule_ReplyWithError(ctx, "failed to get client info"); RedisModule_ReplyWithError(ctx, "failed to get client info");
return REDISMODULE_OK; return REDISMODULE_OK;
} }
...@@ -270,6 +279,27 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -270,6 +279,27 @@ int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK; return REDISMODULE_OK;
} }
int test_getname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
(void)argv;
if (argc != 1) return RedisModule_WrongArity(ctx);
unsigned long long id = RedisModule_GetClientId(ctx);
RedisModuleString *name = RedisModule_GetClientNameById(ctx, id);
if (name == NULL)
return RedisModule_ReplyWithError(ctx, "-ERR No name");
RedisModule_ReplyWithString(ctx, name);
RedisModule_FreeString(ctx, name);
return REDISMODULE_OK;
}
int test_setname(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2) return RedisModule_WrongArity(ctx);
unsigned long long id = RedisModule_GetClientId(ctx);
if (RedisModule_SetClientNameById(id, argv[1]) == REDISMODULE_OK)
return RedisModule_ReplyWithSimpleString(ctx, "OK");
else
return RedisModule_ReplyWithError(ctx, strerror(errno));
}
int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) int test_log_tsctx(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{ {
RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx); RedisModuleCtx *tsctx = RedisModule_GetDetachedThreadSafeContext(ctx);
...@@ -354,6 +384,68 @@ int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){ ...@@ -354,6 +384,68 @@ int test_rm_call_flags(RedisModuleCtx *ctx, RedisModuleString **argv, int argc){
return REDISMODULE_OK; return REDISMODULE_OK;
} }
int test_ull_conv(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
UNUSED(argv);
UNUSED(argc);
unsigned long long ull = 18446744073709551615ULL;
const char *ullstr = "18446744073709551615";
RedisModuleString *s1 = RedisModule_CreateStringFromULongLong(ctx, ull);
RedisModuleString *s2 =
RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
if (RedisModule_StringCompare(s1, s2) != 0) {
char err[4096];
snprintf(err, 4096,
"Failed to convert unsigned long long to string ('%s' != '%s')",
RedisModule_StringPtrLen(s1, NULL),
RedisModule_StringPtrLen(s2, NULL));
RedisModule_ReplyWithError(ctx, err);
goto final;
}
unsigned long long ull2 = 0;
if (RedisModule_StringToULongLong(s2, &ull2) == REDISMODULE_ERR) {
RedisModule_ReplyWithError(ctx,
"Failed to convert string to unsigned long long");
goto final;
}
if (ull2 != ull) {
char err[4096];
snprintf(err, 4096,
"Failed to convert string to unsigned long long (%llu != %llu)",
ull2,
ull);
RedisModule_ReplyWithError(ctx, err);
goto final;
}
/* Make sure we can't convert a string more than ULLONG_MAX or less than 0 */
ullstr = "18446744073709551616";
RedisModuleString *s3 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
unsigned long long ull3;
if (RedisModule_StringToULongLong(s3, &ull3) == REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
RedisModule_FreeString(ctx, s3);
goto final;
}
RedisModule_FreeString(ctx, s3);
ullstr = "-1";
RedisModuleString *s4 = RedisModule_CreateString(ctx, ullstr, strlen(ullstr));
unsigned long long ull4;
if (RedisModule_StringToULongLong(s4, &ull4) == REDISMODULE_OK) {
RedisModule_ReplyWithError(ctx, "Invalid string successfully converted to unsigned long long");
RedisModule_FreeString(ctx, s4);
goto final;
}
RedisModule_FreeString(ctx, s4);
RedisModule_ReplyWithSimpleString(ctx, "ok");
final:
RedisModule_FreeString(ctx, s1);
RedisModule_FreeString(ctx, s2);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) { int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv); REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc); REDISMODULE_NOT_USED(argc);
...@@ -366,6 +458,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -366,6 +458,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.ld_conversion", test_ld_conv, "",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.ull_conversion", test_ull_conv, "",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.flushall", test_flushall,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.dbsize", test_dbsize,"",0,0,0) == REDISMODULE_ERR)
...@@ -384,6 +478,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) ...@@ -384,6 +478,10 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getname", test_getname,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.setname", test_setname,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.redisversion", test_redisversion,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.redisversion", test_redisversion,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR; return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR) if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR)
......
...@@ -12,6 +12,13 @@ if {$::simulate_error} { ...@@ -12,6 +12,13 @@ if {$::simulate_error} {
} }
} }
test "Sentinel commands sanity check" {
foreach_sentinel_id id {
assert_equal {72} [llength [S $id command list]]
assert_equal {15} [S $id command count]
}
}
test "Basic failover works if the master is down" { test "Basic failover works if the master is down" {
set old_port [RPort $master_id] set old_port [RPort $master_id]
set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster] set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]
......
...@@ -126,7 +126,7 @@ test "Sentinels (re)connection following master ACL change" { ...@@ -126,7 +126,7 @@ test "Sentinels (re)connection following master ACL change" {
wait_for_condition 100 50 { wait_for_condition 100 50 {
[string match "*disconnected*" [dict get [S $sent2re SENTINEL MASTER mymaster] flags]] != 0 [string match "*disconnected*" [dict get [S $sent2re SENTINEL MASTER mymaster] flags]] != 0
} else { } else {
fail "Expected: Sentinel to be disconnected from master due to wrong password" fail "Expected: Restarted sentinel to be disconnected from master due to obsolete password"
} }
# Verify sentinel with updated password managed to connect (wait for sentinelTimer to reconnect) # Verify sentinel with updated password managed to connect (wait for sentinelTimer to reconnect)
...@@ -137,8 +137,10 @@ test "Sentinels (re)connection following master ACL change" { ...@@ -137,8 +137,10 @@ test "Sentinels (re)connection following master ACL change" {
} }
# Verify sentinel untouched gets failed to connect master # Verify sentinel untouched gets failed to connect master
if {![string match "*disconnected*" [dict get [S $sent2un SENTINEL MASTER mymaster] flags]]} { wait_for_condition 100 50 {
fail "Expected: Sentinel to be disconnected from master due to wrong password" [string match "*disconnected*" [dict get [S $sent2un SENTINEL MASTER mymaster] flags]] != 0
} else {
fail "Expected: Sentinel to be disconnected from master due to obsolete password"
} }
# Now update all sentinels with new password # Now update all sentinels with new password
...@@ -167,14 +169,14 @@ test "Set parameters in normal case" { ...@@ -167,14 +169,14 @@ test "Set parameters in normal case" {
set origin_down_after_milliseconds [dict get $info down-after-milliseconds] set origin_down_after_milliseconds [dict get $info down-after-milliseconds]
set update_quorum [expr $origin_quorum+1] set update_quorum [expr $origin_quorum+1]
set update_down_after_milliseconds [expr $origin_down_after_milliseconds+1000] set update_down_after_milliseconds [expr $origin_down_after_milliseconds+1000]
assert_equal [S 0 SENTINEL SET mymaster quorum $update_quorum] "OK" assert_equal [S 0 SENTINEL SET mymaster quorum $update_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $update_down_after_milliseconds] "OK" assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $update_down_after_milliseconds] "OK"
set update_info [S 0 SENTINEL master mymaster] set update_info [S 0 SENTINEL master mymaster]
assert {[dict get $update_info quorum] != $origin_quorum} assert {[dict get $update_info quorum] != $origin_quorum}
assert {[dict get $update_info down-after-milliseconds] != $origin_down_after_milliseconds} assert {[dict get $update_info down-after-milliseconds] != $origin_down_after_milliseconds}
#restore to origin config parameters #restore to origin config parameters
assert_equal [S 0 SENTINEL SET mymaster quorum $origin_quorum] "OK" assert_equal [S 0 SENTINEL SET mymaster quorum $origin_quorum] "OK"
assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $origin_down_after_milliseconds] "OK" assert_equal [S 0 SENTINEL SET mymaster down-after-milliseconds $origin_down_after_milliseconds] "OK"
......
...@@ -67,6 +67,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re ...@@ -67,6 +67,33 @@ proc redis {{server 127.0.0.1} {port 6379} {defer 0} {tls 0} {tlsoptions {}} {re
interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id interp alias {} ::redis::redisHandle$id {} ::redis::__dispatch__ $id
} }
# On recent versions of tcl-tls/OpenSSL, reading from a dropped connection
# results with an error we need to catch and mimic the old behavior.
proc ::redis::redis_safe_read {fd len} {
if {$len == -1} {
set err [catch {set val [read $fd]} msg]
} else {
set err [catch {set val [read $fd $len]} msg]
}
if {!$err} {
return $val
}
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
proc ::redis::redis_safe_gets {fd} {
if {[catch {set val [gets $fd]} msg]} {
if {[string match "*connection abort*" $msg]} {
return {}
}
error $msg
}
return $val
}
# This is a wrapper to the actual dispatching procedure that handles # This is a wrapper to the actual dispatching procedure that handles
# reconnection if needed. # reconnection if needed.
proc ::redis::__dispatch__ {id method args} { proc ::redis::__dispatch__ {id method args} {
...@@ -148,8 +175,8 @@ proc ::redis::__method__read {id fd} { ...@@ -148,8 +175,8 @@ proc ::redis::__method__read {id fd} {
::redis::redis_read_reply $id $fd ::redis::redis_read_reply $id $fd
} }
proc ::redis::__method__rawread {id fd len} { proc ::redis::__method__rawread {id fd {len -1}} {
return [read $fd $len] return [redis_safe_read $fd $len]
} }
proc ::redis::__method__write {id fd buf} { proc ::redis::__method__write {id fd buf} {
...@@ -207,8 +234,8 @@ proc ::redis::redis_writenl {fd buf} { ...@@ -207,8 +234,8 @@ proc ::redis::redis_writenl {fd buf} {
} }
proc ::redis::redis_readnl {fd len} { proc ::redis::redis_readnl {fd len} {
set buf [read $fd $len] set buf [redis_safe_read $fd $len]
read $fd 2 ; # discard CR LF redis_safe_read $fd 2 ; # discard CR LF
return $buf return $buf
} }
...@@ -254,11 +281,11 @@ proc ::redis::redis_read_map {id fd} { ...@@ -254,11 +281,11 @@ proc ::redis::redis_read_map {id fd} {
} }
proc ::redis::redis_read_line fd { proc ::redis::redis_read_line fd {
string trim [gets $fd] string trim [redis_safe_gets $fd]
} }
proc ::redis::redis_read_null fd { proc ::redis::redis_read_null fd {
gets $fd redis_safe_gets $fd
return {} return {}
} }
...@@ -281,7 +308,7 @@ proc ::redis::redis_read_reply {id fd} { ...@@ -281,7 +308,7 @@ proc ::redis::redis_read_reply {id fd} {
} }
while {1} { while {1} {
set type [read $fd 1] set type [redis_safe_read $fd 1]
switch -exact -- $type { switch -exact -- $type {
_ {return [redis_read_null $fd]} _ {return [redis_read_null $fd]}
: - : -
......
...@@ -72,8 +72,8 @@ proc sanitizer_errors_from_file {filename} { ...@@ -72,8 +72,8 @@ proc sanitizer_errors_from_file {filename} {
} }
proc getInfoProperty {infostr property} { proc getInfoProperty {infostr property} {
if {[regexp "\r\n$property:(.*?)\r\n" $infostr _ value]} { if {[regexp -lineanchor "^$property:(.*?)\r\n" $infostr _ value]} {
set _ $value return $value
} }
} }
......
...@@ -156,13 +156,13 @@ start_server {} { ...@@ -156,13 +156,13 @@ start_server {} {
test "client evicted due to pubsub subscriptions" { test "client evicted due to pubsub subscriptions" {
r flushdb r flushdb
# Since pubsub subscriptions cause a small overheatd this test uses a minimal maxmemory-clients config # Since pubsub subscriptions cause a small overhead this test uses a minimal maxmemory-clients config
set temp_maxmemory_clients 200000 set temp_maxmemory_clients 200000
r config set maxmemory-clients $temp_maxmemory_clients r config set maxmemory-clients $temp_maxmemory_clients
# Test eviction due to pubsub patterns # Test eviction due to pubsub patterns
set rr [redis_client] set rr [redis_client]
# Add patterns until list maxes out maxmemroy clients and causes client eviction # Add patterns until list maxes out maxmemory clients and causes client eviction
catch { catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} { for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr psubscribe $j $rr psubscribe $j
...@@ -173,7 +173,7 @@ start_server {} { ...@@ -173,7 +173,7 @@ start_server {} {
# Test eviction due to pubsub channels # Test eviction due to pubsub channels
set rr [redis_client] set rr [redis_client]
# Add patterns until list maxes out maxmemroy clients and causes client eviction # Subscribe to global channels until list maxes out maxmemory clients and causes client eviction
catch { catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} { for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr subscribe $j $rr subscribe $j
...@@ -181,6 +181,17 @@ start_server {} { ...@@ -181,6 +181,17 @@ start_server {} {
} e } e
assert_match {I/O error reading reply} $e assert_match {I/O error reading reply} $e
$rr close $rr close
# Test eviction due to sharded pubsub channels
set rr [redis_client]
# Subscribe to sharded pubsub channels until list maxes out maxmemory clients and causes client eviction
catch {
for {set j 0} {$j < $temp_maxmemory_clients} {incr j} {
$rr ssubscribe $j
}
} e
assert_match {I/O error reading reply} $e
$rr close
# Restore config for next tests # Restore config for next tests
r config set maxmemory-clients $maxmemory_clients r config set maxmemory-clients $maxmemory_clients
......
...@@ -3,9 +3,7 @@ ...@@ -3,9 +3,7 @@
source tests/support/cli.tcl source tests/support/cli.tcl
proc cluster_info {r field} { proc cluster_info {r field} {
if {[regexp "^$field:(.*?)\r\n" [$r cluster info] _ value]} { set _ [getInfoProperty [$r cluster info] $field]
set _ $value
}
} }
# Provide easy access to CLUSTER INFO properties. Same semantic as "proc s". # Provide easy access to CLUSTER INFO properties. Same semantic as "proc s".
...@@ -110,7 +108,7 @@ start_multiple_servers 3 [list overrides $base_conf] { ...@@ -110,7 +108,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
} }
$node3_rd close $node3_rd close
test "Run blocking command again on cluster node1" { test "Run blocking command again on cluster node1" {
$node1 del key9184688 $node1 del key9184688
# key9184688 is mapped to slot 10923 which has been moved to node1 # key9184688 is mapped to slot 10923 which has been moved to node1
...@@ -123,9 +121,9 @@ start_multiple_servers 3 [list overrides $base_conf] { ...@@ -123,9 +121,9 @@ start_multiple_servers 3 [list overrides $base_conf] {
fail "Client not blocked" fail "Client not blocked"
} }
} }
test "Kill a cluster node and wait for fail state" { test "Kill a cluster node and wait for fail state" {
# kill node3 in cluster # kill node3 in cluster
exec kill -SIGSTOP $node3_pid exec kill -SIGSTOP $node3_pid
wait_for_condition 1000 50 { wait_for_condition 1000 50 {
...@@ -135,7 +133,7 @@ start_multiple_servers 3 [list overrides $base_conf] { ...@@ -135,7 +133,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
fail "Cluster doesn't fail" fail "Cluster doesn't fail"
} }
} }
test "Verify command got unblocked after cluster failure" { test "Verify command got unblocked after cluster failure" {
assert_error {*CLUSTERDOWN*} {$node1_rd read} assert_error {*CLUSTERDOWN*} {$node1_rd read}
...@@ -208,7 +206,7 @@ start_multiple_servers 5 [list overrides $base_conf] { ...@@ -208,7 +206,7 @@ start_multiple_servers 5 [list overrides $base_conf] {
127.0.0.1:[srv -4 port] \ 127.0.0.1:[srv -4 port] \
127.0.0.1:[srv 0 port] 127.0.0.1:[srv 0 port]
} e } e
assert_match {*node already contains functions*} $e assert_match {*node already contains functions*} $e
} }
} ;# stop servers } ;# stop servers
...@@ -315,6 +313,86 @@ test {Migrate the last slot away from a node using redis-cli} { ...@@ -315,6 +313,86 @@ test {Migrate the last slot away from a node using redis-cli} {
} }
} }
# Test redis-cli --cluster create, add-node with cluster-port.
# Create five nodes, three with custom cluster_port and two with default values.
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1]] {
start_server [list overrides [list cluster-enabled yes cluster-node-timeout 1 cluster-port [find_available_port $::baseport $::portcount]]] {
# The first three are used to test --cluster create.
# The last two are used to test --cluster add-node
set node1_rd [redis_client 0]
set node2_rd [redis_client -1]
set node3_rd [redis_client -2]
set node4_rd [redis_client -3]
set node5_rd [redis_client -4]
test {redis-cli --cluster create with cluster-port} {
exec src/redis-cli --cluster-yes --cluster create \
127.0.0.1:[srv 0 port] \
127.0.0.1:[srv -1 port] \
127.0.0.1:[srv -2 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Make sure each node can meet other nodes
assert_equal 3 [csi 0 cluster_known_nodes]
assert_equal 3 [csi -1 cluster_known_nodes]
assert_equal 3 [csi -2 cluster_known_nodes]
}
test {redis-cli --cluster add-node with cluster-port} {
# Adding node to the cluster (without cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -3 port] \
127.0.0.1:[srv 0 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok} &&
[csi -3 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Adding node to the cluster (with cluster-port)
exec src/redis-cli --cluster-yes --cluster add-node \
127.0.0.1:[srv -4 port] \
127.0.0.1:[srv 0 port]
wait_for_condition 1000 50 {
[csi 0 cluster_state] eq {ok} &&
[csi -1 cluster_state] eq {ok} &&
[csi -2 cluster_state] eq {ok} &&
[csi -3 cluster_state] eq {ok} &&
[csi -4 cluster_state] eq {ok}
} else {
fail "Cluster doesn't stabilize"
}
# Make sure each node can meet other nodes
assert_equal 5 [csi 0 cluster_known_nodes]
assert_equal 5 [csi -1 cluster_known_nodes]
assert_equal 5 [csi -2 cluster_known_nodes]
assert_equal 5 [csi -3 cluster_known_nodes]
assert_equal 5 [csi -4 cluster_known_nodes]
}
# stop 5 servers
}
}
}
}
}
} ;# tags } ;# tags
set ::singledb $old_singledb set ::singledb $old_singledb
...@@ -7,7 +7,7 @@ start_server {tags {"introspection"}} { ...@@ -7,7 +7,7 @@ start_server {tags {"introspection"}} {
test {CLIENT LIST} { test {CLIENT LIST} {
r client list r client list
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*} } {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|list user=* redir=-1 resp=2*}
test {CLIENT LIST with IDs} { test {CLIENT LIST with IDs} {
set myid [r client id] set myid [r client id]
...@@ -17,7 +17,7 @@ start_server {tags {"introspection"}} { ...@@ -17,7 +17,7 @@ start_server {tags {"introspection"}} {
test {CLIENT INFO} { test {CLIENT INFO} {
r client info r client info
} {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*} } {id=* addr=*:* laddr=*:* fd=* name=* age=* idle=* flags=N db=* sub=0 psub=0 ssub=0 multi=-1 qbuf=26 qbuf-free=* argv-mem=* multi-mem=0 rbs=* rbp=* obl=0 oll=0 omem=0 tot-mem=* events=r cmd=client|info user=* redir=-1 resp=2*}
test {CLIENT KILL with illegal arguments} { test {CLIENT KILL with illegal arguments} {
assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill} assert_error "ERR wrong number of arguments for 'client|kill' command" {r client kill}
...@@ -503,6 +503,21 @@ start_server {tags {"introspection"}} { ...@@ -503,6 +503,21 @@ start_server {tags {"introspection"}} {
assert_match {*'replicaof "--127.0.0.1"'*wrong number of arguments*} $err assert_match {*'replicaof "--127.0.0.1"'*wrong number of arguments*} $err
} {} {external:skip} } {} {external:skip}
test {redis-server command line arguments - allow passing option name and option value in the same arg} {
start_server {config "default.conf" args {"--maxmemory 700mb" "--maxmemory-policy volatile-lru"}} {
assert_match [r config get maxmemory] {maxmemory 734003200}
assert_match [r config get maxmemory-policy] {maxmemory-policy volatile-lru}
}
} {} {external:skip}
test {redis-server command line arguments - wrong usage that we support anyway} {
start_server {config "default.conf" args {loglevel verbose "--maxmemory '700mb'" "--maxmemory-policy 'volatile-lru'"}} {
assert_match [r config get loglevel] {loglevel verbose}
assert_match [r config get maxmemory] {maxmemory 734003200}
assert_match [r config get maxmemory-policy] {maxmemory-policy volatile-lru}
}
} {} {external:skip}
test {redis-server command line arguments - allow option value to use the `--` prefix} { test {redis-server command line arguments - allow option value to use the `--` prefix} {
start_server {config "default.conf" args {--proc-title-template --my--title--template --loglevel verbose}} { start_server {config "default.conf" args {--proc-title-template --my--title--template --loglevel verbose}} {
assert_match [r config get proc-title-template] {proc-title-template --my--title--template} assert_match [r config get proc-title-template] {proc-title-template --my--title--template}
...@@ -510,15 +525,40 @@ start_server {tags {"introspection"}} { ...@@ -510,15 +525,40 @@ start_server {tags {"introspection"}} {
} }
} {} {external:skip} } {} {external:skip}
test {redis-server command line arguments - option name and option value in the same arg and `--` prefix} {
start_server {config "default.conf" args {"--proc-title-template --my--title--template" "--loglevel verbose"}} {
assert_match [r config get proc-title-template] {proc-title-template --my--title--template}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip}
test {redis-server command line arguments - save with empty input} { test {redis-server command line arguments - save with empty input} {
# Take `--loglevel` as the save option value. start_server {config "default.conf" args {--save --loglevel verbose}} {
catch {exec src/redis-server --save --loglevel verbose} err assert_match [r config get save] {save {}}
assert_match {*'save "--loglevel" "verbose"'*Invalid save parameters*} $err assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--loglevel verbose --save}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--save {} --loglevel verbose}} { start_server {config "default.conf" args {--save {} --loglevel verbose}} {
assert_match [r config get save] {save {}} assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose} assert_match [r config get loglevel] {loglevel verbose}
} }
start_server {config "default.conf" args {--loglevel verbose --save {}}} {
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
start_server {config "default.conf" args {--proc-title-template --save --save {} --loglevel verbose}} {
assert_match [r config get proc-title-template] {proc-title-template --save}
assert_match [r config get save] {save {}}
assert_match [r config get loglevel] {loglevel verbose}
}
} {} {external:skip} } {} {external:skip}
test {redis-server command line arguments - take one bulk string with spaces for MULTI_ARG configs parsing} { test {redis-server command line arguments - take one bulk string with spaces for MULTI_ARG configs parsing} {
......
...@@ -76,6 +76,19 @@ start_server {tags {"modules"}} { ...@@ -76,6 +76,19 @@ start_server {tags {"modules"}} {
r do_bg_rm_call hgetall hash r do_bg_rm_call hgetall hash
} {foo bar} } {foo bar}
test {RM_Call from blocked client with script mode} {
r do_bg_rm_call_format S hset k foo bar
} {1}
test {RM_Call from blocked client with oom mode} {
r config set maxmemory 1
# will set server.pre_command_oom_state to 1
assert_error {OOM command not allowed*} {r hset hash foo bar}
r config set maxmemory 0
# now its should be OK to call OOM commands
r do_bg_rm_call_format M hset k1 foo bar
} {1} {needs:config-maxmemory}
test {RESP version carries through to blocked client} { test {RESP version carries through to blocked client} {
for {set client_proto 2} {$client_proto <= 3} {incr client_proto} { for {set client_proto 2} {$client_proto <= 3} {incr client_proto} {
r hello $client_proto r hello $client_proto
......
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