Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
a5d2df34
Commit
a5d2df34
authored
Jun 08, 2022
by
Oran Agra
Browse files
Merge remote-tracking branch 'origin/unstable' into 7.0
parents
d375595d
f22bfe86
Changes
108
Expand all
Hide whitespace changes
Inline
Side-by-side
src/sentinel.c
View file @
a5d2df34
...
...
@@ -4127,7 +4127,7 @@ numargserr:
void
addInfoSectionsToDict
(
dict
*
section_dict
,
char
**
sections
);
/*
SENTINEL
INFO [section] */
/* INFO [
<
section
> [<section> ...]
] */
void
sentinelInfoCommand
(
client
*
c
)
{
char
*
sentinel_sections
[]
=
{
"server"
,
"clients"
,
"cpu"
,
"stats"
,
"sentinel"
,
NULL
};
int
sec_all
=
0
,
sec_everything
=
0
;
...
...
src/server.c
View file @
a5d2df34
This diff is collapsed.
Click to expand it.
src/server.h
View file @
a5d2df34
...
...
@@ -70,7 +70,6 @@ typedef long long ustime_t; /* microsecond time type. */
#include "adlist.h"
/* Linked lists */
#include "zmalloc.h"
/* total memory usage aware version of malloc/free */
#include "anet.h"
/* Networking the easy way */
#include "ziplist.h"
/* Compact list data structure */
#include "intset.h"
/* Compact integer set structure */
#include "version.h"
/* Version macro */
#include "util.h"
/* Misc functions useful in many places */
...
...
@@ -86,11 +85,14 @@ typedef long long ustime_t; /* microsecond time type. */
/* Following includes allow test functions to be called from Redis main() */
#include "zipmap.h"
#include "ziplist.h"
/* Compact list data structure */
#include "sha1.h"
#include "endianconv.h"
#include "crc64.h"
/* min/max */
#undef min
#undef max
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
...
...
@@ -112,7 +114,6 @@ typedef long long ustime_t; /* microsecond time type. */
#define LOG_MAX_LEN 1024
/* Default maximum length of syslog messages.*/
#define AOF_REWRITE_ITEMS_PER_CMD 64
#define AOF_ANNOTATION_LINE_MAX_LEN 1024
#define CONFIG_AUTHPASS_MAX_LEN 512
#define CONFIG_RUN_ID_SIZE 40
#define RDB_EOF_MARK_SIZE 40
#define CONFIG_REPL_BACKLOG_MIN_SIZE (1024*16)
/* 16k */
...
...
@@ -153,9 +154,11 @@ typedef long long ustime_t; /* microsecond time type. */
/* Instantaneous metrics tracking. */
#define STATS_METRIC_SAMPLES 16
/* Number of samples per metric. */
#define STATS_METRIC_COMMAND 0
/* Number of commands executed. */
#define STATS_METRIC_NET_INPUT 1
/* Bytes read to network
.*/
#define STATS_METRIC_NET_INPUT 1
/* Bytes read to network.
*/
#define STATS_METRIC_NET_OUTPUT 2
/* Bytes written to network. */
#define STATS_METRIC_COUNT 3
#define STATS_METRIC_NET_INPUT_REPLICATION 3
/* Bytes read to network during replication. */
#define STATS_METRIC_NET_OUTPUT_REPLICATION 4
/* Bytes written to network during replication. */
#define STATS_METRIC_COUNT 5
/* Protocol and I/O related defines */
#define PROTO_IOBUF_LEN (1024*16)
/* Generic I/O buffer size */
...
...
@@ -289,7 +292,7 @@ extern int configOOMScoreAdjValuesDefaults[CONFIG_OOM_COUNT];
#define AOF_ON 1
/* AOF is on */
#define AOF_WAIT_REWRITE 2
/* AOF waits rewrite to start appending */
/* AOF return values for loadAppendOnlyFile() */
/* AOF return values for loadAppendOnlyFile
s() and loadSingleAppendOnlyFile
() */
#define AOF_OK 0
#define AOF_NOT_EXIST 1
#define AOF_EMPTY 2
...
...
@@ -674,6 +677,7 @@ struct redisObject;
struct
RedisModuleDefragCtx
;
struct
RedisModuleInfoCtx
;
struct
RedisModuleKeyOptCtx
;
struct
RedisModuleCommand
;
/* Each module type implementation should export a set of methods in order
* to serialize and deserialize the value in the RDB file, rewrite the AOF
...
...
@@ -825,9 +829,9 @@ typedef struct RedisModuleDigest {
#define OBJ_ENCODING_RAW 0
/* Raw representation */
#define OBJ_ENCODING_INT 1
/* Encoded as integer */
#define OBJ_ENCODING_HT 2
/* Encoded as hash table */
#define OBJ_ENCODING_ZIPMAP 3
/*
Encode
d as
zipmap
*/
#define OBJ_ENCODING_ZIPMAP 3
/*
No longer used: ol
d
h
as
h encoding.
*/
#define OBJ_ENCODING_LINKEDLIST 4
/* No longer used: old list encoding. */
#define OBJ_ENCODING_ZIPLIST 5
/*
Encoded as ziplist
*/
#define OBJ_ENCODING_ZIPLIST 5
/*
No longer used: old list/hash/zset encoding.
*/
#define OBJ_ENCODING_INTSET 6
/* Encoded as intset */
#define OBJ_ENCODING_SKIPLIST 7
/* Encoded as skiplist */
#define OBJ_ENCODING_EMBSTR 8
/* Embedded sds string encoding */
...
...
@@ -1077,6 +1081,7 @@ typedef struct {
typedef
struct
client
{
uint64_t
id
;
/* Client incremental unique ID. */
uint64_t
flags
;
/* Client flags: CLIENT_* macros. */
connection
*
conn
;
int
resp
;
/* RESP protocol version. Can be 2 or 3. */
redisDb
*
db
;
/* Pointer to currently SELECTed DB. */
...
...
@@ -1110,7 +1115,6 @@ typedef struct client {
int
slot
;
/* The slot the client is executing against. Set to -1 if no slot is being used */
time_t
lastinteraction
;
/* Time of the last interaction, used for timeout */
time_t
obuf_soft_limit_reached_time
;
uint64_t
flags
;
/* Client flags: CLIENT_* macros. */
int
authenticated
;
/* Needed when the default user requires auth. */
int
replstate
;
/* Replication state if this is a slave. */
int
repl_start_cmd_stream_on_ack
;
/* Install slave write handler on first ACK. */
...
...
@@ -1225,7 +1229,7 @@ struct sharedObjectsStruct {
*
time
,
*
pxat
,
*
absttl
,
*
retrycount
,
*
force
,
*
justid
,
*
entriesread
,
*
lastid
,
*
ping
,
*
setid
,
*
keepttl
,
*
load
,
*
createconsumer
,
*
getack
,
*
special_asterick
,
*
special_equals
,
*
default_username
,
*
redacted
,
*
ssubscribebulk
,
*
sunsubscribebulk
,
*
ssubscribebulk
,
*
sunsubscribebulk
,
*
smessagebulk
,
*
select
[
PROTO_SHARED_SELECT_CMDS
],
*
integers
[
OBJ_SHARED_INTEGERS
],
*
mbulkhdr
[
OBJ_SHARED_BULKHDR_LEN
],
/* "*<value>\r\n" */
...
...
@@ -1473,7 +1477,6 @@ struct redisServer {
int
sentinel_mode
;
/* True if this instance is a Sentinel. */
size_t
initial_memory_usage
;
/* Bytes used after initialization. */
int
always_show_logo
;
/* Show logo even for non-stdout logging. */
int
in_script
;
/* Are we inside EVAL? */
int
in_exec
;
/* Are we inside EXEC? */
int
busy_module_yield_flags
;
/* Are we inside a busy module? (triggered by RM_Yield). see BUSY_MODULE_YIELD_ flags. */
const
char
*
busy_module_yield_reply
;
/* When non-null, we are inside RM_Yield. */
...
...
@@ -1584,6 +1587,8 @@ struct redisServer {
struct
malloc_stats
cron_malloc_stats
;
/* sampled in serverCron(). */
redisAtomic
long
long
stat_net_input_bytes
;
/* Bytes read from network. */
redisAtomic
long
long
stat_net_output_bytes
;
/* Bytes written to network. */
redisAtomic
long
long
stat_net_repl_input_bytes
;
/* Bytes read during replication, added to stat_net_input_bytes in 'info'. */
redisAtomic
long
long
stat_net_repl_output_bytes
;
/* Bytes written during replication, added to stat_net_output_bytes in 'info'. */
size_t
stat_current_cow_peak
;
/* Peak size of copy on write bytes. */
size_t
stat_current_cow_bytes
;
/* Copy on write bytes while child is active. */
monotime
stat_current_cow_updated
;
/* Last update time of stat_current_cow_bytes */
...
...
@@ -1884,7 +1889,7 @@ struct redisServer {
/* Scripting */
client
*
script_caller
;
/* The client running script right now, or NULL */
mstime_t
busy_reply_threshold
;
/* Script / module timeout in milliseconds */
int
script_oom
;
/* OOM detected when script
start */
int
pre_command_oom_state
;
/* OOM before command (script?) was
start
ed
*/
int
script_disable_deny_script
;
/* Allow running commands marked "no-script" inside a script. */
/* Lazy free */
int
lazyfree_lazy_eviction
;
...
...
@@ -2258,6 +2263,7 @@ struct redisCommand {
dict
*
subcommands_dict
;
/* A dictionary that holds the subcommands, the key is the subcommand sds name
* (not the fullname), and the value is the redisCommand structure pointer. */
struct
redisCommand
*
parent
;
struct
RedisModuleCommand
*
module_cmd
;
/* A pointer to the module command data (NULL if native command) */
};
struct
redisError
{
...
...
@@ -2586,7 +2592,7 @@ void listElementsRemoved(client *c, robj *key, int where, robj *o, long count, i
void
unwatchAllKeys
(
client
*
c
);
void
initClientMultiState
(
client
*
c
);
void
freeClientMultiState
(
client
*
c
);
void
queueMultiCommand
(
client
*
c
);
void
queueMultiCommand
(
client
*
c
,
uint64_t
cmd_flags
);
size_t
multiStateMemOverhead
(
client
*
c
);
void
touchWatchedKey
(
redisDb
*
db
,
robj
*
key
);
int
isWatchedKeyExpired
(
client
*
c
);
...
...
@@ -2988,7 +2994,7 @@ void pubsubUnsubscribeShardChannels(robj **channels, unsigned int count);
int
pubsubUnsubscribeAllPatterns
(
client
*
c
,
int
notify
);
int
pubsubPublishMessage
(
robj
*
channel
,
robj
*
message
,
int
sharded
);
int
pubsubPublishMessageAndPropagateToCluster
(
robj
*
channel
,
robj
*
message
,
int
sharded
);
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
);
void
addReplyPubsubMessage
(
client
*
c
,
robj
*
channel
,
robj
*
msg
,
robj
*
message_bulk
);
int
serverPubsubSubscriptionCount
();
int
serverPubsubShardSubscriptionCount
();
...
...
@@ -3010,6 +3016,10 @@ sds keyspaceEventsFlagsToString(int flags);
#define DENY_LOADING_CONFIG (1ULL<<6)
/* This config is forbidden during loading. */
#define ALIAS_CONFIG (1ULL<<7)
/* For configs with multiple names, this flag is set on the alias. */
#define MODULE_CONFIG (1ULL<<8)
/* This config is a module config */
#define VOLATILE_CONFIG (1ULL<<9)
/* The config is a reference to the config data and not the config data itself (ex.
* a file name containing more configuration like a tls key). In this case we want
* to apply the configuration change even if the new config value is the same as
* the old. */
#define INTEGER_CONFIG 0
/* No flags means a simple integer configuration */
#define MEMORY_CONFIG (1<<0)
/* Indicates if this value can be loaded as a memory value */
...
...
@@ -3191,6 +3201,9 @@ void sha1hex(char *digest, char *script, size_t len);
unsigned
long
evalMemory
();
dict
*
evalScriptsDict
();
unsigned
long
evalScriptsMemory
();
uint64_t
evalGetCommandFlags
(
client
*
c
,
uint64_t
orig_flags
);
uint64_t
fcallGetCommandFlags
(
client
*
c
,
uint64_t
orig_flags
);
int
isInsideYieldingLongCommand
();
typedef
struct
luaScript
{
uint64_t
flags
;
...
...
src/syscheck.c
0 → 100644
View file @
a5d2df34
/*
* Copyright (c) 2022, Redis Ltd.
* Copyright (c) 2016, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "fmacros.h"
#include "config.h"
#include "syscheck.h"
#include "sds.h"
#include "anet.h"
#include <time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#ifdef __linux__
#include <sys/mman.h>
#endif
#ifdef __linux__
static
sds
read_sysfs_line
(
char
*
path
)
{
char
buf
[
256
];
FILE
*
f
=
fopen
(
path
,
"r"
);
if
(
!
f
)
return
NULL
;
if
(
!
fgets
(
buf
,
sizeof
(
buf
),
f
))
{
fclose
(
f
);
return
NULL
;
}
fclose
(
f
);
sds
res
=
sdsnew
(
buf
);
res
=
sdstrim
(
res
,
"
\n
"
);
return
res
;
}
/* Verify our clokcsource implementation doesn't go through a system call (uses vdso).
* Going through a system call to check the time degrades Redis performance. */
static
int
checkClocksource
(
sds
*
error_msg
)
{
unsigned
long
test_time_us
,
system_hz
;
struct
timespec
ts
;
unsigned
long
long
start_us
;
struct
rusage
ru_start
,
ru_end
;
system_hz
=
sysconf
(
_SC_CLK_TCK
);
if
(
getrusage
(
RUSAGE_SELF
,
&
ru_start
)
!=
0
)
return
0
;
if
(
clock_gettime
(
CLOCK_MONOTONIC
,
&
ts
)
<
0
)
{
return
0
;
}
start_us
=
(
ts
.
tv_sec
*
1000000
+
ts
.
tv_nsec
/
1000
);
/* clock_gettime() busy loop of 5 times system tick (for a system_hz of 100 this is 50ms)
* Using system_hz is required to ensure accurate measurements from getrusage().
* If our clocksource is configured correctly (vdso) this will result in no system calls.
* If our clocksource is inefficient it'll waste most of the busy loop in the kernel. */
test_time_us
=
5
*
1000000
/
system_hz
;
while
(
1
)
{
unsigned
long
long
d
;
if
(
clock_gettime
(
CLOCK_MONOTONIC
,
&
ts
)
<
0
)
return
0
;
d
=
(
ts
.
tv_sec
*
1000000
+
ts
.
tv_nsec
/
1000
)
-
start_us
;
if
(
d
>=
test_time_us
)
break
;
}
if
(
getrusage
(
RUSAGE_SELF
,
&
ru_end
)
!=
0
)
return
0
;
long
long
stime_us
=
(
ru_end
.
ru_stime
.
tv_sec
*
1000000
+
ru_end
.
ru_stime
.
tv_usec
)
-
(
ru_start
.
ru_stime
.
tv_sec
*
1000000
+
ru_start
.
ru_stime
.
tv_usec
);
long
long
utime_us
=
(
ru_end
.
ru_utime
.
tv_sec
*
1000000
+
ru_end
.
ru_utime
.
tv_usec
)
-
(
ru_start
.
ru_utime
.
tv_sec
*
1000000
+
ru_start
.
ru_utime
.
tv_usec
);
/* If more than 10% of the process time was in system calls we probably have an inefficient clocksource, print a warning */
if
(
stime_us
*
10
>
stime_us
+
utime_us
)
{
sds
avail
=
read_sysfs_line
(
"/sys/devices/system/clocksource/clocksource0/available_clocksource"
);
sds
curr
=
read_sysfs_line
(
"/sys/devices/system/clocksource/clocksource0/current_clocksource"
);
*
error_msg
=
sdscatprintf
(
sdsempty
(),
"Slow system clocksource detected. This can result in degraded performance. "
"Consider changing the system's clocksource. "
"Current clocksource: %s. Available clocksources: %s. "
"For example: run the command 'echo tsc > /sys/devices/system/clocksource/clocksource0/current_clocksource' as root. "
"To permanently change the system's clocksource you'll need to set the 'clocksource=' kernel command line parameter."
,
curr
?
curr
:
""
,
avail
?
avail
:
""
);
sdsfree
(
avail
);
sdsfree
(
curr
);
return
-
1
;
}
else
{
return
1
;
}
}
/* Verify we're not using the `xen` clocksource. The xen hypervisor's default clocksource is slow and affects
* Redis's performance. This has been measured on ec2 xen based instances. ec2 recommends using the non-default
* tsc clock source for these instances. */
int
checkXenClocksource
(
sds
*
error_msg
)
{
sds
curr
=
read_sysfs_line
(
"/sys/devices/system/clocksource/clocksource0/current_clocksource"
);
int
res
=
1
;
if
(
curr
==
NULL
)
{
res
=
0
;
}
else
if
(
strcmp
(
curr
,
"xen"
)
==
0
)
{
*
error_msg
=
sdsnew
(
"Your system is configured to use the 'xen' clocksource which might lead to degraded performance. "
"Check the result of the [slow-clocksource] system check: run 'redis-server --check-system' to check if "
"the system's clocksource isn't degrading performance."
);
res
=
-
1
;
}
sdsfree
(
curr
);
return
res
;
}
/* Verify overcommit is enabled.
* When overcommit memory is disabled Linux will kill the forked child of a background save
* if we don't have enough free memory to satisfy double the current memory usage even though
* the forked child uses copy-on-write to reduce its actual memory usage. */
int
checkOvercommit
(
sds
*
error_msg
)
{
FILE
*
fp
=
fopen
(
"/proc/sys/vm/overcommit_memory"
,
"r"
);
char
buf
[
64
];
if
(
!
fp
)
return
-
1
;
if
(
fgets
(
buf
,
64
,
fp
)
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
fclose
(
fp
);
if
(
atoi
(
buf
))
{
*
error_msg
=
sdsnew
(
"WARNING 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 "
"command 'sysctl vm.overcommit_memory=1' for this to take effect."
);
return
-
1
;
}
else
{
return
1
;
}
}
/* Make sure transparent huge pages aren't always enabled. When they are this can cause copy-on-write logic
* to consume much more memory and reduce performance during forks. */
int
checkTHPEnabled
(
sds
*
error_msg
)
{
char
buf
[
1024
];
FILE
*
fp
=
fopen
(
"/sys/kernel/mm/transparent_hugepage/enabled"
,
"r"
);
if
(
!
fp
)
return
0
;
if
(
fgets
(
buf
,
sizeof
(
buf
),
fp
)
==
NULL
)
{
fclose
(
fp
);
return
0
;
}
fclose
(
fp
);
if
(
strstr
(
buf
,
"[always]"
)
!=
NULL
)
{
*
error_msg
=
sdsnew
(
"You have Transparent Huge Pages (THP) support enabled in your kernel. "
"This will create latency and memory usage issues with Redis. "
"To fix this issue run the command 'echo madvise > /sys/kernel/mm/transparent_hugepage/enabled' as root, "
"and add it to your /etc/rc.local in order to retain the setting after a reboot. "
"Redis must be restarted after THP is disabled (set to 'madvise' or 'never')."
);
return
-
1
;
}
else
{
return
1
;
}
}
#ifdef __arm64__
/* Get size in kilobytes of the Shared_Dirty pages of the calling process for the
* memory map corresponding to the provided address, or -1 on error. */
static
int
smapsGetSharedDirty
(
unsigned
long
addr
)
{
int
ret
,
in_mapping
=
0
,
val
=
-
1
;
unsigned
long
from
,
to
;
char
buf
[
64
];
FILE
*
f
;
f
=
fopen
(
"/proc/self/smaps"
,
"r"
);
if
(
!
f
)
return
-
1
;
while
(
1
)
{
if
(
!
fgets
(
buf
,
sizeof
(
buf
),
f
))
break
;
ret
=
sscanf
(
buf
,
"%lx-%lx"
,
&
from
,
&
to
);
if
(
ret
==
2
)
in_mapping
=
from
<=
addr
&&
addr
<
to
;
if
(
in_mapping
&&
!
memcmp
(
buf
,
"Shared_Dirty:"
,
13
))
{
sscanf
(
buf
,
"%*s %d"
,
&
val
);
/* If parsing fails, we remain with val == -1 */
break
;
}
}
fclose
(
f
);
return
val
;
}
/* Older arm64 Linux kernels have a bug that could lead to data corruption
* during background save in certain scenarios. This function checks if the
* kernel is affected.
* The bug was fixed in commit ff1712f953e27f0b0718762ec17d0adb15c9fd0b
* titled: "arm64: pgtable: Ensure dirty bit is preserved across pte_wrprotect()"
*/
int
checkLinuxMadvFreeForkBug
(
sds
*
error_msg
)
{
int
ret
,
pipefd
[
2
]
=
{
-
1
,
-
1
};
pid_t
pid
;
char
*
p
=
NULL
,
*
q
;
int
res
=
1
;
long
page_size
=
sysconf
(
_SC_PAGESIZE
);
long
map_size
=
3
*
page_size
;
/* Create a memory map that's in our full control (not one used by the allocator). */
p
=
mmap
(
NULL
,
map_size
,
PROT_READ
,
MAP_ANONYMOUS
|
MAP_PRIVATE
,
-
1
,
0
);
if
(
p
==
MAP_FAILED
)
{
return
0
;
}
q
=
p
+
page_size
;
/* Split the memory map in 3 pages by setting their protection as RO|RW|RO to prevent
* Linux from merging this memory map with adjacent VMAs. */
ret
=
mprotect
(
q
,
page_size
,
PROT_READ
|
PROT_WRITE
);
if
(
ret
<
0
)
{
res
=
0
;
goto
exit
;
}
/* Write to the page once to make it resident */
*
(
volatile
char
*
)
q
=
0
;
/* Tell the kernel that this page is free to be reclaimed. */
#ifndef MADV_FREE
#define MADV_FREE 8
#endif
ret
=
madvise
(
q
,
page_size
,
MADV_FREE
);
if
(
ret
<
0
)
{
/* MADV_FREE is not available on older kernels that are presumably
* not affected. */
if
(
errno
==
EINVAL
)
goto
exit
;
res
=
0
;
goto
exit
;
}
/* Write to the page after being marked for freeing, this is supposed to take
* ownership of that page again. */
*
(
volatile
char
*
)
q
=
0
;
/* Create a pipe for the child to return the info to the parent. */
ret
=
anetPipe
(
pipefd
,
0
,
0
);
if
(
ret
<
0
)
{
res
=
0
;
goto
exit
;
}
/* Fork the process. */
pid
=
fork
();
if
(
pid
<
0
)
{
res
=
0
;
goto
exit
;
}
else
if
(
!
pid
)
{
/* Child: check if the page is marked as dirty, page_size in kb.
* A value of 0 means the kernel is affected by the bug. */
ret
=
smapsGetSharedDirty
((
unsigned
long
)
q
);
if
(
!
ret
)
res
=
-
1
;
else
if
(
ret
==
-
1
)
/* Failed to read */
res
=
0
;
ret
=
write
(
pipefd
[
1
],
&
res
,
sizeof
(
res
));
/* Assume success, ignore return value*/
exit
(
0
);
}
else
{
/* Read the result from the child. */
ret
=
read
(
pipefd
[
0
],
&
res
,
sizeof
(
res
));
if
(
ret
<
0
)
{
res
=
0
;
}
/* Reap the child pid. */
waitpid
(
pid
,
NULL
,
0
);
}
exit:
/* Cleanup */
if
(
pipefd
[
0
]
!=
-
1
)
close
(
pipefd
[
0
]);
if
(
pipefd
[
1
]
!=
-
1
)
close
(
pipefd
[
1
]);
if
(
p
!=
NULL
)
munmap
(
p
,
map_size
);
if
(
res
==
-
1
)
*
error_msg
=
sdsnew
(
"Your kernel has a bug that could lead to data corruption during background save. "
"Please upgrade to the latest stable kernel."
);
return
res
;
}
#endif
/* __arm64__ */
#endif
/* __linux__ */
/*
* Standard system check interface:
* Each check has a name `name` and a functions pointer `check_fn`.
* `check_fn` should return:
* -1 in case the check fails.
* 1 in case the check passes.
* 0 in case the check could not be completed (usually because of some unexpected failed system call).
* When (and only when) the check fails and -1 is returned and error description is places in a new sds pointer to by
* the single `sds*` argument to `check_fn`. This message should be freed by the caller via `sdsfree()`.
*/
typedef
struct
{
const
char
*
name
;
int
(
*
check_fn
)(
sds
*
);
}
check
;
check
checks
[]
=
{
#ifdef __linux__
{.
name
=
"slow-clocksource"
,
.
check_fn
=
checkClocksource
},
{.
name
=
"xen-clocksource"
,
.
check_fn
=
checkXenClocksource
},
{.
name
=
"overcommit"
,
.
check_fn
=
checkOvercommit
},
{.
name
=
"THP"
,
.
check_fn
=
checkTHPEnabled
},
#ifdef __arm64__
{.
name
=
"madvise-free-fork-bug"
,
.
check_fn
=
checkLinuxMadvFreeForkBug
},
#endif
#endif
{.
name
=
NULL
,
.
check_fn
=
NULL
}
};
/* Performs various system checks, returns 0 if any check fails, 1 otherwise. */
int
syscheck
(
void
)
{
check
*
cur_check
=
checks
;
int
ret
=
1
;
sds
err_msg
;
while
(
cur_check
->
check_fn
)
{
int
res
=
cur_check
->
check_fn
(
&
err_msg
);
printf
(
"[%s]..."
,
cur_check
->
name
);
if
(
res
==
0
)
{
printf
(
"skipped
\n
"
);
}
else
if
(
res
==
1
)
{
printf
(
"OK
\n
"
);
}
else
{
printf
(
"WARNING:
\n
"
);
printf
(
"%s
\n
"
,
err_msg
);
sdsfree
(
err_msg
);
ret
=
0
;
}
cur_check
++
;
}
return
ret
;
}
src/syscheck.h
0 → 100644
View file @
a5d2df34
/*
* Copyright (c) 2022, Redis Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __SYSCHECK_H
#define __SYSCHECK_H
#include "sds.h"
#include "config.h"
int
syscheck
(
void
);
#ifdef __linux__
int
checkXenClocksource
(
sds
*
error_msg
);
int
checkTHPEnabled
(
sds
*
error_msg
);
int
checkOvercommit
(
sds
*
error_msg
);
#ifdef __arm64__
int
checkLinuxMadvFreeForkBug
(
sds
*
error_msg
);
#endif
#endif
#endif
src/t_list.c
View file @
a5d2df34
...
...
@@ -679,7 +679,8 @@ void lposCommand(client *c) {
return
;
if
(
rank
==
0
)
{
addReplyError
(
c
,
"RANK can't be zero: use 1 to start from "
"the first match, 2 from the second, ..."
);
"the first match, 2 from the second ... "
"or use negative to start from the end of the list"
);
return
;
}
}
else
if
(
!
strcasecmp
(
opt
,
"COUNT"
)
&&
moreargs
)
{
...
...
@@ -1197,12 +1198,12 @@ void lmpopGenericCommand(client *c, int numkeys_idx, int is_block) {
}
}
/* LMPOP numkeys [<key> ...] LEFT|RIGHT [COUNT count] */
/* LMPOP numkeys
<key>
[<key> ...]
(
LEFT|RIGHT
)
[COUNT count] */
void
lmpopCommand
(
client
*
c
)
{
lmpopGenericCommand
(
c
,
1
,
0
);
}
/* BLMPOP timeout numkeys [<key> ...] LEFT|RIGHT [COUNT count] */
/* BLMPOP timeout numkeys
<key>
[<key> ...]
(
LEFT|RIGHT
)
[COUNT count] */
void
blmpopCommand
(
client
*
c
)
{
lmpopGenericCommand
(
c
,
2
,
1
);
}
src/t_set.c
View file @
a5d2df34
...
...
@@ -203,7 +203,7 @@ sds setTypeNextObject(setTypeIterator *si) {
* The caller provides both pointers to be populated with the right
* object. The return value of the function is the object->encoding
* field of the object and is used by the caller to check if the
* int64_t pointer or the
redis object
pointer was populated.
* int64_t pointer or the
sds
pointer was populated.
*
* Note that both the sdsele and llele pointers should be passed and cannot
* be NULL since the function will try to defensively populate the non
...
...
@@ -805,7 +805,7 @@ void srandmemberWithCountCommand(client *c) {
}
}
/* SRANDMEMBER [<count>] */
/* SRANDMEMBER
<key>
[<count>] */
void
srandmemberCommand
(
client
*
c
)
{
robj
*
set
;
sds
ele
;
...
...
@@ -850,7 +850,7 @@ int qsortCompareSetsByRevCardinality(const void *s1, const void *s2) {
return
0
;
}
/* SINTER / SINTERSTORE / SINTERCARD
/* SINTER /
SMEMBERS /
SINTERSTORE / SINTERCARD
*
* 'cardinality_only' work for SINTERCARD, only return the cardinality
* with minimum processing and memory overheads.
...
...
@@ -1055,6 +1055,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
sds
ele
;
int
j
,
cardinality
=
0
;
int
diff_algo
=
1
;
int
sameset
=
0
;
for
(
j
=
0
;
j
<
setnum
;
j
++
)
{
robj
*
setobj
=
lookupKeyRead
(
c
->
db
,
setkeys
[
j
]);
...
...
@@ -1067,6 +1068,9 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
return
;
}
sets
[
j
]
=
setobj
;
if
(
j
>
0
&&
sets
[
0
]
==
sets
[
j
])
{
sameset
=
1
;
}
}
/* Select what DIFF algorithm to use.
...
...
@@ -1078,7 +1082,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
* the sets.
*
* We compute what is the best bet with the current input here. */
if
(
op
==
SET_OP_DIFF
&&
sets
[
0
])
{
if
(
op
==
SET_OP_DIFF
&&
sets
[
0
]
&&
!
sameset
)
{
long
long
algo_one_work
=
0
,
algo_two_work
=
0
;
for
(
j
=
0
;
j
<
setnum
;
j
++
)
{
...
...
@@ -1120,6 +1124,8 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
}
setTypeReleaseIterator
(
si
);
}
}
else
if
(
op
==
SET_OP_DIFF
&&
sameset
)
{
/* At least one of the sets is the same one (same key) as the first one, result must be empty. */
}
else
if
(
op
==
SET_OP_DIFF
&&
sets
[
0
]
&&
diff_algo
==
1
)
{
/* DIFF Algorithm 1:
*
...
...
src/t_stream.c
View file @
a5d2df34
...
...
@@ -401,7 +401,7 @@ void streamGetEdgeID(stream *s, int first, int skip_tombstones, streamID *edge_i
streamID
min_id
=
{
0
,
0
},
max_id
=
{
UINT64_MAX
,
UINT64_MAX
};
*
edge_id
=
first
?
max_id
:
min_id
;
}
streamIteratorStop
(
&
si
);
}
/* Adds a new item into the stream 's' having the specified number of
...
...
@@ -946,7 +946,7 @@ static int streamParseAddOrTrimArgsOrReply(client *c, streamAddTrimArgs *args, i
}
args
->
approx_trim
=
0
;
char
*
next
=
c
->
argv
[
i
+
1
]
->
ptr
;
/* Check for the form MINID ~ <id>
|<age>.
*/
/* Check for the form MINID ~ <id> */
if
(
moreargs
>=
2
&&
next
[
0
]
==
'~'
&&
next
[
1
]
==
'\0'
)
{
args
->
approx_trim
=
1
;
i
++
;
...
...
src/t_zset.c
View file @
a5d2df34
...
...
@@ -927,7 +927,7 @@ int zzlLexValueLteMax(unsigned char *p, zlexrangespec *spec) {
}
/* Returns if there is a part of the zset is in range. Should only be used
* internally by zzlFirstInRange and zzlLastInRange. */
* internally by zzlFirstIn
Lex
Range and zzlLastIn
Lex
Range. */
int
zzlIsInLexRange
(
unsigned
char
*
zl
,
zlexrangespec
*
range
)
{
unsigned
char
*
p
;
...
...
@@ -1186,9 +1186,10 @@ void zsetConvert(robj *zobj, int encoding) {
zs
->
zsl
=
zslCreate
();
eptr
=
lpSeek
(
zl
,
0
);
serverAssertWithInfo
(
NULL
,
zobj
,
eptr
!=
NULL
);
sptr
=
lpNext
(
zl
,
eptr
);
serverAssertWithInfo
(
NULL
,
zobj
,
sptr
!=
NULL
);
if
(
eptr
!=
NULL
)
{
sptr
=
lpNext
(
zl
,
eptr
);
serverAssertWithInfo
(
NULL
,
zobj
,
sptr
!=
NULL
);
}
while
(
eptr
!=
NULL
)
{
score
=
zzlGetScore
(
sptr
);
...
...
@@ -4041,7 +4042,7 @@ void blockingGenericZpopCommand(client *c, robj **keys, int numkeys, int where,
/* If the keys do not exist we must block */
struct
blockPos
pos
=
{
where
};
blockForKeys
(
c
,
BLOCKED_ZSET
,
c
->
argv
+
1
,
c
->
argc
-
2
,
count
,
timeout
,
NULL
,
&
pos
,
NULL
);
blockForKeys
(
c
,
BLOCKED_ZSET
,
keys
,
numkeys
,
count
,
timeout
,
NULL
,
&
pos
,
NULL
);
}
// BZPOPMIN key [key ...] timeout
...
...
@@ -4054,7 +4055,7 @@ void bzpopmaxCommand(client *c) {
blockingGenericZpopCommand
(
c
,
c
->
argv
+
1
,
c
->
argc
-
2
,
ZSET_MAX
,
c
->
argc
-
1
,
-
1
,
0
,
0
);
}
static
void
z
a
rndmemberReplyWithListpack
(
client
*
c
,
unsigned
int
count
,
listpackEntry
*
keys
,
listpackEntry
*
vals
)
{
static
void
zr
a
ndmemberReplyWithListpack
(
client
*
c
,
unsigned
int
count
,
listpackEntry
*
keys
,
listpackEntry
*
vals
)
{
for
(
unsigned
long
i
=
0
;
i
<
count
;
i
++
)
{
if
(
vals
&&
c
->
resp
>
2
)
addReplyArrayLen
(
c
,
2
);
...
...
@@ -4135,7 +4136,7 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
sample_count
=
count
>
limit
?
limit
:
count
;
count
-=
sample_count
;
lpRandomPairs
(
zsetobj
->
ptr
,
sample_count
,
keys
,
vals
);
z
a
rndmemberReplyWithListpack
(
c
,
sample_count
,
keys
,
vals
);
zr
a
ndmemberReplyWithListpack
(
c
,
sample_count
,
keys
,
vals
);
}
zfree
(
keys
);
zfree
(
vals
);
...
...
@@ -4234,7 +4235,7 @@ void zrandmemberWithCountCommand(client *c, long l, int withscores) {
if
(
withscores
)
vals
=
zmalloc
(
sizeof
(
listpackEntry
)
*
count
);
serverAssert
(
lpRandomPairsUnique
(
zsetobj
->
ptr
,
count
,
keys
,
vals
)
==
count
);
z
a
rndmemberReplyWithListpack
(
c
,
count
,
keys
,
vals
);
zr
a
ndmemberReplyWithListpack
(
c
,
count
,
keys
,
vals
);
zfree
(
keys
);
zfree
(
vals
);
zuiClearIterator
(
&
src
);
...
...
src/tracking.c
View file @
a5d2df34
...
...
@@ -295,7 +295,7 @@ void sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {
}
else
if
(
using_redirection
&&
c
->
flags
&
CLIENT_PUBSUB
)
{
/* We use a static object to speedup things, however we assume
* that addReplyPubsubMessage() will not take a reference. */
addReplyPubsubMessage
(
c
,
TrackingChannelName
,
NULL
);
addReplyPubsubMessage
(
c
,
TrackingChannelName
,
NULL
,
shared
.
messagebulk
);
}
else
{
/* If are here, the client is not using RESP3, nor is
* redirecting to another client. We can't send anything to
...
...
src/util.c
View file @
a5d2df34
...
...
@@ -522,7 +522,7 @@ int string2ld(const char *s, size_t slen, long double *dp) {
if
(
isspace
(
buf
[
0
])
||
eptr
[
0
]
!=
'\0'
||
(
size_t
)(
eptr
-
buf
)
!=
slen
||
(
errno
==
ERANGE
&&
(
value
==
HUGE_VAL
||
value
==
-
HUGE_VAL
||
value
==
0
))
||
(
value
==
HUGE_VAL
||
value
==
-
HUGE_VAL
||
fpclassify
(
value
)
==
FP_ZERO
))
||
errno
==
EINVAL
||
isnan
(
value
))
return
0
;
...
...
@@ -546,7 +546,7 @@ int string2d(const char *s, size_t slen, double *dp) {
isspace
(((
const
char
*
)
s
)[
0
])
||
(
size_t
)(
eptr
-
(
char
*
)
s
)
!=
slen
||
(
errno
==
ERANGE
&&
(
*
dp
==
HUGE_VAL
||
*
dp
==
-
HUGE_VAL
||
*
dp
==
0
))
||
(
*
dp
==
HUGE_VAL
||
*
dp
==
-
HUGE_VAL
||
fpclassify
(
*
dp
)
==
FP_ZERO
))
||
isnan
(
*
dp
))
return
0
;
return
1
;
...
...
src/ziplist.c
View file @
a5d2df34
...
...
@@ -255,7 +255,7 @@
/* Return the pointer to the last byte of a ziplist, which is, the
* end of ziplist FF entry. */
#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-
1
)
#define ZIPLIST_ENTRY_END(zl) ((zl)+intrev32ifbe(ZIPLIST_BYTES(zl))-
ZIPLIST_END_SIZE
)
/* Increment the number of items field in the ziplist header. Note that this
* macro should never overflow the unsigned 16 bit integer, since entries are
...
...
src/zmalloc.c
View file @
a5d2df34
...
...
@@ -55,6 +55,8 @@ void zlibc_free(void *ptr) {
#include "zmalloc.h"
#include "atomicvar.h"
#define UNUSED(x) ((void)(x))
#ifdef HAVE_MALLOC_SIZE
#define PREFIX_SIZE (0)
#define ASSERT_NO_SIZE_OVERFLOW(sz)
...
...
@@ -395,35 +397,58 @@ void zmadvise_dontneed(void *ptr) {
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
size_t
zmalloc_get_rss
(
void
)
{
int
page
=
sysconf
(
_SC_PAGESIZE
);
size_t
rss
;
/* Get the i'th field from "/proc/self/stats" note i is 1 based as appears in the 'proc' man page */
int
get_proc_stat_ll
(
int
i
,
long
long
*
res
)
{
#if defined(HAVE_PROC_STAT)
char
buf
[
4096
];
char
filename
[
256
];
int
fd
,
count
;
int
fd
,
l
;
char
*
p
,
*
x
;
snprintf
(
filename
,
256
,
"/proc/%ld/stat"
,(
long
)
getpid
());
if
((
fd
=
open
(
filename
,
O_RDONLY
))
==
-
1
)
return
0
;
if
(
read
(
fd
,
buf
,
4096
)
<=
0
)
{
if
((
fd
=
open
(
"/proc/self/stat"
,
O_RDONLY
))
==
-
1
)
return
0
;
if
((
l
=
read
(
fd
,
buf
,
sizeof
(
buf
)
-
1
))
<=
0
)
{
close
(
fd
);
return
0
;
}
close
(
fd
);
buf
[
l
]
=
'\0'
;
if
(
buf
[
l
-
1
]
==
'\n'
)
buf
[
l
-
1
]
=
'\0'
;
p
=
buf
;
count
=
23
;
/* RSS is the 24th field in /proc/<pid>/stat */
while
(
p
&&
count
--
)
{
p
=
strchr
(
p
,
' '
);
/* Skip pid and process name (surrounded with parentheses) */
p
=
strrchr
(
buf
,
')'
);
if
(
!
p
)
return
0
;
p
++
;
while
(
*
p
==
' '
)
p
++
;
if
(
*
p
==
'\0'
)
return
0
;
i
-=
3
;
if
(
i
<
0
)
return
0
;
while
(
p
&&
i
--
)
{
p
=
strchr
(
p
,
' '
);
if
(
p
)
p
++
;
else
return
0
;
}
if
(
!
p
)
return
0
;
x
=
strchr
(
p
,
' '
);
if
(
!
x
)
return
0
;
*
x
=
'\0'
;
if
(
x
)
*
x
=
'\0'
;
rss
=
strtoll
(
p
,
NULL
,
10
);
*
res
=
strtoll
(
p
,
&
x
,
10
);
if
(
*
x
!=
'\0'
)
return
0
;
return
1
;
#else
UNUSED
(
i
);
UNUSED
(
res
);
return
0
;
#endif
}
#if defined(HAVE_PROC_STAT)
size_t
zmalloc_get_rss
(
void
)
{
int
page
=
sysconf
(
_SC_PAGESIZE
);
long
long
rss
;
/* RSS is the 24th field in /proc/<pid>/stat */
if
(
!
get_proc_stat_ll
(
24
,
&
rss
))
return
0
;
rss
*=
page
;
return
rss
;
}
...
...
@@ -492,6 +517,23 @@ size_t zmalloc_get_rss(void) {
return
0L
;
}
#elif defined(__HAIKU__)
#include <OS.h>
size_t
zmalloc_get_rss
(
void
)
{
area_info
info
;
thread_info
th
;
size_t
rss
=
0
;
ssize_t
cookie
=
0
;
if
(
get_thread_info
(
find_thread
(
0
),
&
th
)
!=
B_OK
)
return
0
;
while
(
get_next_area_info
(
th
.
team
,
&
cookie
,
&
info
)
==
B_OK
)
rss
+=
info
.
ram_size
;
return
rss
;
}
#elif defined(HAVE_PSINFO)
#include <unistd.h>
#include <sys/procfs.h>
...
...
@@ -731,7 +773,6 @@ size_t zmalloc_get_memory_size(void) {
}
#ifdef REDIS_TEST
#define UNUSED(x) ((void)(x))
int
zmalloc_test
(
int
argc
,
char
**
argv
,
int
flags
)
{
void
*
ptr
;
...
...
src/zmalloc.h
View file @
a5d2df34
...
...
@@ -136,6 +136,8 @@ size_t zmalloc_usable_size(void *ptr);
#define zmalloc_usable_size(p) zmalloc_size(p)
#endif
int
get_proc_stat_ll
(
int
i
,
long
long
*
res
);
#ifdef REDIS_TEST
int
zmalloc_test
(
int
argc
,
char
**
argv
,
int
flags
);
#endif
...
...
tests/README.md
View file @
a5d2df34
...
...
@@ -38,7 +38,7 @@ The following compatibility and capability tags are currently used:
|
`external:skip`
| Not compatible with external servers. |
|
`cluster:skip`
| Not compatible with
`--cluster-mode`
. |
|
`large-memory`
| Test that requires more than 100mb |
|
`tls:skip`
| Not c
a
mpatible with
`--tls`
. |
|
`tls:skip`
| Not c
o
mpatible with
`--tls`
. |
|
`needs:repl`
| Uses replication and needs to be able to
`SYNC`
from server. |
|
`needs:debug`
| Uses the
`DEBUG`
command or other debugging focused commands (like
`OBJECT`
). |
|
`needs:pfdebug`
| Uses the
`PFDEBUG`
command. |
...
...
tests/assets/default.conf
View file @
a5d2df34
...
...
@@ -13,9 +13,8 @@ databases 16
latency
-
monitor
-
threshold
1
repl
-
diskless
-
sync
-
delay
0
# Note the infrastructure in server.tcl uses a dict, we can't provide several save directives
save
900
1
save
300
10
save
60
10000
rdbcompression
yes
dbfilename
dump
.
rdb
...
...
tests/cluster/tests/25-pubsubshard-slot-migration.tcl
View file @
a5d2df34
...
...
@@ -29,7 +29,7 @@ test "Migrate a slot, verify client receives sunsubscribe on primary serving the
# Verify subscribe is still valid, able to receive messages.
$nodefrom
(
link
)
spublish $channelname hello
assert_equal
{
message mychannel hello
}
[
$subscribeclient
read
]
assert_equal
{
s
message mychannel hello
}
[
$subscribeclient
read
]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot $slot node $nodeto
(
id
)]
...
...
@@ -66,7 +66,7 @@ test "Client subscribes to multiple channels, migrate a slot, verify client rece
# Verify subscribe is still valid, able to receive messages.
$nodefrom
(
link
)
spublish $channelname hello
assert_equal
{
message ch3 hello
}
[
$subscribeclient
read
]
assert_equal
{
s
message ch3 hello
}
[
$subscribeclient
read
]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot $slot node $nodeto
(
id
)]
...
...
@@ -82,7 +82,7 @@ test "Client subscribes to multiple channels, migrate a slot, verify client rece
# Verify the client is still connected and receives message from the other channel.
set msg
[
$subscribeclient
read
]
assert
{
"message"
eq
[
lindex $msg 0
]}
assert
{
"
s
message"
eq
[
lindex $msg 0
]}
assert
{
$anotherchannelname
eq
[
lindex $msg 1
]}
assert
{
"hello"
eq
[
lindex $msg 2
]}
...
...
@@ -114,7 +114,7 @@ test "Migrate a slot, verify client receives sunsubscribe on replica serving the
# Verify subscribe is still valid, able to receive messages.
$nodefrom
(
link
)
spublish $channelname hello
assert_equal
{
message mychannel1 hello
}
[
$subscribeclient
read
]
assert_equal
{
s
message mychannel1 hello
}
[
$subscribeclient
read
]
assert_equal
{
OK
}
[
$nodefrom
(
link
)
cluster setslot $slot node $nodeto
(
id
)]
assert_equal
{
OK
}
[
$nodeto
(
link
)
cluster setslot $slot node $nodeto
(
id
)]
...
...
tests/instances.tcl
View file @
a5d2df34
...
...
@@ -441,7 +441,7 @@ proc run_tests {} {
file delete $::leaked_fds_file
}
if
{[
llength $::run_matching
]
!= 0 &&
[
search_pattern_list $test $::run_matching true
]
== -1
}
{
if
{[
llength $::run_matching
]
!= 0 &&
!
[
search_pattern_list $test $::run_matching true
]}
{
continue
}
if
{[
file isdirectory $test
]}
continue
...
...
@@ -699,4 +699,4 @@ proc redis_client {type id} {
proc redis_client_by_addr
{
host port
}
{
set client
[
redis $host $port 0 $::tls
]
return $client
}
\ No newline at end of file
}
tests/integration/aof.tcl
View file @
a5d2df34
...
...
@@ -632,4 +632,50 @@ tags {"aof external:skip"} {
}
result
assert_match
"*Failed to truncate AOF*to timestamp*because it is not the last file*"
$result
}
start_server
{
overrides
{
appendonly yes appendfsync always
}}
{
test
{
FLUSHDB / FLUSHALL should persist in AOF
}
{
set aof
[
get_last_incr_aof_path r
]
r set key value
r flushdb
r set key value2
r flushdb
# DB is empty
r flushdb
r flushdb
r flushdb
r set key value
r flushall
r set key value2
r flushall
# DBs are empty.
r flushall
r flushall
r flushall
# Assert that each FLUSHDB command is persisted even the DB is empty.
# Assert that each FLUSHALL command is persisted even the DBs are empty.
assert_aof_content $aof
{
{
select *
}
{
set key value
}
{
flushdb
}
{
set key value2
}
{
flushdb
}
{
flushdb
}
{
flushdb
}
{
flushdb
}
{
set key value
}
{
flushall
}
{
set key value2
}
{
flushall
}
{
flushall
}
{
flushall
}
{
flushall
}
}
}
}
}
tests/integration/logging.tcl
View file @
a5d2df34
...
...
@@ -9,7 +9,7 @@ if {$system_name eq {darwin}} {
}
elseif
{
$system
_name eq
{
linux
}}
{
# Avoid the test on libmusl, which does not support backtrace
set ldd
[
exec ldd src/redis-server
]
if
{
!
[
string match
{
*libc.musl*
}
$ldd
]}
{
if
{
!
[
string match
{
*libc.
*
musl*
}
$ldd
]}
{
set backtrace_supported 1
}
}
...
...
Prev
1
2
3
4
5
6
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment