Compare commits

..
90 changed files with 1132 additions and 3870 deletions
+1 -3
View File
@@ -12,9 +12,7 @@ jobs:
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
./runtest --clients 2 --verbose
build-ubuntu-old:
runs-on: ubuntu-16.04
-48
View File
@@ -1,48 +0,0 @@
name: Daily
on:
schedule:
- cron: '0 7 * * *'
jobs:
test-jemalloc:
runs-on: ubuntu-latest
timeout-minutes: 1200
steps:
- uses: actions/checkout@v1
- name: make
run: make
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --accurate --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
test-libc-malloc:
runs-on: ubuntu-latest
timeout-minutes: 1200
steps:
- uses: actions/checkout@v1
- name: make
run: make MALLOC=libc
- name: test
run: |
sudo apt-get install tcl8.5
./runtest --accurate --verbose
- name: module api test
run: ./runtest-moduleapi --verbose
test-valgrind:
runs-on: ubuntu-latest
timeout-minutes: 14400
steps:
- uses: actions/checkout@v1
- name: make
run: make valgrind
- name: test
run: |
sudo apt-get install tcl8.5 valgrind -y
./runtest --valgrind --verbose --clients 1
- name: module api test
run: ./runtest-moduleapi --valgrind --verbose --clients 1
+10 -56
View File
@@ -626,23 +626,20 @@ replica-priority 100
# to track the keys fetched by many clients.
#
# For this reason it is possible to configure a maximum fill value for the
# invalidation table. By default it is set to 1M of keys, and once this limit
# is reached, Redis will start to evict keys in the invalidation table
# even if they were not modified, just to reclaim memory: this will in turn
# invalidation table. By default it is set to 10%, and once this limit is
# reached, Redis will start to evict caching slots in the invalidation table
# even if keys are not modified, just to reclaim memory: this will in turn
# force the clients to invalidate the cached values. Basically the table
# maximum size is a trade off between the memory you want to spend server
# maximum fill rate is a trade off between the memory you want to spend server
# side to track information about who cached what, and the ability of clients
# to retain cached objects in memory.
#
# If you set the value to 0, it means there are no limits, and Redis will
# retain as many keys as needed in the invalidation table.
# In the "stats" INFO section, you can find information about the number of
# keys in the invalidation table at every given moment.
# If you set the value to 0, it means there are no limits, and all the 16
# millions of caching slots can be used at the same time. In the "stats"
# INFO section, you can find information about the amount of caching slots
# used at every given moment.
#
# Note: when key tracking is used in broadcasting mode, no memory is used
# in the server side so this setting is useless.
#
# tracking-table-max-keys 1000000
# tracking-table-max-fill 10
################################## SECURITY ###################################
@@ -740,14 +737,6 @@ replica-priority 100
# For more information about ACL configuration please refer to
# the Redis web site at https://redis.io/topics/acl
# ACL LOG
#
# The ACL Log tracks failed commands and authentication events associated
# with ACLs. The ACL Log is useful to troubleshoot failed commands blocked
# by ACLs. The ACL Log is stored in memory. You can reclaim memory with
# ACL LOG RESET. Define the maximum entry length of the ACL Log below.
acllog-max-len 128
# Using an external ACL file
#
# Instead of configuring users here in this file, it is possible to use
@@ -954,20 +943,13 @@ acllog-max-len 128
# In all the above cases the default is to delete objects in a blocking way,
# like if DEL was called. However you can configure each case specifically
# in order to instead release memory in a non-blocking way like if UNLINK
# was called, using the following configuration directives.
# was called, using the following configuration directives:
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
# It is also possible, for the case when to replace the user code DEL calls
# with UNLINK calls is not easy, to modify the default behavior of the DEL
# command to act exactly like UNLINK, using the following configuration
# directive:
lazyfree-lazy-user-del no
################################ THREADED I/O #################################
# Redis is mostly single threaded, however there are certain threaded
@@ -1780,31 +1762,3 @@ rdb-save-incremental-fsync yes
# Maximum number of set/hash/zset/list fields that will be processed from
# the main dictionary scan
# active-defrag-max-scan-fields 1000
# Jemalloc background thread for purging will be enabled by default
jemalloc-bg-thread yes
# It is possible to pin different threads and processes of Redis to specific
# CPUs in your system, in order to maximize the performances of the server.
# This is useful both in order to pin different Redis threads in different
# CPUs, but also in order to make sure that multiple Redis instances running
# in the same host will be pinned to different CPUs.
#
# Normally you can do this using the "taskset" command, however it is also
# possible to this via Redis configuration directly, both in Linux and FreeBSD.
#
# You can pin the server/IO threads, bio threads, aof rewrite child process, and
# the bgsave child process. The syntax to specify the cpu list is the same as
# the taskset command:
#
# Set redis server/io threads to cpu affinity 0,2,4,6:
# server_cpulist 0-7:2
#
# Set bio threads to cpu affinity 1,3:
# bio_cpulist 1,3
#
# Set aof rewrite child process to cpu affinity 8,9,10,11:
# aof_rewrite_cpulist 8-11
#
# Set bgsave child process to cpu affinity 1,10,11
# bgsave_cpulist 1,10-11
+2 -12
View File
@@ -128,14 +128,6 @@ else
ifeq ($(uname_S),DragonFly)
# FreeBSD
FINAL_LIBS+= -lpthread -lexecinfo
else
ifeq ($(uname_S),OpenBSD)
# OpenBSD
FINAL_LIBS+= -lpthread -lexecinfo
else
ifeq ($(uname_S),NetBSD)
# NetBSD
FINAL_LIBS+= -lpthread -lexecinfo
else
# All the other OSes (notably Linux)
FINAL_LDFLAGS+= -rdynamic
@@ -146,8 +138,6 @@ endif
endif
endif
endif
endif
endif
# Include paths to dependencies
FINAL_CFLAGS+= -I../deps/hiredis -I../deps/linenoise -I../deps/lua/src
@@ -216,9 +206,9 @@ endif
REDIS_SERVER_NAME=redis-server
REDIS_SENTINEL_NAME=redis-sentinel
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o timeout.o setcpuaffinity.o
REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o scripting.o bio.o rio.o rand.o memtest.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o gopher.o tracking.o connection.o tls.o sha256.o
REDIS_CLI_NAME=redis-cli
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crcspeed.o crc64.o siphash.o crc16.o
REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o crc64.o siphash.o crc16.o
REDIS_BENCHMARK_NAME=redis-benchmark
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o siphash.o
REDIS_CHECK_RDB_NAME=redis-check-rdb
+20 -74
View File
@@ -30,7 +30,6 @@
#include "server.h"
#include "sha256.h"
#include <fcntl.h>
#include <ctype.h>
/* =============================================================================
* Global state for ACLs
@@ -170,18 +169,6 @@ sds ACLHashPassword(unsigned char *cleartext, size_t len) {
* Low level ACL API
* ==========================================================================*/
/* Return 1 if the specified string contains spaces or null characters.
* We do this for usernames and key patterns for simpler rewriting of
* ACL rules, presentation on ACL list, and to avoid subtle security bugs
* that may arise from parsing the rules in presence of escapes.
* The function returns 0 if the string has no spaces. */
int ACLStringHasSpaces(const char *s, size_t len) {
for (size_t i = 0; i < len; i++) {
if (isspace(s[i]) || s[i] == 0) return 1;
}
return 0;
}
/* Given the category name the command returns the corresponding flag, or
* zero if there is no match. */
uint64_t ACLGetCommandCategoryFlagByName(const char *name) {
@@ -703,8 +690,7 @@ void ACLAddAllowedSubcommand(user *u, unsigned long id, const char *sub) {
*
* When an error is returned, errno is set to the following values:
*
* EINVAL: The specified opcode is not understood or the key pattern is
* invalid (contains non allowed characters).
* EINVAL: The specified opcode is not understood.
* ENOENT: The command name or command category provided with + or - is not
* known.
* EBUSY: The subcommand you want to add is about a command that is currently
@@ -803,10 +789,6 @@ int ACLSetUser(user *u, const char *op, ssize_t oplen) {
errno = EEXIST;
return C_ERR;
}
if (ACLStringHasSpaces(op+1,oplen-1)) {
errno = EINVAL;
return C_ERR;
}
sds newpat = sdsnewlen(op+1,oplen-1);
listNode *ln = listSearchKey(u->patterns,newpat);
/* Avoid re-adding the same pattern multiple times. */
@@ -1182,12 +1164,6 @@ int ACLLoadConfiguredUsers(void) {
while ((ln = listNext(&li)) != NULL) {
sds *aclrules = listNodeValue(ln);
sds username = aclrules[0];
if (ACLStringHasSpaces(aclrules[0],sdslen(aclrules[0]))) {
serverLog(LL_WARNING,"Spaces not allowed in ACL usernames");
return C_ERR;
}
user *u = ACLCreateUser(username,sdslen(username));
if (!u) {
u = ACLGetUserByName(username,sdslen(username));
@@ -1313,14 +1289,6 @@ sds ACLLoadFromFile(const char *filename) {
continue;
}
/* Spaces are not allowed in usernames. */
if (ACLStringHasSpaces(argv[1],sdslen(argv[1]))) {
errors = sdscatprintf(errors,
"'%s:%d: username '%s' contains invalid characters. ",
server.acl_filename, linenum, argv[1]);
continue;
}
/* Try to process the line using the fake user to validate iif
* the rules are able to apply cleanly. */
ACLSetUser(fakeuser,"reset",-1);
@@ -1622,7 +1590,7 @@ void addACLLogEntry(client *c, int reason, int keypos, sds username) {
* ACL SETUSER <username> ... acl rules ...
* ACL DELUSER <username> [...]
* ACL GETUSER <username>
* ACL GENPASS [<bits>]
* ACL GENPASS
* ACL WHOAMI
* ACL LOG [<count> | RESET]
*/
@@ -1630,13 +1598,6 @@ void aclCommand(client *c) {
char *sub = c->argv[1]->ptr;
if (!strcasecmp(sub,"setuser") && c->argc >= 3) {
sds username = c->argv[2]->ptr;
/* Check username validity. */
if (ACLStringHasSpaces(username,sdslen(username))) {
addReplyErrorFormat(c,
"Usernames can't contain spaces or null characters");
return;
}
/* Create a temporary user to validate and stage all changes against
* before applying to an existing user or creating a new user. If all
* arguments are valid the user parameters will all be applied together.
@@ -1814,31 +1775,16 @@ void aclCommand(client *c) {
}
dictReleaseIterator(di);
setDeferredArrayLen(c,dl,arraylen);
} else if (!strcasecmp(sub,"genpass") && (c->argc == 2 || c->argc == 3)) {
#define GENPASS_MAX_BITS 4096
char pass[GENPASS_MAX_BITS/8*2]; /* Hex representation. */
long bits = 256; /* By default generate 256 bits passwords. */
if (c->argc == 3 && getLongFromObjectOrReply(c,c->argv[2],&bits,NULL)
!= C_OK) return;
if (bits <= 0 || bits > GENPASS_MAX_BITS) {
addReplyErrorFormat(c,
"ACL GENPASS argument must be the number of "
"bits for the output password, a positive number "
"up to %d",GENPASS_MAX_BITS);
return;
}
long chars = (bits+3)/4; /* Round to number of characters to emit. */
getRandomHexChars(pass,chars);
addReplyBulkCBuffer(c,pass,chars);
} else if (!strcasecmp(sub,"genpass") && c->argc == 2) {
char pass[32]; /* 128 bits of actual pseudo random data. */
getRandomHexChars(pass,sizeof(pass));
addReplyBulkCBuffer(c,pass,sizeof(pass));
} else if (!strcasecmp(sub,"log") && (c->argc == 2 || c->argc ==3)) {
long count = 10; /* Number of entries to emit by default. */
/* Parse the only argument that LOG may have: it could be either
* the number of entries the user wants to display, or alternatively
* the "RESET" command in order to flush the old entries. */
* the number of entires the user wants to display, or alternatively
* the "RESET" command in order to flush the old entires. */
if (c->argc == 3) {
if (!strcasecmp(c->argv[2]->ptr,"reset")) {
listSetFreeMethod(ACLLog,ACLFreeLogEntry);
@@ -1901,18 +1847,18 @@ void aclCommand(client *c) {
}
} else if (!strcasecmp(sub,"help")) {
const char *help[] = {
"LOAD -- Reload users from the ACL file.",
"SAVE -- Save the current config to the ACL file.",
"LIST -- Show user details in config file format.",
"USERS -- List all the registered usernames.",
"SETUSER <username> [attribs ...] -- Create or modify a user.",
"GETUSER <username> -- Get the user details.",
"DELUSER <username> [...] -- Delete a list of users.",
"CAT -- List available categories.",
"CAT <category> -- List commands inside category.",
"GENPASS [<bits>] -- Generate a secure user password.",
"WHOAMI -- Return the current connection username.",
"LOG [<count> | RESET] -- Show the ACL log entries.",
"LOAD -- Reload users from the ACL file.",
"SAVE -- Save the current config to the ACL file."
"LIST -- Show user details in config file format.",
"USERS -- List all the registered usernames.",
"SETUSER <username> [attribs ...] -- Create or modify a user.",
"GETUSER <username> -- Get the user details.",
"DELUSER <username> [...] -- Delete a list of users.",
"CAT -- List available categories.",
"CAT <category> -- List commands inside category.",
"GENPASS -- Generate a secure user password.",
"WHOAMI -- Return the current connection username.",
"LOG [<count> | RESET] -- Show the ACL log entries.",
NULL
};
addReplyHelp(c,help);
+3 -17
View File
@@ -327,11 +327,12 @@ listNode *listIndex(list *list, long index) {
}
/* Rotate the list removing the tail node and inserting it to the head. */
void listRotateTailToHead(list *list) {
void listRotate(list *list) {
listNode *tail = list->tail;
if (listLength(list) <= 1) return;
/* Detach current tail */
listNode *tail = list->tail;
list->tail = tail->prev;
list->tail->next = NULL;
/* Move it as head */
@@ -341,21 +342,6 @@ void listRotateTailToHead(list *list) {
list->head = tail;
}
/* Rotate the list removing the head node and inserting it to the tail. */
void listRotateHeadToTail(list *list) {
if (listLength(list) <= 1) return;
listNode *head = list->head;
/* Detach current head */
list->head = head->next;
list->head->prev = NULL;
/* Move it as tail */
list->tail->next = head;
head->next = NULL;
head->prev = list->tail;
list->tail = head;
}
/* Add all the elements of the list 'o' at the end of the
* list 'l'. The list 'other' remains empty but otherwise valid. */
void listJoin(list *l, list *o) {
+1 -2
View File
@@ -85,8 +85,7 @@ listNode *listSearchKey(list *list, void *key);
listNode *listIndex(list *list, long index);
void listRewind(list *list, listIter *li);
void listRewindTail(list *list, listIter *li);
void listRotateTailToHead(list *list);
void listRotateHeadToTail(list *list);
void listRotate(list *list);
void listJoin(list *l, list *o);
/* Directions for iterators */
+3 -17
View File
@@ -238,7 +238,6 @@ long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,
te->clientData = clientData;
te->prev = NULL;
te->next = eventLoop->timeEventHead;
te->refcount = 0;
if (te->next)
te->next->prev = te;
eventLoop->timeEventHead = te;
@@ -317,13 +316,6 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
/* Remove events scheduled for deletion. */
if (te->id == AE_DELETED_EVENT_ID) {
aeTimeEvent *next = te->next;
/* If a reference exists for this timer event,
* don't free it. This is currently incremented
* for recursive timerProc calls */
if (te->refcount) {
te = next;
continue;
}
if (te->prev)
te->prev->next = te->next;
else
@@ -353,9 +345,7 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
int retval;
id = te->id;
te->refcount++;
retval = te->timeProc(eventLoop, id, te->clientData);
te->refcount--;
processed++;
if (retval != AE_NOMORE) {
aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
@@ -380,7 +370,6 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
* if flags has AE_DONT_WAIT set the function returns ASAP until all
* the events that's possible to process without to wait are processed.
* if flags has AE_CALL_AFTER_SLEEP set, the aftersleep callback is called.
* if flags has AE_CALL_BEFORE_SLEEP set, the beforesleep callback is called.
*
* The function returns the number of events processed. */
int aeProcessEvents(aeEventLoop *eventLoop, int flags)
@@ -439,9 +428,6 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
tvp = &tv;
}
if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)
eventLoop->beforesleep(eventLoop);
/* Call the multiplexing API, will return only on timeout or when
* some event fires. */
numevents = aeApiPoll(eventLoop, tvp);
@@ -536,9 +522,9 @@ int aeWait(int fd, int mask, long long milliseconds) {
void aeMain(aeEventLoop *eventLoop) {
eventLoop->stop = 0;
while (!eventLoop->stop) {
aeProcessEvents(eventLoop, AE_ALL_EVENTS|
AE_CALL_BEFORE_SLEEP|
AE_CALL_AFTER_SLEEP);
if (eventLoop->beforesleep != NULL)
eventLoop->beforesleep(eventLoop);
aeProcessEvents(eventLoop, AE_ALL_EVENTS|AE_CALL_AFTER_SLEEP);
}
}
+4 -7
View File
@@ -47,12 +47,11 @@
things to disk before sending replies, and want
to do that in a group fashion. */
#define AE_FILE_EVENTS (1<<0)
#define AE_TIME_EVENTS (1<<1)
#define AE_FILE_EVENTS 1
#define AE_TIME_EVENTS 2
#define AE_ALL_EVENTS (AE_FILE_EVENTS|AE_TIME_EVENTS)
#define AE_DONT_WAIT (1<<2)
#define AE_CALL_BEFORE_SLEEP (1<<3)
#define AE_CALL_AFTER_SLEEP (1<<4)
#define AE_DONT_WAIT 4
#define AE_CALL_AFTER_SLEEP 8
#define AE_NOMORE -1
#define AE_DELETED_EVENT_ID -1
@@ -86,8 +85,6 @@ typedef struct aeTimeEvent {
void *clientData;
struct aeTimeEvent *prev;
struct aeTimeEvent *next;
int refcount; /* refcount to prevent timer events from being
* freed in recursive time event calls. */
} aeTimeEvent;
/* A fired event */
+1 -2
View File
@@ -831,7 +831,7 @@ int loadAppendOnlyFile(char *filename) {
if (cmd == server.multiCommand) valid_before_multi = valid_up_to;
/* Run the command in the context of a fake client */
fakeClient->cmd = fakeClient->lastcmd = cmd;
fakeClient->cmd = cmd;
if (fakeClient->flags & CLIENT_MULTI &&
fakeClient->cmd->proc != execCommand)
{
@@ -1596,7 +1596,6 @@ int rewriteAppendOnlyFileBackground(void) {
/* Child */
redisSetProcTitle("redis-aof-rewrite");
redisSetCpuAffinity(server.aof_rewrite_cpulist);
snprintf(tmpfile,256,"temp-rewriteaof-bg-%d.aof", (int) getpid());
if (rewriteAppendOnlyFile(tmpfile) == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_AOF, "AOF rewrite");
+1 -15
View File
@@ -154,20 +154,6 @@ void *bioProcessBackgroundJobs(void *arg) {
return NULL;
}
switch (type) {
case BIO_CLOSE_FILE:
redis_set_thread_title("bio_close_file");
break;
case BIO_AOF_FSYNC:
redis_set_thread_title("bio_aof_fsync");
break;
case BIO_LAZY_FREE:
redis_set_thread_title("bio_lazy_free");
break;
}
redisSetCpuAffinity(server.bio_cpulist);
/* Make the thread killable at any time, so that bioKillThreads()
* can work reliably. */
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
@@ -268,7 +254,7 @@ void bioKillThreads(void) {
int err, j;
for (j = 0; j < BIO_NUM_OPS; j++) {
if (bio_threads[j] && pthread_cancel(bio_threads[j]) == 0) {
if (pthread_cancel(bio_threads[j]) == 0) {
if ((err = pthread_join(bio_threads[j],NULL)) != 0) {
serverLog(LL_WARNING,
"Bio thread for job type #%d can be joined: %s",
+5 -5
View File
@@ -269,7 +269,7 @@ int64_t getSignedBitfield(unsigned char *p, uint64_t offset, uint64_t bits) {
* then zero is returned, otherwise in case of overflow, 1 is returned,
* otherwise in case of underflow, -1 is returned.
*
* When non-zero is returned (overflow or underflow), if not NULL, *limit is
* When non-zero is returned (oferflow or underflow), if not NULL, *limit is
* set to the value the operation should result when an overflow happens,
* depending on the specified overflow semantics:
*
@@ -554,7 +554,7 @@ void setbitCommand(client *c) {
byteval &= ~(1 << bit);
byteval |= ((on & 0x1) << bit);
((uint8_t*)o->ptr)[byte] = byteval;
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
server.dirty++;
addReply(c, bitval ? shared.cone : shared.czero);
@@ -754,11 +754,11 @@ void bitopCommand(client *c) {
/* Store the computed value into the target key */
if (maxlen) {
o = createObject(OBJ_STRING,res);
setKey(c,c->db,targetkey,o);
setKey(c->db,targetkey,o);
notifyKeyspaceEvent(NOTIFY_STRING,"set",targetkey,c->db->id);
decrRefCount(o);
} else if (dbDelete(c->db,targetkey)) {
signalModifiedKey(c,c->db,targetkey);
signalModifiedKey(c->db,targetkey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",targetkey,c->db->id);
}
server.dirty++;
@@ -1135,7 +1135,7 @@ void bitfieldGeneric(client *c, int flags) {
}
if (changes) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"setbit",c->argv[1],c->db->id);
server.dirty += changes;
}
+61 -36
View File
@@ -31,6 +31,9 @@
*
* API:
*
* getTimeoutFromObjectOrReply() is just an utility function to parse a
* timeout argument since blocking operations usually require a timeout.
*
* blockClient() set the CLIENT_BLOCKED flag in the client, and set the
* specified block type 'btype' filed to one of BLOCKED_* macros.
*
@@ -64,20 +67,41 @@
int serveClientBlockedOnList(client *receiver, robj *key, robj *dstkey, redisDb *db, robj *value, int where);
/* This structure represents the blocked key information that we store
* in the client structure. Each client blocked on keys, has a
* client->bpop.keys hash table. The keys of the hash table are Redis
* keys pointers to 'robj' structures. The value is this structure.
* The structure has two goals: firstly we store the list node that this
* client uses to be listed in the database "blocked clients for this key"
* list, so we can later unblock in O(1) without a list scan.
* Secondly for certain blocking types, we have additional info. Right now
* the only use for additional info we have is when clients are blocked
* on streams, as we have to remember the ID it blocked for. */
typedef struct bkinfo {
listNode *listnode; /* List node for db->blocking_keys[key] list. */
streamID stream_id; /* Stream ID if we blocked in a stream. */
} bkinfo;
/* Get a timeout value from an object and store it into 'timeout'.
* The final timeout is always stored as milliseconds as a time where the
* timeout will expire, however the parsing is performed according to
* the 'unit' that can be seconds or milliseconds.
*
* Note that if the timeout is zero (usually from the point of view of
* commands API this means no timeout) the value stored into 'timeout'
* is zero. */
int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) {
long long tval;
long double ftval;
if (unit == UNIT_SECONDS) {
if (getLongDoubleFromObjectOrReply(c,object,&ftval,
"timeout is not an float or out of range") != C_OK)
return C_ERR;
tval = (long long) (ftval * 1000.0);
} else {
if (getLongLongFromObjectOrReply(c,object,&tval,
"timeout is not an integer or out of range") != C_OK)
return C_ERR;
}
if (tval < 0) {
addReplyError(c,"timeout is negative");
return C_ERR;
}
if (tval > 0) {
tval += mstime();
}
*timeout = tval;
return C_OK;
}
/* Block a client for the specific operation type. Once the CLIENT_BLOCKED
* flag is set client query buffer is not longer processed, but accumulated,
@@ -87,7 +111,7 @@ void blockClient(client *c, int btype) {
c->btype = btype;
server.blocked_clients++;
server.blocked_clients_by_type[btype]++;
addClientToTimeoutTable(c);
addClientToShortTimeoutTable(c);
}
/* This function is called in the beforeSleep() function of the event loop
@@ -110,7 +134,7 @@ void processUnblockedClients(void) {
* the code is conceptually more correct this way. */
if (!(c->flags & CLIENT_BLOCKED)) {
if (c->querybuf && sdslen(c->querybuf) > 0) {
processInputBuffer(c);
processInputBufferAndReplicate(c);
}
}
}
@@ -162,7 +186,6 @@ void unblockClient(client *c) {
server.blocked_clients_by_type[c->btype]--;
c->flags &= ~CLIENT_BLOCKED;
c->btype = BLOCKED_NONE;
removeClientFromTimeoutTable(c);
queueClientForReprocessing(c);
}
@@ -226,7 +249,8 @@ void serveClientsBlockedOnListKey(robj *o, readyList *rl) {
if (receiver->btype != BLOCKED_LIST) {
/* Put at the tail, so that at the next call
* we'll not run into it again. */
listRotateHeadToTail(clients);
listDelNode(clients,clientnode);
listAddNodeTail(clients,receiver);
continue;
}
@@ -287,7 +311,8 @@ void serveClientsBlockedOnSortedSetKey(robj *o, readyList *rl) {
if (receiver->btype != BLOCKED_ZSET) {
/* Put at the tail, so that at the next call
* we'll not run into it again. */
listRotateHeadToTail(clients);
listDelNode(clients,clientnode);
listAddNodeTail(clients,receiver);
continue;
}
@@ -333,8 +358,8 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
while((ln = listNext(&li))) {
client *receiver = listNodeValue(ln);
if (receiver->btype != BLOCKED_STREAM) continue;
bkinfo *bki = dictFetchValue(receiver->bpop.keys,rl->key);
streamID *gt = &bki->stream_id;
streamID *gt = dictFetchValue(receiver->bpop.keys,
rl->key);
/* If we blocked in the context of a consumer
* group, we need to resolve the group and update the
@@ -371,10 +396,9 @@ void serveClientsBlockedOnStreamKey(robj *o, readyList *rl) {
int noack = 0;
if (group) {
consumer =
streamLookupConsumer(group,
receiver->bpop.xread_consumer->ptr,
SLC_NONE);
consumer = streamLookupConsumer(group,
receiver->bpop.xread_consumer->ptr,
1);
noack = receiver->bpop.xread_group_noack;
}
@@ -433,7 +457,8 @@ void serveClientsBlockedOnKeyByModule(readyList *rl) {
* ready to be served, so they'll remain in the list
* sometimes. We want also be able to skip clients that are
* not blocked for the MODULE type safely. */
listRotateHeadToTail(clients);
listDelNode(clients,clientnode);
listAddNodeTail(clients,receiver);
if (receiver->btype != BLOCKED_MODULE) continue;
@@ -564,15 +589,17 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
if (target != NULL) incrRefCount(target);
for (j = 0; j < numkeys; j++) {
/* Allocate our bkinfo structure, associated to each key the client
* is blocked for. */
bkinfo *bki = zmalloc(sizeof(*bki));
if (btype == BLOCKED_STREAM)
bki->stream_id = ids[j];
/* The value associated with the key name in the bpop.keys dictionary
* is NULL for lists and sorted sets, or the stream ID for streams. */
void *key_data = NULL;
if (btype == BLOCKED_STREAM) {
key_data = zmalloc(sizeof(streamID));
memcpy(key_data,ids+j,sizeof(streamID));
}
/* If the key already exists in the dictionary ignore it. */
if (dictAdd(c->bpop.keys,keys[j],bki) != DICT_OK) {
zfree(bki);
if (dictAdd(c->bpop.keys,keys[j],key_data) != DICT_OK) {
zfree(key_data);
continue;
}
incrRefCount(keys[j]);
@@ -591,7 +618,6 @@ void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeo
l = dictGetVal(de);
}
listAddNodeTail(l,c);
bki->listnode = listLast(l);
}
blockClient(c,btype);
}
@@ -608,12 +634,11 @@ void unblockClientWaitingData(client *c) {
/* The client may wait for multiple keys, so unblock it for every key. */
while((de = dictNext(di)) != NULL) {
robj *key = dictGetKey(de);
bkinfo *bki = dictGetVal(de);
/* Remove this client from the list of clients waiting for this key. */
l = dictFetchValue(c->db->blocking_keys,key);
serverAssertWithInfo(c,key,l != NULL);
listDelNode(l,bki->listnode);
listDelNode(l,listSearchKey(l,c));
/* If the list is empty we need to remove it to avoid wasting memory */
if (listLength(l) == 0)
dictDelete(c->db->blocking_keys,key);
+28 -65
View File
@@ -749,7 +749,6 @@ clusterNode *createClusterNode(char *nodename, int flags) {
node->slaves = NULL;
node->slaveof = NULL;
node->ping_sent = node->pong_received = 0;
node->data_received = 0;
node->fail_time = 0;
node->link = NULL;
memset(node->ip,0,sizeof(node->ip));
@@ -934,7 +933,7 @@ int clusterAddNode(clusterNode *node) {
return (retval == DICT_OK) ? C_OK : C_ERR;
}
/* Remove a node from the cluster. The function performs the high level
/* Remove a node from the cluster. The functio performs the high level
* cleanup, calling freeClusterNode() for the low level cleanup.
* Here we do the following:
*
@@ -1679,7 +1678,6 @@ int clusterProcessPacket(clusterLink *link) {
clusterMsg *hdr = (clusterMsg*) link->rcvbuf;
uint32_t totlen = ntohl(hdr->totlen);
uint16_t type = ntohs(hdr->type);
mstime_t now = mstime();
if (type < CLUSTERMSG_TYPE_COUNT)
server.cluster->stats_bus_messages_received[type]++;
@@ -1741,17 +1739,8 @@ int clusterProcessPacket(clusterLink *link) {
if (totlen != explen) return 1;
}
/* Check if the sender is a known node. Note that for incoming connections
* we don't store link->node information, but resolve the node by the
* ID in the header each time in the current implementation. */
/* Check if the sender is a known node. */
sender = clusterLookupNode(hdr->sender);
/* Update the last time we saw any data from this node. We
* use this in order to avoid detecting a timeout from a node that
* is just sending a lot of data in the cluster bus, for instance
* because of Pub/Sub. */
if (sender) sender->data_received = now;
if (sender && !nodeInHandshake(sender)) {
/* Update our curretEpoch if we see a newer epoch in the cluster. */
senderCurrentEpoch = ntohu64(hdr->currentEpoch);
@@ -1766,7 +1755,7 @@ int clusterProcessPacket(clusterLink *link) {
}
/* Update the replication offset info for this node. */
sender->repl_offset = ntohu64(hdr->offset);
sender->repl_offset_time = now;
sender->repl_offset_time = mstime();
/* If we are a slave performing a manual failover and our master
* sent its offset while already paused, populate the MF state. */
if (server.cluster->mf_end &&
@@ -1880,7 +1869,7 @@ int clusterProcessPacket(clusterLink *link) {
* address. */
serverLog(LL_DEBUG,"PONG contains mismatching sender ID. About node %.40s added %d ms ago, having flags %d",
link->node->name,
(int)(now-(link->node->ctime)),
(int)(mstime()-(link->node->ctime)),
link->node->flags);
link->node->flags |= CLUSTER_NODE_NOADDR;
link->node->ip[0] = '\0';
@@ -1915,7 +1904,7 @@ int clusterProcessPacket(clusterLink *link) {
/* Update our info about the node */
if (link->node && type == CLUSTERMSG_TYPE_PONG) {
link->node->pong_received = now;
link->node->pong_received = mstime();
link->node->ping_sent = 0;
/* The PFAIL condition can be reversed without external
@@ -2062,7 +2051,7 @@ int clusterProcessPacket(clusterLink *link) {
"FAIL message received from %.40s about %.40s",
hdr->sender, hdr->data.fail.about.nodename);
failing->flags |= CLUSTER_NODE_FAIL;
failing->fail_time = now;
failing->fail_time = mstime();
failing->flags &= ~CLUSTER_NODE_PFAIL;
clusterDoBeforeSleep(CLUSTER_TODO_SAVE_CONFIG|
CLUSTER_TODO_UPDATE_STATE);
@@ -2115,9 +2104,9 @@ int clusterProcessPacket(clusterLink *link) {
/* Manual failover requested from slaves. Initialize the state
* accordingly. */
resetManualFailover();
server.cluster->mf_end = now + CLUSTER_MF_TIMEOUT;
server.cluster->mf_end = mstime() + CLUSTER_MF_TIMEOUT;
server.cluster->mf_slave = sender;
pauseClients(now+(CLUSTER_MF_TIMEOUT*CLUSTER_MF_PAUSE_MULT));
pauseClients(mstime()+(CLUSTER_MF_TIMEOUT*2));
serverLog(LL_WARNING,"Manual failover requested by replica %.40s.",
sender->name);
} else if (type == CLUSTERMSG_TYPE_UPDATE) {
@@ -3540,6 +3529,7 @@ void clusterCron(void) {
while((de = dictNext(di)) != NULL) {
clusterNode *node = dictGetVal(de);
now = mstime(); /* Use an updated time at every iteration. */
mstime_t delay;
if (node->flags &
(CLUSTER_NODE_MYSELF|CLUSTER_NODE_NOADDR|CLUSTER_NODE_HANDSHAKE))
@@ -3563,20 +3553,16 @@ void clusterCron(void) {
this_slaves = okslaves;
}
/* If we are not receiving any data for more than half the cluster
/* If we are waiting for the PONG more than half the cluster
* timeout, reconnect the link: maybe there is a connection
* issue even if the node is alive. */
mstime_t ping_delay = now - node->ping_sent;
mstime_t data_delay = now - node->data_received;
if (node->link && /* is connected */
now - node->link->ctime >
server.cluster_node_timeout && /* was not already reconnected */
node->ping_sent && /* we already sent a ping */
node->pong_received < node->ping_sent && /* still waiting pong */
/* and we are waiting for the pong more than timeout/2 */
ping_delay > server.cluster_node_timeout/2 &&
/* and in such interval we are not seeing any traffic at all. */
data_delay > server.cluster_node_timeout/2)
now - node->ping_sent > server.cluster_node_timeout/2)
{
/* Disconnect the link, it will be reconnected automatically. */
freeClusterLink(node->link);
@@ -3608,18 +3594,12 @@ void clusterCron(void) {
/* Check only if we have an active ping for this instance. */
if (node->ping_sent == 0) continue;
/* Check if this node looks unreachable.
* Note that if we already received the PONG, then node->ping_sent
* is zero, so can't reach this code at all, so we don't risk of
* checking for a PONG delay if we didn't sent the PING.
*
* We also consider every incoming data as proof of liveness, since
* our cluster bus link is also used for data: under heavy data
* load pong delays are possible. */
mstime_t node_delay = (ping_delay < data_delay) ? ping_delay :
data_delay;
/* Compute the delay of the PONG. Note that if we already received
* the PONG, then node->ping_sent is zero, so can't reach this
* code at all. */
delay = now - node->ping_sent;
if (node_delay > server.cluster_node_timeout) {
if (delay > server.cluster_node_timeout) {
/* Timeout reached. Set the node as possibly failing if it is
* not already in this state. */
if (!(node->flags & (CLUSTER_NODE_PFAIL|CLUSTER_NODE_FAIL))) {
@@ -4211,17 +4191,11 @@ void clusterReplyMultiBulkSlots(client *c) {
while((de = dictNext(di)) != NULL) {
clusterNode *node = dictGetVal(de);
int j = 0, start = -1;
int i, nested_elements = 0;
/* Skip slaves (that are iterated when producing the output of their
* master) and masters not serving any slot. */
if (!nodeIsMaster(node) || node->numslots == 0) continue;
for(i = 0; i < node->numslaves; i++) {
if (nodeFailed(node->slaves[i])) continue;
nested_elements++;
}
for (j = 0; j < CLUSTER_SLOTS; j++) {
int bit, i;
@@ -4229,7 +4203,8 @@ void clusterReplyMultiBulkSlots(client *c) {
if (start == -1) start = j;
}
if (start != -1 && (!bit || j == CLUSTER_SLOTS-1)) {
addReplyArrayLen(c, nested_elements + 3); /* slots (2) + master addr (1). */
int nested_elements = 3; /* slots (2) + master addr (1). */
void *nested_replylen = addReplyDeferredLen(c);
if (bit && j == CLUSTER_SLOTS-1) j++;
@@ -4259,7 +4234,9 @@ void clusterReplyMultiBulkSlots(client *c) {
addReplyBulkCString(c, node->slaves[i]->ip);
addReplyLongLong(c, node->slaves[i]->port);
addReplyBulkCBuffer(c, node->slaves[i]->name, CLUSTER_NAMELEN);
nested_elements++;
}
setDeferredArrayLen(c, nested_replylen, nested_elements);
num_masters++;
}
}
@@ -4989,7 +4966,7 @@ void restoreCommand(client *c) {
rioInitWithBuffer(&payload,c->argv[3]->ptr);
if (((type = rdbLoadObjectType(&payload)) == -1) ||
((obj = rdbLoadObject(type,&payload,c->argv[1]->ptr)) == NULL))
((obj = rdbLoadObject(type,&payload,c->argv[1])) == NULL))
{
addReplyError(c,"Bad data format");
return;
@@ -5005,7 +4982,7 @@ void restoreCommand(client *c) {
setExpire(c,c->db,c->argv[1],ttl);
}
objectSetLRUOrLFU(obj,lfu_freq,lru_idle,lru_clock,1000);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"restore",c->argv[1],c->db->id);
addReply(c,shared.ok);
server.dirty++;
@@ -5121,17 +5098,15 @@ void migrateCloseTimedoutSockets(void) {
dictReleaseIterator(di);
}
/* MIGRATE host port key dbid timeout [COPY | REPLACE | AUTH password |
* AUTH2 username password]
/* MIGRATE host port key dbid timeout [COPY | REPLACE | AUTH password]
*
* On in the multiple keys form:
*
* MIGRATE host port "" dbid timeout [COPY | REPLACE | AUTH password |
* AUTH2 username password] KEYS key1 key2 ... keyN */
* MIGRATE host port "" dbid timeout [COPY | REPLACE | AUTH password] KEYS key1
* key2 ... keyN */
void migrateCommand(client *c) {
migrateCachedSocket *cs;
int copy = 0, replace = 0, j;
char *username = NULL;
char *password = NULL;
long timeout;
long dbid;
@@ -5149,7 +5124,7 @@ void migrateCommand(client *c) {
/* Parse additional options */
for (j = 6; j < c->argc; j++) {
int moreargs = (c->argc-1) - j;
int moreargs = j < c->argc-1;
if (!strcasecmp(c->argv[j]->ptr,"copy")) {
copy = 1;
} else if (!strcasecmp(c->argv[j]->ptr,"replace")) {
@@ -5161,13 +5136,6 @@ void migrateCommand(client *c) {
}
j++;
password = c->argv[j]->ptr;
} else if (!strcasecmp(c->argv[j]->ptr,"auth2")) {
if (moreargs < 2) {
addReply(c,shared.syntaxerr);
return;
}
username = c->argv[++j]->ptr;
password = c->argv[++j]->ptr;
} else if (!strcasecmp(c->argv[j]->ptr,"keys")) {
if (sdslen(c->argv[3]->ptr) != 0) {
addReplyError(c,
@@ -5228,13 +5196,8 @@ try_again:
/* Authentication */
if (password) {
int arity = username ? 3 : 2;
serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',arity));
serverAssertWithInfo(c,NULL,rioWriteBulkCount(&cmd,'*',2));
serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,"AUTH",4));
if (username) {
serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,username,
sdslen(username)));
}
serverAssertWithInfo(c,NULL,rioWriteBulkString(&cmd,password,
sdslen(password)));
}
@@ -5366,7 +5329,7 @@ try_again:
if (!copy) {
/* No COPY option: remove the local key, signal the change. */
dbDelete(c->db,kv[j]);
signalModifiedKey(c,c->db,kv[j]);
signalModifiedKey(c->db,kv[j]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",kv[j],c->db->id);
server.dirty++;
-1
View File
@@ -124,7 +124,6 @@ typedef struct clusterNode {
tables. */
mstime_t ping_sent; /* Unix time we sent latest ping */
mstime_t pong_received; /* Unix time we received the pong */
mstime_t data_received; /* Unix time we received any data */
mstime_t fail_time; /* Unix time when FAIL flag was set */
mstime_t voted_time; /* Last time we voted for a slave of this master */
mstime_t repl_offset_time; /* Unix time we received offset for this node */
-5
View File
@@ -2099,7 +2099,6 @@ standardConfig configs[] = {
createBoolConfig("lazyfree-lazy-eviction", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_eviction, 0, NULL, NULL),
createBoolConfig("lazyfree-lazy-expire", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_expire, 0, NULL, NULL),
createBoolConfig("lazyfree-lazy-server-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_server_del, 0, NULL, NULL),
createBoolConfig("lazyfree-lazy-user-del", NULL, MODIFIABLE_CONFIG, server.lazyfree_lazy_user_del , 0, NULL, NULL),
createBoolConfig("repl-disable-tcp-nodelay", NULL, MODIFIABLE_CONFIG, server.repl_disable_tcp_nodelay, 0, NULL, NULL),
createBoolConfig("repl-diskless-sync", NULL, MODIFIABLE_CONFIG, server.repl_diskless_sync, 0, NULL, NULL),
createBoolConfig("gopher-enabled", NULL, MODIFIABLE_CONFIG, server.gopher_enabled, 0, NULL, NULL),
@@ -2133,10 +2132,6 @@ standardConfig configs[] = {
createStringConfig("syslog-ident", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.syslog_ident, "redis", NULL, NULL),
createStringConfig("dbfilename", NULL, MODIFIABLE_CONFIG, ALLOW_EMPTY_STRING, server.rdb_filename, "dump.rdb", isValidDBfilename, NULL),
createStringConfig("appendfilename", NULL, IMMUTABLE_CONFIG, ALLOW_EMPTY_STRING, server.aof_filename, "appendonly.aof", isValidAOFfilename, NULL),
createStringConfig("server_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.server_cpulist, NULL, NULL, NULL),
createStringConfig("bio_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bio_cpulist, NULL, NULL, NULL),
createStringConfig("aof_rewrite_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.aof_rewrite_cpulist, NULL, NULL, NULL),
createStringConfig("bgsave_cpulist", NULL, IMMUTABLE_CONFIG, EMPTY_STRING_IS_NULL, server.bgsave_cpulist, NULL, NULL, NULL),
/* Enum Configs */
createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
-27
View File
@@ -226,31 +226,4 @@ void setproctitle(const char *fmt, ...);
#define USE_ALIGNED_ACCESS
#endif
/* Define for redis_set_thread_title */
#ifdef __linux__
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name)
#else
#if (defined __FreeBSD__ || defined __OpenBSD__)
#include <pthread_np.h>
#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)
#elif defined __NetBSD__
#include <pthread.h>
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)
#else
#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7))
int pthread_setname_np(const char *name);
#include <pthread.h>
#define redis_set_thread_title(name) pthread_setname_np(name)
#else
#define redis_set_thread_title(name)
#endif
#endif
#endif
/* Check if we can use setcpuaffinity(). */
#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)
#define USE_SETCPUAFFINITY
void setcpuaffinity(const char *cpulist);
#endif
#endif
+1 -1
View File
@@ -222,6 +222,6 @@ const char *connGetInfo(connection *conn, char *buf, size_t buf_len);
/* Helpers for tls special considerations */
int tlsHasPendingData();
int tlsProcessPendingData();
void tlsProcessPendingData();
#endif /* __REDIS_CONNECTION_H */
+153 -118
View File
@@ -1,5 +1,16 @@
/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
* Copyright (c) 2020, Amazon Web Services
/* Redis uses the CRC64 variant with "Jones" coefficients and init value of 0.
*
* Specification of this CRC64 variant follows:
* Name: crc-64-jones
* Width: 64 bites
* Poly: 0xad93d23594c935a9
* Reflected In: True
* Xor_In: 0xffffffffffffffff
* Reflected_Out: True
* Xor_Out: 0x0
* Check("123456789"): 0xe9c6d914c4b8d9ca
*
* Copyright (c) 2012, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,100 +37,147 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. */
#include "crc64.h"
#include "crcspeed.h"
static uint64_t crc64_table[8][256] = {{0}};
#include <stdint.h>
#define POLY UINT64_C(0xad93d23594c935a9)
/******************** BEGIN GENERATED PYCRC FUNCTIONS ********************/
/**
* Generated on Sun Dec 21 14:14:07 2014,
* by pycrc v0.8.2, https://www.tty1.net/pycrc/
*
* LICENSE ON GENERATED CODE:
* ==========================
* As of version 0.6, pycrc is released under the terms of the MIT licence.
* The code generated by pycrc is not considered a substantial portion of the
* software, therefore the author of pycrc will not claim any copyright on
* the generated code.
* ==========================
*
* CRC configuration:
* Width = 64
* Poly = 0xad93d23594c935a9
* XorIn = 0xffffffffffffffff
* ReflectIn = True
* XorOut = 0x0000000000000000
* ReflectOut = True
* Algorithm = bit-by-bit-fast
*
* Modifications after generation (by matt):
* - included finalize step in-line with update for single-call generation
* - re-worked some inner variable architectures
* - adjusted function parameters to match expected prototypes.
*****************************************************************************/
static const uint64_t crc64_tab[256] = {
UINT64_C(0x0000000000000000), UINT64_C(0x7ad870c830358979),
UINT64_C(0xf5b0e190606b12f2), UINT64_C(0x8f689158505e9b8b),
UINT64_C(0xc038e5739841b68f), UINT64_C(0xbae095bba8743ff6),
UINT64_C(0x358804e3f82aa47d), UINT64_C(0x4f50742bc81f2d04),
UINT64_C(0xab28ecb46814fe75), UINT64_C(0xd1f09c7c5821770c),
UINT64_C(0x5e980d24087fec87), UINT64_C(0x24407dec384a65fe),
UINT64_C(0x6b1009c7f05548fa), UINT64_C(0x11c8790fc060c183),
UINT64_C(0x9ea0e857903e5a08), UINT64_C(0xe478989fa00bd371),
UINT64_C(0x7d08ff3b88be6f81), UINT64_C(0x07d08ff3b88be6f8),
UINT64_C(0x88b81eabe8d57d73), UINT64_C(0xf2606e63d8e0f40a),
UINT64_C(0xbd301a4810ffd90e), UINT64_C(0xc7e86a8020ca5077),
UINT64_C(0x4880fbd87094cbfc), UINT64_C(0x32588b1040a14285),
UINT64_C(0xd620138fe0aa91f4), UINT64_C(0xacf86347d09f188d),
UINT64_C(0x2390f21f80c18306), UINT64_C(0x594882d7b0f40a7f),
UINT64_C(0x1618f6fc78eb277b), UINT64_C(0x6cc0863448deae02),
UINT64_C(0xe3a8176c18803589), UINT64_C(0x997067a428b5bcf0),
UINT64_C(0xfa11fe77117cdf02), UINT64_C(0x80c98ebf2149567b),
UINT64_C(0x0fa11fe77117cdf0), UINT64_C(0x75796f2f41224489),
UINT64_C(0x3a291b04893d698d), UINT64_C(0x40f16bccb908e0f4),
UINT64_C(0xcf99fa94e9567b7f), UINT64_C(0xb5418a5cd963f206),
UINT64_C(0x513912c379682177), UINT64_C(0x2be1620b495da80e),
UINT64_C(0xa489f35319033385), UINT64_C(0xde51839b2936bafc),
UINT64_C(0x9101f7b0e12997f8), UINT64_C(0xebd98778d11c1e81),
UINT64_C(0x64b116208142850a), UINT64_C(0x1e6966e8b1770c73),
UINT64_C(0x8719014c99c2b083), UINT64_C(0xfdc17184a9f739fa),
UINT64_C(0x72a9e0dcf9a9a271), UINT64_C(0x08719014c99c2b08),
UINT64_C(0x4721e43f0183060c), UINT64_C(0x3df994f731b68f75),
UINT64_C(0xb29105af61e814fe), UINT64_C(0xc849756751dd9d87),
UINT64_C(0x2c31edf8f1d64ef6), UINT64_C(0x56e99d30c1e3c78f),
UINT64_C(0xd9810c6891bd5c04), UINT64_C(0xa3597ca0a188d57d),
UINT64_C(0xec09088b6997f879), UINT64_C(0x96d1784359a27100),
UINT64_C(0x19b9e91b09fcea8b), UINT64_C(0x636199d339c963f2),
UINT64_C(0xdf7adabd7a6e2d6f), UINT64_C(0xa5a2aa754a5ba416),
UINT64_C(0x2aca3b2d1a053f9d), UINT64_C(0x50124be52a30b6e4),
UINT64_C(0x1f423fcee22f9be0), UINT64_C(0x659a4f06d21a1299),
UINT64_C(0xeaf2de5e82448912), UINT64_C(0x902aae96b271006b),
UINT64_C(0x74523609127ad31a), UINT64_C(0x0e8a46c1224f5a63),
UINT64_C(0x81e2d7997211c1e8), UINT64_C(0xfb3aa75142244891),
UINT64_C(0xb46ad37a8a3b6595), UINT64_C(0xceb2a3b2ba0eecec),
UINT64_C(0x41da32eaea507767), UINT64_C(0x3b024222da65fe1e),
UINT64_C(0xa2722586f2d042ee), UINT64_C(0xd8aa554ec2e5cb97),
UINT64_C(0x57c2c41692bb501c), UINT64_C(0x2d1ab4dea28ed965),
UINT64_C(0x624ac0f56a91f461), UINT64_C(0x1892b03d5aa47d18),
UINT64_C(0x97fa21650afae693), UINT64_C(0xed2251ad3acf6fea),
UINT64_C(0x095ac9329ac4bc9b), UINT64_C(0x7382b9faaaf135e2),
UINT64_C(0xfcea28a2faafae69), UINT64_C(0x8632586aca9a2710),
UINT64_C(0xc9622c4102850a14), UINT64_C(0xb3ba5c8932b0836d),
UINT64_C(0x3cd2cdd162ee18e6), UINT64_C(0x460abd1952db919f),
UINT64_C(0x256b24ca6b12f26d), UINT64_C(0x5fb354025b277b14),
UINT64_C(0xd0dbc55a0b79e09f), UINT64_C(0xaa03b5923b4c69e6),
UINT64_C(0xe553c1b9f35344e2), UINT64_C(0x9f8bb171c366cd9b),
UINT64_C(0x10e3202993385610), UINT64_C(0x6a3b50e1a30ddf69),
UINT64_C(0x8e43c87e03060c18), UINT64_C(0xf49bb8b633338561),
UINT64_C(0x7bf329ee636d1eea), UINT64_C(0x012b592653589793),
UINT64_C(0x4e7b2d0d9b47ba97), UINT64_C(0x34a35dc5ab7233ee),
UINT64_C(0xbbcbcc9dfb2ca865), UINT64_C(0xc113bc55cb19211c),
UINT64_C(0x5863dbf1e3ac9dec), UINT64_C(0x22bbab39d3991495),
UINT64_C(0xadd33a6183c78f1e), UINT64_C(0xd70b4aa9b3f20667),
UINT64_C(0x985b3e827bed2b63), UINT64_C(0xe2834e4a4bd8a21a),
UINT64_C(0x6debdf121b863991), UINT64_C(0x1733afda2bb3b0e8),
UINT64_C(0xf34b37458bb86399), UINT64_C(0x8993478dbb8deae0),
UINT64_C(0x06fbd6d5ebd3716b), UINT64_C(0x7c23a61ddbe6f812),
UINT64_C(0x3373d23613f9d516), UINT64_C(0x49aba2fe23cc5c6f),
UINT64_C(0xc6c333a67392c7e4), UINT64_C(0xbc1b436e43a74e9d),
UINT64_C(0x95ac9329ac4bc9b5), UINT64_C(0xef74e3e19c7e40cc),
UINT64_C(0x601c72b9cc20db47), UINT64_C(0x1ac40271fc15523e),
UINT64_C(0x5594765a340a7f3a), UINT64_C(0x2f4c0692043ff643),
UINT64_C(0xa02497ca54616dc8), UINT64_C(0xdafce7026454e4b1),
UINT64_C(0x3e847f9dc45f37c0), UINT64_C(0x445c0f55f46abeb9),
UINT64_C(0xcb349e0da4342532), UINT64_C(0xb1eceec59401ac4b),
UINT64_C(0xfebc9aee5c1e814f), UINT64_C(0x8464ea266c2b0836),
UINT64_C(0x0b0c7b7e3c7593bd), UINT64_C(0x71d40bb60c401ac4),
UINT64_C(0xe8a46c1224f5a634), UINT64_C(0x927c1cda14c02f4d),
UINT64_C(0x1d148d82449eb4c6), UINT64_C(0x67ccfd4a74ab3dbf),
UINT64_C(0x289c8961bcb410bb), UINT64_C(0x5244f9a98c8199c2),
UINT64_C(0xdd2c68f1dcdf0249), UINT64_C(0xa7f41839ecea8b30),
UINT64_C(0x438c80a64ce15841), UINT64_C(0x3954f06e7cd4d138),
UINT64_C(0xb63c61362c8a4ab3), UINT64_C(0xcce411fe1cbfc3ca),
UINT64_C(0x83b465d5d4a0eece), UINT64_C(0xf96c151de49567b7),
UINT64_C(0x76048445b4cbfc3c), UINT64_C(0x0cdcf48d84fe7545),
UINT64_C(0x6fbd6d5ebd3716b7), UINT64_C(0x15651d968d029fce),
UINT64_C(0x9a0d8ccedd5c0445), UINT64_C(0xe0d5fc06ed698d3c),
UINT64_C(0xaf85882d2576a038), UINT64_C(0xd55df8e515432941),
UINT64_C(0x5a3569bd451db2ca), UINT64_C(0x20ed197575283bb3),
UINT64_C(0xc49581ead523e8c2), UINT64_C(0xbe4df122e51661bb),
UINT64_C(0x3125607ab548fa30), UINT64_C(0x4bfd10b2857d7349),
UINT64_C(0x04ad64994d625e4d), UINT64_C(0x7e7514517d57d734),
UINT64_C(0xf11d85092d094cbf), UINT64_C(0x8bc5f5c11d3cc5c6),
UINT64_C(0x12b5926535897936), UINT64_C(0x686de2ad05bcf04f),
UINT64_C(0xe70573f555e26bc4), UINT64_C(0x9ddd033d65d7e2bd),
UINT64_C(0xd28d7716adc8cfb9), UINT64_C(0xa85507de9dfd46c0),
UINT64_C(0x273d9686cda3dd4b), UINT64_C(0x5de5e64efd965432),
UINT64_C(0xb99d7ed15d9d8743), UINT64_C(0xc3450e196da80e3a),
UINT64_C(0x4c2d9f413df695b1), UINT64_C(0x36f5ef890dc31cc8),
UINT64_C(0x79a59ba2c5dc31cc), UINT64_C(0x037deb6af5e9b8b5),
UINT64_C(0x8c157a32a5b7233e), UINT64_C(0xf6cd0afa9582aa47),
UINT64_C(0x4ad64994d625e4da), UINT64_C(0x300e395ce6106da3),
UINT64_C(0xbf66a804b64ef628), UINT64_C(0xc5bed8cc867b7f51),
UINT64_C(0x8aeeace74e645255), UINT64_C(0xf036dc2f7e51db2c),
UINT64_C(0x7f5e4d772e0f40a7), UINT64_C(0x05863dbf1e3ac9de),
UINT64_C(0xe1fea520be311aaf), UINT64_C(0x9b26d5e88e0493d6),
UINT64_C(0x144e44b0de5a085d), UINT64_C(0x6e963478ee6f8124),
UINT64_C(0x21c640532670ac20), UINT64_C(0x5b1e309b16452559),
UINT64_C(0xd476a1c3461bbed2), UINT64_C(0xaeaed10b762e37ab),
UINT64_C(0x37deb6af5e9b8b5b), UINT64_C(0x4d06c6676eae0222),
UINT64_C(0xc26e573f3ef099a9), UINT64_C(0xb8b627f70ec510d0),
UINT64_C(0xf7e653dcc6da3dd4), UINT64_C(0x8d3e2314f6efb4ad),
UINT64_C(0x0256b24ca6b12f26), UINT64_C(0x788ec2849684a65f),
UINT64_C(0x9cf65a1b368f752e), UINT64_C(0xe62e2ad306bafc57),
UINT64_C(0x6946bb8b56e467dc), UINT64_C(0x139ecb4366d1eea5),
UINT64_C(0x5ccebf68aecec3a1), UINT64_C(0x2616cfa09efb4ad8),
UINT64_C(0xa97e5ef8cea5d153), UINT64_C(0xd3a62e30fe90582a),
UINT64_C(0xb0c7b7e3c7593bd8), UINT64_C(0xca1fc72bf76cb2a1),
UINT64_C(0x45775673a732292a), UINT64_C(0x3faf26bb9707a053),
UINT64_C(0x70ff52905f188d57), UINT64_C(0x0a2722586f2d042e),
UINT64_C(0x854fb3003f739fa5), UINT64_C(0xff97c3c80f4616dc),
UINT64_C(0x1bef5b57af4dc5ad), UINT64_C(0x61372b9f9f784cd4),
UINT64_C(0xee5fbac7cf26d75f), UINT64_C(0x9487ca0fff135e26),
UINT64_C(0xdbd7be24370c7322), UINT64_C(0xa10fceec0739fa5b),
UINT64_C(0x2e675fb4576761d0), UINT64_C(0x54bf2f7c6752e8a9),
UINT64_C(0xcdcf48d84fe75459), UINT64_C(0xb71738107fd2dd20),
UINT64_C(0x387fa9482f8c46ab), UINT64_C(0x42a7d9801fb9cfd2),
UINT64_C(0x0df7adabd7a6e2d6), UINT64_C(0x772fdd63e7936baf),
UINT64_C(0xf8474c3bb7cdf024), UINT64_C(0x829f3cf387f8795d),
UINT64_C(0x66e7a46c27f3aa2c), UINT64_C(0x1c3fd4a417c62355),
UINT64_C(0x935745fc4798b8de), UINT64_C(0xe98f353477ad31a7),
UINT64_C(0xa6df411fbfb21ca3), UINT64_C(0xdc0731d78f8795da),
UINT64_C(0x536fa08fdfd90e51), UINT64_C(0x29b7d047efec8728),
};
/**
* Reflect all bits of a \a data word of \a data_len bytes.
*
* \param data The data word to be reflected.
* \param data_len The width of \a data expressed in number of bits.
* \return The reflected data.
*****************************************************************************/
static inline uint_fast64_t crc_reflect(uint_fast64_t data, size_t data_len) {
uint_fast64_t ret = data & 0x01;
for (size_t i = 1; i < data_len; i++) {
data >>= 1;
ret = (ret << 1) | (data & 0x01);
}
return ret;
}
/**
* Update the crc value with new data.
*
* \param crc The current crc value.
* \param data Pointer to a buffer of \a data_len bytes.
* \param data_len Number of bytes in the \a data buffer.
* \return The updated crc value.
******************************************************************************/
uint64_t _crc64(uint_fast64_t crc, const void *in_data, const uint64_t len) {
const uint8_t *data = in_data;
unsigned long long bit;
for (uint64_t offset = 0; offset < len; offset++) {
uint8_t c = data[offset];
for (uint_fast8_t i = 0x01; i & 0xff; i <<= 1) {
bit = crc & 0x8000000000000000;
if (c & i) {
bit = !bit;
}
crc <<= 1;
if (bit) {
crc ^= POLY;
}
}
crc &= 0xffffffffffffffff;
}
crc = crc & 0xffffffffffffffff;
return crc_reflect(crc, 64) ^ 0x0000000000000000;
}
/******************** END GENERATED PYCRC FUNCTIONS ********************/
/* Initializes the 16KB lookup tables. */
void crc64_init(void) {
crcspeed64native_init(_crc64, crc64_table);
}
/* Compute crc64 */
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
return crcspeed64native(crc64_table, crc, (void *) s, l);
uint64_t j;
for (j = 0; j < l; j++) {
uint8_t byte = s[j];
crc = crc64_tab[(uint8_t)crc ^ byte] ^ (crc >> 8);
}
return crc;
}
/* Test main */
@@ -130,31 +188,8 @@ uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l) {
int crc64Test(int argc, char *argv[]) {
UNUSED(argc);
UNUSED(argv);
crc64_init();
printf("[calcula]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
(uint64_t)_crc64(0, "123456789", 9));
printf("[64speed]: e9c6d914c4b8d9ca == %016" PRIx64 "\n",
(uint64_t)crc64(0, "123456789", 9));
char li[] = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed "
"do eiusmod tempor incididunt ut labore et dolore magna "
"aliqua. Ut enim ad minim veniam, quis nostrud exercitation "
"ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis "
"aute irure dolor in reprehenderit in voluptate velit esse "
"cillum dolore eu fugiat nulla pariatur. Excepteur sint "
"occaecat cupidatat non proident, sunt in culpa qui officia "
"deserunt mollit anim id est laborum.";
printf("[calcula]: c7794709e69683b3 == %016" PRIx64 "\n",
(uint64_t)_crc64(0, li, sizeof(li)));
printf("[64speed]: c7794709e69683b3 == %016" PRIx64 "\n",
(uint64_t)crc64(0, li, sizeof(li)));
printf("e9c6d914c4b8d9ca == %016llx\n",
(unsigned long long) crc64(0,(unsigned char*)"123456789",9));
return 0;
}
#endif
#ifdef REDIS_TEST_MAIN
int main(int argc, char *argv[]) {
return crc64Test(argc, argv);
}
#endif
-1
View File
@@ -3,7 +3,6 @@
#include <stdint.h>
void crc64_init(void);
uint64_t crc64(uint64_t crc, const unsigned char *s, uint64_t l);
#ifdef REDIS_TEST
-281
View File
@@ -1,281 +0,0 @@
/*
* Copyright (C) 2013 Mark Adler
* Originally by: crc64.c Version 1.4 16 Dec 2013 Mark Adler
* Modifications by Matt Stancliff <matt@genges.com>:
* - removed CRC64-specific behavior
* - added generation of lookup tables by parameters
* - removed inversion of CRC input/result
* - removed automatic initialization in favor of explicit initialization
This software is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
Mark Adler
madler@alumni.caltech.edu
*/
#include "crcspeed.h"
/* Fill in a CRC constants table. */
void crcspeed64little_init(crcfn64 crcfn, uint64_t table[8][256]) {
uint64_t crc;
/* generate CRCs for all single byte sequences */
for (int n = 0; n < 256; n++) {
table[0][n] = crcfn(0, &n, 1);
}
/* generate nested CRC table for future slice-by-8 lookup */
for (int n = 0; n < 256; n++) {
crc = table[0][n];
for (int k = 1; k < 8; k++) {
crc = table[0][crc & 0xff] ^ (crc >> 8);
table[k][n] = crc;
}
}
}
void crcspeed16little_init(crcfn16 crcfn, uint16_t table[8][256]) {
uint16_t crc;
/* generate CRCs for all single byte sequences */
for (int n = 0; n < 256; n++) {
table[0][n] = crcfn(0, &n, 1);
}
/* generate nested CRC table for future slice-by-8 lookup */
for (int n = 0; n < 256; n++) {
crc = table[0][n];
for (int k = 1; k < 8; k++) {
crc = table[0][(crc >> 8) & 0xff] ^ (crc << 8);
table[k][n] = crc;
}
}
}
/* Reverse the bytes in a 64-bit word. */
static inline uint64_t rev8(uint64_t a) {
#if defined(__GNUC__) || defined(__clang__)
return __builtin_bswap64(a);
#else
uint64_t m;
m = UINT64_C(0xff00ff00ff00ff);
a = ((a >> 8) & m) | (a & m) << 8;
m = UINT64_C(0xffff0000ffff);
a = ((a >> 16) & m) | (a & m) << 16;
return a >> 32 | a << 32;
#endif
}
/* This function is called once to initialize the CRC table for use on a
big-endian architecture. */
void crcspeed64big_init(crcfn64 fn, uint64_t big_table[8][256]) {
/* Create the little endian table then reverse all the entires. */
crcspeed64little_init(fn, big_table);
for (int k = 0; k < 8; k++) {
for (int n = 0; n < 256; n++) {
big_table[k][n] = rev8(big_table[k][n]);
}
}
}
void crcspeed16big_init(crcfn16 fn, uint16_t big_table[8][256]) {
/* Create the little endian table then reverse all the entires. */
crcspeed16little_init(fn, big_table);
for (int k = 0; k < 8; k++) {
for (int n = 0; n < 256; n++) {
big_table[k][n] = rev8(big_table[k][n]);
}
}
}
/* Calculate a non-inverted CRC multiple bytes at a time on a little-endian
* architecture. If you need inverted CRC, invert *before* calling and invert
* *after* calling.
* 64 bit crc = process 8 bytes at once;
*/
uint64_t crcspeed64little(uint64_t little_table[8][256], uint64_t crc,
void *buf, size_t len) {
unsigned char *next = buf;
/* process individual bytes until we reach an 8-byte aligned pointer */
while (len && ((uintptr_t)next & 7) != 0) {
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
len--;
}
/* fast middle processing, 8 bytes (aligned!) per loop */
while (len >= 8) {
crc ^= *(uint64_t *)next;
crc = little_table[7][crc & 0xff] ^
little_table[6][(crc >> 8) & 0xff] ^
little_table[5][(crc >> 16) & 0xff] ^
little_table[4][(crc >> 24) & 0xff] ^
little_table[3][(crc >> 32) & 0xff] ^
little_table[2][(crc >> 40) & 0xff] ^
little_table[1][(crc >> 48) & 0xff] ^
little_table[0][crc >> 56];
next += 8;
len -= 8;
}
/* process remaining bytes (can't be larger than 8) */
while (len) {
crc = little_table[0][(crc ^ *next++) & 0xff] ^ (crc >> 8);
len--;
}
return crc;
}
uint16_t crcspeed16little(uint16_t little_table[8][256], uint16_t crc,
void *buf, size_t len) {
unsigned char *next = buf;
/* process individual bytes until we reach an 8-byte aligned pointer */
while (len && ((uintptr_t)next & 7) != 0) {
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
len--;
}
/* fast middle processing, 8 bytes (aligned!) per loop */
while (len >= 8) {
uint64_t n = *(uint64_t *)next;
crc = little_table[7][(n & 0xff) ^ ((crc >> 8) & 0xff)] ^
little_table[6][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
little_table[5][(n >> 16) & 0xff] ^
little_table[4][(n >> 24) & 0xff] ^
little_table[3][(n >> 32) & 0xff] ^
little_table[2][(n >> 40) & 0xff] ^
little_table[1][(n >> 48) & 0xff] ^
little_table[0][n >> 56];
next += 8;
len -= 8;
}
/* process remaining bytes (can't be larger than 8) */
while (len) {
crc = little_table[0][((crc >> 8) ^ *next++) & 0xff] ^ (crc << 8);
len--;
}
return crc;
}
/* Calculate a non-inverted CRC eight bytes at a time on a big-endian
* architecture.
*/
uint64_t crcspeed64big(uint64_t big_table[8][256], uint64_t crc, void *buf,
size_t len) {
unsigned char *next = buf;
crc = rev8(crc);
while (len && ((uintptr_t)next & 7) != 0) {
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
len--;
}
while (len >= 8) {
crc ^= *(uint64_t *)next;
crc = big_table[0][crc & 0xff] ^
big_table[1][(crc >> 8) & 0xff] ^
big_table[2][(crc >> 16) & 0xff] ^
big_table[3][(crc >> 24) & 0xff] ^
big_table[4][(crc >> 32) & 0xff] ^
big_table[5][(crc >> 40) & 0xff] ^
big_table[6][(crc >> 48) & 0xff] ^
big_table[7][crc >> 56];
next += 8;
len -= 8;
}
while (len) {
crc = big_table[0][(crc >> 56) ^ *next++] ^ (crc << 8);
len--;
}
return rev8(crc);
}
/* WARNING: Completely untested on big endian architecture. Possibly broken. */
uint16_t crcspeed16big(uint16_t big_table[8][256], uint16_t crc_in, void *buf,
size_t len) {
unsigned char *next = buf;
uint64_t crc = crc_in;
crc = rev8(crc);
while (len && ((uintptr_t)next & 7) != 0) {
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
len--;
}
while (len >= 8) {
uint64_t n = *(uint64_t *)next;
crc = big_table[0][(n & 0xff) ^ ((crc >> (56 - 8)) & 0xff)] ^
big_table[1][((n >> 8) & 0xff) ^ (crc & 0xff)] ^
big_table[2][(n >> 16) & 0xff] ^
big_table[3][(n >> 24) & 0xff] ^
big_table[4][(n >> 32) & 0xff] ^
big_table[5][(n >> 40) & 0xff] ^
big_table[6][(n >> 48) & 0xff] ^
big_table[7][n >> 56];
next += 8;
len -= 8;
}
while (len) {
crc = big_table[0][((crc >> (56 - 8)) ^ *next++) & 0xff] ^ (crc >> 8);
len--;
}
return rev8(crc);
}
/* Return the CRC of buf[0..len-1] with initial crc, processing eight bytes
at a time using passed-in lookup table.
This selects one of two routines depending on the endianess of
the architecture. */
uint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,
size_t len) {
uint64_t n = 1;
return *(char *)&n ? crcspeed64little(table, crc, buf, len)
: crcspeed64big(table, crc, buf, len);
}
uint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,
size_t len) {
uint64_t n = 1;
return *(char *)&n ? crcspeed16little(table, crc, buf, len)
: crcspeed16big(table, crc, buf, len);
}
/* Initialize CRC lookup table in architecture-dependent manner. */
void crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]) {
uint64_t n = 1;
*(char *)&n ? crcspeed64little_init(fn, table)
: crcspeed64big_init(fn, table);
}
void crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]) {
uint64_t n = 1;
*(char *)&n ? crcspeed16little_init(fn, table)
: crcspeed16big_init(fn, table);
}
-60
View File
@@ -1,60 +0,0 @@
/* Copyright (c) 2014, Matt Stancliff <matt@genges.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE. */
#ifndef CRCSPEED_H
#define CRCSPEED_H
#include <inttypes.h>
#include <stdio.h>
typedef uint64_t (*crcfn64)(uint64_t, const void *, const uint64_t);
typedef uint16_t (*crcfn16)(uint16_t, const void *, const uint64_t);
/* CRC-64 */
void crcspeed64little_init(crcfn64 fn, uint64_t table[8][256]);
void crcspeed64big_init(crcfn64 fn, uint64_t table[8][256]);
void crcspeed64native_init(crcfn64 fn, uint64_t table[8][256]);
uint64_t crcspeed64little(uint64_t table[8][256], uint64_t crc, void *buf,
size_t len);
uint64_t crcspeed64big(uint64_t table[8][256], uint64_t crc, void *buf,
size_t len);
uint64_t crcspeed64native(uint64_t table[8][256], uint64_t crc, void *buf,
size_t len);
/* CRC-16 */
void crcspeed16little_init(crcfn16 fn, uint16_t table[8][256]);
void crcspeed16big_init(crcfn16 fn, uint16_t table[8][256]);
void crcspeed16native_init(crcfn16 fn, uint16_t table[8][256]);
uint16_t crcspeed16little(uint16_t table[8][256], uint16_t crc, void *buf,
size_t len);
uint16_t crcspeed16big(uint16_t table[8][256], uint16_t crc, void *buf,
size_t len);
uint16_t crcspeed16native(uint16_t table[8][256], uint16_t crc, void *buf,
size_t len);
#endif
+22 -71
View File
@@ -182,28 +182,9 @@ void dbAdd(redisDb *db, robj *key, robj *val) {
serverAssertWithInfo(NULL,key,retval == DICT_OK);
if (val->type == OBJ_LIST ||
val->type == OBJ_ZSET ||
val->type == OBJ_STREAM)
val->type == OBJ_ZSET)
signalKeyAsReady(db, key);
if (server.cluster_enabled) slotToKeyAdd(key->ptr);
}
/* This is a special version of dbAdd() that is used only when loading
* keys from the RDB file: the key is passed as an SDS string that is
* retained by the function (and not freed by the caller).
*
* Moreover this function will not abort if the key is already busy, to
* give more control to the caller, nor will signal the key as ready
* since it is not useful in this context.
*
* The function returns 1 if the key was added to the database, taking
* ownership of the SDS string, otherwise 0 is returned, and is up to the
* caller to free the SDS string. */
int dbAddRDBLoad(redisDb *db, sds key, robj *val) {
int retval = dictAdd(db->dict, key, val);
if (retval != DICT_OK) return 0;
if (server.cluster_enabled) slotToKeyAdd(key);
return 1;
}
/* Overwrite an existing key with a new value. Incrementing the reference
@@ -238,10 +219,8 @@ void dbOverwrite(redisDb *db, robj *key, robj *val) {
* 3) The expire time of the key is reset (the key is made persistent),
* unless 'keepttl' is true.
*
* All the new keys in the database should be created via this interface.
* The client 'c' argument may be set to NULL if the operation is performed
* in a context where there is no clear client performing the operation. */
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal) {
* All the new keys in the database should be created via this interface. */
void genericSetKey(redisDb *db, robj *key, robj *val, int keepttl) {
if (lookupKeyWrite(db,key) == NULL) {
dbAdd(db,key,val);
} else {
@@ -249,12 +228,12 @@ void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, in
}
incrRefCount(val);
if (!keepttl) removeExpire(db,key);
if (signal) signalModifiedKey(c,db,key);
signalModifiedKey(db,key);
}
/* Common case for genericSetKey() where the TTL is not retained. */
void setKey(client *c, redisDb *db, robj *key, robj *val) {
genericSetKey(c,db,key,val,0,1);
void setKey(redisDb *db, robj *key, robj *val) {
genericSetKey(db,key,val,0);
}
/* Return true if the specified key exists in the specified database.
@@ -308,7 +287,7 @@ int dbSyncDelete(redisDb *db, robj *key) {
* the key, because it is shared with the main dictionary. */
if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
if (dictDelete(db->dict,key->ptr) == DICT_OK) {
if (server.cluster_enabled) slotToKeyDel(key->ptr);
if (server.cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
@@ -469,11 +448,9 @@ long long dbTotalServerKeyCount() {
* Every time a DB is flushed the function signalFlushDb() is called.
*----------------------------------------------------------------------------*/
/* Note that the 'c' argument may be NULL if the key was modified out of
* a context of a client. */
void signalModifiedKey(client *c, redisDb *db, robj *key) {
void signalModifiedKey(redisDb *db, robj *key) {
touchWatchedKey(db,key);
trackingInvalidateKey(c,key);
trackingInvalidateKey(key);
}
void signalFlushedDb(int dbid) {
@@ -567,7 +544,7 @@ void delGenericCommand(client *c, int lazy) {
int deleted = lazy ? dbAsyncDelete(c->db,c->argv[j]) :
dbSyncDelete(c->db,c->argv[j]);
if (deleted) {
signalModifiedKey(c,c->db,c->argv[j]);
signalModifiedKey(c->db,c->argv[j]);
notifyKeyspaceEvent(NOTIFY_GENERIC,
"del",c->argv[j],c->db->id);
server.dirty++;
@@ -578,7 +555,7 @@ void delGenericCommand(client *c, int lazy) {
}
void delCommand(client *c) {
delGenericCommand(c,server.lazyfree_lazy_user_del);
delGenericCommand(c,0);
}
void unlinkCommand(client *c) {
@@ -1007,8 +984,8 @@ void renameGenericCommand(client *c, int nx) {
dbAdd(c->db,c->argv[2],o);
if (expire != -1) setExpire(c,c->db,c->argv[2],expire);
dbDelete(c->db,c->argv[1]);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c,c->db,c->argv[2]);
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_from",
c->argv[1],c->db->id);
notifyKeyspaceEvent(NOTIFY_GENERIC,"rename_to",
@@ -1076,8 +1053,8 @@ void moveCommand(client *c) {
/* OK! key moved, free the entry in the source DB */
dbDelete(src,c->argv[1]);
signalModifiedKey(c,src,c->argv[1]);
signalModifiedKey(c,dst,c->argv[1]);
signalModifiedKey(src,c->argv[1]);
signalModifiedKey(dst,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,
"move_from",c->argv[1],src->id);
notifyKeyspaceEvent(NOTIFY_GENERIC,
@@ -1321,7 +1298,7 @@ int expireIfNeeded(redisDb *db, robj *key) {
"expired",key,db->id);
int retval = server.lazyfree_lazy_expire ? dbAsyncDelete(db,key) :
dbSyncDelete(db,key);
if (retval) signalModifiedKey(NULL,db,key);
if (retval) signalModifiedKey(db,key);
return retval;
}
@@ -1576,32 +1553,6 @@ int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numk
return keys;
}
/* LCS ... [KEYS <key1> <key2>] ... */
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
{
int i;
int *keys = getKeysTempBuffer;
UNUSED(cmd);
/* We need to parse the options of the command in order to check for the
* "KEYS" argument before the "STRINGS" argument. */
for (i = 1; i < argc; i++) {
char *arg = argv[i]->ptr;
int moreargs = (argc-1) - i;
if (!strcasecmp(arg, "strings")) {
break;
} else if (!strcasecmp(arg, "keys") && moreargs >= 2) {
keys[0] = i+1;
keys[1] = i+2;
*numkeys = 2;
return keys;
}
}
*numkeys = 0;
return keys;
}
/* Helper function to extract keys from memory command.
* MEMORY USAGE <key> */
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys) {
@@ -1669,17 +1620,17 @@ int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys)
* a fast way a key that belongs to a specified hash slot. This is useful
* while rehashing the cluster and in other conditions when we need to
* understand if we have keys for a given hash slot. */
void slotToKeyUpdateKey(sds key, int add) {
size_t keylen = sdslen(key);
unsigned int hashslot = keyHashSlot(key,keylen);
void slotToKeyUpdateKey(robj *key, int add) {
unsigned int hashslot = keyHashSlot(key->ptr,sdslen(key->ptr));
unsigned char buf[64];
unsigned char *indexed = buf;
size_t keylen = sdslen(key->ptr);
server.cluster->slots_keys_count[hashslot] += add ? 1 : -1;
if (keylen+2 > 64) indexed = zmalloc(keylen+2);
indexed[0] = (hashslot >> 8) & 0xff;
indexed[1] = hashslot & 0xff;
memcpy(indexed+2,key,keylen);
memcpy(indexed+2,key->ptr,keylen);
if (add) {
raxInsert(server.cluster->slots_to_keys,indexed,keylen+2,NULL,NULL);
} else {
@@ -1688,11 +1639,11 @@ void slotToKeyUpdateKey(sds key, int add) {
if (indexed != buf) zfree(indexed);
}
void slotToKeyAdd(sds key) {
void slotToKeyAdd(robj *key) {
slotToKeyUpdateKey(key,1);
}
void slotToKeyDel(sds key) {
void slotToKeyDel(robj *key) {
slotToKeyUpdateKey(key,0);
}
+38 -97
View File
@@ -366,7 +366,7 @@ void debugCommand(client *c) {
"OOM -- Crash the server simulating an out-of-memory error.",
"PANIC -- Crash the server simulating a panic.",
"POPULATE <count> [prefix] [size] -- Create <count> string keys named key:<num>. If a prefix is specified is used instead of the 'key' prefix.",
"RELOAD [MERGE] [NOFLUSH] [NOSAVE] -- Save the RDB on disk and reload it back in memory. By default it will save the RDB file and load it back. With the NOFLUSH option the current database is not removed before loading the new one, but conficts in keys will kill the server with an exception. When MERGE is used, conflicting keys will be loaded (the key in the loaded RDB file will win). When NOSAVE is used, the server will not save the current dataset in the RDB file before loading. Use DEBUG RELOAD NOSAVE when you want just to load the RDB file you placed in the Redis working directory in order to replace the current dataset in memory. Use DEBUG RELOAD NOSAVE NOFLUSH MERGE when you want to add what is in the current RDB file placed in the Redis current directory, with the current memory content. Use DEBUG RELOAD when you want to verify Redis is able to persist the current dataset in the RDB file, flush the memory content, and load it back.",
"RELOAD -- Save the RDB on disk and reload it back in memory.",
"RESTART -- Graceful restart: save config, db, restart.",
"SDSLEN <key> -- Show low level SDS string info representing key and value.",
"SEGFAULT -- Crash the server with sigsegv.",
@@ -411,44 +411,15 @@ NULL
serverLog(LL_WARNING, "DEBUG LOG: %s", (char*)c->argv[2]->ptr);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"reload")) {
int flush = 1, save = 1;
int flags = RDBFLAGS_NONE;
/* Parse the additional options that modify the RELOAD
* behavior. */
for (int j = 2; j < c->argc; j++) {
char *opt = c->argv[j]->ptr;
if (!strcasecmp(opt,"MERGE")) {
flags |= RDBFLAGS_ALLOW_DUP;
} else if (!strcasecmp(opt,"NOFLUSH")) {
flush = 0;
} else if (!strcasecmp(opt,"NOSAVE")) {
save = 0;
} else {
addReplyError(c,"DEBUG RELOAD only supports the "
"MERGE, NOFLUSH and NOSAVE options.");
return;
}
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {
addReply(c,shared.err);
return;
}
/* The default beahvior is to save the RDB file before loading
* it back. */
if (save) {
rdbSaveInfo rsi, *rsiptr;
rsiptr = rdbPopulateSaveInfo(&rsi);
if (rdbSave(server.rdb_filename,rsiptr) != C_OK) {
addReply(c,shared.err);
return;
}
}
/* The default behavior is to remove the current dataset from
* memory before loading the RDB file, however when MERGE is
* used together with NOFLUSH, we are able to merge two datasets. */
if (flush) emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
emptyDb(-1,EMPTYDB_NO_FLAGS,NULL);
protectClient(c);
int ret = rdbLoad(server.rdb_filename,NULL,flags);
int ret = rdbLoad(server.rdb_filename,NULL,RDBFLAGS_NONE);
unprotectClient(c);
if (ret != C_OK) {
addReplyError(c,"Error trying to load the RDB dump");
@@ -519,7 +490,7 @@ NULL
"encoding:%s serializedlength:%zu "
"lru:%d lru_seconds_idle:%llu%s",
(void*)val, val->refcount,
strenc, rdbSavedObjectLen(val, c->argv[2]),
strenc, rdbSavedObjectLen(val),
val->lru, estimateObjectIdleTime(val)/1000, extra);
} else if (!strcasecmp(c->argv[1]->ptr,"sdslen") && c->argc == 3) {
dictEntry *de;
@@ -588,7 +559,7 @@ NULL
memcpy(val->ptr, buf, valsize<=buflen? valsize: buflen);
}
dbAdd(c->db,key,val);
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
decrRefCount(key);
}
addReply(c,shared.ok);
@@ -846,8 +817,6 @@ void serverLogObjectDebugInfo(const robj *o) {
serverLog(LL_WARNING,"Sorted set size: %d", (int) zsetLength(o));
if (o->encoding == OBJ_ENCODING_SKIPLIST)
serverLog(LL_WARNING,"Skiplist level: %d", (int) ((const zset*)o->ptr)->zsl->level);
} else if (o->type == OBJ_STREAM) {
serverLog(LL_WARNING,"Stream size: %d", (int) streamLength(o));
}
}
@@ -1076,61 +1045,6 @@ void logRegisters(ucontext_t *uc) {
(unsigned long) uc->uc_mcontext.gregs[18]
);
logStackContent((void**)uc->uc_mcontext.gregs[15]);
#elif defined(__aarch64__) /* Linux AArch64 */
serverLog(LL_WARNING,
"\n"
"X18:%016lx X19:%016lx\nX20:%016lx X21:%016lx\n"
"X22:%016lx X23:%016lx\nX24:%016lx X25:%016lx\n"
"X26:%016lx X27:%016lx\nX28:%016lx X29:%016lx\n"
"X30:%016lx\n"
"pc:%016lx sp:%016lx\npstate:%016lx fault_address:%016lx\n",
(unsigned long) uc->uc_mcontext.regs[18],
(unsigned long) uc->uc_mcontext.regs[19],
(unsigned long) uc->uc_mcontext.regs[20],
(unsigned long) uc->uc_mcontext.regs[21],
(unsigned long) uc->uc_mcontext.regs[22],
(unsigned long) uc->uc_mcontext.regs[23],
(unsigned long) uc->uc_mcontext.regs[24],
(unsigned long) uc->uc_mcontext.regs[25],
(unsigned long) uc->uc_mcontext.regs[26],
(unsigned long) uc->uc_mcontext.regs[27],
(unsigned long) uc->uc_mcontext.regs[28],
(unsigned long) uc->uc_mcontext.regs[29],
(unsigned long) uc->uc_mcontext.regs[30],
(unsigned long) uc->uc_mcontext.pc,
(unsigned long) uc->uc_mcontext.sp,
(unsigned long) uc->uc_mcontext.pstate,
(unsigned long) uc->uc_mcontext.fault_address
);
logStackContent((void**)uc->uc_mcontext.sp);
#elif defined(__arm__) /* Linux ARM */
serverLog(LL_WARNING,
"\n"
"R10:%016lx R9 :%016lx\nR8 :%016lx R7 :%016lx\n"
"R6 :%016lx R5 :%016lx\nR4 :%016lx R3 :%016lx\n"
"R2 :%016lx R1 :%016lx\nR0 :%016lx EC :%016lx\n"
"fp: %016lx ip:%016lx\n",
"pc:%016lx sp:%016lx\ncpsr:%016lx fault_address:%016lx\n",
(unsigned long) uc->uc_mcontext.arm_r10,
(unsigned long) uc->uc_mcontext.arm_r9,
(unsigned long) uc->uc_mcontext.arm_r8,
(unsigned long) uc->uc_mcontext.arm_r7,
(unsigned long) uc->uc_mcontext.arm_r6,
(unsigned long) uc->uc_mcontext.arm_r5,
(unsigned long) uc->uc_mcontext.arm_r4,
(unsigned long) uc->uc_mcontext.arm_r3,
(unsigned long) uc->uc_mcontext.arm_r2,
(unsigned long) uc->uc_mcontext.arm_r1,
(unsigned long) uc->uc_mcontext.arm_r0,
(unsigned long) uc->uc_mcontext.error_code,
(unsigned long) uc->uc_mcontext.arm_fp,
(unsigned long) uc->uc_mcontext.arm_ip,
(unsigned long) uc->uc_mcontext.arm_pc,
(unsigned long) uc->uc_mcontext.arm_sp,
(unsigned long) uc->uc_mcontext.arm_cpsr,
(unsigned long) uc->uc_mcontext.fault_address
);
logStackContent((void**)uc->uc_mcontext.arm_sp);
#endif
#elif defined(__FreeBSD__)
#if defined(__x86_64__)
@@ -1271,6 +1185,33 @@ void logRegisters(ucontext_t *uc) {
(unsigned long) uc->uc_mcontext.mc_cs
);
logStackContent((void**)uc->uc_mcontext.mc_rsp);
#elif defined(__aarch64__) /* Linux AArch64 */
serverLog(LL_WARNING,
"\n"
"X18:%016lx X19:%016lx\nX20:%016lx X21:%016lx\n"
"X22:%016lx X23:%016lx\nX24:%016lx X25:%016lx\n"
"X26:%016lx X27:%016lx\nX28:%016lx X29:%016lx\n"
"X30:%016lx\n"
"pc:%016lx sp:%016lx\npstate:%016lx fault_address:%016lx\n",
(unsigned long) uc->uc_mcontext.regs[18],
(unsigned long) uc->uc_mcontext.regs[19],
(unsigned long) uc->uc_mcontext.regs[20],
(unsigned long) uc->uc_mcontext.regs[21],
(unsigned long) uc->uc_mcontext.regs[22],
(unsigned long) uc->uc_mcontext.regs[23],
(unsigned long) uc->uc_mcontext.regs[24],
(unsigned long) uc->uc_mcontext.regs[25],
(unsigned long) uc->uc_mcontext.regs[26],
(unsigned long) uc->uc_mcontext.regs[27],
(unsigned long) uc->uc_mcontext.regs[28],
(unsigned long) uc->uc_mcontext.regs[29],
(unsigned long) uc->uc_mcontext.regs[30],
(unsigned long) uc->uc_mcontext.pc,
(unsigned long) uc->uc_mcontext.sp,
(unsigned long) uc->uc_mcontext.pstate,
(unsigned long) uc->uc_mcontext.fault_address
);
logStackContent((void**)uc->uc_mcontext.sp);
#else
serverLog(LL_WARNING,
" Dumping of registers not supported for this OS/arch");
@@ -1636,7 +1577,7 @@ void enableWatchdog(int period) {
/* Watchdog was actually disabled, so we have to setup the signal
* handler. */
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
act.sa_flags = SA_ONSTACK | SA_SIGINFO;
act.sa_sigaction = watchdogSignalHandler;
sigaction(SIGALRM, &act, NULL);
}
+3 -3
View File
@@ -134,7 +134,7 @@ int _dictInit(dict *d, dictType *type,
* but with the invariant of a USED/BUCKETS ratio near to <= 1 */
int dictResize(dict *d)
{
unsigned long minimal;
int minimal;
if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
minimal = d->ht[0].used;
@@ -766,8 +766,8 @@ dictEntry *dictGetFairRandomKey(dict *d) {
/* Function to reverse bits. Algorithm from:
* http://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel */
static unsigned long rev(unsigned long v) {
unsigned long s = CHAR_BIT * sizeof(v); // bit size; must be power of 2
unsigned long mask = ~0UL;
unsigned long s = 8 * sizeof(v); // bit size; must be power of 2
unsigned long mask = ~0;
while ((s >>= 1) > 0) {
mask ^= (mask << s);
v = ((v >> s) & mask) | ((v << s) & ~mask);
+13 -19
View File
@@ -450,10 +450,9 @@ int freeMemoryIfNeeded(void) {
if (server.masterhost && server.repl_slave_ignore_maxmemory) return C_OK;
size_t mem_reported, mem_tofree, mem_freed;
mstime_t latency, eviction_latency, lazyfree_latency;
mstime_t latency, eviction_latency;
long long delta;
int slaves = listLength(server.slaves);
int result = C_ERR;
/* When clients are paused the dataset should be static not just from the
* POV of clients not being able to write, but also from the POV of
@@ -464,10 +463,10 @@ int freeMemoryIfNeeded(void) {
mem_freed = 0;
latencyStartMonitor(latency);
if (server.maxmemory_policy == MAXMEMORY_NO_EVICTION)
goto cant_free; /* We need to free memory, but policy forbids. */
latencyStartMonitor(latency);
while (mem_freed < mem_tofree) {
int j, k, i;
static unsigned int next_db = 0;
@@ -570,9 +569,9 @@ int freeMemoryIfNeeded(void) {
dbAsyncDelete(db,keyobj);
else
dbSyncDelete(db,keyobj);
signalModifiedKey(NULL,db,keyobj);
latencyEndMonitor(eviction_latency);
latencyAddSampleIfNeeded("eviction-del",eviction_latency);
latencyRemoveNestedEvent(latency,eviction_latency);
delta -= (long long) zmalloc_used_memory();
mem_freed += delta;
server.stat_evictedkeys++;
@@ -601,30 +600,25 @@ int freeMemoryIfNeeded(void) {
}
}
} else {
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("eviction-cycle",latency);
goto cant_free; /* nothing to free... */
}
}
result = C_OK;
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("eviction-cycle",latency);
return C_OK;
cant_free:
/* We are here if we are not able to reclaim memory. There is only one
* last thing we can try: check if the lazyfree thread has jobs in queue
* and wait... */
if (result != C_OK) {
latencyStartMonitor(lazyfree_latency);
while(bioPendingJobsOfType(BIO_LAZY_FREE)) {
if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {
result = C_OK;
break;
}
usleep(1000);
}
latencyEndMonitor(lazyfree_latency);
latencyAddSampleIfNeeded("eviction-lazyfree",lazyfree_latency);
while(bioPendingJobsOfType(BIO_LAZY_FREE)) {
if (((mem_reported - zmalloc_used_memory()) + mem_freed) >= mem_tofree)
break;
usleep(1000);
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("eviction-cycle",latency);
return result;
return C_ERR;
}
/* This is a wrapper for freeMemoryIfNeeded() that only really calls the
+3 -4
View File
@@ -64,7 +64,7 @@ int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
dbSyncDelete(db,keyobj);
notifyKeyspaceEvent(NOTIFY_EXPIRED,
"expired",keyobj,db->id);
trackingInvalidateKey(NULL,keyobj);
trackingInvalidateKey(keyobj);
decrRefCount(keyobj);
server.stat_expiredkeys++;
return 1;
@@ -519,14 +519,14 @@ void expireGenericCommand(client *c, long long basetime, int unit) {
/* Replicate/AOF this as an explicit DEL or UNLINK. */
aux = server.lazyfree_lazy_expire ? shared.unlink : shared.del;
rewriteClientCommandVector(c,2,aux,key);
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
addReply(c, shared.cone);
return;
} else {
setExpire(c,c->db,key,when);
addReply(c,shared.cone);
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_GENERIC,"expire",key,c->db->id);
server.dirty++;
return;
@@ -590,7 +590,6 @@ void pttlCommand(client *c) {
void persistCommand(client *c) {
if (lookupKeyWrite(c->db,c->argv[1])) {
if (removeExpire(c->db,c->argv[1])) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"persist",c->argv[1],c->db->id);
addReply(c,shared.cone);
server.dirty++;
} else {
+2 -2
View File
@@ -657,13 +657,13 @@ void georadiusGeneric(client *c, int flags) {
if (returned_items) {
zsetConvertToZiplistIfNeeded(zobj,maxelelen);
setKey(c,c->db,storekey,zobj);
setKey(c->db,storekey,zobj);
decrRefCount(zobj);
notifyKeyspaceEvent(NOTIFY_ZSET,"georadiusstore",storekey,
c->db->id);
server.dirty += returned_items;
} else if (dbDelete(c->db,storekey)) {
signalModifiedKey(c,c->db,storekey);
signalModifiedKey(c->db,storekey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",storekey,c->db->id);
server.dirty++;
}
-4
View File
@@ -206,11 +206,7 @@ int geohashDecodeWGS84(const GeoHashBits hash, GeoHashArea *area) {
int geohashDecodeAreaToLongLat(const GeoHashArea *area, double *xy) {
if (!xy) return 0;
xy[0] = (area->longitude.min + area->longitude.max) / 2;
if (xy[0] > GEO_LONG_MAX) xy[0] = GEO_LONG_MAX;
if (xy[0] < GEO_LONG_MIN) xy[0] = GEO_LONG_MIN;
xy[1] = (area->latitude.min + area->latitude.max) / 2;
if (xy[1] > GEO_LAT_MAX) xy[1] = GEO_LAT_MAX;
if (xy[1] < GEO_LAT_MIN) xy[1] = GEO_LAT_MIN;
return 1;
}
+34 -179
View File
@@ -28,56 +28,6 @@ struct commandHelp {
int group;
char *since;
} commandHelp[] = {
{ "ACL CAT",
"[categoryname]",
"List the ACL categories or the commands inside a category",
9,
"6.0.0" },
{ "ACL DELUSER",
"username [username ...]",
"Remove the specified ACL users and the associated rules",
9,
"6.0.0" },
{ "ACL GENPASS",
"[bits]",
"Generate a pseudorandom secure password to use for ACL users",
9,
"6.0.0" },
{ "ACL LIST",
"-",
"List the current ACL rules in ACL config file format",
9,
"6.0.0" },
{ "ACL LOAD",
"-",
"Reload the ACLs from the configured ACL file",
9,
"6.0.0" },
{ "ACL LOG",
"[count or RESET]",
"List latest events denied because of ACLs in place",
9,
"6.0.0" },
{ "ACL SAVE",
"-",
"Save the current ACL rules in the configured ACL file",
9,
"6.0.0" },
{ "ACL SETUSER",
"rule [rule ...]",
"Modify or create the rules for a specific ACL user",
9,
"6.0.0" },
{ "ACL USERS",
"-",
"List the username of all the configured ACL rules",
9,
"6.0.0" },
{ "ACL WHOAMI",
"-",
"Return the name of the user associated to the current connection",
9,
"6.0.0" },
{ "APPEND",
"key value",
"Append a value to a key",
@@ -94,7 +44,7 @@ struct commandHelp {
9,
"1.0.0" },
{ "BGSAVE",
"[SCHEDULE]",
"-",
"Asynchronously save the dataset to disk",
9,
"1.0.0" },
@@ -130,7 +80,7 @@ struct commandHelp {
"2.0.0" },
{ "BRPOPLPUSH",
"source destination timeout",
"Pop an element from a list, push it to another list and return it; or block until one is available",
"Pop a value from a list, push it to another list and return it; or block until one is available",
2,
"2.2.0" },
{ "BZPOPMAX",
@@ -143,71 +93,51 @@ struct commandHelp {
"Remove and return the member with the lowest score from one or more sorted sets, or block until one is available",
4,
"5.0.0" },
{ "CLIENT CACHING",
"YES|NO",
"Instruct the server about tracking or not keys in the next request",
8,
"6.0.0" },
{ "CLIENT GETNAME",
"-",
"Get the current connection name",
8,
9,
"2.6.9" },
{ "CLIENT GETREDIR",
"-",
"Get tracking notifications redirection client ID if any",
8,
"6.0.0" },
{ "CLIENT ID",
"-",
"Returns the client ID for the current connection",
8,
9,
"5.0.0" },
{ "CLIENT KILL",
"[ip:port] [ID client-id] [TYPE normal|master|slave|pubsub] [ADDR ip:port] [SKIPME yes/no]",
"Kill the connection of a client",
8,
9,
"2.4.0" },
{ "CLIENT LIST",
"[TYPE normal|master|replica|pubsub]",
"-",
"Get the list of client connections",
8,
9,
"2.4.0" },
{ "CLIENT PAUSE",
"timeout",
"Stop processing commands from clients for some time",
8,
9,
"2.9.50" },
{ "CLIENT REPLY",
"ON|OFF|SKIP",
"Instruct the server whether to reply to commands",
8,
9,
"3.2" },
{ "CLIENT SETNAME",
"connection-name",
"Set the current connection name",
8,
9,
"2.6.9" },
{ "CLIENT TRACKING",
"ON|OFF [REDIRECT client-id] [PREFIX prefix] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
"Enable or disable server assisted client side caching support",
8,
"6.0.0" },
{ "CLIENT UNBLOCK",
"client-id [TIMEOUT|ERROR]",
"Unblock a client blocked in a blocking command from a different connection",
8,
9,
"5.0.0" },
{ "CLUSTER ADDSLOTS",
"slot [slot ...]",
"Assign new hash slots to receiving node",
12,
"3.0.0" },
{ "CLUSTER BUMPEPOCH",
"-",
"Advance the cluster config epoch",
12,
"3.0.0" },
{ "CLUSTER COUNT-FAILURE-REPORTS",
"node-id",
"Return the number of failure reports active for a given node",
@@ -228,11 +158,6 @@ struct commandHelp {
"Forces a replica to perform a manual failover of its master.",
12,
"3.0.0" },
{ "CLUSTER FLUSHSLOTS",
"-",
"Delete a node's own slots information",
12,
"3.0.0" },
{ "CLUSTER FORGET",
"node-id",
"Remove a node from the nodes table",
@@ -258,11 +183,6 @@ struct commandHelp {
"Force a node cluster to handshake with another node",
12,
"3.0.0" },
{ "CLUSTER MYID",
"-",
"Return the node id",
12,
"3.0.0" },
{ "CLUSTER NODES",
"-",
"Get Cluster config for the node",
@@ -439,7 +359,7 @@ struct commandHelp {
13,
"3.2.0" },
{ "GEODIST",
"key member1 member2 [m|km|ft|mi]",
"key member1 member2 [unit]",
"Returns the distance between two members of a geospatial index",
13,
"3.2.0" },
@@ -488,11 +408,6 @@ struct commandHelp {
"Delete one or more hash fields",
5,
"2.0.0" },
{ "HELLO",
"protover [AUTH username password] [SETNAME clientname]",
"switch Redis protocol",
8,
"6.0.0" },
{ "HEXISTS",
"key field",
"Determine if a hash field exists",
@@ -544,7 +459,7 @@ struct commandHelp {
5,
"2.8.0" },
{ "HSET",
"key field value [field value ...]",
"key field value",
"Set the string value of a hash field",
5,
"2.0.0" },
@@ -593,43 +508,13 @@ struct commandHelp {
"Get the UNIX time stamp of the last successful save to disk",
9,
"1.0.0" },
{ "LATENCY DOCTOR",
"-",
"Return a human readable latency analysis report.",
9,
"2.8.13" },
{ "LATENCY GRAPH",
"event",
"Return a latency graph for the event.",
9,
"2.8.13" },
{ "LATENCY HELP",
"-",
"Show helpful text about the different subcommands.",
9,
"2.8.13" },
{ "LATENCY HISTORY",
"event",
"Return timestamp-latency samples for the event.",
9,
"2.8.13" },
{ "LATENCY LATEST",
"-",
"Return the latest latency samples for all events.",
9,
"2.8.13" },
{ "LATENCY RESET",
"[event]",
"Reset latency data for one or more events.",
9,
"2.8.13" },
{ "LINDEX",
"key index",
"Get an element from a list by its index",
2,
"1.0.0" },
{ "LINSERT",
"key BEFORE|AFTER pivot element",
"key BEFORE|AFTER pivot value",
"Insert an element before or after another element in a list",
2,
"2.2.0" },
@@ -638,24 +523,19 @@ struct commandHelp {
"Get the length of a list",
2,
"1.0.0" },
{ "LOLWUT",
"[VERSION version]",
"Display some computer art and the Redis version",
9,
"5.0.0" },
{ "LPOP",
"key",
"Remove and get the first element in a list",
2,
"1.0.0" },
{ "LPUSH",
"key element [element ...]",
"Prepend one or multiple elements to a list",
"key value [value ...]",
"Prepend one or multiple values to a list",
2,
"1.0.0" },
{ "LPUSHX",
"key element [element ...]",
"Prepend an element to a list, only if the list exists",
"key value",
"Prepend a value to a list, only if the list exists",
2,
"2.2.0" },
{ "LRANGE",
@@ -664,12 +544,12 @@ struct commandHelp {
2,
"1.0.0" },
{ "LREM",
"key count element",
"key count value",
"Remove elements from a list",
2,
"1.0.0" },
{ "LSET",
"key index element",
"key index value",
"Set the value of an element in a list by its index",
2,
"1.0.0" },
@@ -714,25 +594,10 @@ struct commandHelp {
1,
"1.0.0" },
{ "MIGRATE",
"host port key|"" destination-db timeout [COPY] [REPLACE] [AUTH password] [KEYS key]",
"host port key|"" destination-db timeout [COPY] [REPLACE] [KEYS key]",
"Atomically transfer a key from a Redis instance to another one.",
0,
"2.6.0" },
{ "MODULE LIST",
"-",
"List all modules loaded by the server",
9,
"4.0.0" },
{ "MODULE LOAD",
"path [arg]",
"Load a module",
9,
"4.0.0" },
{ "MODULE UNLOAD",
"name",
"Unload a module",
9,
"4.0.0" },
{ "MONITOR",
"-",
"Listen for all requests received by the server in real time",
@@ -808,11 +673,6 @@ struct commandHelp {
"Listen for messages published to channels matching the given patterns",
6,
"2.0.0" },
{ "PSYNC",
"replicationid offset",
"Internal command used for replication",
9,
"2.8.0" },
{ "PTTL",
"key",
"Get the time to live for a key in milliseconds",
@@ -869,7 +729,7 @@ struct commandHelp {
9,
"5.0.0" },
{ "RESTORE",
"key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]",
"key ttl serialized-value [REPLACE]",
"Create a key using the provided serialized value, previously obtained using DUMP.",
0,
"2.6.0" },
@@ -889,13 +749,13 @@ struct commandHelp {
2,
"1.2.0" },
{ "RPUSH",
"key element [element ...]",
"Append one or multiple elements to a list",
"key value [value ...]",
"Append one or multiple values to a list",
2,
"1.0.0" },
{ "RPUSHX",
"key element [element ...]",
"Append an element to a list, only if the list exists",
"key value",
"Append a value to a list, only if the list exists",
2,
"2.2.0" },
{ "SADD",
@@ -909,7 +769,7 @@ struct commandHelp {
9,
"1.0.0" },
{ "SCAN",
"cursor [MATCH pattern] [COUNT count] [TYPE type]",
"cursor [MATCH pattern] [COUNT count]",
"Incrementally iterate the keys space",
0,
"2.8.0" },
@@ -959,7 +819,7 @@ struct commandHelp {
8,
"1.0.0" },
{ "SET",
"key value [EX seconds|PX milliseconds] [NX|XX] [KEEPTTL]",
"key value [expiration EX seconds|PX milliseconds] [NX|XX]",
"Set the string value of a key",
1,
"1.0.0" },
@@ -1048,11 +908,6 @@ struct commandHelp {
"Incrementally iterate Set elements",
3,
"2.8.0" },
{ "STRALGO",
"LCS algo-specific-argument [algo-specific-argument ...]",
"Run algorithms (currently LCS) against strings",
1,
"6.0.0" },
{ "STRLEN",
"key",
"Get the length of the value stored in a key",
@@ -1074,9 +929,9 @@ struct commandHelp {
3,
"1.0.0" },
{ "SWAPDB",
"index1 index2",
"index index",
"Swaps two Redis databases",
9,
8,
"4.0.0" },
{ "SYNC",
"-",
@@ -1134,7 +989,7 @@ struct commandHelp {
14,
"5.0.0" },
{ "XADD",
"key ID field value [field value ...]",
"key ID field string [field string ...]",
"Appends a new entry to a stream",
14,
"5.0.0" },
@@ -1149,7 +1004,7 @@ struct commandHelp {
14,
"5.0.0" },
{ "XGROUP",
"[CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]",
"[CREATE key groupname id-or-$] [SETID key id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]",
"Create, destroy, and manage consumer groups.",
14,
"5.0.0" },
@@ -1174,12 +1029,12 @@ struct commandHelp {
14,
"5.0.0" },
{ "XREAD",
"[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]",
"[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]",
"Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.",
14,
"5.0.0" },
{ "XREADGROUP",
"GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] ID [ID ...]",
"GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]",
"Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.",
14,
"5.0.0" },
+3 -3
View File
@@ -1209,7 +1209,7 @@ void pfaddCommand(client *c) {
}
hdr = o->ptr;
if (updated) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id);
server.dirty++;
HLL_INVALIDATE_CACHE(hdr);
@@ -1300,7 +1300,7 @@ void pfcountCommand(client *c) {
* data structure is not modified, since the cached value
* may be modified and given that the HLL is a Redis string
* we need to propagate the change. */
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
addReplyLongLong(c,card);
@@ -1373,7 +1373,7 @@ void pfmergeCommand(client *c) {
last hllSparseSet() call. */
HLL_INVALIDATE_CACHE(hdr);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
/* We generate a PFADD event for PFMERGE for semantical simplicity
* since in theory this is a mass-add of elements. */
notifyKeyspaceEvent(NOTIFY_STRING,"pfadd",c->argv[1],c->db->id);
+1 -1
View File
@@ -83,7 +83,7 @@ int dbAsyncDelete(redisDb *db, robj *key) {
* field to NULL in order to lazy free it later. */
if (de) {
dictFreeUnlinkedEntry(db->dict,de);
if (server.cluster_enabled) slotToKeyDel(key->ptr);
if (server.cluster_enabled) slotToKeyDel(key);
return 1;
} else {
return 0;
-6
View File
@@ -34,10 +34,6 @@
/* This represents a very simple generic canvas in order to draw stuff.
* It's up to each LOLWUT versions to translate what they draw to the
* screen, depending on the result to accomplish. */
#ifndef __LOLWUT_H
#define __LOLWUT_H
typedef struct lwCanvas {
int width;
int height;
@@ -51,5 +47,3 @@ void lwDrawPixel(lwCanvas *canvas, int x, int y, int color);
int lwGetPixel(lwCanvas *canvas, int x, int y);
void lwDrawLine(lwCanvas *canvas, int x1, int y1, int x2, int y2, int color);
void lwDrawSquare(lwCanvas *canvas, int x, int y, float size, float angle, int color);
#endif
+18 -14
View File
@@ -896,7 +896,7 @@ void RM_SetModuleOptions(RedisModuleCtx *ctx, int options) {
/* Signals that the key is modified from user's perspective (i.e. invalidate WATCH
* and client side caching). */
int RM_SignalModifiedKey(RedisModuleCtx *ctx, RedisModuleString *keyname) {
signalModifiedKey(ctx->client,ctx->client->db,keyname);
signalModifiedKey(ctx->client->db,keyname);
return REDISMODULE_OK;
}
@@ -2016,7 +2016,7 @@ void *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
static void moduleCloseKey(RedisModuleKey *key) {
int signal = SHOULD_SIGNAL_MODIFIED_KEYS(key->ctx);
if ((key->mode & REDISMODULE_WRITE) && signal)
signalModifiedKey(key->ctx->client,key->db,key->key);
signalModifiedKey(key->db,key->key);
/* TODO: if (key->iter) RM_KeyIteratorStop(kp); */
RM_ZsetRangeStop(key);
decrRefCount(key->key);
@@ -2157,7 +2157,7 @@ RedisModuleString *RM_RandomKey(RedisModuleCtx *ctx) {
int RM_StringSet(RedisModuleKey *key, RedisModuleString *str) {
if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;
RM_DeleteKey(key);
genericSetKey(key->ctx->client,key->db,key->key,str,0,0);
setKey(key->db,key->key,str);
key->value = str;
return REDISMODULE_OK;
}
@@ -2237,7 +2237,7 @@ int RM_StringTruncate(RedisModuleKey *key, size_t newlen) {
if (key->value == NULL) {
/* Empty key: create it with the new size. */
robj *o = createObject(OBJ_STRING,sdsnewlen(NULL, newlen));
genericSetKey(key->ctx->client,key->db,key->key,o,0,0);
setKey(key->db,key->key,o);
key->value = o;
decrRefCount(o);
} else {
@@ -3326,8 +3326,13 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
* a Lua script in the context of AOF and slaves. */
if (replicate) moduleReplicateMultiIfNeeded(ctx);
if (ctx->client->flags & CLIENT_MULTI ||
ctx->flags & REDISMODULE_CTX_MULTI_EMITTED) {
c->flags |= CLIENT_MULTI;
}
/* Run the command */
int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_NOWRAP;
int call_flags = CMD_CALL_SLOWLOG | CMD_CALL_STATS;
if (replicate) {
if (!(flags & REDISMODULE_ARGV_NO_AOF))
call_flags |= CMD_CALL_PROPAGATE_AOF;
@@ -3336,7 +3341,9 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
}
call(c,call_flags);
/* Convert the result of the Redis command into a module reply. */
/* Convert the result of the Redis command into a suitable Lua type.
* The first thing we need is to create a single string from the client
* output buffers. */
sds proto = sdsnewlen(c->buf,c->bufpos);
c->bufpos = 0;
while(listLength(c->reply)) {
@@ -3625,7 +3632,7 @@ int RM_ModuleTypeSetValue(RedisModuleKey *key, moduleType *mt, void *value) {
if (!(key->mode & REDISMODULE_WRITE) || key->iter) return REDISMODULE_ERR;
RM_DeleteKey(key);
robj *o = createModuleObject(mt,value);
genericSetKey(key->ctx->client,key->db,key->key,o,0,0);
setKey(key->db,key->key,o);
decrRefCount(o);
key->value = o;
return REDISMODULE_OK;
@@ -4393,17 +4400,14 @@ RedisModuleBlockedClient *moduleBlockClient(RedisModuleCtx *ctx, RedisModuleCmdF
* can really be unblocked, since the module was able to serve the client.
* If the callback returns REDISMODULE_OK, then the client can be unblocked,
* otherwise the client remains blocked and we'll retry again when one of
* the keys it blocked for becomes "ready" again.
* This function returns 1 if client was served (and should be unblocked) */
* the keys it blocked for becomes "ready" again. */
int moduleTryServeClientBlockedOnKey(client *c, robj *key) {
int served = 0;
RedisModuleBlockedClient *bc = c->bpop.module_blocked_handle;
/* Protect against re-processing: don't serve clients that are already
* in the unblocking list for any reason (including RM_UnblockClient()
* explicit call). See #6798. */
if (bc->unblocked) return 0;
* explicit call). */
if (bc->unblocked) return REDISMODULE_ERR;
RedisModuleCtx ctx = REDISMODULE_CTX_INIT;
ctx.flags |= REDISMODULE_CTX_BLOCKED_REPLY;
ctx.blocked_ready_key = key;
@@ -5978,7 +5982,7 @@ sds modulesCollectInfo(sds info, const char *section, int for_crash_report, int
struct RedisModule *module = dictGetVal(de);
if (!module->info_cb)
continue;
RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0, 0};
RedisModuleInfoCtx info_ctx = {module, section, info, sections, 0};
module->info_cb(&info_ctx, for_crash_report);
/* Implicitly end dicts (no way to handle errors, and we must add the newline). */
if (info_ctx.in_dict_field)
+1 -4
View File
@@ -172,10 +172,7 @@ void execCommand(client *c) {
* This way we'll deliver the MULTI/..../EXEC block as a whole and
* both the AOF and the replication link will have the same consistency
* and atomicity guarantees. */
if (!must_propagate &&
!server.loading &&
!(c->cmd->flags & (CMD_READONLY|CMD_ADMIN)))
{
if (!must_propagate && !(c->cmd->flags & (CMD_READONLY|CMD_ADMIN))) {
execCommandPropagateMulti(c);
must_propagate = 1;
}
+73 -199
View File
@@ -157,8 +157,6 @@ client *createClient(connection *conn) {
c->client_list_node = NULL;
c->client_tracking_redirection = 0;
c->client_tracking_prefixes = NULL;
c->client_cron_last_memory_usage = 0;
c->client_cron_last_memory_type = CLIENT_TYPE_NORMAL;
c->auth_callback = NULL;
c->auth_callback_privdata = NULL;
c->auth_module = NULL;
@@ -375,54 +373,13 @@ void addReplyErrorLength(client *c, const char *s, size_t len) {
* will produce an error. However it is useful to log such events since
* they are rare and may hint at errors in a script or a bug in Redis. */
int ctype = getClientType(c);
if (ctype == CLIENT_TYPE_MASTER || ctype == CLIENT_TYPE_SLAVE || c->id == CLIENT_ID_AOF) {
char *to, *from;
if (c->id == CLIENT_ID_AOF) {
to = "AOF-loading-client";
from = "server";
} else if (ctype == CLIENT_TYPE_MASTER) {
to = "master";
from = "replica";
} else {
to = "replica";
from = "master";
}
if (ctype == CLIENT_TYPE_MASTER || ctype == CLIENT_TYPE_SLAVE) {
char* to = ctype == CLIENT_TYPE_MASTER? "master": "replica";
char* from = ctype == CLIENT_TYPE_MASTER? "replica": "master";
char *cmdname = c->lastcmd ? c->lastcmd->name : "<unknown>";
serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error "
"to its %s: '%s' after processing the command "
"'%s'", from, to, s, cmdname);
if (ctype == CLIENT_TYPE_MASTER && server.repl_backlog &&
server.repl_backlog_histlen > 0)
{
long long dumplen = 256;
if (server.repl_backlog_histlen < dumplen)
dumplen = server.repl_backlog_histlen;
/* Identify the first byte to dump. */
long long idx =
(server.repl_backlog_idx + (server.repl_backlog_size - dumplen)) %
server.repl_backlog_size;
/* Scan the circular buffer to collect 'dumplen' bytes. */
sds dump = sdsempty();
while(dumplen) {
long long thislen =
((server.repl_backlog_size - idx) < dumplen) ?
(server.repl_backlog_size - idx) : dumplen;
dump = sdscatrepr(dump,server.repl_backlog+idx,thislen);
dumplen -= thislen;
idx = 0;
}
/* Finally log such bytes: this is vital debugging info to
* understand what happened. */
serverLog(LL_WARNING,"Latest backlog is: '%s'", dump);
sdsfree(dump);
}
server.stat_unexpected_error_replies++;
}
}
@@ -465,34 +422,6 @@ void addReplyStatusFormat(client *c, const char *fmt, ...) {
sdsfree(s);
}
/* Sometimes we are forced to create a new reply node, and we can't append to
* the previous one, when that happens, we wanna try to trim the unused space
* at the end of the last reply node which we won't use anymore. */
void trimReplyUnusedTailSpace(client *c) {
listNode *ln = listLast(c->reply);
clientReplyBlock *tail = ln? listNodeValue(ln): NULL;
/* Note that 'tail' may be NULL even if we have a tail node, becuase when
* addDeferredMultiBulkLength() is used */
if (!tail) return;
/* We only try to trim the space is relatively high (more than a 1/4 of the
* allocation), otherwise there's a high chance realloc will NOP.
* Also, to avoid large memmove which happens as part of realloc, we only do
* that if the used part is small. */
if (tail->size - tail->used > tail->size / 4 &&
tail->used < PROTO_REPLY_CHUNK_BYTES)
{
size_t old_size = tail->size;
tail = zrealloc(tail, tail->used + sizeof(clientReplyBlock));
/* take over the allocation's internal fragmentation (at least for
* memory usage tracking) */
tail->size = zmalloc_usable(tail) - sizeof(clientReplyBlock);
c->reply_bytes += tail->size - old_size;
listNodeValue(ln) = tail;
}
}
/* Adds an empty object to the reply list that will contain the multi bulk
* length, which is not known when this function is called. */
void *addReplyDeferredLen(client *c) {
@@ -500,7 +429,6 @@ void *addReplyDeferredLen(client *c) {
* ready to be sent, since we are sure that before returning to the
* event loop setDeferredAggregateLen() will be called. */
if (prepareClientToWrite(c) != C_OK) return NULL;
trimReplyUnusedTailSpace(c);
listAddNodeTail(c->reply,NULL); /* NULL is our placeholder. */
return listLast(c->reply);
}
@@ -1134,16 +1062,6 @@ void freeClient(client *c) {
/* Notify module system that this client auth status changed. */
moduleNotifyUserChanged(c);
/* If this client was scheduled for async freeing we need to remove it
* from the queue. Note that we need to do this here, because later
* we may call replicationCacheMaster() and the client should already
* be removed from the list of clients to free. */
if (c->flags & CLIENT_CLOSE_ASAP) {
ln = listSearchKey(server.clients_to_close,c);
serverAssert(ln != NULL);
listDelNode(server.clients_to_close,ln);
}
/* If it is our master that's beging disconnected we should make sure
* to cache the state to try a partial resynchronization later.
*
@@ -1151,8 +1069,10 @@ void freeClient(client *c) {
* some unexpected state, by checking its flags. */
if (server.master && c->flags & CLIENT_MASTER) {
serverLog(LL_WARNING,"Connection with master lost.");
if (!(c->flags & (CLIENT_PROTOCOL_ERROR|CLIENT_BLOCKED))) {
c->flags &= ~(CLIENT_CLOSE_ASAP|CLIENT_CLOSE_AFTER_REPLY);
if (!(c->flags & (CLIENT_CLOSE_AFTER_REPLY|
CLIENT_CLOSE_ASAP|
CLIENT_BLOCKED)))
{
replicationCacheMaster(c);
return;
}
@@ -1220,10 +1140,13 @@ void freeClient(client *c) {
* we lost the connection with the master. */
if (c->flags & CLIENT_MASTER) replicationHandleMasterDisconnection();
/* Remove the contribution that this client gave to our
* incrementally computed memory usage. */
server.stat_clients_type_memory[c->client_cron_last_memory_type] -=
c->client_cron_last_memory_usage;
/* If this client was scheduled for async freeing we need to remove it
* from the queue. */
if (c->flags & CLIENT_CLOSE_ASAP) {
ln = listSearchKey(server.clients_to_close,c);
serverAssert(ln != NULL);
listDelNode(server.clients_to_close,ln);
}
/* Release other dynamically allocated client structure fields,
* and finally release the client structure itself. */
@@ -1257,10 +1180,7 @@ void freeClientAsync(client *c) {
pthread_mutex_unlock(&async_free_queue_mutex);
}
/* Free the clietns marked as CLOSE_ASAP, return the number of clients
* freed. */
int freeClientsInAsyncFreeQueue(void) {
int freed = listLength(server.clients_to_close);
void freeClientsInAsyncFreeQueue(void) {
while (listLength(server.clients_to_close)) {
listNode *ln = listFirst(server.clients_to_close);
client *c = listNodeValue(ln);
@@ -1269,7 +1189,6 @@ int freeClientsInAsyncFreeQueue(void) {
freeClient(c);
listDelNode(server.clients_to_close,ln);
}
return freed;
}
/* Return a client by ID, or NULL if the client ID is not in the set
@@ -1558,8 +1477,7 @@ int processInlineBuffer(client *c) {
}
/* Helper function. Record protocol erro details in server log,
* and set the client as CLIENT_CLOSE_AFTER_REPLY and
* CLIENT_PROTOCOL_ERROR. */
* and set the client as CLIENT_CLOSE_AFTER_REPLY. */
#define PROTO_DUMP_LEN 128
static void setProtocolError(const char *errstr, client *c) {
if (server.verbosity <= LL_VERBOSE) {
@@ -1585,7 +1503,7 @@ static void setProtocolError(const char *errstr, client *c) {
"Protocol error (%s) from client: %s. %s", errstr, client, buf);
sdsfree(client);
}
c->flags |= (CLIENT_CLOSE_AFTER_REPLY|CLIENT_PROTOCOL_ERROR);
c->flags |= CLIENT_CLOSE_AFTER_REPLY;
}
/* Process the query buffer for client 'c', setting up the client argument
@@ -1734,55 +1652,12 @@ int processMultibulkBuffer(client *c) {
return C_ERR;
}
/* Perform necessary tasks after a command was executed:
*
* 1. The client is reset unless there are reasons to avoid doing it.
* 2. In the case of master clients, the replication offset is updated.
* 3. Propagate commands we got from our master to replicas down the line. */
void commandProcessed(client *c) {
int cmd_is_ping = c->cmd && c->cmd->proc == pingCommand;
long long prev_offset = c->reploff;
if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
/* Update the applied replication offset of our master. */
c->reploff = c->read_reploff - sdslen(c->querybuf) + c->qb_pos;
}
/* Don't reset the client structure for clients blocked in a
* module blocking command, so that the reply callback will
* still be able to access the client argv and argc field.
* The client will be reset in unblockClientFromModule(). */
if (!(c->flags & CLIENT_BLOCKED) ||
c->btype != BLOCKED_MODULE)
{
resetClient(c);
}
/* If the client is a master we need to compute the difference
* between the applied offset before and after processing the buffer,
* to understand how much of the replication stream was actually
* applied to the master state: this quantity, and its corresponding
* part of the replication stream, will be propagated to the
* sub-replicas and to the replication backlog. */
if (c->flags & CLIENT_MASTER) {
long long applied = c->reploff - prev_offset;
long long prev_master_repl_meaningful_offset = server.master_repl_meaningful_offset;
if (applied) {
replicationFeedSlavesFromMasterStream(server.slaves,
c->pending_querybuf, applied);
sdsrange(c->pending_querybuf,applied,-1);
}
/* The server.master_repl_meaningful_offset variable represents
* the offset of the replication stream without the pending PINGs. */
if (cmd_is_ping)
server.master_repl_meaningful_offset = prev_master_repl_meaningful_offset;
}
}
/* This function calls processCommand(), but also performs a few sub tasks
* for the client that are useful in that context:
* that are useful in that context:
*
* 1. It sets the current client to the client 'c'.
* 2. calls commandProcessed() if the command was handled.
* 2. In the case of master clients, the replication offset is updated.
* 3. The client is reset unless there are reasons to avoid doing it.
*
* The function returns C_ERR in case the client was freed as a side effect
* of processing the command, otherwise C_OK is returned. */
@@ -1790,7 +1665,20 @@ int processCommandAndResetClient(client *c) {
int deadclient = 0;
server.current_client = c;
if (processCommand(c) == C_OK) {
commandProcessed(c);
if (c->flags & CLIENT_MASTER && !(c->flags & CLIENT_MULTI)) {
/* Update the applied replication offset of our master. */
c->reploff = c->read_reploff - sdslen(c->querybuf) + c->qb_pos;
}
/* Don't reset the client structure for clients blocked in a
* module blocking command, so that the reply callback will
* still be able to access the client argv and argc field.
* The client will be reset in unblockClientFromModule(). */
if (!(c->flags & CLIENT_BLOCKED) ||
c->btype != BLOCKED_MODULE)
{
resetClient(c);
}
}
if (server.current_client == NULL) deadclient = 1;
server.current_client = NULL;
@@ -1887,6 +1775,31 @@ void processInputBuffer(client *c) {
}
}
/* This is a wrapper for processInputBuffer that also cares about handling
* the replication forwarding to the sub-replicas, in case the client 'c'
* is flagged as master. Usually you want to call this instead of the
* raw processInputBuffer(). */
void processInputBufferAndReplicate(client *c) {
if (!(c->flags & CLIENT_MASTER)) {
processInputBuffer(c);
} else {
/* If the client is a master we need to compute the difference
* between the applied offset before and after processing the buffer,
* to understand how much of the replication stream was actually
* applied to the master state: this quantity, and its corresponding
* part of the replication stream, will be propagated to the
* sub-replicas and to the replication backlog. */
size_t prev_offset = c->reploff;
processInputBuffer(c);
size_t applied = c->reploff - prev_offset;
if (applied) {
replicationFeedSlavesFromMasterStream(server.slaves,
c->pending_querybuf, applied);
sdsrange(c->pending_querybuf,applied,-1);
}
}
}
void readQueryFromClient(connection *conn) {
client *c = connGetPrivateData(conn);
int nread, readlen;
@@ -1954,7 +1867,7 @@ void readQueryFromClient(connection *conn) {
/* There is more data in the client input buffer, continue parsing it
* in case to check if there is a full command to execute. */
processInputBuffer(c);
processInputBufferAndReplicate(c);
}
void getClientsMaxBuffers(unsigned long *longest_output_list,
@@ -2133,7 +2046,6 @@ void clientCommand(client *c) {
"KILL <option> <value> [option value ...] -- Kill connections. Options are:",
" ADDR <ip:port> -- Kill connection made from <ip:port>",
" TYPE (normal|master|replica|pubsub) -- Kill connections by type.",
" USER <username> -- Kill connections authenticated with such user.",
" SKIPME (yes|no) -- Skip killing current connection (default: yes).",
"LIST [options ...] -- Return information about client connections. Options:",
" TYPE (normal|master|replica|pubsub) -- Return clients of specified type.",
@@ -2184,7 +2096,6 @@ NULL
/* CLIENT KILL <ip:port>
* CLIENT KILL <option> [value] ... <option> [value] */
char *addr = NULL;
user *user = NULL;
int type = -1;
uint64_t id = 0;
int skipme = 1;
@@ -2216,14 +2127,6 @@ NULL
}
} else if (!strcasecmp(c->argv[i]->ptr,"addr") && moreargs) {
addr = c->argv[i+1]->ptr;
} else if (!strcasecmp(c->argv[i]->ptr,"user") && moreargs) {
user = ACLGetUserByName(c->argv[i+1]->ptr,
sdslen(c->argv[i+1]->ptr));
if (user == NULL) {
addReplyErrorFormat(c,"No such user '%s'",
(char*) c->argv[i+1]->ptr);
return;
}
} else if (!strcasecmp(c->argv[i]->ptr,"skipme") && moreargs) {
if (!strcasecmp(c->argv[i+1]->ptr,"yes")) {
skipme = 1;
@@ -2251,7 +2154,6 @@ NULL
if (addr && strcmp(getClientPeerId(client),addr) != 0) continue;
if (type != -1 && getClientType(client) != type) continue;
if (id != 0 && client->id != id) continue;
if (user && client->user != user) continue;
if (c == client && skipme) continue;
/* Kill it. */
@@ -2368,8 +2270,6 @@ NULL
options |= CLIENT_TRACKING_OPTIN;
} else if (!strcasecmp(c->argv[j]->ptr,"optout")) {
options |= CLIENT_TRACKING_OPTOUT;
} else if (!strcasecmp(c->argv[j]->ptr,"noloop")) {
options |= CLIENT_TRACKING_NOLOOP;
} else if (!strcasecmp(c->argv[j]->ptr,"prefix") && moreargs) {
j++;
prefix = zrealloc(prefix,sizeof(robj*)*(numprefix+1));
@@ -2414,25 +2314,6 @@ NULL
return;
}
if (options & CLIENT_TRACKING_OPTIN && options & CLIENT_TRACKING_OPTOUT)
{
addReplyError(c,
"You can't specify both OPTIN mode and OPTOUT mode");
zfree(prefix);
return;
}
if ((options & CLIENT_TRACKING_OPTIN && c->flags & CLIENT_TRACKING_OPTOUT) ||
(options & CLIENT_TRACKING_OPTOUT && c->flags & CLIENT_TRACKING_OPTIN))
{
addReplyError(c,
"You can't switch OPTIN/OPTOUT mode before disabling "
"tracking for this client, and then re-enabling it with "
"a different mode.");
zfree(prefix);
return;
}
enableTracking(c,redir,options,prefix,numprefix);
} else if (!strcasecmp(c->argv[2]->ptr,"off")) {
disableTracking(c);
@@ -2525,7 +2406,7 @@ void helloCommand(client *c) {
/* Let's switch to the specified RESP mode. */
c->resp = ver;
addReplyMapLen(c,6 + !server.sentinel_mode);
addReplyMapLen(c,7);
addReplyBulkCString(c,"server");
addReplyBulkCString(c,"redis");
@@ -2541,7 +2422,7 @@ void helloCommand(client *c) {
addReplyBulkCString(c,"mode");
if (server.sentinel_mode) addReplyBulkCString(c,"sentinel");
else if (server.cluster_enabled) addReplyBulkCString(c,"cluster");
if (server.cluster_enabled) addReplyBulkCString(c,"cluster");
else addReplyBulkCString(c,"standalone");
if (!server.sentinel_mode) {
@@ -2857,8 +2738,9 @@ int clientsArePaused(void) {
* write, close sequence needed to serve a client.
*
* The function returns the total number of events processed. */
void processEventsWhileBlocked(void) {
int processEventsWhileBlocked(void) {
int iterations = 4; /* See the function top-comment. */
int count = 0;
/* Note: when we are processing events while blocked (for instance during
* busy Lua scripts), we set a global flag. When such flag is set, we
@@ -2866,17 +2748,14 @@ void processEventsWhileBlocked(void) {
* See https://github.com/antirez/redis/issues/6988 for more info. */
ProcessingEventsWhileBlocked = 1;
while (iterations--) {
long long startval = server.events_processed_while_blocked;
long long ae_events = aeProcessEvents(server.el,
AE_FILE_EVENTS|AE_DONT_WAIT|
AE_CALL_BEFORE_SLEEP|AE_CALL_AFTER_SLEEP);
/* Note that server.events_processed_while_blocked will also get
* incremeted by callbacks called by the event loop handlers. */
server.events_processed_while_blocked += ae_events;
long long events = server.events_processed_while_blocked - startval;
int events = 0;
events += aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
events += handleClientsWithPendingWrites();
if (!events) break;
count += events;
}
ProcessingEventsWhileBlocked = 0;
return count;
}
/* ==========================================================================
@@ -2904,11 +2783,6 @@ void *IOThreadMain(void *myid) {
/* The ID is the thread number (from 0 to server.iothreads_num-1), and is
* used by the thread to just manipulate a single sub-array of clients. */
long id = (unsigned long)myid;
char thdname[16];
snprintf(thdname, sizeof(thdname), "io_thd_%ld", id);
redis_set_thread_title(thdname);
redisSetCpuAffinity(server.server_cpulist);
while(1) {
/* Wait for start */
@@ -3032,9 +2906,9 @@ int handleClientsWithPendingWritesUsingThreads(void) {
int processed = listLength(server.clients_pending_write);
if (processed == 0) return 0; /* Return ASAP if there are no clients. */
/* If I/O threads are disabled or we have few clients to serve, don't
* use I/O threads, but thejboring synchronous code. */
if (server.io_threads_num == 1 || stopThreadedIOIfNeeded()) {
/* If we have just a few clients to serve, don't use I/O threads, but the
* boring synchronous code. */
if (stopThreadedIOIfNeeded()) {
return handleClientsWithPendingWrites();
}
@@ -3183,7 +3057,7 @@ int handleClientsWithPendingReadsUsingThreads(void) {
continue;
}
}
processInputBuffer(c);
processInputBufferAndReplicate(c);
}
return processed;
}
+25 -18
View File
@@ -347,15 +347,7 @@ void freeStreamObject(robj *o) {
}
void incrRefCount(robj *o) {
if (o->refcount < OBJ_FIRST_SPECIAL_REFCOUNT) {
o->refcount++;
} else {
if (o->refcount == OBJ_SHARED_REFCOUNT) {
/* Nothing to do: this refcount is immutable. */
} else if (o->refcount == OBJ_STATIC_REFCOUNT) {
serverPanic("You tried to retain an object allocated in the stack");
}
}
if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount++;
}
void decrRefCount(robj *o) {
@@ -982,15 +974,30 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
mh->repl_backlog = mem;
mem_total += mem;
/* Computing the memory used by the clients would be O(N) if done
* here online. We use our values computed incrementally by
* clientsCronTrackClientsMemUsage(). */
mh->clients_slaves = server.stat_clients_type_memory[CLIENT_TYPE_SLAVE];
mh->clients_normal = server.stat_clients_type_memory[CLIENT_TYPE_MASTER]+
server.stat_clients_type_memory[CLIENT_TYPE_PUBSUB]+
server.stat_clients_type_memory[CLIENT_TYPE_NORMAL];
mem_total += mh->clients_slaves;
mem_total += mh->clients_normal;
mem = 0;
if (listLength(server.clients)) {
listIter li;
listNode *ln;
size_t mem_normal = 0, mem_slaves = 0;
listRewind(server.clients,&li);
while((ln = listNext(&li))) {
size_t mem_curr = 0;
client *c = listNodeValue(ln);
int type = getClientType(c);
mem_curr += getClientOutputBufferMemoryUsage(c);
mem_curr += sdsAllocSize(c->querybuf);
mem_curr += sizeof(client);
if (type == CLIENT_TYPE_SLAVE)
mem_slaves += mem_curr;
else
mem_normal += mem_curr;
}
mh->clients_slaves = mem_slaves;
mh->clients_normal = mem_normal;
mem = mem_slaves + mem_normal;
}
mem_total+=mem;
mem = 0;
if (server.aof_state != AOF_OFF) {
+11 -41
View File
@@ -206,8 +206,6 @@ int pubsubUnsubscribeChannel(client *c, robj *channel, int notify) {
/* Subscribe a client to a pattern. Returns 1 if the operation succeeded, or 0 if the client was already subscribed to that pattern. */
int pubsubSubscribePattern(client *c, robj *pattern) {
dictEntry *de;
list *clients;
int retval = 0;
if (listSearchKey(c->pubsub_patterns,pattern) == NULL) {
@@ -219,16 +217,6 @@ int pubsubSubscribePattern(client *c, robj *pattern) {
pat->pattern = getDecodedObject(pattern);
pat->client = c;
listAddNodeTail(server.pubsub_patterns,pat);
/* Add the client to the pattern -> list of clients hash table */
de = dictFind(server.pubsub_patterns_dict,pattern);
if (de == NULL) {
clients = listCreate();
dictAdd(server.pubsub_patterns_dict,pattern,clients);
incrRefCount(pattern);
} else {
clients = dictGetVal(de);
}
listAddNodeTail(clients,c);
}
/* Notify the client */
addReplyPubsubPatSubscribed(c,pattern);
@@ -238,8 +226,6 @@ int pubsubSubscribePattern(client *c, robj *pattern) {
/* Unsubscribe a client from a channel. Returns 1 if the operation succeeded, or
* 0 if the client was not subscribed to the specified channel. */
int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
dictEntry *de;
list *clients;
listNode *ln;
pubsubPattern pat;
int retval = 0;
@@ -252,18 +238,6 @@ int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
pat.pattern = pattern;
ln = listSearchKey(server.pubsub_patterns,&pat);
listDelNode(server.pubsub_patterns,ln);
/* Remove the client from the pattern -> clients list hash table */
de = dictFind(server.pubsub_patterns_dict,pattern);
serverAssertWithInfo(c,NULL,de != NULL);
clients = dictGetVal(de);
ln = listSearchKey(clients,c);
serverAssertWithInfo(c,NULL,ln != NULL);
listDelNode(clients,ln);
if (listLength(clients) == 0) {
/* Free the list and associated hash entry at all if this was
* the latest client. */
dictDelete(server.pubsub_patterns_dict,pattern);
}
}
/* Notify the client */
if (notify) addReplyPubsubPatUnsubscribed(c,pattern);
@@ -310,7 +284,6 @@ int pubsubUnsubscribeAllPatterns(client *c, int notify) {
int pubsubPublishMessage(robj *channel, robj *message) {
int receivers = 0;
dictEntry *de;
dictIterator *di;
listNode *ln;
listIter li;
@@ -329,26 +302,23 @@ int pubsubPublishMessage(robj *channel, robj *message) {
}
}
/* Send to clients listening to matching channels */
di = dictGetIterator(server.pubsub_patterns_dict);
if (di) {
if (listLength(server.pubsub_patterns)) {
listRewind(server.pubsub_patterns,&li);
channel = getDecodedObject(channel);
while((de = dictNext(di)) != NULL) {
robj *pattern = dictGetKey(de);
list *clients = dictGetVal(de);
if (!stringmatchlen((char*)pattern->ptr,
sdslen(pattern->ptr),
(char*)channel->ptr,
sdslen(channel->ptr),0)) continue;
while ((ln = listNext(&li)) != NULL) {
pubsubPattern *pat = ln->value;
listRewind(clients,&li);
while ((ln = listNext(&li)) != NULL) {
client *c = listNodeValue(ln);
addReplyPubsubPatMessage(c,pattern,channel,message);
if (stringmatchlen((char*)pat->pattern->ptr,
sdslen(pat->pattern->ptr),
(char*)channel->ptr,
sdslen(channel->ptr),0))
{
addReplyPubsubPatMessage(pat->client,
pat->pattern,channel,message);
receivers++;
}
}
decrRefCount(channel);
dictReleaseIterator(di);
}
return receivers;
}
+2 -2
View File
@@ -110,7 +110,7 @@ quicklist *quicklistCreate(void) {
return quicklist;
}
#define COMPRESS_MAX ((1 << QL_COMP_BITS)-1)
#define COMPRESS_MAX (1 << QL_COMP_BITS)
void quicklistSetCompressDepth(quicklist *quicklist, int compress) {
if (compress > COMPRESS_MAX) {
compress = COMPRESS_MAX;
@@ -120,7 +120,7 @@ void quicklistSetCompressDepth(quicklist *quicklist, int compress) {
quicklist->compress = compress;
}
#define FILL_MAX ((1 << (QL_FILL_BITS-1))-1)
#define FILL_MAX (1 << (QL_FILL_BITS-1))
void quicklistSetFill(quicklist *quicklist, int fill) {
if (fill > FILL_MAX) {
fill = FILL_MAX;
+2 -4
View File
@@ -1,8 +1,6 @@
/* Rax -- A radix tree implementation.
*
* Version 1.2 -- 7 February 2019
*
* Copyright (c) 2017-2019, Salvatore Sanfilippo <antirez at gmail dot com>
* Copyright (c) 2017-2018, Salvatore Sanfilippo <antirez at gmail dot com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -1739,7 +1737,7 @@ int raxRandomWalk(raxIterator *it, size_t steps) {
}
if (steps == 0) {
size_t fle = 1+floor(log(it->rt->numele));
size_t fle = floor(log(it->rt->numele));
fle *= 2;
steps = 1 + rand() % fle;
}
+41 -98
View File
@@ -1002,8 +1002,8 @@ ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key) {
* the rdbSaveObject() function. Currently we use a trick to get
* this length with very little changes to the code. In the future
* we could switch to a faster solution. */
size_t rdbSavedObjectLen(robj *o, robj *key) {
ssize_t len = rdbSaveObject(NULL,o,key);
size_t rdbSavedObjectLen(robj *o) {
ssize_t len = rdbSaveObject(NULL,o,NULL);
serverAssertWithInfo(NULL,o,len != -1);
return len;
}
@@ -1175,7 +1175,10 @@ int rdbSaveRio(rio *rdb, int *error, int rdbflags, rdbSaveInfo *rsi) {
if (rdbSaveType(rdb,RDB_OPCODE_SELECTDB) == -1) goto werr;
if (rdbSaveLen(rdb,j) == -1) goto werr;
/* Write the RESIZE DB opcode. */
/* Write the RESIZE DB opcode. We trim the size to UINT32_MAX, which
* is currently the largest type we are able to represent in RDB sizes.
* However this does not limit the actual size of the DB to load since
* these sizes are just hints to resize the hash tables. */
uint64_t db_size, expires_size;
db_size = dictSize(db->dict);
expires_size = dictSize(db->expires);
@@ -1351,7 +1354,6 @@ int rdbSaveBackground(char *filename, rdbSaveInfo *rsi) {
/* Child */
redisSetProcTitle("redis-rdb-bgsave");
redisSetCpuAffinity(server.bgsave_cpulist);
retval = rdbSave(filename,rsi);
if (retval == C_OK) {
sendChildCOWInfo(CHILD_INFO_TYPE_RDB, "RDB");
@@ -1423,7 +1425,7 @@ robj *rdbLoadCheckModuleValue(rio *rdb, char *modulename) {
/* Load a Redis object of the specified type from the specified file.
* On success a newly allocated object is returned, otherwise NULL. */
robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
robj *rdbLoadObject(int rdbtype, rio *rdb, robj *key) {
robj *o = NULL, *ele, *dec;
uint64_t len;
unsigned int i;
@@ -1442,10 +1444,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
/* Load every single element of the list */
while(len--) {
if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) {
decrRefCount(o);
return NULL;
}
if ((ele = rdbLoadEncodedStringObject(rdb)) == NULL) return NULL;
dec = getDecodedObject(ele);
size_t len = sdslen(dec->ptr);
quicklistPushTail(o->ptr, dec->ptr, len);
@@ -1472,10 +1471,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
long long llval;
sds sdsele;
if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
decrRefCount(o);
return NULL;
}
if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
if (o->encoding == OBJ_ENCODING_INTSET) {
/* Fetch integer value from element. */
@@ -1514,23 +1511,13 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
double score;
zskiplistNode *znode;
if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
decrRefCount(o);
return NULL;
}
if ((sdsele = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
if (rdbtype == RDB_TYPE_ZSET_2) {
if (rdbLoadBinaryDoubleValue(rdb,&score) == -1) {
decrRefCount(o);
sdsfree(sdsele);
return NULL;
}
if (rdbLoadBinaryDoubleValue(rdb,&score) == -1) return NULL;
} else {
if (rdbLoadDoubleValue(rdb,&score) == -1) {
decrRefCount(o);
sdsfree(sdsele);
return NULL;
}
if (rdbLoadDoubleValue(rdb,&score) == -1) return NULL;
}
/* Don't care about integer-encoded strings. */
@@ -1562,15 +1549,10 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
while (o->encoding == OBJ_ENCODING_ZIPLIST && len > 0) {
len--;
/* Load raw strings */
if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
decrRefCount(o);
return NULL;
}
if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
sdsfree(field);
decrRefCount(o);
return NULL;
}
if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
/* Add pair to ziplist */
o->ptr = ziplistPush(o->ptr, (unsigned char*)field,
@@ -1598,15 +1580,10 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
while (o->encoding == OBJ_ENCODING_HT && len > 0) {
len--;
/* Load encoded strings */
if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
decrRefCount(o);
return NULL;
}
if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL) {
sdsfree(field);
decrRefCount(o);
return NULL;
}
if ((field = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
if ((value = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL))
== NULL) return NULL;
/* Add pair to hash table */
ret = dictAdd((dict*)o->ptr, field, value);
@@ -1626,10 +1603,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
while (len--) {
unsigned char *zl =
rdbGenericLoadStringObject(rdb,RDB_LOAD_PLAIN,NULL);
if (zl == NULL) {
decrRefCount(o);
return NULL;
}
if (zl == NULL) return NULL;
quicklistAppendZiplist(o->ptr, zl);
}
} else if (rdbtype == RDB_TYPE_HASH_ZIPMAP ||
@@ -1852,8 +1826,8 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
decrRefCount(o);
return NULL;
}
streamConsumer *consumer =
streamLookupConsumer(cgroup,cname,SLC_NONE);
streamConsumer *consumer = streamLookupConsumer(cgroup,cname,
1);
sdsfree(cname);
consumer->seen_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);
if (rioGetReadError(rdb)) {
@@ -1915,9 +1889,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb, sds key) {
exit(1);
}
RedisModuleIO io;
robj keyobj;
initStaticStringObject(keyobj,key);
moduleInitIOContext(io,mt,rdb,&keyobj);
moduleInitIOContext(io,mt,rdb,key);
io.ver = (rdbtype == RDB_TYPE_MODULE) ? 1 : 2;
/* Call the rdb_load method of the module providing the 10 bit
* encoding version in the lower 10 bits of the module ID. */
@@ -2075,8 +2047,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
long long lru_clock = LRU_CLOCK();
while(1) {
sds key;
robj *val;
robj *key, *val;
/* Read type. */
if ((type = rdbLoadType(rdb)) == -1) goto eoferr;
@@ -2248,11 +2219,10 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
}
/* Read key */
if ((key = rdbGenericLoadStringObject(rdb,RDB_LOAD_SDS,NULL)) == NULL)
goto eoferr;
if ((key = rdbLoadStringObject(rdb)) == NULL) goto eoferr;
/* Read value */
if ((val = rdbLoadObject(type,rdb,key)) == NULL) {
sdsfree(key);
decrRefCount(key);
goto eoferr;
}
@@ -2260,49 +2230,26 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave.
* Similarly if the RDB is the preamble of an AOF file, we want to
* load all the keys as they are, since the log of operations later
* assume to work in an exact keyspace state. */
if (iAmMaster() &&
!(rdbflags&RDBFLAGS_AOF_PREAMBLE) &&
expiretime != -1 && expiretime < now)
{
sdsfree(key);
* snapshot taken by the master may not be reflected on the slave. */
if (iAmMaster() && !(rdbflags&RDBFLAGS_AOF_PREAMBLE) && expiretime != -1 && expiretime < now) {
decrRefCount(key);
decrRefCount(val);
} else {
robj keyobj;
/* Add the new object in the hash table */
int added = dbAddRDBLoad(db,key,val);
if (!added) {
if (rdbflags & RDBFLAGS_ALLOW_DUP) {
/* This flag is useful for DEBUG RELOAD special modes.
* When it's set we allow new keys to replace the current
* keys with the same name. */
initStaticStringObject(keyobj,key);
dbSyncDelete(db,&keyobj);
dbAddRDBLoad(db,key,val);
} else {
serverLog(LL_WARNING,
"RDB has duplicated key '%s' in DB %d",key,db->id);
serverPanic("Duplicated key found in RDB file");
}
}
dbAdd(db,key,val);
/* Set the expire time if needed */
if (expiretime != -1) {
initStaticStringObject(keyobj,key);
setExpire(NULL,db,&keyobj,expiretime);
}
if (expiretime != -1) setExpire(NULL,db,key,expiretime);
/* Set usage information (for eviction). */
objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock,1000);
}
/* Loading the database more slowly is useful in order to test
* certain edge cases. */
if (server.key_load_delay) usleep(server.key_load_delay);
/* Decrement the key refcount since dbAdd() will take its
* own reference. */
decrRefCount(key);
}
if (server.key_load_delay)
usleep(server.key_load_delay);
/* Reset the state that is key-specified and is populated by
* opcodes before the key, so that we start from scratch again. */
@@ -2320,10 +2267,7 @@ int rdbLoadRio(rio *rdb, int rdbflags, rdbSaveInfo *rsi) {
if (cksum == 0) {
serverLog(LL_WARNING,"RDB file was saved with checksum disabled: no check performed.");
} else if (cksum != expected) {
serverLog(LL_WARNING,"Wrong RDB checksum expected: (%llx) but "
"got (%llx). Aborting now.",
(unsigned long long)expected,
(unsigned long long)cksum);
serverLog(LL_WARNING,"Wrong RDB checksum. Aborting now.");
rdbExitReportCorruptRDB("RDB CRC error");
}
}
@@ -2489,7 +2433,6 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi) {
rioInitWithFd(&rdb,server.rdb_pipe_write);
redisSetProcTitle("redis-rdb-to-slaves");
redisSetCpuAffinity(server.bgsave_cpulist);
retval = rdbSaveRioWithEOFMark(&rdb,NULL,rsi);
if (retval == C_OK && rioFlush(&rdb) == 0)
+5 -6
View File
@@ -122,10 +122,9 @@
#define RDB_LOAD_SDS (1<<2)
/* flags on the purpose of rdb save or load */
#define RDBFLAGS_NONE 0 /* No special RDB loading. */
#define RDBFLAGS_AOF_PREAMBLE (1<<0) /* Load/save the RDB as AOF preamble. */
#define RDBFLAGS_REPLICATION (1<<1) /* Load/save for SYNC. */
#define RDBFLAGS_ALLOW_DUP (1<<2) /* Allow duplicated keys when loading.*/
#define RDBFLAGS_NONE 0
#define RDBFLAGS_AOF_PREAMBLE (1<<0)
#define RDBFLAGS_REPLICATION (1<<1)
int rdbSaveType(rio *rdb, unsigned char type);
int rdbLoadType(rio *rdb);
@@ -144,8 +143,8 @@ int rdbSaveToSlavesSockets(rdbSaveInfo *rsi);
void rdbRemoveTempFile(pid_t childpid);
int rdbSave(char *filename, rdbSaveInfo *rsi);
ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key);
size_t rdbSavedObjectLen(robj *o, robj *key);
robj *rdbLoadObject(int type, rio *rdb, sds key);
size_t rdbSavedObjectLen(robj *o);
robj *rdbLoadObject(int type, rio *rdb, robj *key);
void backgroundSaveDoneHandler(int exitcode, int bysignal);
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime);
ssize_t rdbSaveSingleModuleAux(rio *rdb, int when, moduleType *mt);
+3 -16
View File
@@ -94,7 +94,6 @@ static struct config {
sds dbnumstr;
char *tests;
char *auth;
const char *user;
int precision;
int num_threads;
struct benchmarkThread **threads;
@@ -259,10 +258,7 @@ static redisConfig *getRedisConfig(const char *ip, int port,
if(config.auth) {
void *authReply = NULL;
if (config.user == NULL)
redisAppendCommand(c, "AUTH %s", config.auth);
else
redisAppendCommand(c, "AUTH %s %s", config.user, config.auth);
redisAppendCommand(c, "AUTH %s", config.auth);
if (REDIS_OK != redisGetReply(c, &authReply)) goto fail;
if (reply) freeReplyObject(reply);
reply = ((redisReply *) authReply);
@@ -279,7 +275,7 @@ static redisConfig *getRedisConfig(const char *ip, int port,
for (; i < 2; i++) {
int res = redisGetReply(c, &r);
if (reply) freeReplyObject(reply);
reply = res == REDIS_OK ? ((redisReply *) r) : NULL;
reply = ((redisReply *) r);
if (res != REDIS_OK || !r) goto fail;
if (reply->type == REDIS_REPLY_ERROR) {
fprintf(stderr, "ERROR: %s\n", reply->str);
@@ -632,12 +628,7 @@ static client createClient(char *cmd, size_t len, client from, int thread_id) {
c->prefix_pending = 0;
if (config.auth) {
char *buf = NULL;
int len;
if (config.user == NULL)
len = redisFormatCommand(&buf, "AUTH %s", config.auth);
else
len = redisFormatCommand(&buf, "AUTH %s %s",
config.user, config.auth);
int len = redisFormatCommand(&buf, "AUTH %s", config.auth);
c->obuf = sdscatlen(c->obuf, buf, len);
free(buf);
c->prefix_pending++;
@@ -1308,9 +1299,6 @@ int parseOptions(int argc, const char **argv) {
} else if (!strcmp(argv[i],"-a") ) {
if (lastarg) goto invalid;
config.auth = strdup(argv[++i]);
} else if (!strcmp(argv[i],"--user")) {
if (lastarg) goto invalid;
config.user = argv[++i];
} else if (!strcmp(argv[i],"-d")) {
if (lastarg) goto invalid;
config.datasize = atoi(argv[++i]);
@@ -1397,7 +1385,6 @@ usage:
" -p <port> Server port (default 6379)\n"
" -s <socket> Server socket (overrides host and port)\n"
" -a <password> Password for Redis Auth\n"
" --user <username> Used to send ACL style 'AUTH username pass'. Needs -a.\n"
" -c <clients> Number of parallel connections (default 50)\n"
" -n <requests> Total number of requests (default 100000)\n"
" -d <size> Data size of SET/GET value in bytes (default 3)\n"
+1 -1
View File
@@ -287,7 +287,7 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
rdbstate.keys++;
/* Read value */
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
if ((val = rdbLoadObject(type,&rdb,key->ptr)) == NULL) goto eoferr;
if ((val = rdbLoadObject(type,&rdb,key)) == NULL) goto eoferr;
/* Check if the key already expired. */
if (expiretime != -1 && expiretime < now)
rdbstate.already_expired++;
+31 -82
View File
@@ -123,7 +123,6 @@
#define CLUSTER_MANAGER_CMD_FLAG_COPY 1 << 7
#define CLUSTER_MANAGER_CMD_FLAG_COLOR 1 << 8
#define CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS 1 << 9
#define CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS 1 << 10
#define CLUSTER_MANAGER_OPT_GETFRIENDS 1 << 0
#define CLUSTER_MANAGER_OPT_COLD 1 << 1
@@ -699,8 +698,7 @@ static char *hintsCallback(const char *buf, int *color, int *bold) {
for (i = 0; i < helpEntriesLen; i++) {
if (!(helpEntries[i].type & CLI_HELP_COMMAND)) continue;
if (strcasecmp(argv[0],helpEntries[i].full) == 0 ||
strcasecmp(buf,helpEntries[i].full) == 0)
if (strcasecmp(argv[0],helpEntries[i].full) == 0)
{
*color = 90;
*bold = 0;
@@ -1601,9 +1599,6 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--cluster-search-multiple-owners")) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
} else if (!strcmp(argv[i],"--cluster-fix-with-unreachable-masters")) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
#ifdef USE_OPENSSL
} else if (!strcmp(argv[i],"--tls")) {
config.tls = 1;
@@ -2151,7 +2146,6 @@ static int evalMode(int argc, char **argv) {
static struct clusterManager {
list *nodes; /* List of nodes in the configuration. */
list *errors;
int unreachable_masters; /* Masters we are not able to reach. */
} cluster_manager;
/* Used by clusterManagerFixSlotsCoverage */
@@ -2294,7 +2288,7 @@ clusterManagerCommandDef clusterManagerCommands[] = {
"search-multiple-owners"},
{"info", clusterManagerCommandInfo, -1, "host:port", NULL},
{"fix", clusterManagerCommandFix, -1, "host:port",
"search-multiple-owners,fix-with-unreachable-masters"},
"search-multiple-owners"},
{"reshard", clusterManagerCommandReshard, -1, "host:port",
"from <arg>,to <arg>,slots <arg>,yes,timeout <arg>,pipeline <arg>,"
"replace"},
@@ -3416,7 +3410,6 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
size_t *argv_len = NULL;
int c = (replace ? 8 : 7);
if (config.auth) c += 2;
if (config.user) c += 1;
size_t argc = c + reply->elements;
size_t i, offset = 6; // Keys Offset
argv = zcalloc(argc * sizeof(char *));
@@ -3443,24 +3436,12 @@ static redisReply *clusterManagerMigrateKeysInReply(clusterManagerNode *source,
offset++;
}
if (config.auth) {
if (config.user) {
argv[offset] = "AUTH2";
argv_len[offset] = 5;
offset++;
argv[offset] = config.user;
argv_len[offset] = strlen(config.user);
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
} else {
argv[offset] = "AUTH";
argv_len[offset] = 4;
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
}
argv[offset] = "AUTH";
argv_len[offset] = 4;
offset++;
argv[offset] = config.auth;
argv_len[offset] = strlen(config.auth);
offset++;
}
argv[offset] = "KEYS";
argv_len[offset] = 4;
@@ -4032,9 +4013,7 @@ static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {
if (friend->flags & (CLUSTER_MANAGER_FLAG_NOADDR |
CLUSTER_MANAGER_FLAG_DISCONNECT |
CLUSTER_MANAGER_FLAG_FAIL))
{
goto invalid_friend;
}
listAddNodeTail(cluster_manager.nodes, friend);
} else {
clusterManagerLogErr("[ERR] Unable to load info for "
@@ -4044,8 +4023,6 @@ static int clusterManagerLoadInfoFromNode(clusterManagerNode *node, int opts) {
}
continue;
invalid_friend:
if (!(friend->flags & CLUSTER_MANAGER_FLAG_SLAVE))
cluster_manager.unreachable_masters++;
freeClusterManagerNode(friend);
}
listRelease(node->friends);
@@ -4318,18 +4295,17 @@ static int clusterManagerGetCoveredSlots(char *all_slots) {
}
static void clusterManagerPrintSlotsList(list *slots) {
clusterManagerNode n = {0};
listIter li;
listNode *ln;
listRewind(slots, &li);
sds first = NULL;
while ((ln = listNext(&li)) != NULL) {
int slot = atoi(ln->value);
if (slot >= 0 && slot < CLUSTER_MANAGER_SLOTS)
n.slots[slot] = 1;
sds slot = ln->value;
if (!first) first = slot;
else printf(", ");
printf("%s", slot);
}
sds nodeslist = clusterManagerNodeSlotsString(&n);
printf("%s\n", nodeslist);
sdsfree(nodeslist);
printf("\n");
}
/* Return the node, among 'nodes' with the greatest number of keys
@@ -4419,21 +4395,18 @@ static clusterManagerNode *clusterManagerNodeMasterRandom() {
}
static int clusterManagerFixSlotsCoverage(char *all_slots) {
int force_fix = config.cluster_manager_command.flags &
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
if (cluster_manager.unreachable_masters > 0 && !force_fix) {
clusterManagerLogWarn("*** Fixing slots coverage with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n", cluster_manager.unreachable_masters);
exit(1);
}
int i, fixed = 0;
list *none = NULL, *single = NULL, *multi = NULL;
clusterManagerLogInfo(">>> Fixing slots coverage...\n");
printf("List of not covered slots: \n");
int uncovered_count = 0;
sds log = sdsempty();
for (i = 0; i < CLUSTER_MANAGER_SLOTS; i++) {
int covered = all_slots[i];
if (!covered) {
sds slot = sdsfromlonglong((long long) i);
sds key = sdsfromlonglong((long long) i);
if (uncovered_count++ > 0) printf(",");
printf("%s", (char *) key);
list *slot_nodes = listCreate();
sds slot_nodes_str = sdsempty();
listIter li;
@@ -4460,11 +4433,13 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
}
freeReplyObject(reply);
}
log = sdscatfmt(log, "\nSlot %S has keys in %u nodes: %S",
key, listLength(slot_nodes), slot_nodes_str);
sdsfree(slot_nodes_str);
dictAdd(clusterManagerUncoveredSlots, slot, slot_nodes);
dictAdd(clusterManagerUncoveredSlots, key, slot_nodes);
}
}
printf("\n%s\n", log);
/* For every slot, take action depending on the actual condition:
* 1) No node has keys for this slot.
* 2) A single node has keys for this slot.
@@ -4606,6 +4581,7 @@ static int clusterManagerFixSlotsCoverage(char *all_slots) {
}
}
cleanup:
sdsfree(log);
if (none) listRelease(none);
if (single) listRelease(single);
if (multi) listRelease(multi);
@@ -4616,38 +4592,24 @@ cleanup:
* more nodes. This function fixes this condition by migrating keys where
* it seems more sensible. */
static int clusterManagerFixOpenSlot(int slot) {
int force_fix = config.cluster_manager_command.flags &
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
if (cluster_manager.unreachable_masters > 0 && !force_fix) {
clusterManagerLogWarn("*** Fixing open slots with %d unreachable masters is dangerous: redis-cli will assume that slots about masters that are not reachable are not covered, and will try to reassign them to the reachable nodes. This can cause data loss and is rarely what you want to do. If you really want to proceed use the --cluster-fix-with-unreachable-masters option.\n", cluster_manager.unreachable_masters);
exit(1);
}
clusterManagerLogInfo(">>> Fixing open slot %d\n", slot);
/* Try to obtain the current slot owner, according to the current
* nodes configuration. */
int success = 1;
list *owners = listCreate(); /* List of nodes claiming some ownership.
it could be stating in the configuration
to have the node ownership, or just
holding keys for such slot. */
list *owners = listCreate();
list *migrating = listCreate();
list *importing = listCreate();
sds migrating_str = sdsempty();
sds importing_str = sdsempty();
clusterManagerNode *owner = NULL; /* The obvious slot owner if any. */
/* Iterate all the nodes, looking for potential owners of this slot. */
clusterManagerNode *owner = NULL;
listIter li;
listNode *ln;
listRewind(cluster_manager.nodes, &li);
while ((ln = listNext(&li)) != NULL) {
clusterManagerNode *n = ln->value;
if (n->flags & CLUSTER_MANAGER_FLAG_SLAVE) continue;
if (n->slots[slot]) {
listAddNodeTail(owners, n);
} else {
if (n->slots[slot]) listAddNodeTail(owners, n);
else {
redisReply *r = CLUSTER_MANAGER_COMMAND(n,
"CLUSTER COUNTKEYSINSLOT %d", slot);
success = clusterManagerCheckRedisReply(n, r, NULL);
@@ -4661,14 +4623,7 @@ static int clusterManagerFixOpenSlot(int slot) {
if (!success) goto cleanup;
}
}
/* If we have only a single potential owner for this slot,
* set it as "owner". */
if (listLength(owners) == 1) owner = listFirst(owners)->value;
/* Scan the list of nodes again, in order to populate the
* list of nodes in importing or migrating state for
* this slot. */
listRewind(cluster_manager.nodes, &li);
while ((ln = listNext(&li)) != NULL) {
clusterManagerNode *n = ln->value;
@@ -4700,7 +4655,6 @@ static int clusterManagerFixOpenSlot(int slot) {
}
}
}
/* If the node is neither migrating nor importing and it's not
* the owner, then is added to the importing list in case
* it has keys in the slot. */
@@ -4725,12 +4679,11 @@ static int clusterManagerFixOpenSlot(int slot) {
printf("Set as migrating in: %s\n", migrating_str);
if (sdslen(importing_str) > 0)
printf("Set as importing in: %s\n", importing_str);
/* If there is no slot owner, set as owner the node with the biggest
* number of keys, among the set of migrating / importing nodes. */
if (owner == NULL) {
clusterManagerLogInfo(">>> No single clear owner for the slot, "
"selecting an owner by # of keys...\n");
clusterManagerLogInfo(">>> Nobody claims ownership, "
"selecting an owner...\n");
owner = clusterManagerGetNodeWithMostKeysInSlot(cluster_manager.nodes,
slot, NULL);
// If we still don't have an owner, we can't fix it.
@@ -4761,7 +4714,6 @@ static int clusterManagerFixOpenSlot(int slot) {
clusterManagerRemoveNodeFromList(migrating, owner);
clusterManagerRemoveNodeFromList(importing, owner);
}
/* If there are multiple owners of the slot, we need to fix it
* so that a single node is the owner and all the other nodes
* are in importing state. Later the fix can be handled by one
@@ -4794,7 +4746,6 @@ static int clusterManagerFixOpenSlot(int slot) {
}
}
int move_opts = CLUSTER_MANAGER_OPT_VERBOSE;
/* Case 1: The slot is in migrating state in one node, and in
* importing state in 1 node. That's trivial to address. */
if (listLength(migrating) == 1 && listLength(importing) == 1) {
@@ -4806,7 +4757,6 @@ static int clusterManagerFixOpenSlot(int slot) {
move_opts |= CLUSTER_MANAGER_OPT_UPDATE;
success = clusterManagerMoveSlot(src, dst, slot, move_opts, NULL);
}
/* Case 2: There are multiple nodes that claim the slot as importing,
* they probably got keys about the slot after a restart so opened
* the slot. In this case we just move all the keys to the owner
@@ -4837,7 +4787,6 @@ static int clusterManagerFixOpenSlot(int slot) {
if (!success) goto cleanup;
}
}
/* Case 3: The slot is in migrating state in one node but multiple
* other nodes claim to be in importing state and don't have any key in
* the slot. We search for the importing node having the same ID as
+1 -10
View File
@@ -470,11 +470,7 @@ RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongLong)(Re
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromDouble)(RedisModuleCtx *ctx, double d);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromLongDouble)(RedisModuleCtx *ctx, long double ld, int humanfriendly);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringFromString)(RedisModuleCtx *ctx, const RedisModuleString *str);
#ifdef __GNUC__
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...) __attribute__ ((format (printf, 2, 3)));
#else
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_CreateStringPrintf)(RedisModuleCtx *ctx, const char *fmt, ...);
#endif
void REDISMODULE_API_FUNC(RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str);
const char *REDISMODULE_API_FUNC(RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len);
int REDISMODULE_API_FUNC(RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err);
@@ -558,13 +554,8 @@ void REDISMODULE_API_FUNC(RedisModule_SaveLongDouble)(RedisModuleIO *io, long do
long double REDISMODULE_API_FUNC(RedisModule_LoadLongDouble)(RedisModuleIO *io);
void *REDISMODULE_API_FUNC(RedisModule_LoadDataTypeFromString)(const RedisModuleString *str, const RedisModuleType *mt);
RedisModuleString *REDISMODULE_API_FUNC(RedisModule_SaveDataTypeToString)(RedisModuleCtx *ctx, void *data, const RedisModuleType *mt);
#ifdef __GNUC__
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
#else
void REDISMODULE_API_FUNC(RedisModule_Log)(RedisModuleCtx *ctx, const char *level, const char *fmt, ...);
void REDISMODULE_API_FUNC(RedisModule_LogIOError)(RedisModuleIO *io, const char *levelstr, const char *fmt, ...);
#endif
void REDISMODULE_API_FUNC(RedisModule__Assert)(const char *estr, const char *file, int line);
void REDISMODULE_API_FUNC(RedisModule_LatencyAddSample)(const char *event, mstime_t latency);
int REDISMODULE_API_FUNC(RedisModule_StringAppendBuffer)(RedisModuleCtx *ctx, RedisModuleString *str, const char *buf, size_t len);
@@ -682,7 +673,7 @@ int REDISMODULE_API_FUNC(RedisModule_AuthenticateClientWithUser)(RedisModuleCtx
void REDISMODULE_API_FUNC(RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id);
#endif
#define RedisModule_IsAOFClient(id) ((id) == CLIENT_ID_AOF)
#define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX)
/* This is included inline inside each Redis module. */
static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int apiver) __attribute__((unused));
+21 -38
View File
@@ -39,7 +39,6 @@
#include <sys/socket.h>
#include <sys/stat.h>
long long adjustMeaningfulReplOffset();
void replicationDiscardCachedMaster(void);
void replicationResurrectCachedMaster(connection *conn);
void replicationSendAck(void);
@@ -2694,10 +2693,6 @@ void replicationCacheMaster(client *c) {
* pending outputs to the master. */
sdsclear(server.master->querybuf);
sdsclear(server.master->pending_querybuf);
/* Adjust reploff and read_reploff to the last meaningful offset we
* executed. This is the offset the replica will use for future PSYNC. */
server.master->reploff = adjustMeaningfulReplOffset();
server.master->read_reploff = server.master->reploff;
if (c->flags & CLIENT_MULTI) discardTransaction(c);
listEmpty(c->reply);
@@ -2722,38 +2717,6 @@ void replicationCacheMaster(client *c) {
replicationHandleMasterDisconnection();
}
/* If the "meaningful" offset, that is the offset without the final PINGs
* in the stream, is different than the last offset, use it instead:
* often when the master is no longer reachable, replicas will never
* receive the PINGs, however the master will end with an incremented
* offset because of the PINGs and will not be able to incrementally
* PSYNC with the new master.
* This function trims the replication backlog when needed, and returns
* the offset to be used for future partial sync. */
long long adjustMeaningfulReplOffset() {
if (server.master_repl_offset > server.master_repl_meaningful_offset) {
long long delta = server.master_repl_offset -
server.master_repl_meaningful_offset;
serverLog(LL_NOTICE,
"Using the meaningful offset %lld instead of %lld to exclude "
"the final PINGs (%lld bytes difference)",
server.master_repl_meaningful_offset,
server.master_repl_offset,
delta);
server.master_repl_offset = server.master_repl_meaningful_offset;
if (server.repl_backlog_histlen <= delta) {
server.repl_backlog_histlen = 0;
server.repl_backlog_idx = 0;
} else {
server.repl_backlog_histlen -= delta;
server.repl_backlog_idx =
(server.repl_backlog_idx + (server.repl_backlog_size - delta)) %
server.repl_backlog_size;
}
}
return server.master_repl_offset;
}
/* This function is called when a master is turend into a slave, in order to
* create from scratch a cached master for the new client, that will allow
* to PSYNC with the slave that was promoted as the new master after a
@@ -2773,7 +2736,27 @@ void replicationCacheMasterUsingMyself(void) {
* by replicationCreateMasterClient(). We'll later set the created
* master as server.cached_master, so the replica will use such
* offset for PSYNC. */
server.master_initial_offset = adjustMeaningfulReplOffset();
server.master_initial_offset = server.master_repl_offset;
/* However if the "meaningful" offset, that is the offset without
* the final PINGs in the stream, is different, use this instead:
* often when the master is no longer reachable, replicas will never
* receive the PINGs, however the master will end with an incremented
* offset because of the PINGs and will not be able to incrementally
* PSYNC with the new master. */
if (server.master_repl_offset > server.master_repl_meaningful_offset) {
long long delta = server.master_repl_offset -
server.master_repl_meaningful_offset;
serverLog(LL_NOTICE,
"Using the meaningful offset %lld instead of %lld to exclude "
"the final PINGs (%lld bytes difference)",
server.master_repl_meaningful_offset,
server.master_repl_offset,
delta);
server.master_initial_offset = server.master_repl_meaningful_offset;
server.repl_backlog_histlen -= delta;
if (server.repl_backlog_histlen < 0) server.repl_backlog_histlen = 0;
}
/* The master client we create can be set to any DBID, because
* the new master will start its replication stream with SELECT. */
+4 -4
View File
@@ -657,11 +657,12 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
!server.loading && /* Don't care about mem if loading. */
!server.masterhost && /* Slave must execute the script. */
server.lua_write_dirty == 0 && /* Script had no side effects so far. */
server.lua_oom && /* Detected OOM when script start. */
(cmd->flags & CMD_DENYOOM))
{
luaPushError(lua, shared.oomerr->ptr);
goto cleanup;
if (getMaxmemoryState(NULL,NULL,NULL,NULL) != C_OK) {
luaPushError(lua, shared.oomerr->ptr);
goto cleanup;
}
}
if (cmd->flags & CMD_RANDOM) server.lua_random_dirty = 1;
@@ -959,7 +960,6 @@ int luaLogCommand(lua_State *lua) {
lua_pushstring(lua, "Invalid debug level.");
return lua_error(lua);
}
if (level < server.verbosity) return 0;
/* Glue together all the arguments */
log = sdsempty();
+1 -1
View File
@@ -97,11 +97,11 @@ sds sdsnewlen(const void *init, size_t initlen) {
unsigned char *fp; /* flags pointer. */
sh = s_malloc(hdrlen+initlen+1);
if (sh == NULL) return NULL;
if (init==SDS_NOINIT)
init = NULL;
else if (!init)
memset(sh, 0, hdrlen+initlen+1);
if (sh == NULL) return NULL;
s = (char*)sh+hdrlen;
fp = ((unsigned char*)s)-1;
switch(type) {
+1 -1
View File
@@ -869,8 +869,8 @@ void sentinelCollectTerminatedScripts(void) {
}
listDelNode(sentinel.scripts_queue,ln);
sentinelReleaseScriptJob(sj);
sentinel.running_scripts--;
}
sentinel.running_scripts--;
}
}
+174 -153
View File
@@ -672,15 +672,15 @@ struct redisCommand redisCommandTable[] = {
0,NULL,1,1,1,0,0,0},
{"multi",multiCommand,1,
"no-script fast ok-loading ok-stale @transaction",
"no-script fast @transaction",
0,NULL,0,0,0,0,0,0},
{"exec",execCommand,1,
"no-script no-monitor no-slowlog ok-loading ok-stale @transaction",
"no-script no-monitor no-slowlog @transaction",
0,NULL,0,0,0,0,0,0},
{"discard",discardCommand,1,
"no-script fast ok-loading ok-stale @transaction",
"no-script fast @transaction",
0,NULL,0,0,0,0,0,0},
{"sync",syncCommand,1,
@@ -947,11 +947,11 @@ struct redisCommand redisCommandTable[] = {
0,NULL,1,1,1,0,0,0},
{"xread",xreadCommand,-4,
"read-only @stream @blocking",
"read-only no-script @stream @blocking",
0,xreadGetKeys,1,1,1,0,0,0},
{"xreadgroup",xreadCommand,-7,
"write @stream @blocking",
"write no-script @stream @blocking",
0,xreadGetKeys,1,1,1,0,0,0},
{"xgroup",xgroupCommand,-2,
@@ -1004,11 +1004,7 @@ struct redisCommand redisCommandTable[] = {
{"acl",aclCommand,-2,
"admin no-script no-slowlog ok-loading ok-stale",
0,NULL,0,0,0,0,0,0},
{"stralgo",stralgoCommand,-2,
"read-only @string",
0,lcsGetKeys,0,0,0,0,0,0}
0,NULL,0,0,0,0,0,0}
};
/*============================ Utility functions ============================ */
@@ -1219,15 +1215,11 @@ int dictEncObjKeyCompare(void *privdata, const void *key1,
o2->encoding == OBJ_ENCODING_INT)
return o1->ptr == o2->ptr;
/* Due to OBJ_STATIC_REFCOUNT, we avoid calling getDecodedObject() without
* good reasons, because it would incrRefCount() the object, which
* is invalid. So we check to make sure dictFind() works with static
* objects as well. */
if (o1->refcount != OBJ_STATIC_REFCOUNT) o1 = getDecodedObject(o1);
if (o2->refcount != OBJ_STATIC_REFCOUNT) o2 = getDecodedObject(o2);
o1 = getDecodedObject(o1);
o2 = getDecodedObject(o2);
cmp = dictSdsKeyCompare(privdata,o1->ptr,o2->ptr);
if (o1->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o1);
if (o2->refcount != OBJ_STATIC_REFCOUNT) decrRefCount(o2);
decrRefCount(o1);
decrRefCount(o2);
return cmp;
}
@@ -1481,6 +1473,135 @@ int allPersistenceDisabled(void) {
return server.saveparamslen == 0 && server.aof_state == AOF_OFF;
}
/* ========================== Clients timeouts ============================= */
/* Check if this blocked client timedout (does nothing if the client is
* not blocked right now). If so send a reply, unblock it, and return 1.
* Otherwise 0 is returned and no operation is performed. */
int checkBlockedClientTimeout(client *c, mstime_t now) {
if (c->flags & CLIENT_BLOCKED &&
c->bpop.timeout != 0
&& c->bpop.timeout < now)
{
/* Handle blocking operation specific timeout. */
replyToBlockedClientTimedOut(c);
unblockClient(c);
return 1;
} else {
return 0;
}
}
/* Check for timeouts. Returns non-zero if the client was terminated.
* The function gets the current time in milliseconds as argument since
* it gets called multiple times in a loop, so calling gettimeofday() for
* each iteration would be costly without any actual gain. */
int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
time_t now = now_ms/1000;
if (server.maxidletime &&
/* This handles the idle clients connection timeout if set. */
!(c->flags & CLIENT_SLAVE) && /* No timeout for slaves and monitors */
!(c->flags & CLIENT_MASTER) && /* No timeout for masters */
!(c->flags & CLIENT_BLOCKED) && /* No timeout for BLPOP */
!(c->flags & CLIENT_PUBSUB) && /* No timeout for Pub/Sub clients */
(now - c->lastinteraction > server.maxidletime))
{
serverLog(LL_VERBOSE,"Closing idle client");
freeClient(c);
return 1;
} else if (c->flags & CLIENT_BLOCKED) {
/* Blocked OPS timeout is handled with milliseconds resolution.
* However note that the actual resolution is limited by
* server.hz. So for short timeouts (less than SERVER_SHORT_TIMEOUT
* milliseconds) we populate a Radix tree and handle such timeouts
* in clientsHandleShortTimeout(). */
if (checkBlockedClientTimeout(c,now_ms)) return 0;
/* Cluster: handle unblock & redirect of clients blocked
* into keys no longer served by this server. */
if (server.cluster_enabled) {
if (clusterRedirectBlockedClientIfNeeded(c))
unblockClient(c);
}
}
return 0;
}
/* For shor timeouts, less than < CLIENT_SHORT_TIMEOUT milliseconds, we
* populate a radix tree of 128 bit keys composed as such:
*
* [8 byte big endian expire time]+[8 byte client ID]
*
* We don't do any cleanup in the Radix tree: when we run the clients that
* reached the timeout already, if they are no longer existing or no longer
* blocked with such timeout, we just go forward.
*
* Every time a client blocks with a short timeout, we add the client in
* the tree. In beforeSleep() we call clientsHandleShortTimeout() to run
* the tree and unblock the clients.
*
* Design hint: why we block only clients with short timeouts? For frugality:
* Clients blocking for 30 seconds usually don't need to be unblocked
* precisely, and anyway for the nature of Redis to *guarantee* unblock time
* precision is hard, so we can avoid putting a large number of clients in
* the radix tree without a good reason. This idea also has a role in memory
* usage as well given that we don't do cleanup, the shorter a client timeout,
* the less time it will stay in the radix tree. */
#define CLIENT_ST_KEYLEN 16 /* 8 bytes mstime + 8 bytes client ID. */
/* Given client ID and timeout, write the resulting radix tree key in buf. */
void encodeTimeoutKey(unsigned char *buf, uint64_t timeout, uint64_t id) {
timeout = htonu64(timeout);
memcpy(buf,&timeout,sizeof(timeout));
memcpy(buf+8,&id,sizeof(id));
}
/* Given a key encoded with encodeTimeoutKey(), resolve the fields and write
* the timeout into *toptr and the client ID into *idptr. */
void decodeTimeoutKey(unsigned char *buf, uint64_t *toptr, uint64_t *idptr) {
memcpy(toptr,buf,sizeof(*toptr));
*toptr = ntohu64(*toptr);
memcpy(idptr,buf+8,sizeof(*idptr));
}
/* Add the specified client id / timeout as a key in the radix tree we use
* to handle short timeouts. The client is not added to the list if its
* timeout is longer than CLIENT_SHORT_TIMEOUT milliseconds. */
void addClientToShortTimeoutTable(client *c) {
if (c->bpop.timeout == 0 ||
c->bpop.timeout - mstime() > CLIENT_SHORT_TIMEOUT)
{
return;
}
uint64_t timeout = c->bpop.timeout;
uint64_t id = c->id;
unsigned char buf[CLIENT_ST_KEYLEN];
encodeTimeoutKey(buf,timeout,id);
raxTryInsert(server.clients_timeout_table,buf,sizeof(buf),NULL,NULL);
}
/* This function is called in beforeSleep() in order to unblock ASAP clients
* that are waiting in blocking operations with a short timeout set. */
void clientsHandleShortTimeout(void) {
if (raxSize(server.clients_timeout_table) == 0) return;
uint64_t now = mstime();
raxIterator ri;
raxStart(&ri,server.clients_timeout_table);
raxSeek(&ri,"^",NULL,0);
while(raxNext(&ri)) {
uint64_t id, timeout;
decodeTimeoutKey(ri.key,&timeout,&id);
if (timeout >= now) break; /* All the timeouts are in the future. */
client *c = lookupClientByID(id);
if (c) checkBlockedClientTimeout(c,now);
raxRemove(server.clients_timeout_table,ri.key,ri.key_len,NULL);
raxSeek(&ri,"^",NULL,0);
}
}
/* ======================= Cron: called every 100 ms ======================== */
/* Add a sample to the operations per second array of samples. */
@@ -1597,28 +1718,6 @@ int clientsCronTrackExpansiveClients(client *c) {
return 0; /* This function never terminates the client. */
}
/* Iterating all the clients in getMemoryOverheadData() is too slow and
* in turn would make the INFO command too slow. So we perform this
* computation incrementally and track the (not instantaneous but updated
* to the second) total memory used by clients using clinetsCron() in
* a more incremental way (depending on server.hz). */
int clientsCronTrackClientsMemUsage(client *c) {
size_t mem = 0;
int type = getClientType(c);
mem += getClientOutputBufferMemoryUsage(c);
mem += sdsAllocSize(c->querybuf);
mem += sizeof(client);
/* Now that we have the memory used by the client, remove the old
* value from the old categoty, and add it back. */
server.stat_clients_type_memory[c->client_cron_last_memory_type] -=
c->client_cron_last_memory_usage;
server.stat_clients_type_memory[type] += mem;
/* Remember what we added and where, to remove it next time. */
c->client_cron_last_memory_usage = mem;
c->client_cron_last_memory_type = type;
return 0;
}
/* Return the max samples in the memory usage of clients tracked by
* the function clientsCronTrackExpansiveClients(). */
void getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {
@@ -1648,6 +1747,9 @@ void getExpansiveClientsInfo(size_t *in_usage, size_t *out_usage) {
*/
#define CLIENTS_CRON_MIN_ITERATIONS 5
void clientsCron(void) {
/* Unblock short timeout clients ASAP. */
clientsHandleShortTimeout();
/* Try to process at least numclients/server.hz of clients
* per call. Since normally (if there are no big latency events) this
* function is called server.hz times per second, in the average case we
@@ -1670,7 +1772,7 @@ void clientsCron(void) {
/* Rotate the list, take the current head, process.
* This way if the client must be removed from the list it's the
* first element and we don't incur into O(N) computation. */
listRotateTailToHead(server.clients);
listRotate(server.clients);
head = listFirst(server.clients);
c = listNodeValue(head);
/* The following functions do different service checks on the client.
@@ -1679,7 +1781,6 @@ void clientsCron(void) {
if (clientsCronHandleTimeout(c,now)) continue;
if (clientsCronResizeQueryBuffer(c)) continue;
if (clientsCronTrackExpansiveClients(c)) continue;
if (clientsCronTrackClientsMemUsage(c)) continue;
}
}
@@ -2053,12 +2154,6 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Stop the I/O threads if we don't have enough pending work. */
stopThreadedIOIfNeeded();
/* Resize tracking keys table if needed. This is also done at every
* command execution, but we want to be sure that if the last command
* executed changes the value via CONFIG SET, the server will perform
* the operation even if completely idle. */
if (server.tracking_clients) trackingLimitUsedSlots();
/* Start a scheduled BGSAVE if the corresponding flag is set. This is
* useful when we are forced to postpone a BGSAVE because an AOF
* rewrite is in progress.
@@ -2087,42 +2182,14 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
return 1000/server.hz;
}
extern int ProcessingEventsWhileBlocked;
/* This function gets called every time Redis is entering the
* main loop of the event driven library, that is, before to sleep
* for ready file descriptors.
*
* Note: This function is (currently) called from two functions:
* 1. aeMain - The main server loop
* 2. processEventsWhileBlocked - Process clients during RDB/AOF load
*
* If it was called from processEventsWhileBlocked we don't want
* to perform all actions (For example, we don't want to expire
* keys), but we do need to perform some actions.
*
* The most important is freeClientsInAsyncFreeQueue but we also
* call some other low-risk functions. */
* for ready file descriptors. */
void beforeSleep(struct aeEventLoop *eventLoop) {
UNUSED(eventLoop);
/* Just call a subset of vital functions in case we are re-entering
* the event loop from processEventsWhileBlocked(). Note that in this
* case we keep track of the number of events we are processing, since
* processEventsWhileBlocked() wants to stop ASAP if there are no longer
* events to handle. */
if (ProcessingEventsWhileBlocked) {
uint64_t processed = 0;
processed += handleClientsWithPendingReadsUsingThreads();
processed += tlsProcessPendingData();
processed += handleClientsWithPendingWrites();
processed += freeClientsInAsyncFreeQueue();
server.events_processed_while_blocked += processed;
return;
}
/* Handle precise timeouts of blocked clients. */
handleBlockedClientsTimeout();
clientsHandleShortTimeout();
/* We should handle pending reads clients ASAP after event loop. */
handleClientsWithPendingReadsUsingThreads();
@@ -2144,24 +2211,8 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
if (server.active_expire_enabled && server.masterhost == NULL)
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
/* Unblock all the clients blocked for synchronous replication
* in WAIT. */
if (listLength(server.clients_waiting_acks))
processClientsWaitingReplicas();
/* 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();
/* Send all the slaves an ACK request if at least one client blocked
* during the previous event loop iteration. Note that we do this after
* processUnblockedClients(), so if there are multiple pipelined WAITs
* and the just unblocked WAIT gets blocked again, we don't have to wait
* a server cron cycle in absence of other event loop events. See #6623. */
* during the previous event loop iteration. */
if (server.get_ack_from_slaves) {
robj *argv[3];
@@ -2175,6 +2226,19 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
server.get_ack_from_slaves = 0;
}
/* Unblock all the clients blocked for synchronous replication
* in WAIT. */
if (listLength(server.clients_waiting_acks))
processClientsWaitingReplicas();
/* 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();
/* Send the invalidation messages to clients participating to the
* client side caching protocol in broadcasting (BCAST) mode. */
trackingBroadcastInvalidationMessages();
@@ -2199,10 +2263,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
* the different events callbacks. */
void afterSleep(struct aeEventLoop *eventLoop) {
UNUSED(eventLoop);
if (!ProcessingEventsWhileBlocked) {
if (moduleCount()) moduleAcquireGIL();
}
if (moduleCount()) moduleAcquireGIL();
}
/* =========================== Server initialization ======================== */
@@ -2732,7 +2793,6 @@ void resetServerStats(void) {
}
server.stat_net_input_bytes = 0;
server.stat_net_output_bytes = 0;
server.stat_unexpected_error_replies = 0;
server.aof_delayed_fsync = 0;
}
@@ -2768,7 +2828,6 @@ void initServer(void) {
server.clients_waiting_acks = listCreate();
server.get_ack_from_slaves = 0;
server.clients_paused = 0;
server.events_processed_while_blocked = 0;
server.system_memory_size = zmalloc_get_memory_size();
if (server.tls_port && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
@@ -2829,7 +2888,6 @@ void initServer(void) {
evictionPoolAlloc(); /* Initialize the LRU keys pool. */
server.pubsub_channels = dictCreate(&keylistDictType,NULL);
server.pubsub_patterns = listCreate();
server.pubsub_patterns_dict = dictCreate(&keylistDictType,NULL);
listSetFreeMethod(server.pubsub_patterns,freePubsubPattern);
listSetMatchMethod(server.pubsub_patterns,listMatchPubsubPattern);
server.cronloops = 0;
@@ -2860,8 +2918,6 @@ void initServer(void) {
server.stat_rdb_cow_bytes = 0;
server.stat_aof_cow_bytes = 0;
server.stat_module_cow_bytes = 0;
for (int j = 0; j < CLIENT_TYPE_COUNT; j++)
server.stat_clients_type_memory[j] = 0;
server.cron_malloc_stats.zmalloc_used = 0;
server.cron_malloc_stats.process_rss = 0;
server.cron_malloc_stats.allocator_allocated = 0;
@@ -2911,11 +2967,6 @@ void initServer(void) {
"blocked clients subsystem.");
}
/* Register before and after sleep handlers (note this needs to be done
* before loading persistence since it is used by processEventsWhileBlocked. */
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
/* Open the AOF file if needed. */
if (server.aof_state == AOF_ON) {
server.aof_fd = open(server.aof_filename,
@@ -3132,13 +3183,8 @@ struct redisCommand *lookupCommandOrOriginal(sds name) {
* + PROPAGATE_AOF (propagate into the AOF file if is enabled)
* + PROPAGATE_REPL (propagate into the replication link)
*
* This should not be used inside commands implementation since it will not
* wrap the resulting commands in MULTI/EXEC. Use instead alsoPropagate(),
* preventCommandPropagation(), forceCommandPropagation().
*
* However for functions that need to (also) propagate out of the context of a
* command execution, for example when serving a blocked client, you
* want to use propagate().
* This should not be used inside commands implementation. Use instead
* alsoPropagate(), preventCommandPropagation(), forceCommandPropagation().
*/
void propagate(struct redisCommand *cmd, int dbid, robj **argv, int argc,
int flags)
@@ -3247,8 +3293,8 @@ void call(client *c, int flags) {
server.fixed_time_expire++;
/* Send the command to clients in MONITOR mode if applicable.
* Administrative commands are considered too dangerous to be shown. */
/* Sent the command to clients in MONITOR mode, only if the commands are
* not generated from reading an AOF. */
if (listLength(server.monitors) &&
!server.loading &&
!(c->cmd->flags & (CMD_SKIP_MONITOR|CMD_ADMIN)))
@@ -3351,16 +3397,12 @@ void call(client *c, int flags) {
if (flags & CMD_CALL_PROPAGATE) {
int multi_emitted = 0;
/* Wrap the commands in server.also_propagate array,
* but don't wrap it if we are already in MULTI context,
* in case the nested MULTI/EXEC.
* but don't wrap it if we are already in MULIT context,
* in case the nested MULIT/EXEC.
*
* And if the array contains only one command, no need to
* wrap it, since the single command is atomic. */
if (server.also_propagate.numops > 1 &&
!(c->cmd->flags & CMD_MODULE) &&
!(c->flags & CLIENT_MULTI) &&
!(flags & CMD_CALL_NOWRAP))
{
if (server.also_propagate.numops > 1 && !(c->flags & CLIENT_MULTI)) {
execCommandPropagateMulti(c);
multi_emitted = 1;
}
@@ -3525,13 +3567,6 @@ int processCommand(client *c) {
addReply(c, shared.oomerr);
return C_OK;
}
/* Save out_of_memory result at script start, otherwise if we check OOM
* untill first write within script, memory used by lua stack and
* arguments might interfere. */
if (c->cmd->proc == evalCommand || c->cmd->proc == evalShaCommand) {
server.lua_oom = out_of_memory;
}
}
/* Make sure to use a reasonable amount of memory for client side
@@ -4066,13 +4101,11 @@ sds genRedisInfoString(const char *section) {
"client_recent_max_input_buffer:%zu\r\n"
"client_recent_max_output_buffer:%zu\r\n"
"blocked_clients:%d\r\n"
"tracking_clients:%d\r\n"
"clients_in_timeout_table:%llu\r\n",
"tracking_clients:%d\r\n",
listLength(server.clients)-listLength(server.slaves),
maxin, maxout,
server.blocked_clients,
server.tracking_clients,
(unsigned long long) raxSize(server.clients_timeout_table));
server.tracking_clients);
}
/* Memory */
@@ -4087,7 +4120,7 @@ sds genRedisInfoString(const char *section) {
size_t zmalloc_used = zmalloc_used_memory();
size_t total_system_mem = server.system_memory_size;
const char *evict_policy = evictPolicyToString();
long long memory_lua = server.lua ? (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024 : 0;
long long memory_lua = (long long)lua_gc(server.lua,LUA_GCCOUNT,0)*1024;
struct redisMemOverhead *mh = getMemoryOverheadData();
/* Peak memory is updated from time to time by serverCron() so it
@@ -4322,9 +4355,7 @@ sds genRedisInfoString(const char *section) {
"active_defrag_key_hits:%lld\r\n"
"active_defrag_key_misses:%lld\r\n"
"tracking_total_keys:%lld\r\n"
"tracking_total_items:%lld\r\n"
"tracking_total_prefixes:%lld\r\n"
"unexpected_error_replies:%lld\r\n",
"tracking_total_items:%lld\r\n",
server.stat_numconnections,
server.stat_numcommands,
getInstantaneousMetric(STATS_METRIC_COMMAND),
@@ -4353,9 +4384,7 @@ sds genRedisInfoString(const char *section) {
server.stat_active_defrag_key_hits,
server.stat_active_defrag_key_misses,
(unsigned long long) trackingGetTotalKeys(),
(unsigned long long) trackingGetTotalItems(),
(unsigned long long) trackingGetTotalPrefixes(),
server.stat_unexpected_error_replies);
(unsigned long long) trackingGetTotalItems());
}
/* Replication */
@@ -4894,14 +4923,6 @@ void redisSetProcTitle(char *title) {
#endif
}
void redisSetCpuAffinity(const char *cpulist) {
#ifdef USE_SETCPUAFFINITY
setcpuaffinity(cpulist);
#else
UNUSED(cpulist);
#endif
}
/*
* Check whether systemd or upstart have been used to start redis.
*/
@@ -5005,7 +5026,6 @@ int main(int argc, char **argv) {
zmalloc_set_oom_handler(redisOutOfMemoryHandler);
srand(time(NULL)^getpid());
gettimeofday(&tv,NULL);
crc64_init();
uint8_t hashseed[16];
getRandomBytes(hashseed,sizeof(hashseed));
@@ -5171,7 +5191,8 @@ int main(int argc, char **argv) {
serverLog(LL_WARNING,"WARNING: You specified a maxmemory value that is less than 1MB (current value is %llu bytes). Are you sure this is what you really want?", server.maxmemory);
}
redisSetCpuAffinity(server.server_cpulist);
aeSetBeforeSleepProc(server.el,beforeSleep);
aeSetAfterSleepProc(server.el,afterSleep);
aeMain(server.el);
aeDeleteEventLoop(server.el);
return 0;
+19 -52
View File
@@ -252,10 +252,6 @@ typedef long long ustime_t; /* microsecond time type. */
#define CLIENT_TRACKING_OPTOUT (1ULL<<35) /* Tracking in opt-out mode. */
#define CLIENT_TRACKING_CACHING (1ULL<<36) /* CACHING yes/no was given,
depending on optin/optout mode. */
#define CLIENT_TRACKING_NOLOOP (1ULL<<37) /* Don't send invalidation messages
about writes performed by myself.*/
#define CLIENT_IN_TO_TABLE (1ULL<<38) /* This client is in the timeout table. */
#define CLIENT_PROTOCOL_ERROR (1ULL<<39) /* Protocol error chatting with it. */
/* Client block type (btype field in client structure)
* if CLIENT_BLOCKED flag is set. */
@@ -277,11 +273,13 @@ typedef long long ustime_t; /* microsecond time type. */
#define CLIENT_TYPE_SLAVE 1 /* Slaves. */
#define CLIENT_TYPE_PUBSUB 2 /* Clients subscribed to PubSub channels. */
#define CLIENT_TYPE_MASTER 3 /* Master. */
#define CLIENT_TYPE_COUNT 4 /* Total number of client types. */
#define CLIENT_TYPE_OBUF_COUNT 3 /* Number of clients to expose to output
buffer configuration. Just the first
three: normal, slave, pubsub. */
/* Other client related defines. */
#define CLIENT_SHORT_TIMEOUT 2000 /* See clientsHandleShortTimeout(). */
/* Slave replication state. Used in server.repl_state for slaves to remember
* what to do next. */
#define REPL_STATE_NONE 0 /* No active replication */
@@ -399,8 +397,6 @@ typedef long long ustime_t; /* microsecond time type. */
#define CMD_CALL_PROPAGATE_REPL (1<<3)
#define CMD_CALL_PROPAGATE (CMD_CALL_PROPAGATE_AOF|CMD_CALL_PROPAGATE_REPL)
#define CMD_CALL_FULL (CMD_CALL_SLOWLOG | CMD_CALL_STATS | CMD_CALL_PROPAGATE)
#define CMD_CALL_NOWRAP (1<<4) /* Don't wrap also propagate array into
MULTI/EXEC: the caller will handle it. */
/* Command propagation flags, see propagate() function */
#define PROPAGATE_NONE 0
@@ -600,9 +596,7 @@ typedef struct RedisModuleDigest {
#define LRU_CLOCK_MAX ((1<<LRU_BITS)-1) /* Max value of obj->lru */
#define LRU_CLOCK_RESOLUTION 1000 /* LRU clock resolution in ms */
#define OBJ_SHARED_REFCOUNT INT_MAX /* Global object never destroyed. */
#define OBJ_STATIC_REFCOUNT (INT_MAX-1) /* Object allocated in the stack. */
#define OBJ_FIRST_SPECIAL_REFCOUNT OBJ_STATIC_REFCOUNT
#define OBJ_SHARED_REFCOUNT INT_MAX
typedef struct redisObject {
unsigned type:4;
unsigned encoding:4;
@@ -623,7 +617,7 @@ char *getObjectTypeName(robj*);
* we'll update it when the structure is changed, to avoid bugs like
* bug #85 introduced exactly in this way. */
#define initStaticStringObject(_var,_ptr) do { \
_var.refcount = OBJ_STATIC_REFCOUNT; \
_var.refcount = 1; \
_var.type = OBJ_STRING; \
_var.encoding = OBJ_ENCODING_RAW; \
_var.ptr = _ptr; \
@@ -733,7 +727,7 @@ typedef struct readyList {
no AUTH is needed, and every
connection is immediately
authenticated. */
typedef struct {
typedef struct user {
sds name; /* The username as an SDS string. */
uint64_t flags; /* See USER_FLAG_* */
@@ -826,10 +820,10 @@ typedef struct client {
* when the authenticated user
* changes. */
void *auth_callback_privdata; /* Private data that is passed when the auth
* changed callback is executed. Opaque for
* changed callback is executed. Opaque for
* Redis Core. */
void *auth_module; /* The module that owns the callback, which is used
* to disconnect the client if the module is
* to disconnect the client if the module is
* unloaded for cleanup. Opaque for Redis Core.*/
/* If this client is in tracking mode and this field is non zero,
@@ -839,13 +833,6 @@ typedef struct client {
rax *client_tracking_prefixes; /* A dictionary of prefixes we are already
subscribed to in BCAST mode, in the
context of client side caching. */
/* In clientsCronTrackClientsMemUsage() we track the memory usage of
* each client and add it to the sum of all the clients of a given type,
* however we need to remember what was the old contribution of each
* client, and in which categoty the client was, in order to remove it
* before adding it the new value. */
uint64_t client_cron_last_memory_usage;
int client_cron_last_memory_type;
/* Response buffer */
int bufpos;
char buf[PROTO_REPLY_CHUNK_BYTES];
@@ -1083,7 +1070,7 @@ struct redisServer {
list *clients_pending_read; /* Client has pending read socket buffers. */
list *slaves, *monitors; /* List of slaves and MONITORs */
client *current_client; /* Current client executing the command. */
rax *clients_timeout_table; /* Radix tree for blocked clients timeouts. */
rax *clients_timeout_table; /* Radix tree for clients with short timeout. */
long fixed_time_expire; /* If > 0, expire keys against server.mstime. */
rax *clients_index; /* Active clients dictionary by client ID. */
int clients_paused; /* True if clients are currently paused */
@@ -1096,7 +1083,6 @@ struct redisServer {
queries. Will still serve RESP2 queries. */
int io_threads_num; /* Number of IO threads to use. */
int io_threads_do_reads; /* Read and parse from IO threads? */
long long events_processed_while_blocked; /* processEventsWhileBlocked() */
/* RDB / AOF loading information */
int loading; /* We are loading data from disk if true */
@@ -1143,8 +1129,6 @@ struct redisServer {
size_t stat_rdb_cow_bytes; /* Copy on write bytes during RDB saving. */
size_t stat_aof_cow_bytes; /* Copy on write bytes during AOF rewrite. */
size_t stat_module_cow_bytes; /* Copy on write bytes during module fork. */
uint64_t stat_clients_type_memory[CLIENT_TYPE_COUNT];/* Mem usage by type */
long long stat_unexpected_error_replies; /* Number of unexpected (aof-loading, replica to master, etc.) error replies */
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */
struct {
@@ -1364,7 +1348,6 @@ struct redisServer {
/* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */
list *pubsub_patterns; /* A list of pubsub_patterns */
dict *pubsub_patterns_dict; /* A dict of pubsub_patterns */
int notify_keyspace_events; /* Events to propagate via Pub/Sub. This is an
xor of NOTIFY_... flags. */
/* Cluster */
@@ -1407,12 +1390,10 @@ struct redisServer {
execution. */
int lua_kill; /* Kill the script if true. */
int lua_always_replicate_commands; /* Default replication type. */
int lua_oom; /* OOM detected when script start? */
/* Lazy free */
int lazyfree_lazy_eviction;
int lazyfree_lazy_expire;
int lazyfree_lazy_server_del;
int lazyfree_lazy_user_del;
/* Latency monitor */
long long latency_monitor_threshold;
dict *latency_events;
@@ -1435,11 +1416,6 @@ struct redisServer {
int tls_replication;
int tls_auth_clients;
redisTLSContextConfig tls_ctx_config;
/* cpu affinity */
char *server_cpulist; /* cpu affinity list of redis server main/io thread. */
char *bio_cpulist; /* cpu affinity list of bio thread. */
char *aof_rewrite_cpulist; /* cpu affinity list of aof rewrite process. */
char *bgsave_cpulist; /* cpu affinity list of bgsave process. */
};
typedef struct pubsubPattern {
@@ -1592,7 +1568,6 @@ void exitFromChild(int retcode);
size_t redisPopcount(void *s, long count);
void redisSetProcTitle(char *title);
int redisCommunicateSystemd(const char *sd_notify_msg);
void redisSetCpuAffinity(const char *cpulist);
/* networking.c -- Networking and Client related operations */
client *createClient(connection *conn);
@@ -1608,6 +1583,7 @@ void setDeferredSetLen(client *c, void *node, long length);
void setDeferredAttributeLen(client *c, void *node, long length);
void setDeferredPushLen(client *c, void *node, long length);
void processInputBuffer(client *c);
void processInputBufferAndReplicate(client *c);
void processGopherRequest(client *c);
void acceptHandler(aeEventLoop *el, int fd, void *privdata, int mask);
void acceptTcpHandler(aeEventLoop *el, int fd, void *privdata, int mask);
@@ -1654,7 +1630,7 @@ void rewriteClientCommandVector(client *c, int argc, ...);
void rewriteClientCommandArgument(client *c, int i, robj *newval);
void replaceClientCommandVector(client *c, int argc, robj **argv);
unsigned long getClientOutputBufferMemoryUsage(client *c);
int freeClientsInAsyncFreeQueue(void);
void freeClientsInAsyncFreeQueue(void);
void asyncCloseClientOnOutputBufferLimitReached(client *c);
int getClientType(client *c);
int getClientTypeByName(char *name);
@@ -1664,7 +1640,7 @@ void disconnectSlaves(void);
int listenToPort(int port, int *fds, int *count);
void pauseClients(mstime_t duration);
int clientsArePaused(void);
void processEventsWhileBlocked(void);
int processEventsWhileBlocked(void);
int handleClientsWithPendingWrites(void);
int handleClientsWithPendingWritesUsingThreads(void);
int handleClientsWithPendingReadsUsingThreads(void);
@@ -1692,12 +1668,11 @@ void addReplyStatusFormat(client *c, const char *fmt, ...);
void enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **prefix, size_t numprefix);
void disableTracking(client *c);
void trackingRememberKeys(client *c);
void trackingInvalidateKey(client *c, robj *keyobj);
void trackingInvalidateKey(robj *keyobj);
void trackingInvalidateKeysOnFlush(int dbid);
void trackingLimitUsedSlots(void);
uint64_t trackingGetTotalItems(void);
uint64_t trackingGetTotalKeys(void);
uint64_t trackingGetTotalPrefixes(void);
void trackingBroadcastInvalidationMessages(void);
/* List data type */
@@ -2079,10 +2054,9 @@ int objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
#define LOOKUP_NONE 0
#define LOOKUP_NOTOUCH (1<<0)
void dbAdd(redisDb *db, robj *key, robj *val);
int dbAddRDBLoad(redisDb *db, sds key, robj *val);
void dbOverwrite(redisDb *db, robj *key, robj *val);
void genericSetKey(client *c, redisDb *db, robj *key, robj *val, int keepttl, int signal);
void setKey(client *c, redisDb *db, robj *key, robj *val);
void genericSetKey(redisDb *db, robj *key, robj *val, int keepttl);
void setKey(redisDb *db, robj *key, robj *val);
int dbExists(redisDb *db, robj *key);
robj *dbRandomKey(redisDb *db);
int dbSyncDelete(redisDb *db, robj *key);
@@ -2098,7 +2072,7 @@ void flushAllDataAndResetRDB(int flags);
long long dbTotalServerKeyCount();
int selectDb(client *c, int id);
void signalModifiedKey(client *c, redisDb *db, robj *key);
void signalModifiedKey(redisDb *db, robj *key);
void signalFlushedDb(int dbid);
unsigned int getKeysInSlot(unsigned int hashslot, robj **keys, unsigned int count);
unsigned int countKeysInSlot(unsigned int hashslot);
@@ -2106,8 +2080,8 @@ unsigned int delKeysInSlot(unsigned int hashslot);
int verifyClusterConfigWithData(void);
void scanGenericCommand(client *c, robj *o, unsigned long cursor);
int parseScanCursorOrReply(client *c, robj *o, unsigned long *cursor);
void slotToKeyAdd(sds key);
void slotToKeyDel(sds key);
void slotToKeyAdd(robj *key);
void slotToKeyDel(robj *key);
void slotToKeyFlush(void);
int dbAsyncDelete(redisDb *db, robj *key);
void emptyDbAsync(redisDb *db);
@@ -2125,7 +2099,6 @@ int *migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkey
int *georadiusGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *xreadGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *memoryGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
int *lcsGetKeys(struct redisCommand *cmd, robj **argv, int argc, int *numkeys);
/* Cluster */
void clusterInit(void);
@@ -2167,12 +2140,7 @@ void disconnectAllBlockedClients(void);
void handleClientsBlockedOnKeys(void);
void signalKeyAsReady(redisDb *db, robj *key);
void blockForKeys(client *c, int btype, robj **keys, int numkeys, mstime_t timeout, robj *target, streamID *ids);
/* timeout.c -- Blocked clients timeout and connections timeout. */
void addClientToTimeoutTable(client *c);
void removeClientFromTimeoutTable(client *c);
void handleBlockedClientsTimeout(void);
int clientsCronHandleTimeout(client *c, mstime_t now_ms);
void addClientToShortTimeoutTable(client *c);
/* expire.c -- Handling of expired keys */
void activeExpireCycle(int type);
@@ -2397,7 +2365,6 @@ void xdelCommand(client *c);
void xtrimCommand(client *c);
void lolwutCommand(client *c);
void aclCommand(client *c);
void stralgoCommand(client *c);
#if defined(__GNUC__)
void *calloc(size_t count, size_t size) __attribute__ ((deprecated));
-148
View File
@@ -1,148 +0,0 @@
/* ==========================================================================
* setcpuaffinity.c - Linux/BSD setcpuaffinity.
* --------------------------------------------------------------------------
* Copyright (C) 2020 zhenwei pi
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to permit
* persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
* NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
* USE OR OTHER DEALINGS IN THE SOFTWARE.
* ==========================================================================
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef __linux__
#include <sched.h>
#endif
#ifdef __FreeBSD__
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#ifdef __NetBSD__
#include <pthread.h>
#include <sched.h>
#endif
#include "config.h"
#ifdef USE_SETCPUAFFINITY
static const char *next_token(const char *q, int sep) {
if (q)
q = strchr(q, sep);
if (q)
q++;
return q;
}
static int next_num(const char *str, char **end, int *result) {
if (!str || *str == '\0' || !isdigit(*str))
return -1;
*result = strtoul(str, end, 10);
if (str == *end)
return -1;
return 0;
}
/* set current thread cpu affinity to cpu list, this function works like
* taskset command (actually cpulist parsing logic reference to util-linux).
* example of this function: "0,2,3", "0,2-3", "0-20:2". */
void setcpuaffinity(const char *cpulist) {
const char *p, *q;
char *end = NULL;
#ifdef __linux__
cpu_set_t cpuset;
#endif
#ifdef __FreeBSD__
cpuset_t cpuset;
#endif
#ifdef __NetBSD__
cpuset_t *cpuset;
#endif
if (!cpulist)
return;
#ifndef __NetBSD__
CPU_ZERO(&cpuset);
#else
cpuset = cpuset_create();
#endif
q = cpulist;
while (p = q, q = next_token(q, ','), p) {
int a, b, s;
const char *c1, *c2;
if (next_num(p, &end, &a) != 0)
return;
b = a;
s = 1;
p = end;
c1 = next_token(p, '-');
c2 = next_token(p, ',');
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if (next_num(c1, &end, &b) != 0)
return;
c1 = end && *end ? next_token(end, ':') : NULL;
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if (next_num(c1, &end, &s) != 0)
return;
if (s == 0)
return;
}
}
if ((a > b))
return;
while (a <= b) {
#ifndef __NetBSD__
CPU_SET(a, &cpuset);
#else
cpuset_set(a, cpuset);
#endif
a += s;
}
}
if (end && *end)
return;
#ifdef __linux__
sched_setaffinity(0, sizeof(cpuset), &cpuset);
#endif
#ifdef __FreeBSD__
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);
#endif
#ifdef __NetBSD__
pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);
cpuset_destroy(cpuset);
#endif
}
#endif /* USE_SETCPUAFFINITY */
+2 -2
View File
@@ -570,12 +570,12 @@ void sortCommand(client *c) {
}
}
if (outputlen) {
setKey(c,c->db,storekey,sobj);
setKey(c->db,storekey,sobj);
notifyKeyspaceEvent(NOTIFY_LIST,"sortstore",storekey,
c->db->id);
server.dirty += outputlen;
} else if (dbDelete(c->db,storekey)) {
signalModifiedKey(c,c->db,storekey);
signalModifiedKey(c->db,storekey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",storekey,c->db->id);
server.dirty++;
}
+1 -6
View File
@@ -96,11 +96,6 @@ typedef struct streamPropInfo {
/* Prototypes of exported APIs. */
struct client;
/* Flags for streamLookupConsumer */
#define SLC_NONE 0
#define SLC_NOCREAT (1<<0) /* Do not create the consumer if it doesn't exist */
#define SLC_NOREFRESH (1<<1) /* Do not update consumer's seen-time */
stream *streamNew(void);
void freeStream(stream *s);
unsigned long streamLength(const robj *subject);
@@ -110,7 +105,7 @@ int streamIteratorGetID(streamIterator *si, streamID *id, int64_t *numfields);
void streamIteratorGetField(streamIterator *si, unsigned char **fieldptr, unsigned char **valueptr, int64_t *fieldlen, int64_t *valuelen);
void streamIteratorStop(streamIterator *si);
streamCG *streamLookupCG(stream *s, sds groupname);
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags);
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create);
streamCG *streamCreateCG(stream *s, char *name, size_t namelen, streamID *id);
streamNACK *streamCreateNACK(streamConsumer *consumer);
void streamDecodeID(void *buf, streamID *id);
+5 -5
View File
@@ -521,7 +521,7 @@ void hsetnxCommand(client *c) {
} else {
hashTypeSet(o,c->argv[2]->ptr,c->argv[3]->ptr,HASH_SET_COPY);
addReply(c, shared.cone);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
server.dirty++;
}
@@ -551,7 +551,7 @@ void hsetCommand(client *c) {
/* HMSET */
addReply(c, shared.ok);
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hset",c->argv[1],c->db->id);
server.dirty++;
}
@@ -586,7 +586,7 @@ void hincrbyCommand(client *c) {
new = sdsfromlonglong(value);
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyLongLong(c,value);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrby",c->argv[1],c->db->id);
server.dirty++;
}
@@ -625,7 +625,7 @@ void hincrbyfloatCommand(client *c) {
new = sdsnewlen(buf,len);
hashTypeSet(o,c->argv[2]->ptr,new,HASH_SET_TAKE_VALUE);
addReplyBulkCBuffer(c,buf,len);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hincrbyfloat",c->argv[1],c->db->id);
server.dirty++;
@@ -721,7 +721,7 @@ void hdelCommand(client *c) {
}
}
if (deleted) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_HASH,"hdel",c->argv[1],c->db->id);
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
+10 -10
View File
@@ -217,7 +217,7 @@ void pushGenericCommand(client *c, int where) {
if (pushed) {
char *event = (where == LIST_HEAD) ? "lpush" : "rpush";
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
}
server.dirty += pushed;
@@ -247,7 +247,7 @@ void pushxGenericCommand(client *c, int where) {
if (pushed) {
char *event = (where == LIST_HEAD) ? "lpush" : "rpush";
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,event,c->argv[1],c->db->id);
}
server.dirty += pushed;
@@ -292,7 +292,7 @@ void linsertCommand(client *c) {
listTypeReleaseIterator(iter);
if (inserted) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"linsert",
c->argv[1],c->db->id);
server.dirty++;
@@ -355,7 +355,7 @@ void lsetCommand(client *c) {
addReply(c,shared.outofrangeerr);
} else {
addReply(c,shared.ok);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"lset",c->argv[1],c->db->id);
server.dirty++;
}
@@ -382,7 +382,7 @@ void popGenericCommand(client *c, int where) {
c->argv[1],c->db->id);
dbDelete(c->db,c->argv[1]);
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
}
@@ -482,7 +482,7 @@ void ltrimCommand(client *c) {
dbDelete(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],c->db->id);
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
addReply(c,shared.ok);
}
@@ -519,7 +519,7 @@ void lremCommand(client *c) {
listTypeReleaseIterator(li);
if (removed) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_LIST,"lrem",c->argv[1],c->db->id);
}
@@ -555,7 +555,7 @@ void rpoplpushHandlePush(client *c, robj *dstkey, robj *dstobj, robj *value) {
server.list_compress_depth);
dbAdd(c->db,dstkey,dstobj);
}
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
listTypePush(dstobj,value,LIST_HEAD);
notifyKeyspaceEvent(NOTIFY_LIST,"lpush",dstkey,c->db->id);
/* Always send the pushed value to the client. */
@@ -593,7 +593,7 @@ void rpoplpushCommand(client *c) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
touchedkey,c->db->id);
}
signalModifiedKey(c,c->db,touchedkey);
signalModifiedKey(c->db,touchedkey);
decrRefCount(touchedkey);
server.dirty++;
if (c->cmd->proc == brpoplpushCommand) {
@@ -708,7 +708,7 @@ void blockingPopGenericCommand(client *c, int where) {
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
c->argv[j],c->db->id);
}
signalModifiedKey(c,c->db,c->argv[j]);
signalModifiedKey(c->db,c->argv[j]);
server.dirty++;
/* Replicate it as an [LR]POP instead of B[LR]POP. */
+10 -10
View File
@@ -280,7 +280,7 @@ void saddCommand(client *c) {
if (setTypeAdd(set,c->argv[j]->ptr)) added++;
}
if (added) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_SET,"sadd",c->argv[1],c->db->id);
}
server.dirty += added;
@@ -305,7 +305,7 @@ void sremCommand(client *c) {
}
}
if (deleted) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_SET,"srem",c->argv[1],c->db->id);
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[1],
@@ -358,8 +358,8 @@ void smoveCommand(client *c) {
dbAdd(c->db,c->argv[2],dstset);
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c,c->db,c->argv[2]);
signalModifiedKey(c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[2]);
server.dirty++;
/* An extra key has changed when ele was successfully added to dstset */
@@ -444,7 +444,7 @@ void spopWithCountCommand(client *c) {
/* Propagate this command as an DEL operation */
rewriteClientCommandVector(c,2,shared.del,c->argv[1]);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
return;
}
@@ -546,7 +546,7 @@ void spopWithCountCommand(client *c) {
* the alsoPropagate() API. */
decrRefCount(propargv[0]);
preventCommandPropagation(c);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -599,7 +599,7 @@ void spopCommand(client *c) {
}
/* Set has been modified */
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
server.dirty++;
}
@@ -808,7 +808,7 @@ void sinterGenericCommand(client *c, robj **setkeys,
zfree(sets);
if (dstkey) {
if (dbDelete(c->db,dstkey)) {
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
server.dirty++;
}
addReply(c,shared.czero);
@@ -908,7 +908,7 @@ void sinterGenericCommand(client *c, robj **setkeys,
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
dstkey,c->db->id);
}
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
server.dirty++;
} else {
setDeferredSetLen(c,replylen,cardinality);
@@ -1083,7 +1083,7 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",
dstkey,c->db->id);
}
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
server.dirty++;
}
zfree(sets);
+59 -258
View File
@@ -848,12 +848,7 @@ void streamPropagateXCLAIM(client *c, robj *key, streamCG *group, robj *groupnam
argv[11] = createStringObject("JUSTID",6);
argv[12] = createStringObject("LASTID",6);
argv[13] = createObjectFromStreamID(&group->last_id);
/* We use progagate() because this code path is not always called from
* the command execution context. Moreover this will just alter the
* consumer group state, and we don't need MULTI/EXEC wrapping because
* there is no message state cross-message atomicity required. */
propagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);
alsoPropagate(server.xclaimCommand,c->db->id,argv,14,PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(argv[0]);
decrRefCount(argv[3]);
decrRefCount(argv[4]);
@@ -880,12 +875,7 @@ void streamPropagateGroupID(client *c, robj *key, streamCG *group, robj *groupna
argv[2] = key;
argv[3] = groupname;
argv[4] = createObjectFromStreamID(&group->last_id);
/* We use progagate() because this code path is not always called from
* the command execution context. Moreover this will just alter the
* consumer group state, and we don't need MULTI/EXEC wrapping because
* there is no message state cross-message atomicity required. */
propagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
alsoPropagate(server.xgroupCommand,c->db->id,argv,5,PROPAGATE_AOF|PROPAGATE_REPL);
decrRefCount(argv[0]);
decrRefCount(argv[1]);
decrRefCount(argv[4]);
@@ -946,7 +936,6 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
int64_t numfields;
streamID id;
int propagate_last_id = 0;
int noack = flags & STREAM_RWR_NOACK;
/* If the client is asking for some history, we serve it using a
* different function, so that we return entries *solely* from its
@@ -965,10 +954,7 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
/* Update the group last_id if needed. */
if (group && streamCompareID(&id,&group->last_id) > 0) {
group->last_id = id;
/* Group last ID should be propagated only if NOACK was
* specified, otherwise the last id will be included
* in the propagation of XCLAIM itself. */
if (noack) propagate_last_id = 1;
propagate_last_id = 1;
}
/* Emit a two elements array for each item. The first is
@@ -976,7 +962,7 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
addReplyArrayLen(c,2);
addReplyStreamID(c,&id);
addReplyArrayLen(c,numfields*2);
addReplyMapLen(c,numfields);
/* Emit the field-value pairs. */
while(numfields--) {
@@ -996,7 +982,7 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
* XGROUP SETID command. So if we find that there is already
* a NACK for the entry, we need to associate it to the new
* consumer. */
if (group && !noack) {
if (group && !(flags & STREAM_RWR_NOACK)) {
unsigned char buf[sizeof(streamID)];
streamEncodeID(buf,&id);
@@ -1033,15 +1019,14 @@ size_t streamReplyWithRange(client *c, stream *s, streamID *start, streamID *end
streamPropagateXCLAIM(c,spi->keyname,group,spi->groupname,idarg,nack);
decrRefCount(idarg);
}
} else {
if (propagate_last_id)
streamPropagateGroupID(c,spi->keyname,group,spi->groupname);
}
arraylen++;
if (count && count == arraylen) break;
}
if (spi && propagate_last_id)
streamPropagateGroupID(c,spi->keyname,group,spi->groupname);
streamIteratorStop(&si);
if (arraylen_ptr) setDeferredArrayLen(c,arraylen_ptr,arraylen);
return arraylen;
@@ -1276,7 +1261,7 @@ void xaddCommand(client *c) {
}
addReplyStreamID(c,&id);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xadd",c->argv[1],c->db->id);
server.dirty++;
@@ -1388,11 +1373,6 @@ void xreadCommand(client *c) {
int moreargs = c->argc-i-1;
char *o = c->argv[i]->ptr;
if (!strcasecmp(o,"BLOCK") && moreargs) {
if (c->flags & CLIENT_LUA) {
/* There is no sense to use BLOCK option within LUA */
addReplyErrorFormat(c, "%s command is not allowed with BLOCK option from scripts", (char *)c->argv[0]->ptr);
return;
}
i++;
if (getTimeoutFromObjectOrReply(c,c->argv[i],&timeout,
UNIT_MILLISECONDS) != C_OK) return;
@@ -1570,8 +1550,7 @@ void xreadCommand(client *c) {
addReplyBulk(c,c->argv[streams_arg+i]);
streamConsumer *consumer = NULL;
if (groups) consumer = streamLookupConsumer(groups[i],
consumername->ptr,
SLC_NONE);
consumername->ptr,1);
streamPropInfo spi = {c->argv[i+streams_arg],groupname};
int flags = 0;
if (noack) flags |= STREAM_RWR_NOACK;
@@ -1707,9 +1686,7 @@ streamCG *streamLookupCG(stream *s, sds groupname) {
* consumer does not exist it is automatically created as a side effect
* of calling this function, otherwise its last seen time is updated and
* the existing consumer reference returned. */
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {
int create = !(flags & SLC_NOCREAT);
int refresh = !(flags & SLC_NOREFRESH);
streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int create) {
streamConsumer *consumer = raxFind(cg->consumers,(unsigned char*)name,
sdslen(name));
if (consumer == raxNotFound) {
@@ -1720,7 +1697,7 @@ streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {
raxInsert(cg->consumers,(unsigned char*)name,sdslen(name),
consumer,NULL);
}
if (refresh) consumer->seen_time = mstime();
consumer->seen_time = mstime();
return consumer;
}
@@ -1728,8 +1705,7 @@ streamConsumer *streamLookupConsumer(streamCG *cg, sds name, int flags) {
* may have pending messages: they are removed from the PEL, and the number
* of pending messages "lost" is returned. */
uint64_t streamDelConsumer(streamCG *cg, sds name) {
streamConsumer *consumer =
streamLookupConsumer(cg,name,SLC_NOCREAT|SLC_NOREFRESH);
streamConsumer *consumer = streamLookupConsumer(cg,name,0);
if (consumer == NULL) return 0;
uint64_t retval = raxSize(consumer->pel);
@@ -1945,21 +1921,11 @@ void xackCommand(client *c) {
return;
}
/* Start parsing the IDs, so that we abort ASAP if there is a syntax
* error: the return value of this command cannot be an error in case
* the client successfully acknowledged some messages, so it should be
* executed in a "all or nothing" fashion. */
for (int j = 3; j < c->argc; j++) {
streamID id;
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
}
int acknowledged = 0;
for (int j = 3; j < c->argc; j++) {
streamID id;
unsigned char buf[sizeof(streamID)];
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK)
serverPanic("StreamID invalid after check. Should not be possible.");
if (streamParseStrictIDOrReply(c,c->argv[j],&id,0) != C_OK) return;
streamEncodeID(buf,&id);
/* Lookup the ID in the group PEL: it will have a reference to the
@@ -2072,18 +2038,15 @@ void xpendingCommand(client *c) {
}
/* XPENDING <key> <group> <start> <stop> <count> [<consumer>] variant. */
else {
streamConsumer *consumer = NULL;
if (consumername) {
consumer = streamLookupConsumer(group,
consumername->ptr,
SLC_NOCREAT|SLC_NOREFRESH);
streamConsumer *consumer = consumername ?
streamLookupConsumer(group,consumername->ptr,0):
NULL;
/* If a consumer name was mentioned but it does not exist, we can
* just return an empty array. */
if (consumer == NULL) {
addReplyArrayLen(c,0);
return;
}
/* If a consumer name was mentioned but it does not exist, we can
* just return an empty array. */
if (consumername && consumer == NULL) {
addReplyArrayLen(c,0);
return;
}
rax *pel = consumer ? consumer->pel : group->pel;
@@ -2345,7 +2308,7 @@ void xclaimCommand(client *c) {
raxRemove(nack->consumer->pel,buf,sizeof(buf),NULL);
/* Update the consumer and idle time. */
if (consumer == NULL)
consumer = streamLookupConsumer(group,c->argv[3]->ptr,SLC_NONE);
consumer = streamLookupConsumer(group,c->argv[3]->ptr,1);
nack->consumer = consumer;
nack->delivery_time = deliverytime;
/* Set the delivery attempts counter if given, otherwise
@@ -2411,7 +2374,7 @@ void xdelCommand(client *c) {
/* Propagate the write if needed. */
if (deleted) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xdel",c->argv[1],c->db->id);
server.dirty += deleted;
}
@@ -2488,7 +2451,7 @@ void xtrimCommand(client *c) {
/* Propagate the write if needed. */
if (deleted) {
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STREAM,"xtrim",c->argv[1],c->db->id);
server.dirty += deleted;
if (approx_maxlen) streamRewriteApproxMaxlen(c,s,maxlen_arg_idx);
@@ -2496,204 +2459,16 @@ void xtrimCommand(client *c) {
addReplyLongLong(c,deleted);
}
/* Helper function for xinfoCommand.
* Handles the variants of XINFO STREAM */
void xinfoReplyWithStreamInfo(client *c, stream *s) {
int full = 1;
long long count = 10; /* Default COUNT is 10 so we don't block the server */
robj **optv = c->argv + 3; /* Options start after XINFO STREAM <key> */
int optc = c->argc - 3;
/* Parse options. */
if (optc == 0) {
full = 0;
} else {
/* Valid options are [FULL] or [FULL COUNT <count>] */
if (optc != 1 && optc != 3) {
addReplySubcommandSyntaxError(c);
return;
}
/* First option must be "FULL" */
if (strcasecmp(optv[0]->ptr,"full")) {
addReplySubcommandSyntaxError(c);
return;
}
if (optc == 3) {
/* First option must be "FULL" */
if (strcasecmp(optv[1]->ptr,"count")) {
addReplySubcommandSyntaxError(c);
return;
}
if (getLongLongFromObjectOrReply(c,optv[2],&count,NULL) == C_ERR)
return;
if (count < 0) count = 10;
}
}
addReplyMapLen(c,full ? 6 : 7);
addReplyBulkCString(c,"length");
addReplyLongLong(c,s->length);
addReplyBulkCString(c,"radix-tree-keys");
addReplyLongLong(c,raxSize(s->rax));
addReplyBulkCString(c,"radix-tree-nodes");
addReplyLongLong(c,s->rax->numnodes);
addReplyBulkCString(c,"last-generated-id");
addReplyStreamID(c,&s->last_id);
if (!full) {
/* XINFO STREAM <key> */
addReplyBulkCString(c,"groups");
addReplyLongLong(c,s->cgroups ? raxSize(s->cgroups) : 0);
/* To emit the first/last entry we use streamReplyWithRange(). */
int emitted;
streamID start, end;
start.ms = start.seq = 0;
end.ms = end.seq = UINT64_MAX;
addReplyBulkCString(c,"first-entry");
emitted = streamReplyWithRange(c,s,&start,&end,1,0,NULL,NULL,
STREAM_RWR_RAWENTRIES,NULL);
if (!emitted) addReplyNull(c);
addReplyBulkCString(c,"last-entry");
emitted = streamReplyWithRange(c,s,&start,&end,1,1,NULL,NULL,
STREAM_RWR_RAWENTRIES,NULL);
if (!emitted) addReplyNull(c);
} else {
/* XINFO STREAM <key> FULL [COUNT <count>] */
/* Stream entries */
addReplyBulkCString(c,"entries");
streamReplyWithRange(c,s,NULL,NULL,count,0,NULL,NULL,0,NULL);
/* Consumer groups */
addReplyBulkCString(c,"groups");
if (s->cgroups == NULL) {
addReplyArrayLen(c,0);
} else {
addReplyArrayLen(c,raxSize(s->cgroups));
raxIterator ri_cgroups;
raxStart(&ri_cgroups,s->cgroups);
raxSeek(&ri_cgroups,"^",NULL,0);
while(raxNext(&ri_cgroups)) {
streamCG *cg = ri_cgroups.data;
addReplyMapLen(c,5);
/* Name */
addReplyBulkCString(c,"name");
addReplyBulkCBuffer(c,ri_cgroups.key,ri_cgroups.key_len);
/* Last delivered ID */
addReplyBulkCString(c,"last-delivered-id");
addReplyStreamID(c,&cg->last_id);
/* Group PEL count */
addReplyBulkCString(c,"pel-count");
addReplyLongLong(c,raxSize(cg->pel));
/* Group PEL */
addReplyBulkCString(c,"pending");
long long arraylen_cg_pel = 0;
void *arrayptr_cg_pel = addReplyDeferredLen(c);
raxIterator ri_cg_pel;
raxStart(&ri_cg_pel,cg->pel);
raxSeek(&ri_cg_pel,"^",NULL,0);
while(raxNext(&ri_cg_pel) && (!count || arraylen_cg_pel < count)) {
streamNACK *nack = ri_cg_pel.data;
addReplyArrayLen(c,4);
/* Entry ID. */
streamID id;
streamDecodeID(ri_cg_pel.key,&id);
addReplyStreamID(c,&id);
/* Consumer name. */
addReplyBulkCBuffer(c,nack->consumer->name,
sdslen(nack->consumer->name));
/* Last delivery. */
addReplyLongLong(c,nack->delivery_time);
/* Number of deliveries. */
addReplyLongLong(c,nack->delivery_count);
arraylen_cg_pel++;
}
setDeferredArrayLen(c,arrayptr_cg_pel,arraylen_cg_pel);
raxStop(&ri_cg_pel);
/* Consumers */
addReplyBulkCString(c,"consumers");
addReplyArrayLen(c,raxSize(cg->consumers));
raxIterator ri_consumers;
raxStart(&ri_consumers,cg->consumers);
raxSeek(&ri_consumers,"^",NULL,0);
while(raxNext(&ri_consumers)) {
streamConsumer *consumer = ri_consumers.data;
addReplyMapLen(c,4);
/* Consumer name */
addReplyBulkCString(c,"name");
addReplyBulkCBuffer(c,consumer->name,sdslen(consumer->name));
/* Seen-time */
addReplyBulkCString(c,"seen-time");
addReplyLongLong(c,consumer->seen_time);
/* Consumer PEL count */
addReplyBulkCString(c,"pel-count");
addReplyLongLong(c,raxSize(consumer->pel));
/* Consumer PEL */
addReplyBulkCString(c,"pending");
long long arraylen_cpel = 0;
void *arrayptr_cpel = addReplyDeferredLen(c);
raxIterator ri_cpel;
raxStart(&ri_cpel,consumer->pel);
raxSeek(&ri_cpel,"^",NULL,0);
while(raxNext(&ri_cpel) && (!count || arraylen_cpel < count)) {
streamNACK *nack = ri_cpel.data;
addReplyArrayLen(c,3);
/* Entry ID. */
streamID id;
streamDecodeID(ri_cpel.key,&id);
addReplyStreamID(c,&id);
/* Last delivery. */
addReplyLongLong(c,nack->delivery_time);
/* Number of deliveries. */
addReplyLongLong(c,nack->delivery_count);
arraylen_cpel++;
}
setDeferredArrayLen(c,arrayptr_cpel,arraylen_cpel);
raxStop(&ri_cpel);
}
raxStop(&ri_consumers);
}
raxStop(&ri_cgroups);
}
}
}
/* XINFO CONSUMERS <key> <group>
* XINFO GROUPS <key>
* XINFO STREAM <key> [FULL [COUNT <count>]]
* XINFO STREAM <key>
* XINFO HELP. */
void xinfoCommand(client *c) {
const char *help[] = {
"CONSUMERS <key> <groupname> -- Show consumer groups of group <groupname>.",
"GROUPS <key> -- Show the stream consumer groups.",
"STREAM <key> [FULL [COUNT <count>]] -- Show information about the stream.",
" FULL will return the full state of the stream,",
" including all entries, groups, consumers and PELs.",
" It's possible to show only the first stream/PEL entries",
" by using the COUNT modifier (Default is 10)",
"HELP -- Print this help.",
"CONSUMERS <key> <groupname> -- Show consumer groups of group <groupname>.",
"GROUPS <key> -- Show the stream consumer groups.",
"STREAM <key> -- Show information about the stream.",
"HELP -- Print this help.",
NULL
};
stream *s = NULL;
@@ -2773,10 +2548,36 @@ NULL
addReplyStreamID(c,&cg->last_id);
}
raxStop(&ri);
} else if (!strcasecmp(opt,"STREAM")) {
/* XINFO STREAM <key> [FULL [COUNT <count>]]. */
xinfoReplyWithStreamInfo(c,s);
} else if (!strcasecmp(opt,"STREAM") && c->argc == 3) {
/* XINFO STREAM <key> (or the alias XINFO <key>). */
addReplyMapLen(c,7);
addReplyBulkCString(c,"length");
addReplyLongLong(c,s->length);
addReplyBulkCString(c,"radix-tree-keys");
addReplyLongLong(c,raxSize(s->rax));
addReplyBulkCString(c,"radix-tree-nodes");
addReplyLongLong(c,s->rax->numnodes);
addReplyBulkCString(c,"groups");
addReplyLongLong(c,s->cgroups ? raxSize(s->cgroups) : 0);
addReplyBulkCString(c,"last-generated-id");
addReplyStreamID(c,&s->last_id);
/* To emit the first/last entry we us the streamReplyWithRange()
* API. */
int count;
streamID start, end;
start.ms = start.seq = 0;
end.ms = end.seq = UINT64_MAX;
addReplyBulkCString(c,"first-entry");
count = streamReplyWithRange(c,s,&start,&end,1,0,NULL,NULL,
STREAM_RWR_RAWENTRIES,NULL);
if (!count) addReplyNull(c);
addReplyBulkCString(c,"last-entry");
count = streamReplyWithRange(c,s,&start,&end,1,1,NULL,NULL,
STREAM_RWR_RAWENTRIES,NULL);
if (!count) addReplyNull(c);
} else {
addReplySubcommandSyntaxError(c);
}
}
+7 -224
View File
@@ -84,7 +84,7 @@ void setGenericCommand(client *c, int flags, robj *key, robj *val, robj *expire,
addReply(c, abort_reply ? abort_reply : shared.null[c->resp]);
return;
}
genericSetKey(c,c->db,key,val,flags & OBJ_SET_KEEPTTL,1);
genericSetKey(c->db,key,val,flags & OBJ_SET_KEEPTTL);
server.dirty++;
if (expire) setExpire(c,c->db,key,mstime()+milliseconds);
notifyKeyspaceEvent(NOTIFY_STRING,"set",key,c->db->id);
@@ -183,7 +183,7 @@ void getCommand(client *c) {
void getsetCommand(client *c) {
if (getGenericCommand(c) == C_ERR) return;
c->argv[2] = tryObjectEncoding(c->argv[2]);
setKey(c,c->db,c->argv[1],c->argv[2]);
setKey(c->db,c->argv[1],c->argv[2]);
notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[1],c->db->id);
server.dirty++;
}
@@ -240,7 +240,7 @@ void setrangeCommand(client *c) {
if (sdslen(value) > 0) {
o->ptr = sdsgrowzero(o->ptr,offset+sdslen(value));
memcpy((char*)o->ptr+offset,value,sdslen(value));
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,
"setrange",c->argv[1],c->db->id);
server.dirty++;
@@ -328,7 +328,7 @@ void msetGenericCommand(client *c, int nx) {
for (j = 1; j < c->argc; j += 2) {
c->argv[j+1] = tryObjectEncoding(c->argv[j+1]);
setKey(c,c->db,c->argv[j],c->argv[j+1]);
setKey(c->db,c->argv[j],c->argv[j+1]);
notifyKeyspaceEvent(NOTIFY_STRING,"set",c->argv[j],c->db->id);
}
server.dirty += (c->argc-1)/2;
@@ -373,7 +373,7 @@ void incrDecrCommand(client *c, long long incr) {
dbAdd(c->db,c->argv[1],new);
}
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"incrby",c->argv[1],c->db->id);
server.dirty++;
addReply(c,shared.colon);
@@ -423,7 +423,7 @@ void incrbyfloatCommand(client *c) {
dbOverwrite(c->db,c->argv[1],new);
else
dbAdd(c->db,c->argv[1],new);
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"incrbyfloat",c->argv[1],c->db->id);
server.dirty++;
addReplyBulk(c,new);
@@ -467,7 +467,7 @@ void appendCommand(client *c) {
o->ptr = sdscatlen(o->ptr,append->ptr,sdslen(append->ptr));
totlen = sdslen(o->ptr);
}
signalModifiedKey(c,c->db,c->argv[1]);
signalModifiedKey(c->db,c->argv[1]);
notifyKeyspaceEvent(NOTIFY_STRING,"append",c->argv[1],c->db->id);
server.dirty++;
addReplyLongLong(c,totlen);
@@ -479,220 +479,3 @@ void strlenCommand(client *c) {
checkType(c,o,OBJ_STRING)) return;
addReplyLongLong(c,stringObjectLen(o));
}
/* STRALGO -- Implement complex algorithms on strings.
*
* STRALGO <algorithm> ... arguments ... */
void stralgoLCS(client *c); /* This implements the LCS algorithm. */
void stralgoCommand(client *c) {
/* Select the algorithm. */
if (!strcasecmp(c->argv[1]->ptr,"lcs")) {
stralgoLCS(c);
} else {
addReply(c,shared.syntaxerr);
}
}
/* STRALGO <algo> [IDX] [MINMATCHLEN <len>] [WITHMATCHLEN]
* STRINGS <string> <string> | KEYS <keya> <keyb>
*/
void stralgoLCS(client *c) {
uint32_t i, j;
long long minmatchlen = 0;
sds a = NULL, b = NULL;
int getlen = 0, getidx = 0, withmatchlen = 0;
robj *obja = NULL, *objb = NULL;
for (j = 2; j < (uint32_t)c->argc; j++) {
char *opt = c->argv[j]->ptr;
int moreargs = (c->argc-1) - j;
if (!strcasecmp(opt,"IDX")) {
getidx = 1;
} else if (!strcasecmp(opt,"LEN")) {
getlen = 1;
} else if (!strcasecmp(opt,"WITHMATCHLEN")) {
withmatchlen = 1;
} else if (!strcasecmp(opt,"MINMATCHLEN") && moreargs) {
if (getLongLongFromObjectOrReply(c,c->argv[j+1],&minmatchlen,NULL)
!= C_OK) return;
if (minmatchlen < 0) minmatchlen = 0;
j++;
} else if (!strcasecmp(opt,"STRINGS")) {
if (a != NULL) {
addReplyError(c,"Either use STRINGS or KEYS");
return;
}
a = c->argv[j+1]->ptr;
b = c->argv[j+2]->ptr;
j += 2;
} else if (!strcasecmp(opt,"KEYS")) {
if (a != NULL) {
addReplyError(c,"Either use STRINGS or KEYS");
return;
}
obja = lookupKeyRead(c->db,c->argv[j+1]);
objb = lookupKeyRead(c->db,c->argv[j+2]);
obja = obja ? getDecodedObject(obja) : createStringObject("",0);
objb = objb ? getDecodedObject(objb) : createStringObject("",0);
a = obja->ptr;
b = objb->ptr;
j += 2;
} else {
addReply(c,shared.syntaxerr);
return;
}
}
/* Complain if the user passed ambiguous parameters. */
if (a == NULL) {
addReplyError(c,"Please specify two strings: "
"STRINGS or KEYS options are mandatory");
return;
} else if (getlen && getidx) {
addReplyError(c,
"If you want both the length and indexes, please "
"just use IDX.");
return;
}
/* Compute the LCS using the vanilla dynamic programming technique of
* building a table of LCS(x,y) substrings. */
uint32_t alen = sdslen(a);
uint32_t blen = sdslen(b);
/* Setup an uint32_t array to store at LCS[i,j] the length of the
* LCS A0..i-1, B0..j-1. Note that we have a linear array here, so
* we index it as LCS[j+(blen+1)*j] */
uint32_t *lcs = zmalloc((alen+1)*(blen+1)*sizeof(uint32_t));
#define LCS(A,B) lcs[(B)+((A)*(blen+1))]
/* Start building the LCS table. */
for (uint32_t i = 0; i <= alen; i++) {
for (uint32_t j = 0; j <= blen; j++) {
if (i == 0 || j == 0) {
/* If one substring has length of zero, the
* LCS length is zero. */
LCS(i,j) = 0;
} else if (a[i-1] == b[j-1]) {
/* The len LCS (and the LCS itself) of two
* sequences with the same final character, is the
* LCS of the two sequences without the last char
* plus that last char. */
LCS(i,j) = LCS(i-1,j-1)+1;
} else {
/* If the last character is different, take the longest
* between the LCS of the first string and the second
* minus the last char, and the reverse. */
uint32_t lcs1 = LCS(i-1,j);
uint32_t lcs2 = LCS(i,j-1);
LCS(i,j) = lcs1 > lcs2 ? lcs1 : lcs2;
}
}
}
/* Store the actual LCS string in "result" if needed. We create
* it backward, but the length is already known, we store it into idx. */
uint32_t idx = LCS(alen,blen);
sds result = NULL; /* Resulting LCS string. */
void *arraylenptr = NULL; /* Deffered length of the array for IDX. */
uint32_t arange_start = alen, /* alen signals that values are not set. */
arange_end = 0,
brange_start = 0,
brange_end = 0;
/* Do we need to compute the actual LCS string? Allocate it in that case. */
int computelcs = getidx || !getlen;
if (computelcs) result = sdsnewlen(SDS_NOINIT,idx);
/* Start with a deferred array if we have to emit the ranges. */
uint32_t arraylen = 0; /* Number of ranges emitted in the array. */
if (getidx) {
addReplyMapLen(c,2);
addReplyBulkCString(c,"matches");
arraylenptr = addReplyDeferredLen(c);
}
i = alen, j = blen;
while (computelcs && i > 0 && j > 0) {
int emit_range = 0;
if (a[i-1] == b[j-1]) {
/* If there is a match, store the character and reduce
* the indexes to look for a new match. */
result[idx-1] = a[i-1];
/* Track the current range. */
if (arange_start == alen) {
arange_start = i-1;
arange_end = i-1;
brange_start = j-1;
brange_end = j-1;
} else {
/* Let's see if we can extend the range backward since
* it is contiguous. */
if (arange_start == i && brange_start == j) {
arange_start--;
brange_start--;
} else {
emit_range = 1;
}
}
/* Emit the range if we matched with the first byte of
* one of the two strings. We'll exit the loop ASAP. */
if (arange_start == 0 || brange_start == 0) emit_range = 1;
idx--; i--; j--;
} else {
/* Otherwise reduce i and j depending on the largest
* LCS between, to understand what direction we need to go. */
uint32_t lcs1 = LCS(i-1,j);
uint32_t lcs2 = LCS(i,j-1);
if (lcs1 > lcs2)
i--;
else
j--;
if (arange_start != alen) emit_range = 1;
}
/* Emit the current range if needed. */
uint32_t match_len = arange_end - arange_start + 1;
if (emit_range) {
if (minmatchlen == 0 || match_len >= minmatchlen) {
if (arraylenptr) {
addReplyArrayLen(c,2+withmatchlen);
addReplyArrayLen(c,2);
addReplyLongLong(c,arange_start);
addReplyLongLong(c,arange_end);
addReplyArrayLen(c,2);
addReplyLongLong(c,brange_start);
addReplyLongLong(c,brange_end);
if (withmatchlen) addReplyLongLong(c,match_len);
arraylen++;
}
}
arange_start = alen; /* Restart at the next match. */
}
}
/* Signal modified key, increment dirty, ... */
/* Reply depending on the given options. */
if (arraylenptr) {
addReplyBulkCString(c,"len");
addReplyLongLong(c,LCS(alen,blen));
setDeferredArrayLen(c,arraylenptr,arraylen);
} else if (getlen) {
addReplyLongLong(c,LCS(alen,blen));
} else {
addReplyBulkSds(c,result);
result = NULL;
}
/* Cleanup. */
if (obja) decrRefCount(obja);
if (objb) decrRefCount(objb);
sdsfree(result);
zfree(lcs);
return;
}
+10 -11
View File
@@ -186,8 +186,7 @@ zskiplistNode *zslInsert(zskiplist *zsl, double score, sds ele) {
return x;
}
/* Internal function used by zslDelete, zslDeleteRangeByScore and
* zslDeleteRangeByRank. */
/* Internal function used by zslDelete, zslDeleteByScore and zslDeleteByRank */
void zslDeleteNode(zskiplist *zsl, zskiplistNode *x, zskiplistNode **update) {
int i;
for (i = 0; i < zsl->level; i++) {
@@ -1301,14 +1300,14 @@ int zsetScore(robj *zobj, sds member, double *score) {
* none could be set if we re-added an element using the same score it used
* to have, or in the case a zero increment is used).
*
* The function returns 0 on error, currently only when the increment
* The function returns 0 on erorr, currently only when the increment
* produces a NAN condition, or when the 'score' value is NAN since the
* start.
*
* The command as a side effect of adding a new element may convert the sorted
* The commad as a side effect of adding a new element may convert the sorted
* set internal encoding from ziplist to hashtable+skiplist.
*
* Memory management of 'ele':
* Memory managemnet of 'ele':
*
* The function does not take ownership of the 'ele' SDS string, but copies
* it if needed. */
@@ -1646,7 +1645,7 @@ reply_to_client:
cleanup:
zfree(scores);
if (added || updated) {
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_ZSET,
incr ? "zincr" : "zadd", key, c->db->id);
}
@@ -1681,7 +1680,7 @@ void zremCommand(client *c) {
notifyKeyspaceEvent(NOTIFY_ZSET,"zrem",key,c->db->id);
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
server.dirty += deleted;
}
addReplyLongLong(c,deleted);
@@ -1779,7 +1778,7 @@ void zremrangeGenericCommand(client *c, int rangetype) {
/* Step 4: Notifications and reply. */
if (deleted) {
char *event[3] = {"zremrangebyrank","zremrangebyscore","zremrangebylex"};
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
notifyKeyspaceEvent(NOTIFY_ZSET,event[rangetype],key,c->db->id);
if (keyremoved)
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",key,c->db->id);
@@ -2383,7 +2382,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
zsetConvertToZiplistIfNeeded(dstobj,maxelelen);
dbAdd(c->db,dstkey,dstobj);
addReplyLongLong(c,zsetLength(dstobj));
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
notifyKeyspaceEvent(NOTIFY_ZSET,
(op == SET_OP_UNION) ? "zunionstore" : "zinterstore",
dstkey,c->db->id);
@@ -2392,7 +2391,7 @@ void zunionInterGenericCommand(client *c, robj *dstkey, int op) {
decrRefCount(dstobj);
addReply(c,shared.czero);
if (touched) {
signalModifiedKey(c,c->db,dstkey);
signalModifiedKey(c->db,dstkey);
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",dstkey,c->db->id);
server.dirty++;
}
@@ -3216,7 +3215,7 @@ void genericZpopCommand(client *c, robj **keyv, int keyc, int where, int emitkey
if (arraylen == 0) { /* Do this only for the first iteration. */
char *events[2] = {"zpopmin","zpopmax"};
notifyKeyspaceEvent(NOTIFY_ZSET,events[where],key,c->db->id);
signalModifiedKey(c,c->db,key);
signalModifiedKey(c->db,key);
}
addReplyBulkCBuffer(c,ele,sdslen(ele));
-189
View File
@@ -1,189 +0,0 @@
/* Copyright (c) 2009-2020, 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 "server.h"
#include "cluster.h"
/* ========================== Clients timeouts ============================= */
/* Check if this blocked client timedout (does nothing if the client is
* not blocked right now). If so send a reply, unblock it, and return 1.
* Otherwise 0 is returned and no operation is performed. */
int checkBlockedClientTimeout(client *c, mstime_t now) {
if (c->flags & CLIENT_BLOCKED &&
c->bpop.timeout != 0
&& c->bpop.timeout < now)
{
/* Handle blocking operation specific timeout. */
replyToBlockedClientTimedOut(c);
unblockClient(c);
return 1;
} else {
return 0;
}
}
/* Check for timeouts. Returns non-zero if the client was terminated.
* The function gets the current time in milliseconds as argument since
* it gets called multiple times in a loop, so calling gettimeofday() for
* each iteration would be costly without any actual gain. */
int clientsCronHandleTimeout(client *c, mstime_t now_ms) {
time_t now = now_ms/1000;
if (server.maxidletime &&
/* This handles the idle clients connection timeout if set. */
!(c->flags & CLIENT_SLAVE) && /* No timeout for slaves and monitors */
!(c->flags & CLIENT_MASTER) && /* No timeout for masters */
!(c->flags & CLIENT_BLOCKED) && /* No timeout for BLPOP */
!(c->flags & CLIENT_PUBSUB) && /* No timeout for Pub/Sub clients */
(now - c->lastinteraction > server.maxidletime))
{
serverLog(LL_VERBOSE,"Closing idle client");
freeClient(c);
return 1;
} else if (c->flags & CLIENT_BLOCKED) {
/* Cluster: handle unblock & redirect of clients blocked
* into keys no longer served by this server. */
if (server.cluster_enabled) {
if (clusterRedirectBlockedClientIfNeeded(c))
unblockClient(c);
}
}
return 0;
}
/* For blocked clients timeouts we populate a radix tree of 128 bit keys
* composed as such:
*
* [8 byte big endian expire time]+[8 byte client ID]
*
* We don't do any cleanup in the Radix tree: when we run the clients that
* reached the timeout already, if they are no longer existing or no longer
* blocked with such timeout, we just go forward.
*
* Every time a client blocks with a timeout, we add the client in
* the tree. In beforeSleep() we call handleBlockedClientsTimeout() to run
* the tree and unblock the clients. */
#define CLIENT_ST_KEYLEN 16 /* 8 bytes mstime + 8 bytes client ID. */
/* Given client ID and timeout, write the resulting radix tree key in buf. */
void encodeTimeoutKey(unsigned char *buf, uint64_t timeout, client *c) {
timeout = htonu64(timeout);
memcpy(buf,&timeout,sizeof(timeout));
memcpy(buf+8,&c,sizeof(c));
if (sizeof(c) == 4) memset(buf+12,0,4); /* Zero padding for 32bit target. */
}
/* Given a key encoded with encodeTimeoutKey(), resolve the fields and write
* the timeout into *toptr and the client pointer into *cptr. */
void decodeTimeoutKey(unsigned char *buf, uint64_t *toptr, client **cptr) {
memcpy(toptr,buf,sizeof(*toptr));
*toptr = ntohu64(*toptr);
memcpy(cptr,buf+8,sizeof(*cptr));
}
/* Add the specified client id / timeout as a key in the radix tree we use
* to handle blocked clients timeouts. The client is not added to the list
* if its timeout is zero (block forever). */
void addClientToTimeoutTable(client *c) {
if (c->bpop.timeout == 0) return;
uint64_t timeout = c->bpop.timeout;
unsigned char buf[CLIENT_ST_KEYLEN];
encodeTimeoutKey(buf,timeout,c);
if (raxTryInsert(server.clients_timeout_table,buf,sizeof(buf),NULL,NULL))
c->flags |= CLIENT_IN_TO_TABLE;
}
/* Remove the client from the table when it is unblocked for reasons
* different than timing out. */
void removeClientFromTimeoutTable(client *c) {
if (!(c->flags & CLIENT_IN_TO_TABLE)) return;
c->flags &= ~CLIENT_IN_TO_TABLE;
uint64_t timeout = c->bpop.timeout;
unsigned char buf[CLIENT_ST_KEYLEN];
encodeTimeoutKey(buf,timeout,c);
raxRemove(server.clients_timeout_table,buf,sizeof(buf),NULL);
}
/* This function is called in beforeSleep() in order to unblock clients
* that are waiting in blocking operations with a timeout set. */
void handleBlockedClientsTimeout(void) {
if (raxSize(server.clients_timeout_table) == 0) return;
uint64_t now = mstime();
raxIterator ri;
raxStart(&ri,server.clients_timeout_table);
raxSeek(&ri,"^",NULL,0);
while(raxNext(&ri)) {
uint64_t timeout;
client *c;
decodeTimeoutKey(ri.key,&timeout,&c);
if (timeout >= now) break; /* All the timeouts are in the future. */
c->flags &= ~CLIENT_IN_TO_TABLE;
checkBlockedClientTimeout(c,now);
raxRemove(server.clients_timeout_table,ri.key,ri.key_len,NULL);
raxSeek(&ri,"^",NULL,0);
}
}
/* Get a timeout value from an object and store it into 'timeout'.
* The final timeout is always stored as milliseconds as a time where the
* timeout will expire, however the parsing is performed according to
* the 'unit' that can be seconds or milliseconds.
*
* Note that if the timeout is zero (usually from the point of view of
* commands API this means no timeout) the value stored into 'timeout'
* is zero. */
int getTimeoutFromObjectOrReply(client *c, robj *object, mstime_t *timeout, int unit) {
long long tval;
long double ftval;
if (unit == UNIT_SECONDS) {
if (getLongDoubleFromObjectOrReply(c,object,&ftval,
"timeout is not a float or out of range") != C_OK)
return C_ERR;
tval = (long long) (ftval * 1000.0);
} else {
if (getLongLongFromObjectOrReply(c,object,&tval,
"timeout is not an integer or out of range") != C_OK)
return C_ERR;
}
if (tval < 0) {
addReplyError(c,"timeout is negative");
return C_ERR;
}
if (tval > 0) {
tval += mstime();
}
*timeout = tval;
return C_OK;
}
+3 -53
View File
@@ -93,56 +93,11 @@ static int parseProtocolsConfig(const char *str) {
* served to the reader yet. */
static list *pending_list = NULL;
/**
* OpenSSL global initialization and locking handling callbacks.
* Note that this is only required for OpenSSL < 1.1.0.
*/
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define USE_CRYPTO_LOCKS
#endif
#ifdef USE_CRYPTO_LOCKS
static pthread_mutex_t *openssl_locks;
static void sslLockingCallback(int mode, int lock_id, const char *f, int line) {
pthread_mutex_t *mt = openssl_locks + lock_id;
if (mode & CRYPTO_LOCK) {
pthread_mutex_lock(mt);
} else {
pthread_mutex_unlock(mt);
}
(void)f;
(void)line;
}
static void initCryptoLocks(void) {
unsigned i, nlocks;
if (CRYPTO_get_locking_callback() != NULL) {
/* Someone already set the callback before us. Don't destroy it! */
return;
}
nlocks = CRYPTO_num_locks();
openssl_locks = zmalloc(sizeof(*openssl_locks) * nlocks);
for (i = 0; i < nlocks; i++) {
pthread_mutex_init(openssl_locks + i, NULL);
}
CRYPTO_set_locking_callback(sslLockingCallback);
}
#endif /* USE_CRYPTO_LOCKS */
void tlsInit(void) {
ERR_load_crypto_strings();
SSL_load_error_strings();
SSL_library_init();
#ifdef USE_CRYPTO_LOCKS
initCryptoLocks();
#endif
if (!RAND_poll()) {
serverLog(LL_WARNING, "OpenSSL: Failed to seed random number generator.");
}
@@ -205,7 +160,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
#endif
#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
#endif
if (ctx_config->prefer_server_ciphers)
@@ -213,9 +168,7 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
#if defined(SSL_CTX_set_ecdh_auto)
SSL_CTX_set_ecdh_auto(ctx, 1);
#endif
if (SSL_CTX_use_certificate_file(ctx, ctx_config->cert_file, SSL_FILETYPE_PEM) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
@@ -813,17 +766,15 @@ int tlsHasPendingData() {
return listLength(pending_list) > 0;
}
int tlsProcessPendingData() {
void tlsProcessPendingData() {
listIter li;
listNode *ln;
int processed = listLength(pending_list);
listRewind(pending_list,&li);
while((ln = listNext(&li))) {
tls_connection *conn = listNodeValue(ln);
tlsHandleEvent(conn, AE_READABLE);
}
return processed;
}
#else /* USE_OPENSSL */
@@ -851,8 +802,7 @@ int tlsHasPendingData() {
return 0;
}
int tlsProcessPendingData() {
return 0;
void tlsProcessPendingData() {
}
#endif
+42 -127
View File
@@ -94,8 +94,7 @@ void disableTracking(client *c) {
server.tracking_clients--;
c->flags &= ~(CLIENT_TRACKING|CLIENT_TRACKING_BROKEN_REDIR|
CLIENT_TRACKING_BCAST|CLIENT_TRACKING_OPTIN|
CLIENT_TRACKING_OPTOUT|CLIENT_TRACKING_CACHING|
CLIENT_TRACKING_NOLOOP);
CLIENT_TRACKING_OPTOUT|CLIENT_TRACKING_CACHING);
}
}
@@ -130,19 +129,14 @@ void enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **pr
if (!(c->flags & CLIENT_TRACKING)) server.tracking_clients++;
c->flags |= CLIENT_TRACKING;
c->flags &= ~(CLIENT_TRACKING_BROKEN_REDIR|CLIENT_TRACKING_BCAST|
CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|
CLIENT_TRACKING_NOLOOP);
CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT);
c->client_tracking_redirection = redirect_to;
/* This may be the first client we ever enable. Crete the tracking
* table if it does not exist. */
if (TrackingTable == NULL) {
TrackingTable = raxNew();
PrefixTable = raxNew();
TrackingChannelName = createStringObject("__redis__:invalidate",20);
}
/* For broadcasting, set the list of prefixes in the client. */
if (options & CLIENT_TRACKING_BCAST) {
c->flags |= CLIENT_TRACKING_BCAST;
if (numprefix == 0) enableBcastTrackingForPrefix(c,"",0);
@@ -151,10 +145,7 @@ void enableTracking(client *c, uint64_t redirect_to, uint64_t options, robj **pr
enableBcastTrackingForPrefix(c,sdsprefix,sdslen(sdsprefix));
}
}
/* Set the remaining flags that don't need any special handling. */
c->flags |= options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT|
CLIENT_TRACKING_NOLOOP);
c->flags |= options & (CLIENT_TRACKING_OPTIN|CLIENT_TRACKING_OPTOUT);
}
/* This function is called after the execution of a readonly command in the
@@ -254,7 +245,7 @@ void sendTrackingMessage(client *c, char *keyname, size_t keylen, int proto) {
* matches one or more prefixes in the prefix table. Later when we
* return to the event loop, we'll send invalidation messages to the
* clients subscribed to each prefix. */
void trackingRememberKeyToBroadcast(client *c, char *keyname, size_t keylen) {
void trackingRememberKeyToBroadcast(char *keyname, size_t keylen) {
raxIterator ri;
raxStart(&ri,PrefixTable);
raxSeek(&ri,"^",NULL,0);
@@ -263,11 +254,7 @@ void trackingRememberKeyToBroadcast(client *c, char *keyname, size_t keylen) {
if (ri.key_len != 0 && memcmp(ri.key,keyname,ri.key_len) != 0)
continue;
bcastState *bs = ri.data;
/* We insert the client pointer as associated value in the radix
* tree. This way we know who was the client that did the last
* change to the key, and can avoid sending the notification in the
* case the client is in NOLOOP mode. */
raxTryInsert(bs->keys,(unsigned char*)keyname,keylen,c,NULL);
raxTryInsert(bs->keys,(unsigned char*)keyname,keylen,NULL,NULL);
}
raxStop(&ri);
}
@@ -275,26 +262,15 @@ void trackingRememberKeyToBroadcast(client *c, char *keyname, size_t keylen) {
/* This function is called from signalModifiedKey() or other places in Redis
* when a key changes value. In the context of keys tracking, our task here is
* to send a notification to every client that may have keys about such caching
* slot.
*
* Note that 'c' may be NULL in case the operation was performed outside the
* context of a client modifying the database (for instance when we delete a
* key because of expire).
*
* The last argument 'bcast' tells the function if it should also schedule
* the key for broadcasting to clients in BCAST mode. This is the case when
* the function is called from the Redis core once a key is modified, however
* we also call the function in order to evict keys in the key table in case
* of memory pressure: in that case the key didn't really change, so we want
* just to notify the clients that are in the table for this key, that would
* otherwise miss the fact we are no longer tracking the key for them. */
void trackingInvalidateKeyRaw(client *c, char *key, size_t keylen, int bcast) {
* slot. */
void trackingInvalidateKey(robj *keyobj) {
if (TrackingTable == NULL) return;
sds sdskey = keyobj->ptr;
if (bcast && raxSize(PrefixTable) > 0)
trackingRememberKeyToBroadcast(c,key,keylen);
if (raxSize(PrefixTable) > 0)
trackingRememberKeyToBroadcast(sdskey,sdslen(sdskey));
rax *ids = raxFind(TrackingTable,(unsigned char*)key,keylen);
rax *ids = raxFind(TrackingTable,(unsigned char*)sdskey,sdslen(sdskey));
if (ids == raxNotFound) return;
raxIterator ri;
@@ -303,28 +279,19 @@ void trackingInvalidateKeyRaw(client *c, char *key, size_t keylen, int bcast) {
while(raxNext(&ri)) {
uint64_t id;
memcpy(&id,ri.key,sizeof(id));
client *target = lookupClientByID(id);
client *c = lookupClientByID(id);
/* Note that if the client is in BCAST mode, we don't want to
* send invalidation messages that were pending in the case
* previously the client was not in BCAST mode. This can happen if
* TRACKING is enabled normally, and then the client switches to
* BCAST mode. */
if (target == NULL ||
!(target->flags & CLIENT_TRACKING)||
target->flags & CLIENT_TRACKING_BCAST)
if (c == NULL ||
!(c->flags & CLIENT_TRACKING)||
c->flags & CLIENT_TRACKING_BCAST)
{
continue;
}
/* If the client enabled the NOLOOP mode, don't send notifications
* about keys changed by the client itself. */
if (target->flags & CLIENT_TRACKING_NOLOOP &&
target == c)
{
continue;
}
sendTrackingMessage(target,key,keylen,0);
sendTrackingMessage(c,sdskey,sdslen(sdskey),0);
}
raxStop(&ri);
@@ -332,13 +299,7 @@ void trackingInvalidateKeyRaw(client *c, char *key, size_t keylen, int bcast) {
* again if more keys will be modified in this caching slot. */
TrackingTableTotalItems -= raxSize(ids);
raxFree(ids);
raxRemove(TrackingTable,(unsigned char*)key,keylen,NULL);
}
/* Wrapper (the one actually called across the core) to pass the key
* as object. */
void trackingInvalidateKey(client *c, robj *keyobj) {
trackingInvalidateKeyRaw(c,keyobj->ptr,sdslen(keyobj->ptr),1);
raxRemove(TrackingTable,(unsigned char*)sdskey,sdslen(sdskey),NULL);
}
/* This function is called when one or all the Redis databases are flushed
@@ -405,8 +366,10 @@ void trackingLimitUsedSlots(void) {
effort--;
raxSeek(&ri,"^",NULL,0);
raxRandomWalk(&ri,0);
if (raxEOF(&ri)) break;
trackingInvalidateKeyRaw(NULL,(char*)ri.key,ri.key_len,0);
rax *ids = ri.data;
TrackingTableTotalItems -= raxSize(ids);
raxFree(ids);
raxRemove(TrackingTable,ri.key,ri.key_len,NULL);
if (raxSize(TrackingTable) <= max_keys) {
timeout_counter = 0;
raxStop(&ri);
@@ -420,54 +383,6 @@ void trackingLimitUsedSlots(void) {
timeout_counter++;
}
/* Generate Redis protocol for an array containing all the key names
* in the 'keys' radix tree. If the client is not NULL, the list will not
* include keys that were modified the last time by this client, in order
* to implement the NOLOOP option.
*
* If the resultin array would be empty, NULL is returned instead. */
sds trackingBuildBroadcastReply(client *c, rax *keys) {
raxIterator ri;
uint64_t count;
if (c == NULL) {
count = raxSize(keys);
} else {
count = 0;
raxStart(&ri,keys);
raxSeek(&ri,"^",NULL,0);
while(raxNext(&ri)) {
if (ri.data != c) count++;
}
raxStop(&ri);
if (count == 0) return NULL;
}
/* Create the array reply with the list of keys once, then send
* it to all the clients subscribed to this prefix. */
char buf[32];
size_t len = ll2string(buf,sizeof(buf),count);
sds proto = sdsempty();
proto = sdsMakeRoomFor(proto,count*15);
proto = sdscatlen(proto,"*",1);
proto = sdscatlen(proto,buf,len);
proto = sdscatlen(proto,"\r\n",2);
raxStart(&ri,keys);
raxSeek(&ri,"^",NULL,0);
while(raxNext(&ri)) {
if (c && ri.data == c) continue;
len = ll2string(buf,sizeof(buf),ri.key_len);
proto = sdscatlen(proto,"$",1);
proto = sdscatlen(proto,buf,len);
proto = sdscatlen(proto,"\r\n",2);
proto = sdscatlen(proto,ri.key,ri.key_len);
proto = sdscatlen(proto,"\r\n",2);
}
raxStop(&ri);
return proto;
}
/* This function will run the prefixes of clients in BCAST mode and
* keys that were modified about each prefix, and will send the
* notifications to each client in each prefix. */
@@ -479,15 +394,29 @@ void trackingBroadcastInvalidationMessages(void) {
raxStart(&ri,PrefixTable);
raxSeek(&ri,"^",NULL,0);
/* For each prefix... */
while(raxNext(&ri)) {
bcastState *bs = ri.data;
if (raxSize(bs->keys)) {
/* Generate the common protocol for all the clients that are
* not using the NOLOOP option. */
sds proto = trackingBuildBroadcastReply(NULL,bs->keys);
/* Create the array reply with the list of keys once, then send
* it to all the clients subscribed to this prefix. */
char buf[32];
size_t len = ll2string(buf,sizeof(buf),raxSize(bs->keys));
sds proto = sdsempty();
proto = sdsMakeRoomFor(proto,raxSize(bs->keys)*15);
proto = sdscatlen(proto,"*",1);
proto = sdscatlen(proto,buf,len);
proto = sdscatlen(proto,"\r\n",2);
raxStart(&ri2,bs->keys);
raxSeek(&ri2,"^",NULL,0);
while(raxNext(&ri2)) {
len = ll2string(buf,sizeof(buf),ri2.key_len);
proto = sdscatlen(proto,"$",1);
proto = sdscatlen(proto,buf,len);
proto = sdscatlen(proto,"\r\n",2);
proto = sdscatlen(proto,ri2.key,ri2.key_len);
proto = sdscatlen(proto,"\r\n",2);
}
raxStop(&ri2);
/* Send this array of keys to every client in the list. */
raxStart(&ri2,bs->clients);
@@ -495,16 +424,7 @@ void trackingBroadcastInvalidationMessages(void) {
while(raxNext(&ri2)) {
client *c;
memcpy(&c,ri2.key,sizeof(c));
if (c->flags & CLIENT_TRACKING_NOLOOP) {
/* This client may have certain keys excluded. */
sds adhoc = trackingBuildBroadcastReply(c,bs->keys);
if (adhoc) {
sendTrackingMessage(c,adhoc,sdslen(adhoc),1);
sdsfree(adhoc);
}
} else {
sendTrackingMessage(c,proto,sdslen(proto),1);
}
sendTrackingMessage(c,proto,sdslen(proto),1);
}
raxStop(&ri2);
@@ -529,8 +449,3 @@ uint64_t trackingGetTotalKeys(void) {
if (TrackingTable == NULL) return 0;
return raxSize(TrackingTable);
}
uint64_t trackingGetTotalPrefixes(void) {
if (PrefixTable == NULL) return 0;
return raxSize(PrefixTable);
}
+13 -35
View File
@@ -42,7 +42,7 @@
#include <time.h>
#include "util.h"
#include "sha256.h"
#include "sha1.h"
/* Glob-style pattern matching. */
int stringmatchlen(const char *pattern, int patternLen,
@@ -51,7 +51,7 @@ int stringmatchlen(const char *pattern, int patternLen,
while(patternLen && stringLen) {
switch(pattern[0]) {
case '*':
while (patternLen && pattern[1] == '*') {
while (pattern[1] == '*') {
pattern++;
patternLen--;
}
@@ -67,6 +67,8 @@ int stringmatchlen(const char *pattern, int patternLen,
return 0; /* no match */
break;
case '?':
if (stringLen == 0)
return 0; /* no match */
string++;
stringLen--;
break;
@@ -94,7 +96,7 @@ int stringmatchlen(const char *pattern, int patternLen,
pattern--;
patternLen++;
break;
} else if (patternLen >= 3 && pattern[1] == '-') {
} else if (pattern[1] == '-' && patternLen >= 3) {
int start = pattern[0];
int end = pattern[2];
int c = string[0];
@@ -600,10 +602,6 @@ int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
}
if (*p == '.') l--;
}
if (l == 2 && buf[0] == '-' && buf[1] == '0') {
buf[0] = '0';
l = 1;
}
break;
default: return 0; /* Invalid mode. */
}
@@ -620,7 +618,7 @@ int ld2string(char *buf, size_t len, long double value, ld2string_mode mode) {
void getRandomBytes(unsigned char *p, size_t len) {
/* Global state. */
static int seed_initialized = 0;
static unsigned char seed[64]; /* 512 bit internal block size. */
static unsigned char seed[20]; /* The SHA1 seed, from /dev/urandom. */
static uint64_t counter = 0; /* The counter we hash with the seed. */
if (!seed_initialized) {
@@ -645,34 +643,14 @@ void getRandomBytes(unsigned char *p, size_t len) {
}
while(len) {
/* This implements SHA256-HMAC. */
unsigned char digest[SHA256_BLOCK_SIZE];
unsigned char kxor[64];
unsigned int copylen =
len > SHA256_BLOCK_SIZE ? SHA256_BLOCK_SIZE : len;
unsigned char digest[20];
SHA1_CTX ctx;
unsigned int copylen = len > 20 ? 20 : len;
/* IKEY: key xored with 0x36. */
memcpy(kxor,seed,sizeof(kxor));
for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x36;
/* Obtain HASH(IKEY||MESSAGE). */
SHA256_CTX ctx;
sha256_init(&ctx);
sha256_update(&ctx,kxor,sizeof(kxor));
sha256_update(&ctx,(unsigned char*)&counter,sizeof(counter));
sha256_final(&ctx,digest);
/* OKEY: key xored with 0x5c. */
memcpy(kxor,seed,sizeof(kxor));
for (unsigned int i = 0; i < sizeof(kxor); i++) kxor[i] ^= 0x5C;
/* Obtain HASH(OKEY || HASH(IKEY||MESSAGE)). */
sha256_init(&ctx);
sha256_update(&ctx,kxor,sizeof(kxor));
sha256_update(&ctx,digest,SHA256_BLOCK_SIZE);
sha256_final(&ctx,digest);
/* Increment the counter for the next iteration. */
SHA1Init(&ctx);
SHA1Update(&ctx, seed, sizeof(seed));
SHA1Update(&ctx, (unsigned char*)&counter,sizeof(counter));
SHA1Final(digest, &ctx);
counter++;
memcpy(p,digest,copylen);
+1 -1
View File
@@ -440,7 +440,7 @@ unsigned int zipStorePrevEntryLength(unsigned char *p, unsigned int len) {
if ((prevlensize) == 1) { \
(prevlen) = (ptr)[0]; \
} else if ((prevlensize) == 5) { \
assert(sizeof((prevlen)) == 4); \
assert(sizeof((prevlen)) == 4); \
memcpy(&(prevlen), ((char*)(ptr)) + 1, 4); \
memrev32ifbe(&prevlen); \
} \
-44
View File
@@ -1,44 +0,0 @@
source "../tests/includes/init-tests.tcl"
proc cluster_allocate_mixedSlots {n} {
set slot 16383
while {$slot >= 0} {
set node [expr {$slot % $n}]
lappend slots_$node $slot
incr slot -1
}
for {set j 0} {$j < $n} {incr j} {
R $j cluster addslots {*}[set slots_${j}]
}
}
proc create_cluster_with_mixedSlot {masters slaves} {
cluster_allocate_mixedSlots $masters
if {$slaves} {
cluster_allocate_slaves $masters $slaves
}
assert_cluster_state ok
}
test "Create a 5 nodes cluster" {
create_cluster_with_mixedSlot 5 15
}
test "Cluster is up" {
assert_cluster_state ok
}
test "Cluster is writable" {
cluster_write_test 0
}
test "Instance #5 is a slave" {
assert {[RI 5 role] eq {slave}}
}
test "client do not break when cluster slot" {
R 0 config set client-output-buffer-limit "normal 33554432 16777216 60"
if { [catch {R 0 cluster slots}] } {
fail "output overflow when cluster slots"
}
}
-12
View File
@@ -54,12 +54,6 @@ tags {"aof"} {
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
wait_for_condition 50 100 {
[catch {$client ping} e] == 0
} else {
fail "Loading DB is taking too much time."
}
test "Truncated AOF loaded: we expect foo to be equal to 5" {
assert {[$client get foo] eq "5"}
}
@@ -77,12 +71,6 @@ tags {"aof"} {
set client [redis [dict get $srv host] [dict get $srv port] 0 $::tls]
wait_for_condition 50 100 {
[catch {$client ping} e] == 0
} else {
fail "Loading DB is taking too much time."
}
test "Truncated AOF loaded: we expect foo to be equal to 6 now" {
assert {[$client get foo] eq "6"}
}
-1
View File
@@ -20,7 +20,6 @@ start_server {} {
$R(1) replicaof $R_host(0) $R_port(0)
$R(0) set foo bar
wait_for_condition 50 1000 {
[status $R(1) master_link_status] == "up" &&
[$R(0) dbsize] == 1 && [$R(1) dbsize] == 1
} else {
fail "Replicas not replicating from master"
+1 -4
View File
@@ -28,10 +28,7 @@ start_server {} {
$R(2) slaveof $R_host(0) $R_port(0)
$R(0) set foo bar
wait_for_condition 50 1000 {
[status $R(1) master_link_status] == "up" &&
[status $R(2) master_link_status] == "up" &&
[$R(1) dbsize] == 1 &&
[$R(2) dbsize] == 1
[$R(1) dbsize] == 1 && [$R(2) dbsize] == 1
} else {
fail "Replicas not replicating from master"
}
+27 -163
View File
@@ -12,7 +12,7 @@ start_server {} {
set no_exit 0 ; # Do not exit at end of the test
set duration 40 ; # Total test seconds
set duration 20 ; # Total test seconds
set genload 1 ; # Load master with writes at every cycle
@@ -44,7 +44,6 @@ start_server {} {
set used [list $master_id]
test "PSYNC2: \[NEW LAYOUT\] Set #$master_id as master" {
$R($master_id) slaveof no one
$R($master_id) config set repl-ping-replica-period 1 ;# increse the chance that random ping will cause issues
if {$counter_value == 0} {
$R($master_id) set x $counter_value
}
@@ -67,16 +66,6 @@ start_server {} {
lappend used $slave_id
}
# Wait for replicas to sync. so next loop won't get -LOADING error
wait_for_condition 50 1000 {
[status $R([expr {($master_id+1)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+2)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+3)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+4)%5}]) master_link_status] == "up"
} else {
fail "Replica not reconnecting"
}
# 3) Increment the counter and wait for all the instances
# to converge.
test "PSYNC2: cluster is consistent after failover" {
@@ -125,28 +114,35 @@ start_server {} {
}
}
# wait for all the slaves to be in sync.
set masteroff [status $R($master_id) master_repl_offset]
# wait for all the slaves to be in sync with the master
set master_ofs [status $R($master_id) master_repl_offset]
wait_for_condition 500 100 {
[status $R(0) master_repl_offset] >= $masteroff &&
[status $R(1) master_repl_offset] >= $masteroff &&
[status $R(2) master_repl_offset] >= $masteroff &&
[status $R(3) master_repl_offset] >= $masteroff &&
[status $R(4) master_repl_offset] >= $masteroff
$master_ofs == [status $R(0) master_repl_offset] &&
$master_ofs == [status $R(1) master_repl_offset] &&
$master_ofs == [status $R(2) master_repl_offset] &&
$master_ofs == [status $R(3) master_repl_offset] &&
$master_ofs == [status $R(4) master_repl_offset]
} else {
puts "Master ID is $master_id"
for {set j 0} {$j < 5} {incr j} {
puts "$j: sync_full: [status $R($j) sync_full]"
puts "$j: id1 : [status $R($j) master_replid]:[status $R($j) master_repl_offset]"
puts "$j: id2 : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]"
puts "$j: backlog : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]"
puts "---"
if {$debug_msg} {
for {set j 0} {$j < 5} {incr j} {
puts "$j: sync_full: [status $R($j) sync_full]"
puts "$j: id1 : [status $R($j) master_replid]:[status $R($j) master_repl_offset]"
puts "$j: id2 : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]"
puts "$j: backlog : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]"
puts "---"
}
}
fail "Replicas offsets didn't catch up with the master after too long time."
fail "Slaves are not in sync with the master after too long time."
}
# Put down the old master so that it cannot generate more
# replication stream, this way in the next master switch, the time at
# which we move slaves away is not important, each will have full
# history (otherwise PINGs will make certain slaves have more history),
# and sometimes a full resync will be needed.
$R($master_id) slaveof 127.0.0.1 0 ;# We use port zero to make it fail.
if {$debug_msg} {
puts "Master ID is $master_id"
for {set j 0} {$j < 5} {incr j} {
puts "$j: sync_full: [status $R($j) sync_full]"
puts "$j: id1 : [status $R($j) master_replid]:[status $R($j) master_repl_offset]"
@@ -164,28 +160,6 @@ start_server {} {
assert {$sum == 4}
}
# In absence of pings, are the instances really able to have
# the exact same offset?
$R($master_id) config set repl-ping-replica-period 3600
wait_for_condition 500 100 {
[status $R($master_id) master_repl_offset] == [status $R(0) master_repl_offset] &&
[status $R($master_id) master_repl_offset] == [status $R(1) master_repl_offset] &&
[status $R($master_id) master_repl_offset] == [status $R(2) master_repl_offset] &&
[status $R($master_id) master_repl_offset] == [status $R(3) master_repl_offset] &&
[status $R($master_id) master_repl_offset] == [status $R(4) master_repl_offset]
} else {
puts "Master ID is $master_id"
for {set j 0} {$j < 5} {incr j} {
puts "$j: sync_full: [status $R($j) sync_full]"
puts "$j: id1 : [status $R($j) master_replid]:[status $R($j) master_repl_offset]"
puts "$j: id2 : [status $R($j) master_replid2]:[status $R($j) second_repl_offset]"
puts "$j: backlog : firstbyte=[status $R($j) repl_backlog_first_byte_offset] len=[status $R($j) repl_backlog_histlen]"
puts "---"
}
fail "Replicas and master offsets were unable to match *exactly*."
}
$R($master_id) config set repl-ping-replica-period 10
# Limit anyway the maximum number of cycles. This is useful when the
# test is skipped via --only option of the test suite. In that case
# we don't want to see many seconds of this test being just skipped.
@@ -201,14 +175,9 @@ start_server {} {
$R($j) slaveof $master_host $master_port
}
# Wait for replicas to sync. it is not enough to just wait for connected_slaves==4
# since we might do the check before the master realized that they're disconnected
# Wait for slaves to sync
wait_for_condition 50 1000 {
[status $R($master_id) connected_slaves] == 4 &&
[status $R([expr {($master_id+1)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+2)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+3)%5}]) master_link_status] == "up" &&
[status $R([expr {($master_id+4)%5}]) master_link_status] == "up"
[status $R($master_id) connected_slaves] == 4
} else {
fail "Replica not reconnecting"
}
@@ -219,7 +188,6 @@ start_server {} {
set slave_id [expr {($master_id+1)%5}]
set sync_count [status $R($master_id) sync_full]
set sync_partial [status $R($master_id) sync_partial_ok]
set sync_partial_err [status $R($master_id) sync_partial_err]
catch {
$R($slave_id) config rewrite
$R($slave_id) debug restart
@@ -229,11 +197,7 @@ start_server {} {
wait_for_condition 50 1000 {
[status $R($master_id) sync_partial_ok] == $sync_partial + 1
} else {
puts "prev sync_full: $sync_count"
puts "prev sync_partial_ok: $sync_partial"
puts "prev sync_partial_err: $sync_partial_err"
puts [$R($master_id) info stats]
fail "Replica didn't partial sync"
fail "Replica not reconnecting"
}
set new_sync_count [status $R($master_id) sync_full]
assert {$sync_count == $new_sync_count}
@@ -307,103 +271,3 @@ start_server {} {
}
}}}}}
start_server {tags {"psync2"}} {
start_server {} {
start_server {} {
start_server {} {
start_server {} {
test {pings at the end of replication stream are ignored for psync} {
set master [srv -4 client]
set master_host [srv -4 host]
set master_port [srv -4 port]
set replica1 [srv -3 client]
set replica2 [srv -2 client]
set replica3 [srv -1 client]
set replica4 [srv -0 client]
$replica1 replicaof $master_host $master_port
$replica2 replicaof $master_host $master_port
$replica3 replicaof $master_host $master_port
$replica4 replicaof $master_host $master_port
wait_for_condition 50 1000 {
[status $master connected_slaves] == 4
} else {
fail "replicas didn't connect"
}
$master incr x
wait_for_condition 50 1000 {
[$replica1 get x] == 1 && [$replica2 get x] == 1 &&
[$replica3 get x] == 1 && [$replica4 get x] == 1
} else {
fail "replicas didn't get incr"
}
# disconnect replica1 and replica2
# and wait for the master to send a ping to replica3 and replica4
$replica1 replicaof no one
$replica2 replicaof 127.0.0.1 1 ;# we can't promote it to master since that will cycle the replication id
$master config set repl-ping-replica-period 1
after 1500
# make everyone sync from the replica1 that didn't get the last ping from the old master
# replica4 will keep syncing from the old master which now syncs from replica1
# and replica2 will re-connect to the old master (which went back in time)
set new_master_host [srv -3 host]
set new_master_port [srv -3 port]
$replica3 replicaof $new_master_host $new_master_port
$master replicaof $new_master_host $new_master_port
$replica2 replicaof $master_host $master_port
wait_for_condition 50 1000 {
[status $replica2 master_link_status] == "up" &&
[status $replica3 master_link_status] == "up" &&
[status $replica4 master_link_status] == "up" &&
[status $master master_link_status] == "up"
} else {
fail "replicas didn't connect"
}
# make sure replication is still alive and kicking
$replica1 incr x
wait_for_condition 50 1000 {
[$replica2 get x] == 2 &&
[$replica3 get x] == 2 &&
[$replica4 get x] == 2 &&
[$master get x] == 2
} else {
fail "replicas didn't get incr"
}
# make sure there are full syncs other than the initial ones
assert_equal [status $master sync_full] 4
assert_equal [status $replica1 sync_full] 0
assert_equal [status $replica2 sync_full] 0
assert_equal [status $replica3 sync_full] 0
assert_equal [status $replica4 sync_full] 0
# force psync
$master client kill type master
$replica2 client kill type master
$replica3 client kill type master
$replica4 client kill type master
# make sure replication is still alive and kicking
$replica1 incr x
wait_for_condition 50 1000 {
[$replica2 get x] == 3 &&
[$replica3 get x] == 3 &&
[$replica4 get x] == 3 &&
[$master get x] == 3
} else {
fail "replicas didn't get incr"
}
# make sure there are full syncs other than the initial ones
assert_equal [status $master sync_full] 4
assert_equal [status $replica1 sync_full] 0
assert_equal [status $replica2 sync_full] 0
assert_equal [status $replica3 sync_full] 0
assert_equal [status $replica4 sync_full] 0
}
}}}}}
-52
View File
@@ -128,56 +128,4 @@ start_server {} {
# make sure the server is still writable
r set x xx
}
}
test {client freed during loading} {
start_server [list overrides [list key-load-delay 10 rdbcompression no]] {
# create a big rdb that will take long to load. it is important
# for keys to be big since the server processes events only once in 2mb.
# 100mb of rdb, 100k keys will load in more than 1 second
r debug populate 100000 key 1000
catch {
r debug restart
}
set stdout [srv 0 stdout]
while 1 {
# check that the new server actually started and is ready for connections
if {[exec grep -i "Server initialized" | wc -l < $stdout] > 1} {
break
}
after 10
}
# make sure it's still loading
assert_equal [s loading] 1
# connect and disconnect 10 clients
set clients {}
for {set j 0} {$j < 10} {incr j} {
lappend clients [redis_deferring_client]
}
foreach rd $clients {
$rd debug log bla
}
foreach rd $clients {
$rd read
}
foreach rd $clients {
$rd close
}
# make sure the server freed the clients
wait_for_condition 100 100 {
[s connected_clients] < 3
} else {
fail "clients didn't disconnect"
}
# make sure it's still loading
assert_equal [s loading] 1
# no need to keep waiting for loading to complete
exec kill [srv 0 pid]
}
}
+1 -1
View File
@@ -118,7 +118,7 @@ start_server {tags {"repl"}} {
# correctly the RDB file: such file will contain "lua" AUX
# sections with scripts already in the memory of the master.
wait_for_condition 500 100 {
wait_for_condition 50 100 {
[s -1 master_link_status] eq {up}
} else {
fail "Replication not started."
+2 -2
View File
@@ -513,9 +513,9 @@ start_server {tags {"repl"}} {
set master_port [srv 0 port]
set master_pid [srv 0 pid]
# put enough data in the db that the rdb file will be bigger than the socket buffers
# and since we'll have key-load-delay of 100, 20000 keys will take at least 2 seconds
# and since we'll have key-load-delay of 100, 10000 keys will take at least 1 second
# we also need the replica to process requests during transfer (which it does only once in 2mb)
$master debug populate 20000 test 10000
$master debug populate 10000 test 10000
$master config set rdbcompression no
# If running on Linux, we also measure utime/stime to detect possible I/O handling issues
set os [catch {exec unamee}]
+23 -99
View File
@@ -109,33 +109,41 @@ int fsl_push(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return RedisModule_ReplyWithError(ctx,"ERR new element has to be greater than the head element");
fsl->list[fsl->length++] = ele;
RedisModule_SignalKeyAsReady(ctx, argv[1]);
if (fsl->length >= 2)
RedisModule_SignalKeyAsReady(ctx, argv[1]);
return RedisModule_ReplyWithSimpleString(ctx, "OK");
}
int bpop_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int bpop2_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
RedisModuleString *keyname = RedisModule_GetBlockedClientReadyKey(ctx);
fsl_t *fsl;
if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)
if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0))
return REDISMODULE_ERR;
if (!fsl || fsl->length < 2)
return REDISMODULE_ERR;
RedisModule_ReplyWithArray(ctx, 2);
RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);
RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);
return REDISMODULE_OK;
}
int bpop_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int bpop2_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
return RedisModule_ReplyWithSimpleString(ctx, "Request timedout");
}
/* FSL.BPOP <key> <timeout> - Block clients until list has two or more elements.
/* FSL.BPOP2 <key> <timeout> - Block clients until list has two or more elements.
* When that happens, unblock client and pop the last two elements (from the right). */
int fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
int fsl_bpop2(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 3)
return RedisModule_WrongArity(ctx);
@@ -147,10 +155,13 @@ int fsl_bpop(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))
return REDISMODULE_OK;
if (!fsl) {
RedisModule_BlockClientOnKeys(ctx, bpop_reply_callback, bpop_timeout_callback,
if (!fsl || fsl->length < 2) {
/* Key is empty or has <2 elements, we must block */
RedisModule_BlockClientOnKeys(ctx, bpop2_reply_callback, bpop2_timeout_callback,
NULL, timeout, &argv[1], 1, NULL);
} else {
RedisModule_ReplyWithArray(ctx, 2);
RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);
RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);
}
@@ -164,10 +175,10 @@ int bpopgt_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int arg
long long *pgt = RedisModule_GetBlockedClientPrivateData(ctx);
fsl_t *fsl;
if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0) || !fsl)
if (!get_fsl(ctx, keyname, REDISMODULE_READ, 0, &fsl, 0))
return REDISMODULE_ERR;
if (fsl->list[fsl->length-1] <= *pgt)
if (!fsl || fsl->list[fsl->length-1] <= *pgt)
return REDISMODULE_ERR;
RedisModule_ReplyWithLongLong(ctx, fsl->list[--fsl->length]);
@@ -207,6 +218,7 @@ int fsl_bpopgt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
/* We use malloc so the tests in blockedonkeys.tcl can check for memory leaks */
long long *pgt = RedisModule_Alloc(sizeof(long long));
*pgt = gt;
/* Key is empty or has <2 elements, we must block */
RedisModule_BlockClientOnKeys(ctx, bpopgt_reply_callback, bpopgt_timeout_callback,
bpopgt_free_privdata, timeout, &argv[1], 1, pgt);
} else {
@@ -216,88 +228,6 @@ int fsl_bpopgt(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return REDISMODULE_OK;
}
int bpoppush_reply_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
RedisModuleString *src_keyname = RedisModule_GetBlockedClientReadyKey(ctx);
RedisModuleString *dst_keyname = RedisModule_GetBlockedClientPrivateData(ctx);
fsl_t *src;
if (!get_fsl(ctx, src_keyname, REDISMODULE_READ, 0, &src, 0) || !src)
return REDISMODULE_ERR;
fsl_t *dst;
if (!get_fsl(ctx, dst_keyname, REDISMODULE_WRITE, 1, &dst, 0) || !dst)
return REDISMODULE_ERR;
long long ele = src->list[--src->length];
dst->list[dst->length++] = ele;
RedisModule_SignalKeyAsReady(ctx, dst_keyname);
return RedisModule_ReplyWithLongLong(ctx, ele);
}
int bpoppush_timeout_callback(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
return RedisModule_ReplyWithSimpleString(ctx, "Request timedout");
}
void bpoppush_free_privdata(RedisModuleCtx *ctx, void *privdata) {
RedisModule_FreeString(ctx, privdata);
}
/* FSL.BPOPPUSH <src> <dst> <timeout> - Block clients until <src> has an element.
* When that happens, unblock client, pop the last element from <src> and push it to <dst>
* (from the right). */
int fsl_bpoppush(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 4)
return RedisModule_WrongArity(ctx);
long long timeout;
if (RedisModule_StringToLongLong(argv[3],&timeout) != REDISMODULE_OK || timeout < 0)
return RedisModule_ReplyWithError(ctx,"ERR invalid timeout");
fsl_t *src;
if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &src, 1))
return REDISMODULE_OK;
if (!src) {
/* Retain string for reply callback */
RedisModule_RetainString(ctx, argv[2]);
/* Key is empty, we must block */
RedisModule_BlockClientOnKeys(ctx, bpoppush_reply_callback, bpoppush_timeout_callback,
bpoppush_free_privdata, timeout, &argv[1], 1, argv[2]);
} else {
fsl_t *dst;
if (!get_fsl(ctx, argv[2], REDISMODULE_WRITE, 1, &dst, 1))
return REDISMODULE_OK;
long long ele = src->list[--src->length];
dst->list[dst->length++] = ele;
RedisModule_SignalKeyAsReady(ctx, argv[2]);
RedisModule_ReplyWithLongLong(ctx, ele);
}
return REDISMODULE_OK;
}
/* FSL.GETALL <key> - Reply with an array containing all elements. */
int fsl_getall(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2)
return RedisModule_WrongArity(ctx);
fsl_t *fsl;
if (!get_fsl(ctx, argv[1], REDISMODULE_READ, 0, &fsl, 1))
return REDISMODULE_OK;
if (!fsl)
return RedisModule_ReplyWithArray(ctx, 0);
RedisModule_ReplyWithArray(ctx, fsl->length);
for (int i = 0; i < fsl->length; i++)
RedisModule_ReplyWithLongLong(ctx, fsl->list[i]);
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
@@ -322,17 +252,11 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
if (RedisModule_CreateCommand(ctx,"fsl.push",fsl_push,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fsl.bpop",fsl_bpop,"",0,0,0) == REDISMODULE_ERR)
if (RedisModule_CreateCommand(ctx,"fsl.bpop2",fsl_bpop2,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fsl.bpopgt",fsl_bpopgt,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fsl.bpoppush",fsl_bpoppush,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"fsl.getall",fsl_getall,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
+1 -45
View File
@@ -64,8 +64,7 @@ void *threadMain(void *arg) {
RedisModule_SelectDb(ctx,9); /* Tests ran in database number 9. */
for (int i = 0; i < 10; i++) {
RedisModule_ThreadSafeContextLock(ctx);
RedisModule_Replicate(ctx,"INCR","c","a-from-thread");
RedisModule_Replicate(ctx,"INCR","c","b-from-thread");
RedisModule_Replicate(ctx,"INCR","c","thread");
RedisModule_ThreadSafeContextUnlock(ctx);
}
RedisModule_FreeThreadSafeContext(ctx);
@@ -90,38 +89,6 @@ int propagateTestCommand(RedisModuleCtx *ctx, RedisModuleString **argv, int argc
return REDISMODULE_OK;
}
int propagateTest2Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
/* Replicate two commands to test MULTI/EXEC wrapping. */
RedisModule_Replicate(ctx,"INCR","c","counter-1");
RedisModule_Replicate(ctx,"INCR","c","counter-2");
RedisModule_ReplyWithSimpleString(ctx,"OK");
return REDISMODULE_OK;
}
int propagateTest3Command(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
RedisModuleCallReply *reply;
/* This test mixes multiple propagation systems. */
reply = RedisModule_Call(ctx, "INCR", "c!", "using-call");
RedisModule_FreeCallReply(reply);
RedisModule_Replicate(ctx,"INCR","c","counter-1");
RedisModule_Replicate(ctx,"INCR","c","counter-2");
reply = RedisModule_Call(ctx, "INCR", "c!", "after-call");
RedisModule_FreeCallReply(reply);
RedisModule_ReplyWithSimpleString(ctx,"OK");
return REDISMODULE_OK;
}
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
@@ -133,16 +100,5 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
propagateTestCommand,
"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"propagate-test-2",
propagateTest2Command,
"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"propagate-test-3",
propagateTest3Command,
"",1,1,1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
return REDISMODULE_OK;
}
+5 -2
View File
@@ -159,9 +159,12 @@ proc start_server {options {code undefined}} {
if {$::external} {
if {[llength $::servers] == 0} {
set srv {}
# In test_server_main(tests/test_helper.tcl:215~218), increase the value of start_port
# and assign it to ::port through the `--port` option, so we need to reduce it.
set baseport [expr {$::port-100}]
dict set srv "host" $::host
dict set srv "port" $::port
set client [redis $::host $::port 0 $::tls]
dict set srv "port" $baseport
set client [redis $::host $baseport 0 $::tls]
dict set srv "client" $client
$client select 9
+14 -61
View File
@@ -82,7 +82,6 @@ set ::skiptests {}
set ::allowtags {}
set ::only_tests {}
set ::single_tests {}
set ::run_solo_tests {}
set ::skip_till ""
set ::external 0; # If "1" this means, we are running against external instance
set ::file ""; # If set, runs only the tests in this comma separated list
@@ -106,27 +105,13 @@ set ::tlsdir "tests/tls"
set ::client 0
set ::numclients 16
# This function is called by one of the test clients when it receives
# a "run" command from the server, with a filename as data.
# It will run the specified test source file and signal it to the
# test server when finished.
proc execute_test_file name {
proc execute_tests name {
set path "tests/$name.tcl"
set ::curfile $path
source $path
send_data_packet $::test_server_fd done "$name"
}
# This function is called by one of the test clients when it receives
# a "run_code" command from the server, with a verbatim test source code
# as argument, and an associated name.
# It will run the specified code and signal it to the test server when
# finished.
proc execute_test_code {name code} {
eval $code
send_data_packet $::test_server_fd done "$name"
}
# Setup a list to hold a stack of server configs. When calls to start_server
# are nested, use "srv 0 pid" to get the pid of the inner server. To access
# outer servers, use "srv -1 pid" etcetera.
@@ -203,19 +188,10 @@ proc s {args} {
status [srv $level "client"] [lindex $args 0]
}
# Test wrapped into run_solo are sent back from the client to the
# test server, so that the test server will send them again to
# clients once the clients are idle.
proc run_solo {name code} {
if {$::numclients == 1 || $::loop || $::external} {
# run_solo is not supported in these scenarios, just run the code.
eval $code
proc cleanup {} {
if {$::dont_clean} {
return
}
send_data_packet $::test_server_fd run_solo [list $name $code]
}
proc cleanup {} {
if {!$::quiet} {puts -nonewline "Cleanup: may take some time... "}
flush stdout
catch {exec rm -rf {*}[glob tests/tmp/redis.conf.*]}
@@ -236,19 +212,13 @@ proc test_server_main {} {
# Start the client instances
set ::clients_pids {}
if {$::external} {
set start_port [expr {$::port+100}]
for {set j 0} {$j < $::numclients} {incr j} {
set start_port [find_available_port $start_port]
set p [exec $tclsh [info script] {*}$::argv \
--client $port --port $::port &]
--client $port --port $start_port &]
lappend ::clients_pids $p
} else {
set start_port [expr {$::port+100}]
for {set j 0} {$j < $::numclients} {incr j} {
set start_port [find_available_port $start_port]
set p [exec $tclsh [info script] {*}$::argv \
--client $port --port $start_port &]
lappend ::clients_pids $p
incr start_port 10
}
incr start_port 10
}
# Setup global state for the test server
@@ -361,8 +331,6 @@ proc read_from_test_client fd {
} elseif {$status eq {server-killed}} {
set ::active_servers [lsearch -all -inline -not -exact $::active_servers $data]
set ::active_clients_task($fd) "(KILLED SERVER) pid:$data"
} elseif {$status eq {run_solo}} {
lappend ::run_solo_tests $data
} else {
if {!$::quiet} {
puts "\[$status\]: $data"
@@ -395,13 +363,6 @@ proc force_kill_all_servers {} {
}
}
proc lpop {listVar {count 1}} {
upvar 1 $listVar l
set ele [lindex $l 0]
set l [lrange $l 1 end]
set ele
}
# A new client is idle. Remove it from the list of active clients and
# if there are still test units to run, launch them.
proc signal_idle_client fd {
@@ -422,14 +383,6 @@ proc signal_idle_client fd {
if {$::loop && $::next_test == [llength $::all_tests]} {
set ::next_test 0
}
} elseif {[llength $::run_solo_tests] != 0 && [llength $::active_clients] == 0} {
if {!$::quiet} {
puts [colorstr bold-white "Testing solo test"]
set ::active_clients_task($fd) "ASSIGNED: $fd solo test"
}
set ::clients_start_time($fd) [clock seconds]
send_data_packet $fd run_code [lpop ::run_solo_tests]
lappend ::active_clients $fd
} else {
lappend ::idle_clients $fd
set ::active_clients_task($fd) "SLEEPING, no more units to assign"
@@ -453,11 +406,11 @@ proc the_end {} {
foreach failed $::failed_tests {
puts "*** $failed"
}
if {!$::dont_clean} cleanup
cleanup
exit 1
} else {
puts "\n[colorstr bold-white {\o/}] [colorstr bold-green {All tests passed without errors!}]\n"
if {!$::dont_clean} cleanup
cleanup
exit 0
}
}
@@ -473,10 +426,7 @@ proc test_client_main server_port {
set payload [read $::test_server_fd $bytes]
foreach {cmd data} $payload break
if {$cmd eq {run}} {
execute_test_file $data
} elseif {$cmd eq {run_code}} {
foreach {name code} $data break
execute_test_code $name $code
execute_tests $data
} else {
error "Unknown test client command: $cmd"
}
@@ -556,6 +506,9 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--host}} {
set ::external 1
set ::host $arg
# If we use an external server, we can only set numclients to 1,
# otherwise the port will be miscalculated.
set ::numclients 1
incr j
} elseif {$opt eq {--port}} {
set ::port $arg
-4
View File
@@ -94,10 +94,6 @@ start_server {tags {"introspection"}} {
slaveof
bind
requirepass
server_cpulist
bio_cpulist
aof_rewrite_cpulist
bgsave_cpulist
}
set configs {}
-2
View File
@@ -36,7 +36,6 @@ start_server {tags {"memefficiency"}} {
}
}
run_solo {defrag} {
start_server {tags {"defrag"}} {
if {[string match {*jemalloc*} [s mem_allocator]]} {
test "Active defrag" {
@@ -329,4 +328,3 @@ start_server {tags {"defrag"}} {
} {1}
}
}
} ;# run_solo
+20 -88
View File
@@ -3,70 +3,37 @@ set testmodule [file normalize tests/modules/blockonkeys.so]
start_server {tags {"modules"}} {
r module load $testmodule
test "Module client blocked on keys: Circular BPOPPUSH" {
set rd1 [redis_deferring_client]
set rd2 [redis_deferring_client]
r del src dst
$rd1 fsl.bpoppush src dst 0
$rd2 fsl.bpoppush dst src 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {2}
} else {
fail "Clients are not blocked"
}
r fsl.push src 42
assert_equal {42} [r fsl.getall src]
assert_equal {} [r fsl.getall dst]
}
test "Module client blocked on keys: Self-referential BPOPPUSH" {
set rd1 [redis_deferring_client]
r del src
$rd1 fsl.bpoppush src src 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r fsl.push src 42
assert_equal {42} [r fsl.getall src]
}
test {Module client blocked on keys (no metadata): No block} {
r del k
r fsl.push k 33
r fsl.push k 34
r fsl.bpop k 0
} {34}
r fsl.bpop2 k 0
} {34 33}
test {Module client blocked on keys (no metadata): Timeout} {
r del k
set rd [redis_deferring_client]
$rd fsl.bpop k 1
r fsl.push k 33
$rd fsl.bpop2 k 1
assert_equal {Request timedout} [$rd read]
}
test {Module client blocked on keys (no metadata): Blocked} {
test {Module client blocked on keys (no metadata): Blocked, case 1} {
r del k
set rd [redis_deferring_client]
$rd fsl.bpop k 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r fsl.push k 33
$rd fsl.bpop2 k 0
r fsl.push k 34
assert_equal {34} [$rd read]
assert_equal {34 33} [$rd read]
}
test {Module client blocked on keys (no metadata): Blocked, case 2} {
r del k
set rd [redis_deferring_client]
r fsl.push k 33
r fsl.push k 34
$rd fsl.bpop2 k 0
assert_equal {34 33} [$rd read]
}
test {Module client blocked on keys (with metadata): No block} {
@@ -93,12 +60,6 @@ start_server {tags {"modules"}} {
set cid [$rd read]
r fsl.push k 33
$rd fsl.bpopgt k 33 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r fsl.push k 34
assert_equal {34} [$rd read]
r client kill id $cid ;# try to smoke-out client-related memory leak
@@ -108,12 +69,6 @@ start_server {tags {"modules"}} {
r del k
set rd [redis_deferring_client]
$rd fsl.bpopgt k 35 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r fsl.push k 33
r fsl.push k 34
r fsl.push k 35
@@ -127,12 +82,6 @@ start_server {tags {"modules"}} {
$rd client id
set cid [$rd read]
$rd fsl.bpopgt k 35 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r client kill id $cid ;# try to smoke-out client-related memory leak
}
@@ -142,12 +91,6 @@ start_server {tags {"modules"}} {
$rd client id
set cid [$rd read]
$rd fsl.bpopgt k 35 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r client unblock $cid timeout ;# try to smoke-out client-related memory leak
assert_equal {Request timedout} [$rd read]
}
@@ -158,12 +101,6 @@ start_server {tags {"modules"}} {
$rd client id
set cid [$rd read]
$rd fsl.bpopgt k 35 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
r client unblock $cid error ;# try to smoke-out client-related memory leak
assert_error "*unblocked*" {$rd read}
}
@@ -171,18 +108,13 @@ start_server {tags {"modules"}} {
test {Module client blocked on keys does not wake up on wrong type} {
r del k
set rd [redis_deferring_client]
$rd fsl.bpop k 0
;# wait until clients are actually blocked
wait_for_condition 50 100 {
[s 0 blocked_clients] eq {1}
} else {
fail "Clients are not blocked"
}
$rd fsl.bpop2 k 0
r lpush k 12
r lpush k 13
r lpush k 14
r del k
r fsl.push k 33
r fsl.push k 34
assert_equal {34} [$rd read]
assert_equal {34 33} [$rd read]
}
}
+1 -34
View File
@@ -20,44 +20,11 @@ tags "modules" {
wait_for_condition 5000 10 {
([$replica get timer] eq "10") && \
([$replica get a-from-thread] eq "10")
([$replica get thread] eq "10")
} else {
fail "The two counters don't match the expected value."
}
$master propagate-test-2
$master propagate-test-3
$master multi
$master propagate-test-2
$master propagate-test-3
$master exec
wait_for_ofs_sync $master $replica
assert_equal [s -1 unexpected_error_replies] 0
}
}
}
}
tags "modules aof" {
test {Modules RM_Replicate replicates MULTI/EXEC correctly} {
start_server [list overrides [list loadmodule "$testmodule"]] {
# Enable the AOF
r config set appendonly yes
r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.
waitForBgrewriteaof r
r propagate-test-2
r propagate-test-3
r multi
r propagate-test-2
r propagate-test-3
r exec
# Load the AOF
r debug loadaof
assert_equal [s 0 unexpected_error_replies] 0
}
}
}
-11
View File
@@ -146,17 +146,6 @@ start_server {tags {"scripting"}} {
set e
} {*not allowed*}
test {EVAL - Scripts can't run XREAD and XREADGROUP with BLOCK option} {
r del s
r xgroup create s g $ MKSTREAM
set res [r eval {return redis.pcall('xread','STREAMS','s','$')} 1 s]
assert {$res eq {}}
assert_error "*xread command is not allowed with BLOCK option from scripts" {r eval {return redis.pcall('xread','BLOCK',0,'STREAMS','s','$')} 1 s}
set res [r eval {return redis.pcall('xreadgroup','group','g','c','STREAMS','s','>')} 1 s]
assert {$res eq {}}
assert_error "*xreadgroup command is not allowed with BLOCK option from scripts" {r eval {return redis.pcall('xreadgroup','group','g','c','BLOCK',0,'STREAMS','s','>')} 1 s}
}
test {EVAL - Scripts can't run certain commands} {
set e {}
r debug lua-always-replicate-commands 0
+20
View File
@@ -25,6 +25,26 @@ start_server {tags {"tls"}} {
}
test {TLS: Verify tls-protocols behaves as expected} {
r CONFIG SET tls-protocols TLSv1
set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1 0}]
catch {$s PING} e
assert_match {*I/O error*} $e
set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1 1}]
catch {$s PING} e
assert_match {PONG} $e
r CONFIG SET tls-protocols TLSv1.1
set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.1 0}]
catch {$s PING} e
assert_match {*I/O error*} $e
set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.1 1}]
catch {$s PING} e
assert_match {PONG} $e
r CONFIG SET tls-protocols TLSv1.2
set s [redis [srv 0 host] [srv 0 port] 0 1 {-tls1.2 0}]
-67
View File
@@ -7,9 +7,6 @@ start_server {tags {"tracking"}} {
$rd1 subscribe __redis__:invalidate
$rd1 read ; # Consume the SUBSCRIBE reply.
# Create another client as well in order to test NOLOOP
set rd2 [redis_deferring_client]
test {Clients are able to enable tracking and redirect it} {
r CLIENT TRACKING on REDIRECT $redir
} {*OK}
@@ -65,69 +62,5 @@ start_server {tags {"tracking"}} {
assert {$keys eq {c:1234}}
}
test {Tracking NOLOOP mode in standard mode works} {
r CLIENT TRACKING off
r CLIENT TRACKING on REDIRECT $redir NOLOOP
r MGET otherkey1 loopkey otherkey2
$rd2 SET otherkey1 1; # We should get this
r SET loopkey 1 ; # We should not get this
$rd2 SET otherkey2 1; # We should get this
# Because of the internals, we know we are going to receive
# two separated notifications for the two different prefixes.
set keys1 [lsort [lindex [$rd1 read] 2]]
set keys2 [lsort [lindex [$rd1 read] 2]]
set keys [lsort [list {*}$keys1 {*}$keys2]]
assert {$keys eq {otherkey1 otherkey2}}
}
test {Tracking NOLOOP mode in BCAST mode works} {
r CLIENT TRACKING off
r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP
$rd2 SET otherkey1 1; # We should get this
r SET loopkey 1 ; # We should not get this
$rd2 SET otherkey2 1; # We should get this
# Because of the internals, we know we are going to receive
# two separated notifications for the two different prefixes.
set keys1 [lsort [lindex [$rd1 read] 2]]
set keys2 [lsort [lindex [$rd1 read] 2]]
set keys [lsort [list {*}$keys1 {*}$keys2]]
assert {$keys eq {otherkey1 otherkey2}}
}
test {Tracking gets notification of expired keys} {
r CLIENT TRACKING off
r CLIENT TRACKING on BCAST REDIRECT $redir NOLOOP
r SET mykey myval px 1
r SET mykeyotherkey myval ; # We should not get it
after 1000
# Because of the internals, we know we are going to receive
# two separated notifications for the two different prefixes.
set keys1 [lsort [lindex [$rd1 read] 2]]
set keys [lsort [list {*}$keys1]]
assert {$keys eq {mykey}}
}
test {Tracking gets notification on tracking table key eviction} {
r CLIENT TRACKING off
r CLIENT TRACKING on REDIRECT $redir NOLOOP
r MSET key1 1 key2 2
# Let the server track the two keys for us
r MGET key1 key2
# Force the eviction of all the keys but one:
r config set tracking-table-max-keys 1
# Note that we may have other keys in the table for this client,
# since we disabled/enabled tracking multiple time with the same
# ID, and tracking does not do ID cleanups for performance reasons.
# So we check that eventually we'll receive one or the other key,
# otherwise the test will die for timeout.
while 1 {
set keys [lindex [$rd1 read] 2]
if {$keys eq {key1} || $keys eq {key2}} break
}
# We should receive an expire notification for one of
# the two keys (only one must remain)
assert {$keys eq {key1} || $keys eq {key2}}
}
$rd1 close
}
-7
View File
@@ -151,11 +151,4 @@ start_server {tags {"incr"}} {
catch {r incrbyfloat foo 1} err
format $err
} {ERR*valid*}
test {No negative zero} {
r del foo
r incrbyfloat foo [expr double(1)/41]
r incrbyfloat foo [expr double(-1)/41]
r get foo
} {0}
}
-67
View File
@@ -93,18 +93,6 @@ start_server {
assert {[r XACK mystream mygroup $id1 $id2] eq 1}
}
test {XACK should fail if got at least one invalid ID} {
r del mystream
r xgroup create s g $ MKSTREAM
r xadd s * f1 v1
set c [llength [lindex [r xreadgroup group g c streams s >] 0 1]]
assert {$c == 1}
set pending [r xpending s g - + 10 c]
set id1 [lindex $pending 0 0]
assert_error "*Invalid stream ID specified*" {r xack s g $id1 invalid-id}
assert {[r xack s g $id1] eq 1}
}
test {PEL NACK reassignment after XGROUP SETID event} {
r del events
r xadd events * f1 v1
@@ -182,27 +170,6 @@ start_server {
assert_error "*NOGROUP*" {$rd read}
}
test {RENAME can unblock XREADGROUP with data} {
r del mystream
r XGROUP CREATE mystream mygroup $ MKSTREAM
set rd [redis_deferring_client]
$rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream ">"
r XGROUP CREATE mystream2 mygroup $ MKSTREAM
r XADD mystream2 100 f1 v1
r RENAME mystream2 mystream
assert_equal "{mystream {{100-0 {f1 v1}}}}" [$rd read] ;# mystream2 had mygroup before RENAME
}
test {RENAME can unblock XREADGROUP with -NOGROUP} {
r del mystream
r XGROUP CREATE mystream mygroup $ MKSTREAM
set rd [redis_deferring_client]
$rd XREADGROUP GROUP mygroup Alice BLOCK 0 STREAMS mystream ">"
r XADD mystream2 100 f1 v1
r RENAME mystream2 mystream
assert_error "*NOGROUP*" {$rd read} ;# mystream2 didn't have mygroup before RENAME
}
test {XCLAIM can claim PEL items from another consumer} {
# Add 3 items into the stream, and create a consumer group
r del mystream
@@ -294,40 +261,6 @@ start_server {
assert {[lindex $reply 0 3] == 2}
}
test {XINFO FULL output} {
r del x
r XADD x 100 a 1
r XADD x 101 b 1
r XADD x 102 c 1
r XADD x 103 e 1
r XADD x 104 f 1
r XGROUP CREATE x g1 0
r XGROUP CREATE x g2 0
r XREADGROUP GROUP g1 Alice COUNT 1 STREAMS x >
r XREADGROUP GROUP g1 Bob COUNT 1 STREAMS x >
r XREADGROUP GROUP g1 Bob NOACK COUNT 1 STREAMS x >
r XREADGROUP GROUP g2 Charlie COUNT 4 STREAMS x >
r XDEL x 103
set reply [r XINFO STREAM x FULL]
assert_equal [llength $reply] 12
assert_equal [lindex $reply 1] 4 ;# stream length
assert_equal [lindex $reply 9] "{100-0 {a 1}} {101-0 {b 1}} {102-0 {c 1}} {104-0 {f 1}}" ;# entries
assert_equal [lindex $reply 11 0 1] "g1" ;# first group name
assert_equal [lindex $reply 11 0 7 0 0] "100-0" ;# first entry in group's PEL
assert_equal [lindex $reply 11 0 9 0 1] "Alice" ;# first consumer
assert_equal [lindex $reply 11 0 9 0 7 0 0] "100-0" ;# first entry in first consumer's PEL
assert_equal [lindex $reply 11 1 1] "g2" ;# second group name
assert_equal [lindex $reply 11 1 9 0 1] "Charlie" ;# first consumer
assert_equal [lindex $reply 11 1 9 0 7 0 0] "100-0" ;# first entry in first consumer's PEL
assert_equal [lindex $reply 11 1 9 0 7 1 0] "101-0" ;# second entry in first consumer's PEL
set reply [r XINFO STREAM x FULL COUNT 1]
assert_equal [llength $reply] 12
assert_equal [lindex $reply 1] 4
assert_equal [lindex $reply 9] "{100-0 {a 1}}"
}
start_server {} {
set master [srv -1 client]
set master_host [srv -1 host]
+1 -1
View File
@@ -414,7 +414,7 @@ start_server {tags {"stream"} overrides {appendonly yes stream-node-max-entries
}
}
start_server {tags {"stream xsetid"}} {
start_server {tags {"xsetid"}} {
test {XADD can CREATE an empty stream} {
r XADD mystream MAXLEN 0 * a b
assert {[dict get [r xinfo stream mystream] length] == 0}
-30
View File
@@ -419,34 +419,4 @@ start_server {tags {"string"}} {
r set foo bar
r getrange foo 0 4294967297
} {bar}
set rna1 {CACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTTCGTCCGGGTGTG}
set rna2 {ATTAAAGGTTTATACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}
set rnalcs {ACCTTCCCAGGTAACAAACCAACCAACTTTCGATCTCTTGTAGATCTGTTCTCTAAACGAACTTTAAAATCTGTGTGGCTGTCACTCGGCTGCATGCTTAGTGCACTCACGCAGTATAATTAATAACTAATTACTGTCGTTGACAGGACACGAGTAACTCGTCTATCTTCTGCAGGCTGCTTACGGTTTCGTCCGTGTTGCAGCCGATCATCAGCACATCTAGGTTT}
test {STRALGO LCS string output with STRINGS option} {
r STRALGO LCS STRINGS $rna1 $rna2
} $rnalcs
test {STRALGO LCS len} {
r STRALGO LCS LEN STRINGS $rna1 $rna2
} [string length $rnalcs]
test {LCS with KEYS option} {
r set virus1 $rna1
r set virus2 $rna2
r STRALGO LCS KEYS virus1 virus2
} $rnalcs
test {LCS indexes} {
dict get [r STRALGO LCS IDX KEYS virus1 virus2] matches
} {{{238 238} {239 239}} {{236 236} {238 238}} {{229 230} {236 237}} {{224 224} {235 235}} {{1 222} {13 234}}}
test {LCS indexes with match len} {
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN] matches
} {{{238 238} {239 239} 1} {{236 236} {238 238} 1} {{229 230} {236 237} 2} {{224 224} {235 235} 1} {{1 222} {13 234} 222}}
test {LCS indexes with match len and minimum match len} {
dict get [r STRALGO LCS IDX KEYS virus1 virus2 WITHMATCHLEN MINMATCHLEN 5] matches
} {{{1 222} {13 234} 222}}
}
+6 -8
View File
@@ -1,7 +1,6 @@
#!/bin/bash
# Settings
BIN_PATH="../../src/"
CLUSTER_HOST=127.0.0.1
PORT=30000
TIMEOUT=2000
@@ -26,7 +25,7 @@ then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Starting $PORT"
$BIN_PATH/redis-server --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
../../src/redis-server --port $PORT --protected-mode $PROTECTED_MODE --cluster-enabled yes --cluster-config-file nodes-${PORT}.conf --cluster-node-timeout $TIMEOUT --appendonly yes --appendfilename appendonly-${PORT}.aof --dbfilename dump-${PORT}.rdb --logfile ${PORT}.log --daemonize yes ${ADDITIONAL_OPTIONS}
done
exit 0
fi
@@ -38,7 +37,7 @@ then
PORT=$((PORT+1))
HOSTS="$HOSTS $CLUSTER_HOST:$PORT"
done
$BIN_PATH/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS
../../src/redis-cli --cluster create $HOSTS --cluster-replicas $REPLICAS
exit 0
fi
@@ -47,7 +46,7 @@ then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
echo "Stopping $PORT"
$BIN_PATH/redis-cli -p $PORT shutdown nosave
../../src/redis-cli -p $PORT shutdown nosave
done
exit 0
fi
@@ -58,7 +57,7 @@ then
while [ 1 ]; do
clear
date
$BIN_PATH/redis-cli -p $PORT cluster nodes | head -30
../../src/redis-cli -p $PORT cluster nodes | head -30
sleep 1
done
exit 0
@@ -82,7 +81,7 @@ if [ "$1" == "call" ]
then
while [ $((PORT < ENDPORT)) != "0" ]; do
PORT=$((PORT+1))
$BIN_PATH/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9
../../src/redis-cli -p $PORT $2 $3 $4 $5 $6 $7 $8 $9
done
exit 0
fi
@@ -102,7 +101,7 @@ then
exit 0
fi
echo "Usage: $0 [start|create|stop|watch|tail|clean|call]"
echo "Usage: $0 [start|create|stop|watch|tail|clean]"
echo "start -- Launch Redis Cluster instances."
echo "create -- Create a cluster using redis-cli --cluster create."
echo "stop -- Stop Redis Cluster instances."
@@ -111,4 +110,3 @@ echo "tail <id> -- Run tail -f of instance at base port + ID."
echo "tailall -- Run tail -f for all the log files at once."
echo "clean -- Remove all instances data, logs, configs."
echo "clean-logs -- Remove just instances logs."
echo "call <cmd> -- Call a command (up to 7 arguments) on all nodes."