mirror of
https://github.com/fluencelabs/redis
synced 2025-06-21 13:01:32 +00:00
Merge remote-tracking branch 'origin/2.6' into 2.6
This commit is contained in:
20
src/ae.c
20
src/ae.c
@ -37,6 +37,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <poll.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "ae.h"
|
||||
#include "zmalloc.h"
|
||||
@ -67,6 +68,7 @@ aeEventLoop *aeCreateEventLoop(int setsize) {
|
||||
eventLoop->fired = zmalloc(sizeof(aeFiredEvent)*setsize);
|
||||
if (eventLoop->events == NULL || eventLoop->fired == NULL) goto err;
|
||||
eventLoop->setsize = setsize;
|
||||
eventLoop->lastTime = time(NULL);
|
||||
eventLoop->timeEventHead = NULL;
|
||||
eventLoop->timeEventNextId = 0;
|
||||
eventLoop->stop = 0;
|
||||
@ -236,6 +238,24 @@ static int processTimeEvents(aeEventLoop *eventLoop) {
|
||||
int processed = 0;
|
||||
aeTimeEvent *te;
|
||||
long long maxId;
|
||||
time_t now = time(NULL);
|
||||
|
||||
/* If the system clock is moved to the future, and then set back to the
|
||||
* right value, time events may be delayed in a random way. Often this
|
||||
* means that scheduled operations will not be performed soon enough.
|
||||
*
|
||||
* Here we try to detect system clock skews, and force all the time
|
||||
* events to be processed ASAP when this happens: the idea is that
|
||||
* processing events earlier is less dangerous than delaying them
|
||||
* indefinitely, and practice suggests it is. */
|
||||
if (now < eventLoop->lastTime) {
|
||||
te = eventLoop->timeEventHead;
|
||||
while(te) {
|
||||
te->when_sec = 0;
|
||||
te = te->next;
|
||||
}
|
||||
}
|
||||
eventLoop->lastTime = now;
|
||||
|
||||
te = eventLoop->timeEventHead;
|
||||
maxId = eventLoop->timeEventNextId-1;
|
||||
|
1
src/ae.h
1
src/ae.h
@ -88,6 +88,7 @@ typedef struct aeEventLoop {
|
||||
int maxfd; /* highest file descriptor currently registered */
|
||||
int setsize; /* max number of file descriptors tracked */
|
||||
long long timeEventNextId;
|
||||
time_t lastTime; /* Used to detect system clock skew */
|
||||
aeFileEvent *events; /* Registered events */
|
||||
aeFiredEvent *fired; /* Fired events */
|
||||
aeTimeEvent *timeEventHead;
|
||||
|
@ -438,7 +438,12 @@ void configSetCommand(redisClient *c) {
|
||||
if (getLongLongFromObject(o,&ll) == REDIS_ERR ||
|
||||
ll < 0) goto badfmt;
|
||||
server.maxmemory = ll;
|
||||
if (server.maxmemory) freeMemoryIfNeeded();
|
||||
if (server.maxmemory) {
|
||||
if (server.maxmemory < zmalloc_used_memory()) {
|
||||
redisLog(REDIS_WARNING,"WARNING: the new maxmemory value set via CONFIG SET is smaller than the current memory usage. This will result in keys eviction and/or inability to accept new write commands depending on the maxmemory-policy.");
|
||||
}
|
||||
freeMemoryIfNeeded();
|
||||
}
|
||||
} else if (!strcasecmp(c->argv[2]->ptr,"maxmemory-policy")) {
|
||||
if (!strcasecmp(o->ptr,"volatile-lru")) {
|
||||
server.maxmemory_policy = REDIS_MAXMEMORY_VOLATILE_LRU;
|
||||
|
68
src/dict.c
68
src/dict.c
@ -85,29 +85,73 @@ unsigned int dictIdentityHashFunction(unsigned int key)
|
||||
return key;
|
||||
}
|
||||
|
||||
static int dict_hash_function_seed = 5381;
|
||||
static uint32_t dict_hash_function_seed = 5381;
|
||||
|
||||
void dictSetHashFunctionSeed(unsigned int seed) {
|
||||
void dictSetHashFunctionSeed(uint32_t seed) {
|
||||
dict_hash_function_seed = seed;
|
||||
}
|
||||
|
||||
unsigned int dictGetHashFunctionSeed(void) {
|
||||
uint32_t dictGetHashFunctionSeed(void) {
|
||||
return dict_hash_function_seed;
|
||||
}
|
||||
|
||||
/* Generic hash function (a popular one from Bernstein).
|
||||
* I tested a few and this was the best. */
|
||||
unsigned int dictGenHashFunction(const unsigned char *buf, int len) {
|
||||
unsigned int hash = dict_hash_function_seed;
|
||||
/* MurmurHash2, by Austin Appleby
|
||||
* Note - This code makes a few assumptions about how your machine behaves -
|
||||
* 1. We can read a 4-byte value from any address without crashing
|
||||
* 2. sizeof(int) == 4
|
||||
*
|
||||
* And it has a few limitations -
|
||||
*
|
||||
* 1. It will not work incrementally.
|
||||
* 2. It will not produce the same results on little-endian and big-endian
|
||||
* machines.
|
||||
*/
|
||||
unsigned int dictGenHashFunction(const void *key, int len) {
|
||||
/* 'm' and 'r' are mixing constants generated offline.
|
||||
They're not really 'magic', they just happen to work well. */
|
||||
uint32_t seed = dict_hash_function_seed;
|
||||
const uint32_t m = 0x5bd1e995;
|
||||
const int r = 24;
|
||||
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*buf++); /* hash * 33 + c */
|
||||
return hash;
|
||||
/* Initialize the hash to a 'random' value */
|
||||
uint32_t h = seed ^ len;
|
||||
|
||||
/* Mix 4 bytes at a time into the hash */
|
||||
const unsigned char *data = (const unsigned char *)key;
|
||||
|
||||
while(len >= 4) {
|
||||
uint32_t k = *(uint32_t*)data;
|
||||
|
||||
k *= m;
|
||||
k ^= k >> r;
|
||||
k *= m;
|
||||
|
||||
h *= m;
|
||||
h ^= k;
|
||||
|
||||
data += 4;
|
||||
len -= 4;
|
||||
}
|
||||
|
||||
/* Handle the last few bytes of the input array */
|
||||
switch(len) {
|
||||
case 3: h ^= data[2] << 16;
|
||||
case 2: h ^= data[1] << 8;
|
||||
case 1: h ^= data[0]; h *= m;
|
||||
};
|
||||
|
||||
/* Do a few final mixes of the hash to ensure the last few
|
||||
* bytes are well-incorporated. */
|
||||
h ^= h >> 13;
|
||||
h *= m;
|
||||
h ^= h >> 15;
|
||||
|
||||
return (unsigned int)h;
|
||||
}
|
||||
|
||||
/* And a case insensitive version */
|
||||
/* And a case insensitive hash function (based on djb hash) */
|
||||
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len) {
|
||||
unsigned int hash = dict_hash_function_seed;
|
||||
unsigned int hash = (unsigned int)dict_hash_function_seed;
|
||||
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (tolower(*buf++)); /* hash * 33 + c */
|
||||
|
@ -155,7 +155,7 @@ dictEntry *dictNext(dictIterator *iter);
|
||||
void dictReleaseIterator(dictIterator *iter);
|
||||
dictEntry *dictGetRandomKey(dict *d);
|
||||
void dictPrintStats(dict *d);
|
||||
unsigned int dictGenHashFunction(const unsigned char *buf, int len);
|
||||
unsigned int dictGenHashFunction(const void *key, int len);
|
||||
unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len);
|
||||
void dictEmpty(dict *d);
|
||||
void dictEnableResize(void);
|
||||
|
@ -2579,6 +2579,11 @@ int main(int argc, char **argv) {
|
||||
redisLog(REDIS_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
|
||||
}
|
||||
|
||||
/* Warning the user about suspicious maxmemory setting. */
|
||||
if (server.maxmemory > 0 && server.maxmemory < 1024*1024) {
|
||||
redisLog(REDIS_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);
|
||||
}
|
||||
|
||||
aeSetBeforeSleepProc(server.el,beforeSleep);
|
||||
aeMain(server.el);
|
||||
aeDeleteEventLoop(server.el);
|
||||
|
@ -721,7 +721,7 @@ void replicationCron(void) {
|
||||
if (server.masterhost && server.repl_state == REDIS_REPL_TRANSFER &&
|
||||
(time(NULL)-server.repl_transfer_lastio) > server.repl_timeout)
|
||||
{
|
||||
redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER...");
|
||||
redisLog(REDIS_WARNING,"Timeout receiving bulk data from MASTER... If the problem persists try to set the 'repl-timeout' parameter in redis.conf to a larger value.");
|
||||
replicationAbortSyncTransfer();
|
||||
}
|
||||
|
||||
|
108
src/sort.c
108
src/sort.c
@ -2,6 +2,8 @@
|
||||
#include "pqsort.h" /* Partial qsort for SORT+LIMIT */
|
||||
#include <math.h> /* isnan() */
|
||||
|
||||
zskiplistNode* zslGetElementByRank(zskiplist *zsl, unsigned long rank);
|
||||
|
||||
redisSortOperation *createSortOperation(int type, robj *pattern) {
|
||||
redisSortOperation *so = zmalloc(sizeof(*so));
|
||||
so->type = type;
|
||||
@ -156,8 +158,9 @@ void sortCommand(redisClient *c) {
|
||||
|
||||
/* Lookup the key to sort. It must be of the right types */
|
||||
sortval = lookupKeyRead(c->db,c->argv[1]);
|
||||
if (sortval && sortval->type != REDIS_SET && sortval->type != REDIS_LIST &&
|
||||
sortval->type != REDIS_ZSET)
|
||||
if (sortval && sortval->type != REDIS_SET &&
|
||||
sortval->type != REDIS_LIST &&
|
||||
sortval->type != REDIS_ZSET)
|
||||
{
|
||||
addReply(c,shared.wrongtypeerr);
|
||||
return;
|
||||
@ -167,7 +170,7 @@ void sortCommand(redisClient *c) {
|
||||
* Operations can be GET/DEL/INCR/DECR */
|
||||
operations = listCreate();
|
||||
listSetFreeMethod(operations,zfree);
|
||||
j = 2;
|
||||
j = 2; /* options start at argv[2] */
|
||||
|
||||
/* Now we need to protect sortval incrementing its count, in the future
|
||||
* SORT may have options able to overwrite/delete keys during the sorting
|
||||
@ -213,13 +216,18 @@ void sortCommand(redisClient *c) {
|
||||
j++;
|
||||
}
|
||||
|
||||
/* If we have STORE we need to force sorting for deterministic output
|
||||
* and replication. We use alpha sorting since this is guaranteed to
|
||||
* work with any input.
|
||||
/* For the STORE option, or when SORT is called from a Lua script,
|
||||
* we want to force a specific ordering even when no explicit ordering
|
||||
* was asked (SORT BY nosort). This guarantees that replication / AOF
|
||||
* is deterministic.
|
||||
*
|
||||
* We also want determinism when SORT is called from Lua scripts, so
|
||||
* in this case we also force alpha sorting. */
|
||||
if ((storekey || c->flags & REDIS_LUA_CLIENT) && dontsort) {
|
||||
* However in the case 'dontsort' is true, but the type to sort is a
|
||||
* sorted set, we don't need to do anything as ordering is guaranteed
|
||||
* in this special case. */
|
||||
if ((storekey || c->flags & REDIS_LUA_CLIENT) &&
|
||||
(dontsort && sortval->type != REDIS_ZSET))
|
||||
{
|
||||
/* Force ALPHA sorting */
|
||||
dontsort = 0;
|
||||
alpha = 1;
|
||||
sortby = NULL;
|
||||
@ -229,13 +237,41 @@ void sortCommand(redisClient *c) {
|
||||
if (sortval->type == REDIS_ZSET)
|
||||
zsetConvert(sortval, REDIS_ENCODING_SKIPLIST);
|
||||
|
||||
/* Load the sorting vector with all the objects to sort */
|
||||
/* Objtain the length of the object to sort. */
|
||||
switch(sortval->type) {
|
||||
case REDIS_LIST: vectorlen = listTypeLength(sortval); break;
|
||||
case REDIS_SET: vectorlen = setTypeSize(sortval); break;
|
||||
case REDIS_ZSET: vectorlen = dictSize(((zset*)sortval->ptr)->dict); break;
|
||||
default: vectorlen = 0; redisPanic("Bad SORT type"); /* Avoid GCC warning */
|
||||
}
|
||||
|
||||
/* Perform LIMIT start,count sanity checking. */
|
||||
start = (limit_start < 0) ? 0 : limit_start;
|
||||
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
|
||||
if (start >= vectorlen) {
|
||||
start = vectorlen-1;
|
||||
end = vectorlen-2;
|
||||
}
|
||||
if (end >= vectorlen) end = vectorlen-1;
|
||||
|
||||
/* Optimization:
|
||||
*
|
||||
* 1) if the object to sort is a sorted set.
|
||||
* 2) There is nothing to sort as dontsort is true (BY <constant string>).
|
||||
* 3) We have a LIMIT option that actually reduces the number of elements
|
||||
* to fetch.
|
||||
*
|
||||
* In this case to load all the objects in the vector is a huge waste of
|
||||
* resources. We just allocate a vector that is big enough for the selected
|
||||
* range length, and make sure to load just this part in the vector. */
|
||||
if (sortval->type == REDIS_ZSET &&
|
||||
dontsort &&
|
||||
(start != 0 || end != vectorlen-1))
|
||||
{
|
||||
vectorlen = end-start+1;
|
||||
}
|
||||
|
||||
/* Load the sorting vector with all the objects to sort */
|
||||
vector = zmalloc(sizeof(redisSortObject)*vectorlen);
|
||||
j = 0;
|
||||
|
||||
@ -259,6 +295,48 @@ void sortCommand(redisClient *c) {
|
||||
j++;
|
||||
}
|
||||
setTypeReleaseIterator(si);
|
||||
} else if (sortval->type == REDIS_ZSET && dontsort) {
|
||||
/* Special handling for a sorted set, if 'dontsort' is true.
|
||||
* This makes sure we return elements in the sorted set original
|
||||
* ordering, accordingly to DESC / ASC options.
|
||||
*
|
||||
* Note that in this case we also handle LIMIT here in a direct
|
||||
* way, just getting the required range, as an optimization. */
|
||||
|
||||
zset *zs = sortval->ptr;
|
||||
zskiplist *zsl = zs->zsl;
|
||||
zskiplistNode *ln;
|
||||
robj *ele;
|
||||
int rangelen = vectorlen;
|
||||
|
||||
/* Check if starting point is trivial, before doing log(N) lookup. */
|
||||
if (desc) {
|
||||
long zsetlen = dictSize(((zset*)sortval->ptr)->dict);
|
||||
|
||||
ln = zsl->tail;
|
||||
if (start > 0)
|
||||
ln = zslGetElementByRank(zsl,zsetlen-start);
|
||||
} else {
|
||||
ln = zsl->header->level[0].forward;
|
||||
if (start > 0)
|
||||
ln = zslGetElementByRank(zsl,start+1);
|
||||
}
|
||||
|
||||
while(rangelen--) {
|
||||
redisAssertWithInfo(c,sortval,ln != NULL);
|
||||
ele = ln->obj;
|
||||
vector[j].obj = ele;
|
||||
vector[j].u.score = 0;
|
||||
vector[j].u.cmpobj = NULL;
|
||||
j++;
|
||||
ln = desc ? ln->backward : ln->level[0].forward;
|
||||
}
|
||||
/* The code producing the output does not know that in the case of
|
||||
* sorted set, 'dontsort', and LIMIT, we are able to get just the
|
||||
* range, already sorted, so we need to adjust "start" and "end"
|
||||
* to make sure start is set to 0. */
|
||||
end -= start;
|
||||
start = 0;
|
||||
} else if (sortval->type == REDIS_ZSET) {
|
||||
dict *set = ((zset*)sortval->ptr)->dict;
|
||||
dictIterator *di;
|
||||
@ -319,16 +397,6 @@ void sortCommand(redisClient *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* We are ready to sort the vector... perform a bit of sanity check
|
||||
* on the LIMIT option too. We'll use a partial version of quicksort. */
|
||||
start = (limit_start < 0) ? 0 : limit_start;
|
||||
end = (limit_count < 0) ? vectorlen-1 : start+limit_count-1;
|
||||
if (start >= vectorlen) {
|
||||
start = vectorlen-1;
|
||||
end = vectorlen-2;
|
||||
}
|
||||
if (end >= vectorlen) end = vectorlen-1;
|
||||
|
||||
if (dontsort == 0) {
|
||||
server.sort_desc = desc;
|
||||
server.sort_alpha = alpha;
|
||||
|
@ -1 +1 @@
|
||||
#define REDIS_VERSION "2.5.13"
|
||||
#define REDIS_VERSION "2.5.14"
|
||||
|
Reference in New Issue
Block a user