Unverified Commit 68ceb466 authored by chendianqiang's avatar chendianqiang Committed by GitHub
Browse files

Merge pull request #1 from antirez/unstable

update
parents 3d5e2c62 1c95c075
...@@ -123,9 +123,25 @@ robj *createStringObject(const char *ptr, size_t len) { ...@@ -123,9 +123,25 @@ robj *createStringObject(const char *ptr, size_t len) {
return createRawStringObject(ptr,len); return createRawStringObject(ptr,len);
} }
robj *createStringObjectFromLongLong(long long value) { /* Create a string object from a long long value. When possible returns a
* shared integer object, or at least an integer encoded one.
*
* If valueobj is non zero, the function avoids returning a a shared
* integer, because the object is going to be used as value in the Redis key
* space (for instance when the INCR command is used), so we want LFU/LRU
* values specific for each key. */
robj *createStringObjectFromLongLongWithOptions(long long value, int valueobj) {
robj *o; robj *o;
if (value >= 0 && value < OBJ_SHARED_INTEGERS) {
if (server.maxmemory == 0 ||
!(server.maxmemory_policy & MAXMEMORY_FLAG_NO_SHARED_INTEGERS))
{
/* If the maxmemory policy permits, we can still return shared integers
* even if valueobj is true. */
valueobj = 0;
}
if (value >= 0 && value < OBJ_SHARED_INTEGERS && valueobj == 0) {
incrRefCount(shared.integers[value]); incrRefCount(shared.integers[value]);
o = shared.integers[value]; o = shared.integers[value];
} else { } else {
...@@ -140,6 +156,20 @@ robj *createStringObjectFromLongLong(long long value) { ...@@ -140,6 +156,20 @@ robj *createStringObjectFromLongLong(long long value) {
return o; return o;
} }
/* Wrapper for createStringObjectFromLongLongWithOptions() always demanding
* to create a shared object if possible. */
robj *createStringObjectFromLongLong(long long value) {
return createStringObjectFromLongLongWithOptions(value,0);
}
/* Wrapper for createStringObjectFromLongLongWithOptions() avoiding a shared
* object when LFU/LRU info are needed, that is, when the object is used
* as a value in the key space, and Redis is configured to evict based on
* LFU/LRU. */
robj *createStringObjectFromLongLongForValue(long long value) {
return createStringObjectFromLongLongWithOptions(value,1);
}
/* Create a string object from a long double. If humanfriendly is non-zero /* Create a string object from a long double. If humanfriendly is non-zero
* it does not use exponential format and trims trailing zeroes at the end, * it does not use exponential format and trims trailing zeroes at the end,
* however this results in loss of precision. Otherwise exp format is used * however this results in loss of precision. Otherwise exp format is used
...@@ -715,7 +745,7 @@ char *strEncoding(int encoding) { ...@@ -715,7 +745,7 @@ char *strEncoding(int encoding) {
* size of a radix tree that is used to store Stream IDs. * size of a radix tree that is used to store Stream IDs.
* *
* Note: to guess the size of the radix tree is not trivial, so we * Note: to guess the size of the radix tree is not trivial, so we
* approximate it considering 128 bytes of data overhead for each * approximate it considering 16 bytes of data overhead for each
* key (the ID), and then adding the number of bare nodes, plus some * key (the ID), and then adding the number of bare nodes, plus some
* overhead due by the data and child pointers. This secret recipe * overhead due by the data and child pointers. This secret recipe
* was obtained by checking the average radix tree created by real * was obtained by checking the average radix tree created by real
...@@ -874,6 +904,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) { ...@@ -874,6 +904,7 @@ size_t objectComputeSize(robj *o, size_t sample_size) {
* structures and the PEL memory usage. */ * structures and the PEL memory usage. */
raxIterator cri; raxIterator cri;
raxStart(&cri,cg->consumers); raxStart(&cri,cg->consumers);
raxSeek(&cri,"^",NULL,0);
while(raxNext(&cri)) { while(raxNext(&cri)) {
streamConsumer *consumer = cri.data; streamConsumer *consumer = cri.data;
asize += sizeof(*consumer); asize += sizeof(*consumer);
...@@ -968,7 +999,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) { ...@@ -968,7 +999,7 @@ struct redisMemOverhead *getMemoryOverheadData(void) {
listRewind(server.clients,&li); listRewind(server.clients,&li);
while((ln = listNext(&li))) { while((ln = listNext(&li))) {
client *c = listNodeValue(ln); client *c = listNodeValue(ln);
if (c->flags & CLIENT_SLAVE) if (c->flags & CLIENT_SLAVE && !(c->flags & CLIENT_MONITOR))
continue; continue;
mem += getClientOutputBufferMemoryUsage(c); mem += getClientOutputBufferMemoryUsage(c);
mem += sdsAllocSize(c->querybuf); mem += sdsAllocSize(c->querybuf);
...@@ -1136,6 +1167,32 @@ sds getMemoryDoctorReport(void) { ...@@ -1136,6 +1167,32 @@ sds getMemoryDoctorReport(void) {
return s; return s;
} }
/* Set the object LRU/LFU depending on server.maxmemory_policy.
* The lfu_freq arg is only relevant if policy is MAXMEMORY_FLAG_LFU.
* The lru_idle and lru_clock args are only relevant if policy
* is MAXMEMORY_FLAG_LRU.
* Either or both of them may be <0, in that case, nothing is set. */
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
long long lru_clock) {
if (server.maxmemory_policy & MAXMEMORY_FLAG_LFU) {
if (lfu_freq >= 0) {
serverAssert(lfu_freq <= 255);
val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq;
}
} else if (lru_idle >= 0) {
/* Serialized LRU idle time is in seconds. Scale
* according to the LRU clock resolution this Redis
* instance was compiled with (normally 1000 ms, so the
* below statement will expand to lru_idle*1000/1000. */
lru_idle = lru_idle*1000/LRU_CLOCK_RESOLUTION;
val->lru = lru_clock - lru_idle;
/* If the lru field overflows (since LRU it is a wrapping
* clock), the best we can do is to provide the maximum
* representable idle time. */
if (val->lru < 0) val->lru = lru_clock+1;
}
}
/* ======================= The OBJECT and MEMORY commands =================== */ /* ======================= The OBJECT and MEMORY commands =================== */
/* This is a helper function for the OBJECT command. We need to lookup keys /* This is a helper function for the OBJECT command. We need to lookup keys
...@@ -1161,10 +1218,10 @@ void objectCommand(client *c) { ...@@ -1161,10 +1218,10 @@ void objectCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"encoding <key> -- Return the kind of internal representation used in order to store the value associated with a key.", "ENCODING <key> -- Return the kind of internal representation used in order to store the value associated with a key.",
"freq <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.", "FREQ <key> -- Return the access frequency index of the key. The returned integer is proportional to the logarithm of the recent access frequency of the key.",
"idletime <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.", "IDLETIME <key> -- Return the idle time of the key, that is the approximated number of seconds elapsed since the last access to the key.",
"refcount <key> -- Return the number of references of the value associated with the specified key.", "REFCOUNT <key> -- Return the number of references of the value associated with the specified key.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -1197,7 +1254,7 @@ NULL ...@@ -1197,7 +1254,7 @@ NULL
* when the key is read or overwritten. */ * when the key is read or overwritten. */
addReplyLongLong(c,LFUDecrAndReturn(o)); addReplyLongLong(c,LFUDecrAndReturn(o));
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try OBJECT help", (char *)c->argv[1]->ptr); addReplySubcommandSyntaxError(c);
} }
} }
......
...@@ -327,9 +327,9 @@ void publishCommand(client *c) { ...@@ -327,9 +327,9 @@ void publishCommand(client *c) {
void pubsubCommand(client *c) { void pubsubCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"channels [<pattern>] -- Return the currently active channels matching a pattern (default: all).", "CHANNELS [<pattern>] -- Return the currently active channels matching a pattern (default: all).",
"numpat -- Return number of subscriptions to patterns.", "NUMPAT -- Return number of subscriptions to patterns.",
"numsub [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).", "NUMSUB [channel-1 .. channel-N] -- Returns the number of subscribers for the specified channels (excluding patterns, default: none).",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -372,7 +372,6 @@ NULL ...@@ -372,7 +372,6 @@ NULL
/* PUBSUB NUMPAT */ /* PUBSUB NUMPAT */
addReplyLongLong(c,listLength(server.pubsub_patterns)); addReplyLongLong(c,listLength(server.pubsub_patterns));
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try PUBSUB HELP", addReplySubcommandSyntaxError(c);
(char*)c->argv[1]->ptr);
} }
} }
...@@ -1636,7 +1636,7 @@ int quicklistTest(int argc, char *argv[]) { ...@@ -1636,7 +1636,7 @@ int quicklistTest(int argc, char *argv[]) {
TEST("add to tail of empty list") { TEST("add to tail of empty list") {
quicklist *ql = quicklistNew(-2, options[_i]); quicklist *ql = quicklistNew(-2, options[_i]);
quicklistPushTail(ql, "hello", 6); quicklistPushTail(ql, "hello", 6);
/* 1 for head and 1 for tail beacuse 1 node = head = tail */ /* 1 for head and 1 for tail because 1 node = head = tail */
ql_verify(ql, 1, 1, 1, 1); ql_verify(ql, 1, 1, 1, 1);
quicklistRelease(ql); quicklistRelease(ql);
} }
...@@ -1644,7 +1644,7 @@ int quicklistTest(int argc, char *argv[]) { ...@@ -1644,7 +1644,7 @@ int quicklistTest(int argc, char *argv[]) {
TEST("add to head of empty list") { TEST("add to head of empty list") {
quicklist *ql = quicklistNew(-2, options[_i]); quicklist *ql = quicklistNew(-2, options[_i]);
quicklistPushHead(ql, "hello", 6); quicklistPushHead(ql, "hello", 6);
/* 1 for head and 1 for tail beacuse 1 node = head = tail */ /* 1 for head and 1 for tail because 1 node = head = tail */
ql_verify(ql, 1, 1, 1, 1); ql_verify(ql, 1, 1, 1, 1);
quicklistRelease(ql); quicklistRelease(ql);
} }
......
...@@ -359,7 +359,18 @@ raxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **chi ...@@ -359,7 +359,18 @@ raxNode *raxCompressNode(raxNode *n, unsigned char *s, size_t len, raxNode **chi
* parent's node is returned as '*plink' if not NULL. Finally, if the * parent's node is returned as '*plink' if not NULL. Finally, if the
* search stopped in a compressed node, '*splitpos' returns the index * search stopped in a compressed node, '*splitpos' returns the index
* inside the compressed node where the search ended. This is useful to * inside the compressed node where the search ended. This is useful to
* know where to split the node for insertion. */ * know where to split the node for insertion.
*
* Note that when we stop in the middle of a compressed node with
* a perfect match, this function will return a length equal to the
* 'len' argument (all the key matched), and will return a *splitpos which is
* always positive (that will represent the index of the character immediately
* *after* the last match in the current compressed node).
*
* When instead we stop at a compressed node and *splitpos is zero, it
* means that the current node represents the key (that is, none of the
* compressed node characters are needed to represent the key, just all
* its parents nodes). */
static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) { static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode **stopnode, raxNode ***plink, int *splitpos, raxStack *ts) {
raxNode *h = rax->head; raxNode *h = rax->head;
raxNode **parentlink = &rax->head; raxNode **parentlink = &rax->head;
...@@ -405,10 +416,12 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode ...@@ -405,10 +416,12 @@ static inline size_t raxLowWalk(rax *rax, unsigned char *s, size_t len, raxNode
/* Insert the element 's' of size 'len', setting as auxiliary data /* Insert the element 's' of size 'len', setting as auxiliary data
* the pointer 'data'. If the element is already present, the associated * the pointer 'data'. If the element is already present, the associated
* data is updated, and 0 is returned, otherwise the element is inserted * data is updated (only if 'overwrite' is set to 1), and 0 is returned,
* and 1 is returned. On out of memory the function returns 0 as well but * otherwise the element is inserted and 1 is returned. On out of memory the
* sets errno to ENOMEM, otherwise errno will be set to 0. */ * function returns 0 as well but sets errno to ENOMEM, otherwise errno will
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { * be set to 0.
*/
int raxGenericInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old, int overwrite) {
size_t i; size_t i;
int j = 0; /* Split position. If raxLowWalk() stops in a compressed int j = 0; /* Split position. If raxLowWalk() stops in a compressed
node, the index 'j' represents the char we stopped within the node, the index 'j' represents the char we stopped within the
...@@ -426,7 +439,8 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { ...@@ -426,7 +439,8 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
* data pointer. */ * data pointer. */
if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) { if (i == len && (!h->iscompr || j == 0 /* not in the middle if j is 0 */)) {
debugf("### Insert: node representing key exists\n"); debugf("### Insert: node representing key exists\n");
if (!h->iskey || h->isnull) { /* Make space for the value pointer if needed. */
if (!h->iskey || (h->isnull && overwrite)) {
h = raxReallocForData(h,data); h = raxReallocForData(h,data);
if (h) memcpy(parentlink,&h,sizeof(h)); if (h) memcpy(parentlink,&h,sizeof(h));
} }
...@@ -434,12 +448,17 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { ...@@ -434,12 +448,17 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
errno = ENOMEM; errno = ENOMEM;
return 0; return 0;
} }
/* Update the existing key if there is already one. */
if (h->iskey) { if (h->iskey) {
if (old) *old = raxGetData(h); if (old) *old = raxGetData(h);
raxSetData(h,data); if (overwrite) raxSetData(h,data);
errno = 0; errno = 0;
return 0; /* Element already exists. */ return 0; /* Element already exists. */
} }
/* Otherwise set the node as a key. Note that raxSetData()
* will set h->iskey. */
raxSetData(h,data); raxSetData(h,data);
rax->numele++; rax->numele++;
return 1; /* Element inserted. */ return 1; /* Element inserted. */
...@@ -448,7 +467,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { ...@@ -448,7 +467,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
/* If the node we stopped at is a compressed node, we need to /* If the node we stopped at is a compressed node, we need to
* split it before to continue. * split it before to continue.
* *
* Splitting a compressed node have a few possibile cases. * Splitting a compressed node have a few possible cases.
* Imagine that the node 'h' we are currently at is a compressed * Imagine that the node 'h' we are currently at is a compressed
* node contaning the string "ANNIBALE" (it means that it represents * node contaning the string "ANNIBALE" (it means that it represents
* nodes A -> N -> N -> I -> B -> A -> L -> E with the only child * nodes A -> N -> N -> I -> B -> A -> L -> E with the only child
...@@ -730,7 +749,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) { ...@@ -730,7 +749,7 @@ int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
cp = raxNodeLastChildPtr(trimmed); cp = raxNodeLastChildPtr(trimmed);
memcpy(cp,&postfix,sizeof(postfix)); memcpy(cp,&postfix,sizeof(postfix));
/* Finish! We don't need to contine with the insertion /* Finish! We don't need to continue with the insertion
* algorithm for ALGO 2. The key is already inserted. */ * algorithm for ALGO 2. The key is already inserted. */
rax->numele++; rax->numele++;
rax_free(h); rax_free(h);
...@@ -793,6 +812,19 @@ oom: ...@@ -793,6 +812,19 @@ oom:
return 0; return 0;
} }
/* Overwriting insert. Just a wrapper for raxGenericInsert() that will
* update the element if there is already one for the same key. */
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
return raxGenericInsert(rax,s,len,data,old,1);
}
/* Non overwriting insert function: this if an element with the same key
* exists, the value is not updated and the function returns 0.
* This is a just a wrapper for raxGenericInsert(). */
int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old) {
return raxGenericInsert(rax,s,len,data,old,0);
}
/* Find a key in the rax, returns raxNotFound special void pointer value /* Find a key in the rax, returns raxNotFound special void pointer value
* if the item was not found, otherwise the value associated with the * if the item was not found, otherwise the value associated with the
* item is returned. */ * item is returned. */
...@@ -1135,6 +1167,7 @@ void raxStart(raxIterator *it, rax *rt) { ...@@ -1135,6 +1167,7 @@ void raxStart(raxIterator *it, rax *rt) {
it->key = it->key_static_string; it->key = it->key_static_string;
it->key_max = RAX_ITER_STATIC_LEN; it->key_max = RAX_ITER_STATIC_LEN;
it->data = NULL; it->data = NULL;
it->node_cb = NULL;
raxStackInit(&it->stack); raxStackInit(&it->stack);
} }
...@@ -1208,6 +1241,10 @@ int raxIteratorNextStep(raxIterator *it, int noup) { ...@@ -1208,6 +1241,10 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
if (!raxIteratorAddChars(it,it->node->data, if (!raxIteratorAddChars(it,it->node->data,
it->node->iscompr ? it->node->size : 1)) return 0; it->node->iscompr ? it->node->size : 1)) return 0;
memcpy(&it->node,cp,sizeof(it->node)); memcpy(&it->node,cp,sizeof(it->node));
/* Call the node callback if any, and replace the node pointer
* if the callback returns true. */
if (it->node_cb && it->node_cb(&it->node))
memcpy(cp,&it->node,sizeof(it->node));
/* For "next" step, stop every time we find a key along the /* For "next" step, stop every time we find a key along the
* way, since the key is lexicograhically smaller compared to * way, since the key is lexicograhically smaller compared to
* what follows in the sub-children. */ * what follows in the sub-children. */
...@@ -1260,6 +1297,10 @@ int raxIteratorNextStep(raxIterator *it, int noup) { ...@@ -1260,6 +1297,10 @@ int raxIteratorNextStep(raxIterator *it, int noup) {
raxIteratorAddChars(it,it->node->data+i,1); raxIteratorAddChars(it,it->node->data+i,1);
if (!raxStackPush(&it->stack,it->node)) return 0; if (!raxStackPush(&it->stack,it->node)) return 0;
memcpy(&it->node,cp,sizeof(it->node)); memcpy(&it->node,cp,sizeof(it->node));
/* Call the node callback if any, and replace the node
* pointer if the callback returns true. */
if (it->node_cb && it->node_cb(&it->node))
memcpy(cp,&it->node,sizeof(it->node));
if (it->node->iskey) { if (it->node->iskey) {
it->data = raxGetData(it->node); it->data = raxGetData(it->node);
return 1; return 1;
...@@ -1293,7 +1334,7 @@ int raxSeekGreatest(raxIterator *it) { ...@@ -1293,7 +1334,7 @@ int raxSeekGreatest(raxIterator *it) {
/* Like raxIteratorNextStep() but implements an iteration step moving /* Like raxIteratorNextStep() but implements an iteration step moving
* to the lexicographically previous element. The 'noup' option has a similar * to the lexicographically previous element. The 'noup' option has a similar
* effect to the one of raxIteratorPrevSte(). */ * effect to the one of raxIteratorNextStep(). */
int raxIteratorPrevStep(raxIterator *it, int noup) { int raxIteratorPrevStep(raxIterator *it, int noup) {
if (it->flags & RAX_ITER_EOF) { if (it->flags & RAX_ITER_EOF) {
return 1; return 1;
...@@ -1523,11 +1564,26 @@ int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) { ...@@ -1523,11 +1564,26 @@ int raxSeek(raxIterator *it, const char *op, unsigned char *ele, size_t len) {
/* If there was no mismatch we are into a node representing the /* If there was no mismatch we are into a node representing the
* key, (but which is not a key or the seek operator does not * key, (but which is not a key or the seek operator does not
* include 'eq'), or we stopped in the middle of a compressed node * include 'eq'), or we stopped in the middle of a compressed node
* after processing all the key. Cotinue iterating as this was * after processing all the key. Continue iterating as this was
* a legitimate key we stopped at. */ * a legitimate key we stopped at. */
it->flags &= ~RAX_ITER_JUST_SEEKED; it->flags &= ~RAX_ITER_JUST_SEEKED;
if (it->node->iscompr && it->node->iskey && splitpos && lt) {
/* If we stopped in the middle of a compressed node with
* perfect match, and the condition is to seek a key "<" than
* the specified one, then if this node is a key it already
* represents our match. For instance we may have nodes:
*
* "f" -> "oobar" = 1 -> "" = 2
*
* Representing keys "f" = 1, "foobar" = 2. A seek for
* the key < "foo" will stop in the middle of the "oobar"
* node, but will be our match, representing the key "f".
*
* So in that case, we don't seek backward. */
} else {
if (gt && !raxIteratorNextStep(it,0)) return 0; if (gt && !raxIteratorNextStep(it,0)) return 0;
if (lt && !raxIteratorPrevStep(it,0)) return 0; if (lt && !raxIteratorPrevStep(it,0)) return 0;
}
it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */ it->flags |= RAX_ITER_JUST_SEEKED; /* Ignore next call. */
} }
} else { } else {
......
...@@ -94,7 +94,7 @@ typedef struct raxNode { ...@@ -94,7 +94,7 @@ typedef struct raxNode {
* *
* If the node has an associated key (iskey=1) and is not NULL * If the node has an associated key (iskey=1) and is not NULL
* (isnull=0), then after the raxNode pointers poiting to the * (isnull=0), then after the raxNode pointers poiting to the
* childen, an additional value pointer is present (as you can see * children, an additional value pointer is present (as you can see
* in the representation above as "value-ptr" field). * in the representation above as "value-ptr" field).
*/ */
unsigned char data[]; unsigned char data[];
...@@ -119,6 +119,21 @@ typedef struct raxStack { ...@@ -119,6 +119,21 @@ typedef struct raxStack {
int oom; /* True if pushing into this stack failed for OOM at some point. */ int oom; /* True if pushing into this stack failed for OOM at some point. */
} raxStack; } raxStack;
/* Optional callback used for iterators and be notified on each rax node,
* including nodes not representing keys. If the callback returns true
* the callback changed the node pointer in the iterator structure, and the
* iterator implementation will have to replace the pointer in the radix tree
* internals. This allows the callback to reallocate the node to perform
* very special operations, normally not needed by normal applications.
*
* This callback is used to perform very low level analysis of the radix tree
* structure, scanning each possible node (but the root node), or in order to
* reallocate the nodes to reduce the allocation fragmentation (this is the
* Redis application for this callback).
*
* This is currently only supported in forward iterations (raxNext) */
typedef int (*raxNodeCallback)(raxNode **noderef);
/* Radix tree iterator state is encapsulated into this data structure. */ /* Radix tree iterator state is encapsulated into this data structure. */
#define RAX_ITER_STATIC_LEN 128 #define RAX_ITER_STATIC_LEN 128
#define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current #define RAX_ITER_JUST_SEEKED (1<<0) /* Iterator was just seeked. Return current
...@@ -137,6 +152,7 @@ typedef struct raxIterator { ...@@ -137,6 +152,7 @@ typedef struct raxIterator {
unsigned char key_static_string[RAX_ITER_STATIC_LEN]; unsigned char key_static_string[RAX_ITER_STATIC_LEN];
raxNode *node; /* Current node. Only for unsafe iteration. */ raxNode *node; /* Current node. Only for unsafe iteration. */
raxStack stack; /* Stack used for unsafe iteration. */ raxStack stack; /* Stack used for unsafe iteration. */
raxNodeCallback node_cb; /* Optional node callback. Normally set to NULL. */
} raxIterator; } raxIterator;
/* A special pointer returned for not found items. */ /* A special pointer returned for not found items. */
...@@ -145,6 +161,7 @@ extern void *raxNotFound; ...@@ -145,6 +161,7 @@ extern void *raxNotFound;
/* Exported API. */ /* Exported API. */
rax *raxNew(void); rax *raxNew(void);
int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old); int raxInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
int raxTryInsert(rax *rax, unsigned char *s, size_t len, void *data, void **old);
int raxRemove(rax *rax, unsigned char *s, size_t len, void **old); int raxRemove(rax *rax, unsigned char *s, size_t len, void **old);
void *raxFind(rax *rax, unsigned char *s, size_t len); void *raxFind(rax *rax, unsigned char *s, size_t len);
void raxFree(rax *rax); void raxFree(rax *rax);
...@@ -160,4 +177,8 @@ int raxEOF(raxIterator *it); ...@@ -160,4 +177,8 @@ int raxEOF(raxIterator *it);
void raxShow(rax *rax); void raxShow(rax *rax);
uint64_t raxSize(rax *rax); uint64_t raxSize(rax *rax);
/* Internal API. May be used by the node callback in order to access rax nodes
* in a low level way, so this function is exported as well. */
void raxSetData(raxNode *n, void *data);
#endif #endif
...@@ -100,6 +100,9 @@ int rdbLoadType(rio *rdb) { ...@@ -100,6 +100,9 @@ int rdbLoadType(rio *rdb) {
return type; return type;
} }
/* This is only used to load old databases stored with the RDB_OPCODE_EXPIRETIME
* opcode. New versions of Redis store using the RDB_OPCODE_EXPIRETIME_MS
* opcode. */
time_t rdbLoadTime(rio *rdb) { time_t rdbLoadTime(rio *rdb) {
int32_t t32; int32_t t32;
rdbLoadRaw(rdb,&t32,4); rdbLoadRaw(rdb,&t32,4);
...@@ -108,12 +111,26 @@ time_t rdbLoadTime(rio *rdb) { ...@@ -108,12 +111,26 @@ time_t rdbLoadTime(rio *rdb) {
int rdbSaveMillisecondTime(rio *rdb, long long t) { int rdbSaveMillisecondTime(rio *rdb, long long t) {
int64_t t64 = (int64_t) t; int64_t t64 = (int64_t) t;
memrev64ifbe(&t64); /* Store in little endian. */
return rdbWriteRaw(rdb,&t64,8); return rdbWriteRaw(rdb,&t64,8);
} }
long long rdbLoadMillisecondTime(rio *rdb) { /* This function loads a time from the RDB file. It gets the version of the
* RDB because, unfortunately, before Redis 5 (RDB version 9), the function
* failed to convert data to/from little endian, so RDB files with keys having
* expires could not be shared between big endian and little endian systems
* (because the expire time will be totally wrong). The fix for this is just
* to call memrev64ifbe(), however if we fix this for all the RDB versions,
* this call will introduce an incompatibility for big endian systems:
* after upgrading to Redis version 5 they will no longer be able to load their
* own old RDB files. Because of that, we instead fix the function only for new
* RDB versions, and load older RDB versions as we used to do in the past,
* allowing big endian systems to load their own old RDB files. */
long long rdbLoadMillisecondTime(rio *rdb, int rdbver) {
int64_t t64; int64_t t64;
rdbLoadRaw(rdb,&t64,8); rdbLoadRaw(rdb,&t64,8);
if (rdbver >= 9) /* Check the top comment of this function. */
memrev64ifbe(&t64); /* Convert in big endian if the system is BE. */
return (long long)t64; return (long long)t64;
} }
...@@ -271,7 +288,7 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) { ...@@ -271,7 +288,7 @@ void *rdbLoadIntegerObject(rio *rdb, int enctype, int flags, size_t *lenptr) {
memcpy(p,buf,len); memcpy(p,buf,len);
return p; return p;
} else if (encode) { } else if (encode) {
return createStringObjectFromLongLong(val); return createStringObjectFromLongLongForValue(val);
} else { } else {
return createObject(OBJ_STRING,sdsfromlonglong(val)); return createObject(OBJ_STRING,sdsfromlonglong(val));
} }
...@@ -988,8 +1005,7 @@ size_t rdbSavedObjectLen(robj *o) { ...@@ -988,8 +1005,7 @@ size_t rdbSavedObjectLen(robj *o) {
* On error -1 is returned. * On error -1 is returned.
* On success if the key was actually saved 1 is returned, otherwise 0 * On success if the key was actually saved 1 is returned, otherwise 0
* is returned (the key was already expired). */ * is returned (the key was already expired). */
int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) {
{
int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU; int savelru = server.maxmemory_policy & MAXMEMORY_FLAG_LRU;
int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU; int savelfu = server.maxmemory_policy & MAXMEMORY_FLAG_LFU;
...@@ -1001,7 +1017,7 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime) ...@@ -1001,7 +1017,7 @@ int rdbSaveKeyValuePair(rio *rdb, robj *key, robj *val, long long expiretime)
/* Save the LRU info. */ /* Save the LRU info. */
if (savelru) { if (savelru) {
int idletime = estimateObjectIdleTime(val); uint64_t idletime = estimateObjectIdleTime(val);
idletime /= 1000; /* Using seconds is enough and requires less space.*/ idletime /= 1000; /* Using seconds is enough and requires less space.*/
if (rdbSaveType(rdb,RDB_OPCODE_IDLE) == -1) return -1; if (rdbSaveType(rdb,RDB_OPCODE_IDLE) == -1) return -1;
if (rdbSaveLen(rdb,idletime) == -1) return -1; if (rdbSaveLen(rdb,idletime) == -1) return -1;
...@@ -1111,13 +1127,9 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) { ...@@ -1111,13 +1127,9 @@ int rdbSaveRio(rio *rdb, int *error, int flags, rdbSaveInfo *rsi) {
* is currently the largest type we are able to represent in RDB sizes. * 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 * However this does not limit the actual size of the DB to load since
* these sizes are just hints to resize the hash tables. */ * these sizes are just hints to resize the hash tables. */
uint32_t db_size, expires_size; uint64_t db_size, expires_size;
db_size = (dictSize(db->dict) <= UINT32_MAX) ? db_size = dictSize(db->dict);
dictSize(db->dict) : expires_size = dictSize(db->expires);
UINT32_MAX;
expires_size = (dictSize(db->expires) <= UINT32_MAX) ?
dictSize(db->expires) :
UINT32_MAX;
if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr; if (rdbSaveType(rdb,RDB_OPCODE_RESIZEDB) == -1) goto werr;
if (rdbSaveLen(rdb,db_size) == -1) goto werr; if (rdbSaveLen(rdb,db_size) == -1) goto werr;
if (rdbSaveLen(rdb,expires_size) == -1) goto werr; if (rdbSaveLen(rdb,expires_size) == -1) goto werr;
...@@ -1225,6 +1237,10 @@ int rdbSave(char *filename, rdbSaveInfo *rsi) { ...@@ -1225,6 +1237,10 @@ int rdbSave(char *filename, rdbSaveInfo *rsi) {
} }
rioInitWithFile(&rdb,fp); rioInitWithFile(&rdb,fp);
if (server.rdb_save_incremental_fsync)
rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);
if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) { if (rdbSaveRio(&rdb,&error,RDB_SAVE_NONE,rsi) == C_ERR) {
errno = error; errno = error;
goto werr; goto werr;
...@@ -1441,6 +1457,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -1441,6 +1457,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
o = createZsetObject(); o = createZsetObject();
zs = o->ptr; zs = o->ptr;
if (zsetlen > DICT_HT_INITIAL_SIZE)
dictExpand(zs->dict,zsetlen);
/* Load every single element of the sorted set. */ /* Load every single element of the sorted set. */
while(zsetlen--) { while(zsetlen--) {
sds sdsele; sds sdsele;
...@@ -1509,6 +1528,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -1509,6 +1528,9 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
sdsfree(value); sdsfree(value);
} }
if (o->encoding == OBJ_ENCODING_HT && len > DICT_HT_INITIAL_SIZE)
dictExpand(o->ptr,len);
/* Load remaining fields and values into the hash table */ /* Load remaining fields and values into the hash table */
while (o->encoding == OBJ_ENCODING_HT && len > 0) { while (o->encoding == OBJ_ENCODING_HT && len > 0) {
len--; len--;
...@@ -1636,7 +1658,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -1636,7 +1658,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
if (first == NULL) { if (first == NULL) {
/* Serialized listpacks should never be empty, since on /* Serialized listpacks should never be empty, since on
* deletion we should remove the radix tree key if the * deletion we should remove the radix tree key if the
* resulting listpack is emtpy. */ * resulting listpack is empty. */
rdbExitReportCorruptRDB("Empty listpack inside stream"); rdbExitReportCorruptRDB("Empty listpack inside stream");
} }
...@@ -1683,7 +1705,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -1683,7 +1705,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
unsigned char rawid[sizeof(streamID)]; unsigned char rawid[sizeof(streamID)];
rdbLoadRaw(rdb,rawid,sizeof(rawid)); rdbLoadRaw(rdb,rawid,sizeof(rawid));
streamNACK *nack = streamCreateNACK(NULL); streamNACK *nack = streamCreateNACK(NULL);
nack->delivery_time = rdbLoadMillisecondTime(rdb); nack->delivery_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);
nack->delivery_count = rdbLoadLen(rdb,NULL); nack->delivery_count = rdbLoadLen(rdb,NULL);
if (!raxInsert(cgroup->pel,rawid,sizeof(rawid),nack,NULL)) if (!raxInsert(cgroup->pel,rawid,sizeof(rawid),nack,NULL))
rdbExitReportCorruptRDB("Duplicated gobal PEL entry " rdbExitReportCorruptRDB("Duplicated gobal PEL entry "
...@@ -1702,7 +1724,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) { ...@@ -1702,7 +1724,7 @@ robj *rdbLoadObject(int rdbtype, rio *rdb) {
streamConsumer *consumer = streamLookupConsumer(cgroup,cname, streamConsumer *consumer = streamLookupConsumer(cgroup,cname,
1); 1);
sdsfree(cname); sdsfree(cname);
consumer->seen_time = rdbLoadMillisecondTime(rdb); consumer->seen_time = rdbLoadMillisecondTime(rdb,RDB_VERSION);
/* Load the PEL about entries owned by this specific /* Load the PEL about entries owned by this specific
* consumer. */ * consumer. */
...@@ -1845,10 +1867,8 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) { ...@@ -1845,10 +1867,8 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
} }
/* Key-specific attributes, set by opcodes before the key type. */ /* Key-specific attributes, set by opcodes before the key type. */
long long expiretime = -1, now = mstime(); long long lru_idle = -1, lfu_freq = -1, expiretime = -1, now = mstime();
long long lru_clock = LRU_CLOCK(); long long lru_clock = LRU_CLOCK();
uint64_t lru_idle = -1;
int lfu_freq = -1;
while(1) { while(1) {
robj *key, *val; robj *key, *val;
...@@ -1867,7 +1887,7 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) { ...@@ -1867,7 +1887,7 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
} else if (type == RDB_OPCODE_EXPIRETIME_MS) { } else if (type == RDB_OPCODE_EXPIRETIME_MS) {
/* EXPIRETIME_MS: milliseconds precision expire times introduced /* EXPIRETIME_MS: milliseconds precision expire times introduced
* with RDB v3. Like EXPIRETIME but no with more precision. */ * with RDB v3. Like EXPIRETIME but no with more precision. */
expiretime = rdbLoadMillisecondTime(rdb); expiretime = rdbLoadMillisecondTime(rdb,rdbver);
continue; /* Read next opcode. */ continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_FREQ) { } else if (type == RDB_OPCODE_FREQ) {
/* FREQ: LFU frequency. */ /* FREQ: LFU frequency. */
...@@ -1877,7 +1897,9 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) { ...@@ -1877,7 +1897,9 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
continue; /* Read next opcode. */ continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_IDLE) { } else if (type == RDB_OPCODE_IDLE) {
/* IDLE: LRU idle time. */ /* IDLE: LRU idle time. */
if ((lru_idle = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr; uint64_t qword;
if ((qword = rdbLoadLen(rdb,NULL)) == RDB_LENERR) goto eoferr;
lru_idle = qword;
continue; /* Read next opcode. */ continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_EOF) { } else if (type == RDB_OPCODE_EOF) {
/* EOF: End of file, exit the main loop. */ /* EOF: End of file, exit the main loop. */
...@@ -1996,20 +2018,9 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) { ...@@ -1996,20 +2018,9 @@ int rdbLoadRio(rio *rdb, rdbSaveInfo *rsi, int loading_aof) {
/* Set the expire time if needed */ /* Set the expire time if needed */
if (expiretime != -1) setExpire(NULL,db,key,expiretime); if (expiretime != -1) setExpire(NULL,db,key,expiretime);
if (lfu_freq != -1) {
val->lru = (LFUGetTimeInMinutes()<<8) | lfu_freq; /* Set usage information (for eviction). */
} else { objectSetLRUOrLFU(val,lfu_freq,lru_idle,lru_clock);
/* LRU idle time loaded from RDB is in seconds. Scale
* according to the LRU clock resolution this Redis
* instance was compiled with (normaly 1000 ms, so the
* below statement will expand to lru_idle*1000/1000. */
lru_idle = lru_idle*1000/LRU_CLOCK_RESOLUTION;
val->lru = lru_clock - lru_idle;
/* If the lru field overflows (since LRU it is a wrapping
* clock), the best we can do is to provide the maxium
* representable idle time. */
if (val->lru < 0) val->lru = lru_clock+1;
}
/* Decrement the key refcount since dbAdd() will take its /* Decrement the key refcount since dbAdd() will take its
* own reference. */ * own reference. */
...@@ -2088,7 +2099,7 @@ void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) { ...@@ -2088,7 +2099,7 @@ void backgroundSaveDoneHandlerDisk(int exitcode, int bysignal) {
latencyEndMonitor(latency); latencyEndMonitor(latency);
latencyAddSampleIfNeeded("rdb-unlink-temp-file",latency); latencyAddSampleIfNeeded("rdb-unlink-temp-file",latency);
/* SIGUSR1 is whitelisted, so we have a way to kill a child without /* SIGUSR1 is whitelisted, so we have a way to kill a child without
* tirggering an error conditon. */ * tirggering an error condition. */
if (bysignal != SIGUSR1) if (bysignal != SIGUSR1)
server.lastbgsave_status = C_ERR; server.lastbgsave_status = C_ERR;
} }
...@@ -2125,7 +2136,7 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) { ...@@ -2125,7 +2136,7 @@ void backgroundSaveDoneHandlerSocket(int exitcode, int bysignal) {
* in error state. * in error state.
* *
* If the process returned an error, consider the list of slaves that * If the process returned an error, consider the list of slaves that
* can continue to be emtpy, so that it's just a special case of the * can continue to be empty, so that it's just a special case of the
* normal code path. */ * normal code path. */
ok_slaves = zmalloc(sizeof(uint64_t)); /* Make space for the count. */ ok_slaves = zmalloc(sizeof(uint64_t)); /* Make space for the count. */
ok_slaves[0] = 0; ok_slaves[0] = 0;
......
...@@ -129,6 +129,8 @@ int rdbLoadType(rio *rdb); ...@@ -129,6 +129,8 @@ int rdbLoadType(rio *rdb);
int rdbSaveTime(rio *rdb, time_t t); int rdbSaveTime(rio *rdb, time_t t);
time_t rdbLoadTime(rio *rdb); time_t rdbLoadTime(rio *rdb);
int rdbSaveLen(rio *rdb, uint64_t len); int rdbSaveLen(rio *rdb, uint64_t len);
int rdbSaveMillisecondTime(rio *rdb, long long t);
long long rdbLoadMillisecondTime(rio *rdb, int rdbver);
uint64_t rdbLoadLen(rio *rdb, int *isencoded); uint64_t rdbLoadLen(rio *rdb, int *isencoded);
int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr); int rdbLoadLenByRef(rio *rdb, int *isencoded, uint64_t *lenptr);
int rdbSaveObjectType(rio *rdb, robj *o); int rdbSaveObjectType(rio *rdb, robj *o);
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
void createSharedObjects(void); void createSharedObjects(void);
void rdbLoadProgressCallback(rio *r, const void *buf, size_t len); void rdbLoadProgressCallback(rio *r, const void *buf, size_t len);
long long rdbLoadMillisecondTime(rio *rdb);
int rdbCheckMode = 0; int rdbCheckMode = 0;
struct { struct {
...@@ -224,7 +223,7 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) { ...@@ -224,7 +223,7 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
/* EXPIRETIME_MS: milliseconds precision expire times introduced /* EXPIRETIME_MS: milliseconds precision expire times introduced
* with RDB v3. Like EXPIRETIME but no with more precision. */ * with RDB v3. Like EXPIRETIME but no with more precision. */
rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE; rdbstate.doing = RDB_CHECK_DOING_READ_EXPIRE;
if ((expiretime = rdbLoadMillisecondTime(&rdb)) == -1) goto eoferr; if ((expiretime = rdbLoadMillisecondTime(&rdb, rdbver)) == -1) goto eoferr;
continue; /* Read next opcode. */ continue; /* Read next opcode. */
} else if (type == RDB_OPCODE_FREQ) { } else if (type == RDB_OPCODE_FREQ) {
/* FREQ: LFU frequency. */ /* FREQ: LFU frequency. */
...@@ -287,12 +286,8 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) { ...@@ -287,12 +286,8 @@ int redis_check_rdb(char *rdbfilename, FILE *fp) {
/* Read value */ /* Read value */
rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE; rdbstate.doing = RDB_CHECK_DOING_READ_OBJECT_VALUE;
if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr; if ((val = rdbLoadObject(type,&rdb)) == NULL) goto eoferr;
/* Check if the key already expired. This function is used when loading /* Check if the key already expired. */
* an RDB file from disk, either at startup, or when an RDB was if (expiretime != -1 && expiretime < now)
* 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. */
if (server.masterhost == NULL && expiretime != -1 && expiretime < now)
rdbstate.already_expired++; rdbstate.already_expired++;
if (expiretime != -1) rdbstate.expires++; if (expiretime != -1) rdbstate.expires++;
rdbstate.key = NULL; rdbstate.key = NULL;
......
This diff is collapsed.
This diff is collapsed.
/* redisassert.h -- Drop in replacemnet assert.h that prints the stack trace /* redisassert.h -- Drop in replacements assert.h that prints the stack trace
* in the Redis logs. * in the Redis logs.
* *
* This file should be included instead of "assert.h" inside libraries used by * This file should be included instead of "assert.h" inside libraries used by
......
...@@ -553,7 +553,7 @@ need_full_resync: ...@@ -553,7 +553,7 @@ need_full_resync:
* Side effects, other than starting a BGSAVE: * Side effects, other than starting a BGSAVE:
* *
* 1) Handle the slaves in WAIT_START state, by preparing them for a full * 1) Handle the slaves in WAIT_START state, by preparing them for a full
* sync if the BGSAVE was succesfully started, or sending them an error * sync if the BGSAVE was successfully started, or sending them an error
* and dropping them from the list of slaves. * and dropping them from the list of slaves.
* *
* 2) Flush the Lua scripting script cache if the BGSAVE was actually * 2) Flush the Lua scripting script cache if the BGSAVE was actually
...@@ -896,7 +896,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -896,7 +896,7 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
} }
} }
/* If the preamble was already transfered, send the RDB bulk data. */ /* If the preamble was already transferred, send the RDB bulk data. */
lseek(slave->repldbfd,slave->repldboff,SEEK_SET); lseek(slave->repldbfd,slave->repldboff,SEEK_SET);
buflen = read(slave->repldbfd,buf,PROTO_IOBUF_LEN); buflen = read(slave->repldbfd,buf,PROTO_IOBUF_LEN);
if (buflen <= 0) { if (buflen <= 0) {
...@@ -965,7 +965,7 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) { ...@@ -965,7 +965,7 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
replicationGetSlaveName(slave)); replicationGetSlaveName(slave));
/* Note: we wait for a REPLCONF ACK message from slave in /* Note: we wait for a REPLCONF ACK message from slave in
* order to really put it online (install the write handler * order to really put it online (install the write handler
* so that the accumulated data can be transfered). However * so that the accumulated data can be transferred). However
* we change the replication state ASAP, since our slave * we change the replication state ASAP, since our slave
* is technically online now. */ * is technically online now. */
slave->replstate = SLAVE_STATE_ONLINE; slave->replstate = SLAVE_STATE_ONLINE;
...@@ -1048,7 +1048,7 @@ int slaveIsInHandshakeState(void) { ...@@ -1048,7 +1048,7 @@ int slaveIsInHandshakeState(void) {
/* Avoid the master to detect the slave is timing out while loading the /* Avoid the master to detect the slave is timing out while loading the
* RDB file in initial synchronization. We send a single newline character * RDB file in initial synchronization. We send a single newline character
* that is valid protocol but is guaranteed to either be sent entierly or * that is valid protocol but is guaranteed to either be sent entirely or
* not, since the byte is indivisible. * not, since the byte is indivisible.
* *
* The function is called in two contexts: while we flush the current * The function is called in two contexts: while we flush the current
...@@ -1105,7 +1105,7 @@ void restartAOF() { ...@@ -1105,7 +1105,7 @@ void restartAOF() {
#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */ #define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8) /* 8 MB */
void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
char buf[4096]; char buf[4096];
ssize_t nread, readlen; ssize_t nread, readlen, nwritten;
off_t left; off_t left;
UNUSED(el); UNUSED(el);
UNUSED(privdata); UNUSED(privdata);
...@@ -1206,8 +1206,9 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1206,8 +1206,9 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
} }
server.repl_transfer_lastio = server.unixtime; server.repl_transfer_lastio = server.unixtime;
if (write(server.repl_transfer_fd,buf,nread) != nread) { if ((nwritten = write(server.repl_transfer_fd,buf,nread)) != nread) {
serverLog(LL_WARNING,"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchronization: %s", strerror(errno)); serverLog(LL_WARNING,"Write error or short write writing to the DB dump file needed for MASTER <-> SLAVE synchronization: %s",
(nwritten == -1) ? strerror(errno) : "short write");
goto error; goto error;
} }
server.repl_transfer_read += nread; server.repl_transfer_read += nread;
...@@ -1278,6 +1279,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) { ...@@ -1278,6 +1279,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
close(server.repl_transfer_fd); close(server.repl_transfer_fd);
replicationCreateMasterClient(server.repl_transfer_s,rsi.repl_stream_db); replicationCreateMasterClient(server.repl_transfer_s,rsi.repl_stream_db);
server.repl_state = REPL_STATE_CONNECTED; server.repl_state = REPL_STATE_CONNECTED;
server.repl_down_since = 0;
/* After a full resynchroniziation we use the replication ID and /* After a full resynchroniziation we use the replication ID and
* offset of the master. The secondary ID / offset are cleared since * offset of the master. The secondary ID / offset are cleared since
* we are starting a new history. */ * we are starting a new history. */
...@@ -1314,24 +1316,31 @@ error: ...@@ -1314,24 +1316,31 @@ error:
#define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE) #define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE)
char *sendSynchronousCommand(int flags, int fd, ...) { char *sendSynchronousCommand(int flags, int fd, ...) {
/* Create the command to send to the master, we use simple inline /* Create the command to send to the master, we use redis binary
* protocol for simplicity as currently we only send simple strings. */ * protocol to make sure correct arguments are sent. This function
* is not safe for all binary data. */
if (flags & SYNC_CMD_WRITE) { if (flags & SYNC_CMD_WRITE) {
char *arg; char *arg;
va_list ap; va_list ap;
sds cmd = sdsempty(); sds cmd = sdsempty();
sds cmdargs = sdsempty();
size_t argslen = 0;
va_start(ap,fd); va_start(ap,fd);
while(1) { while(1) {
arg = va_arg(ap, char*); arg = va_arg(ap, char*);
if (arg == NULL) break; if (arg == NULL) break;
if (sdslen(cmd) != 0) cmd = sdscatlen(cmd," ",1); cmdargs = sdscatprintf(cmdargs,"$%zu\r\n%s\r\n",strlen(arg),arg);
cmd = sdscat(cmd,arg); argslen++;
} }
cmd = sdscatlen(cmd,"\r\n",2);
va_end(ap); va_end(ap);
cmd = sdscatprintf(cmd,"*%zu\r\n",argslen);
cmd = sdscatsds(cmd,cmdargs);
sdsfree(cmdargs);
/* Transfer command to the server. */ /* Transfer command to the server. */
if (syncWrite(fd,cmd,sdslen(cmd),server.repl_syncio_timeout*1000) if (syncWrite(fd,cmd,sdslen(cmd),server.repl_syncio_timeout*1000)
== -1) == -1)
...@@ -1388,7 +1397,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) { ...@@ -1388,7 +1397,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
* *
* The function returns: * The function returns:
* *
* PSYNC_CONTINUE: If the PSYNC command succeded and we can continue. * PSYNC_CONTINUE: If the PSYNC command succeeded and we can continue.
* PSYNC_FULLRESYNC: If PSYNC is supported but a full resync is needed. * PSYNC_FULLRESYNC: If PSYNC is supported but a full resync is needed.
* In this case the master run_id and global replication * In this case the master run_id and global replication
* offset is saved. * offset is saved.
...@@ -1942,7 +1951,6 @@ void replicationSetMaster(char *ip, int port) { ...@@ -1942,7 +1951,6 @@ void replicationSetMaster(char *ip, int port) {
* our own parameters, to later PSYNC with the new master. */ * our own parameters, to later PSYNC with the new master. */
if (was_master) replicationCacheMasterUsingMyself(); if (was_master) replicationCacheMasterUsingMyself();
server.repl_state = REPL_STATE_CONNECT; server.repl_state = REPL_STATE_CONNECT;
server.repl_down_since = 0;
} }
/* Cancel replication, setting the instance as a master itself. */ /* Cancel replication, setting the instance as a master itself. */
...@@ -2112,7 +2120,7 @@ void replicationSendAck(void) { ...@@ -2112,7 +2120,7 @@ void replicationSendAck(void) {
* functions. */ * functions. */
/* This function is called by freeClient() in order to cache the master /* This function is called by freeClient() in order to cache the master
* client structure instead of destryoing it. freeClient() will return * client structure instead of destroying it. freeClient() will return
* ASAP after this function returns, so every action needed to avoid problems * ASAP after this function returns, so every action needed to avoid problems
* with a client that is really "suspended" has to be done by this function. * with a client that is really "suspended" has to be done by this function.
* *
...@@ -2140,6 +2148,8 @@ void replicationCacheMaster(client *c) { ...@@ -2140,6 +2148,8 @@ void replicationCacheMaster(client *c) {
server.master->read_reploff = server.master->reploff; server.master->read_reploff = server.master->reploff;
if (c->flags & CLIENT_MULTI) discardTransaction(c); if (c->flags & CLIENT_MULTI) discardTransaction(c);
listEmpty(c->reply); listEmpty(c->reply);
c->sentlen = 0;
c->reply_bytes = 0;
c->bufpos = 0; c->bufpos = 0;
resetClient(c); resetClient(c);
...@@ -2209,6 +2219,7 @@ void replicationResurrectCachedMaster(int newfd) { ...@@ -2209,6 +2219,7 @@ void replicationResurrectCachedMaster(int newfd) {
server.master->authenticated = 1; server.master->authenticated = 1;
server.master->lastinteraction = server.unixtime; server.master->lastinteraction = server.unixtime;
server.repl_state = REPL_STATE_CONNECTED; server.repl_state = REPL_STATE_CONNECTED;
server.repl_down_since = 0;
/* Re-add to the list of clients. */ /* Re-add to the list of clients. */
linkClient(server.master); linkClient(server.master);
......
...@@ -116,7 +116,7 @@ static size_t rioFileWrite(rio *r, const void *buf, size_t len) { ...@@ -116,7 +116,7 @@ static size_t rioFileWrite(rio *r, const void *buf, size_t len) {
r->io.file.buffered >= r->io.file.autosync) r->io.file.buffered >= r->io.file.autosync)
{ {
fflush(r->io.file.fp); fflush(r->io.file.fp);
aof_fsync(fileno(r->io.file.fp)); redis_fsync(fileno(r->io.file.fp));
r->io.file.buffered = 0; r->io.file.buffered = 0;
} }
return retval; return retval;
......
...@@ -575,9 +575,9 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) { ...@@ -575,9 +575,9 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
reply = sdsnewlen(c->buf,c->bufpos); reply = sdsnewlen(c->buf,c->bufpos);
c->bufpos = 0; c->bufpos = 0;
while(listLength(c->reply)) { while(listLength(c->reply)) {
sds o = listNodeValue(listFirst(c->reply)); clientReplyBlock *o = listNodeValue(listFirst(c->reply));
reply = sdscatsds(reply,o); reply = sdscatlen(reply,o->buf,o->used);
listDelNode(c->reply,listFirst(c->reply)); listDelNode(c->reply,listFirst(c->reply));
} }
} }
...@@ -1457,11 +1457,11 @@ void evalShaCommand(client *c) { ...@@ -1457,11 +1457,11 @@ void evalShaCommand(client *c) {
void scriptCommand(client *c) { void scriptCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"debug (yes|sync|no) -- Set the debug mode for subsequent scripts executed.", "DEBUG (yes|sync|no) -- Set the debug mode for subsequent scripts executed.",
"exists <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.", "EXISTS <sha1> [<sha1> ...] -- Return information about the existence of the scripts in the script cache.",
"flush -- Flush the Lua scripts cache. Very dangerous on slaves.", "FLUSH -- Flush the Lua scripts cache. Very dangerous on slaves.",
"kill -- Kill the currently executing Lua script.", "KILL -- Kill the currently executing Lua script.",
"load <script> -- Load a script into the scripts cache, without executing it.", "LOAD <script> -- Load a script into the scripts cache, without executing it.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -1514,7 +1514,7 @@ NULL ...@@ -1514,7 +1514,7 @@ NULL
return; return;
} }
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try SCRIPT HELP", (char*)c->argv[1]->ptr); addReplySubcommandSyntaxError(c);
} }
} }
......
...@@ -67,8 +67,10 @@ static inline char sdsReqType(size_t string_size) { ...@@ -67,8 +67,10 @@ static inline char sdsReqType(size_t string_size) {
#if (LONG_MAX == LLONG_MAX) #if (LONG_MAX == LLONG_MAX)
if (string_size < 1ll<<32) if (string_size < 1ll<<32)
return SDS_TYPE_32; return SDS_TYPE_32;
#endif
return SDS_TYPE_64; return SDS_TYPE_64;
#else
return SDS_TYPE_32;
#endif
} }
/* Create a new sds string with the content specified by the 'init' pointer /* Create a new sds string with the content specified by the 'init' pointer
...@@ -283,7 +285,7 @@ sds sdsRemoveFreeSpace(sds s) { ...@@ -283,7 +285,7 @@ sds sdsRemoveFreeSpace(sds s) {
return s; return s;
} }
/* Return the total size of the allocation of the specifed sds string, /* Return the total size of the allocation of the specified sds string,
* including: * including:
* 1) The sds header before the pointer. * 1) The sds header before the pointer.
* 2) The string. * 2) The string.
......
This diff is collapsed.
...@@ -198,8 +198,8 @@ struct redisCommand redisCommandTable[] = { ...@@ -198,8 +198,8 @@ struct redisCommand redisCommandTable[] = {
{"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0}, {"zrank",zrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0}, {"zrevrank",zrevrankCommand,3,"rF",0,NULL,1,1,1,0,0},
{"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0}, {"zscan",zscanCommand,-3,"rR",0,NULL,1,1,1,0,0},
{"zpopmin",zpopminCommand,-2,"wF",0,NULL,1,-1,1,0,0}, {"zpopmin",zpopminCommand,-2,"wF",0,NULL,1,1,1,0,0},
{"zpopmax",zpopmaxCommand,-2,"wF",0,NULL,1,-1,1,0,0}, {"zpopmax",zpopmaxCommand,-2,"wF",0,NULL,1,1,1,0,0},
{"bzpopmin",bzpopminCommand,-2,"wsF",0,NULL,1,-2,1,0,0}, {"bzpopmin",bzpopminCommand,-2,"wsF",0,NULL,1,-2,1,0,0},
{"bzpopmax",bzpopmaxCommand,-2,"wsF",0,NULL,1,-2,1,0,0}, {"bzpopmax",bzpopmaxCommand,-2,"wsF",0,NULL,1,-2,1,0,0},
{"hset",hsetCommand,-4,"wmF",0,NULL,1,1,1,0,0}, {"hset",hsetCommand,-4,"wmF",0,NULL,1,1,1,0,0},
...@@ -326,6 +326,10 @@ struct redisCommand redisCommandTable[] = { ...@@ -326,6 +326,10 @@ struct redisCommand redisCommandTable[] = {
/*============================ Utility functions ============================ */ /*============================ Utility functions ============================ */
/* We use a private localtime implementation which is fork-safe. The logging
* function of Redis may be called from other threads. */
void nolocks_localtime(struct tm *tmp, time_t t, time_t tz, int dst);
/* Low level logging. To use only for very big messages, otherwise /* Low level logging. To use only for very big messages, otherwise
* serverLog() is to prefer. */ * serverLog() is to prefer. */
void serverLogRaw(int level, const char *msg) { void serverLogRaw(int level, const char *msg) {
...@@ -351,7 +355,9 @@ void serverLogRaw(int level, const char *msg) { ...@@ -351,7 +355,9 @@ void serverLogRaw(int level, const char *msg) {
pid_t pid = getpid(); pid_t pid = getpid();
gettimeofday(&tv,NULL); gettimeofday(&tv,NULL);
off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",localtime(&tv.tv_sec)); struct tm tm;
nolocks_localtime(&tm,tv.tv_sec,server.timezone,server.daylight_active);
off = strftime(buf,sizeof(buf),"%d %b %H:%M:%S.",&tm);
snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000); snprintf(buf+off,sizeof(buf)-off,"%03d",(int)tv.tv_usec/1000);
if (server.sentinel_mode) { if (server.sentinel_mode) {
role_char = 'X'; /* Sentinel. */ role_char = 'X'; /* Sentinel. */
...@@ -845,19 +851,37 @@ int clientsCronResizeQueryBuffer(client *c) { ...@@ -845,19 +851,37 @@ int clientsCronResizeQueryBuffer(client *c) {
/* There are two conditions to resize the query buffer: /* There are two conditions to resize the query buffer:
* 1) Query buffer is > BIG_ARG and too big for latest peak. * 1) Query buffer is > BIG_ARG and too big for latest peak.
* 2) Client is inactive and the buffer is bigger than 1k. */ * 2) Query buffer is > BIG_ARG and client is idle. */
if (((querybuf_size > PROTO_MBULK_BIG_ARG) && if (querybuf_size > PROTO_MBULK_BIG_ARG &&
(querybuf_size/(c->querybuf_peak+1)) > 2) || ((querybuf_size/(c->querybuf_peak+1)) > 2 ||
(querybuf_size > 1024 && idletime > 2)) idletime > 2))
{ {
/* Only resize the query buffer if it is actually wasting space. */ /* Only resize the query buffer if it is actually wasting
if (sdsavail(c->querybuf) > 1024) { * at least a few kbytes. */
if (sdsavail(c->querybuf) > 1024*4) {
c->querybuf = sdsRemoveFreeSpace(c->querybuf); c->querybuf = sdsRemoveFreeSpace(c->querybuf);
} }
} }
/* Reset the peak again to capture the peak memory usage in the next /* Reset the peak again to capture the peak memory usage in the next
* cycle. */ * cycle. */
c->querybuf_peak = 0; c->querybuf_peak = 0;
/* Clients representing masters also use a "pending query buffer" that
* is the yet not applied part of the stream we are reading. Such buffer
* also needs resizing from time to time, otherwise after a very large
* transfer (a huge value or a big MIGRATE operation) it will keep using
* a lot of memory. */
if (c->flags & CLIENT_MASTER) {
/* There are two conditions to resize the pending query buffer:
* 1) Pending Query buffer is > LIMIT_PENDING_QUERYBUF.
* 2) Used length is smaller than pending_querybuf_size/2 */
size_t pending_querybuf_size = sdsAllocSize(c->pending_querybuf);
if(pending_querybuf_size > LIMIT_PENDING_QUERYBUF &&
sdslen(c->pending_querybuf) < (pending_querybuf_size/2))
{
c->pending_querybuf = sdsRemoveFreeSpace(c->pending_querybuf);
}
}
return 0; return 0;
} }
...@@ -959,6 +983,14 @@ void updateCachedTime(void) { ...@@ -959,6 +983,14 @@ void updateCachedTime(void) {
time_t unixtime = time(NULL); time_t unixtime = time(NULL);
atomicSet(server.unixtime,unixtime); atomicSet(server.unixtime,unixtime);
server.mstime = mstime(); server.mstime = mstime();
/* To get information about daylight saving time, we need to call localtime_r
* and cache the result. However calling localtime_r in this context is safe
* since we will never fork() while here, in the main thread. The logging
* function will call a thread safe version of localtime that has no locks. */
struct tm tm;
localtime_r(&server.unixtime,&tm);
server.daylight_active = tm.tm_isdst;
} }
/* This is our timer interrupt, called server.hz times per second. /* This is our timer interrupt, called server.hz times per second.
...@@ -1401,10 +1433,12 @@ void initServerConfig(void) { ...@@ -1401,10 +1433,12 @@ void initServerConfig(void) {
pthread_mutex_init(&server.lruclock_mutex,NULL); pthread_mutex_init(&server.lruclock_mutex,NULL);
pthread_mutex_init(&server.unixtime_mutex,NULL); pthread_mutex_init(&server.unixtime_mutex,NULL);
updateCachedTime();
getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE); getRandomHexChars(server.runid,CONFIG_RUN_ID_SIZE);
server.runid[CONFIG_RUN_ID_SIZE] = '\0'; server.runid[CONFIG_RUN_ID_SIZE] = '\0';
changeReplicationId(); changeReplicationId();
clearReplicationId2(); clearReplicationId2();
server.timezone = timezone; /* Initialized by tzset(). */
server.configfile = NULL; server.configfile = NULL;
server.executable = NULL; server.executable = NULL;
server.hz = CONFIG_DEFAULT_HZ; server.hz = CONFIG_DEFAULT_HZ;
...@@ -1456,6 +1490,7 @@ void initServerConfig(void) { ...@@ -1456,6 +1490,7 @@ void initServerConfig(void) {
server.aof_selected_db = -1; /* Make sure the first time will not match */ server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0; server.aof_flush_postponed_start = 0;
server.aof_rewrite_incremental_fsync = CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC; server.aof_rewrite_incremental_fsync = CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC;
server.rdb_save_incremental_fsync = CONFIG_DEFAULT_RDB_SAVE_INCREMENTAL_FSYNC;
server.aof_load_truncated = CONFIG_DEFAULT_AOF_LOAD_TRUNCATED; server.aof_load_truncated = CONFIG_DEFAULT_AOF_LOAD_TRUNCATED;
server.aof_use_rdb_preamble = CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE; server.aof_use_rdb_preamble = CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE;
server.pidfile = NULL; server.pidfile = NULL;
...@@ -1485,6 +1520,8 @@ void initServerConfig(void) { ...@@ -1485,6 +1520,8 @@ void initServerConfig(void) {
server.zset_max_ziplist_entries = OBJ_ZSET_MAX_ZIPLIST_ENTRIES; server.zset_max_ziplist_entries = OBJ_ZSET_MAX_ZIPLIST_ENTRIES;
server.zset_max_ziplist_value = OBJ_ZSET_MAX_ZIPLIST_VALUE; server.zset_max_ziplist_value = OBJ_ZSET_MAX_ZIPLIST_VALUE;
server.hll_sparse_max_bytes = CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES; server.hll_sparse_max_bytes = CONFIG_DEFAULT_HLL_SPARSE_MAX_BYTES;
server.stream_node_max_bytes = OBJ_STREAM_NODE_MAX_BYTES;
server.stream_node_max_entries = OBJ_STREAM_NODE_MAX_ENTRIES;
server.shutdown_asap = 0; server.shutdown_asap = 0;
server.cluster_enabled = 0; server.cluster_enabled = 0;
server.cluster_node_timeout = CLUSTER_DEFAULT_NODE_TIMEOUT; server.cluster_node_timeout = CLUSTER_DEFAULT_NODE_TIMEOUT;
...@@ -1886,6 +1923,7 @@ void initServer(void) { ...@@ -1886,6 +1923,7 @@ void initServer(void) {
server.pid = getpid(); server.pid = getpid();
server.current_client = NULL; server.current_client = NULL;
server.clients = listCreate(); server.clients = listCreate();
server.clients_index = raxNew();
server.clients_to_close = listCreate(); server.clients_to_close = listCreate();
server.slaves = listCreate(); server.slaves = listCreate();
server.monitors = listCreate(); server.monitors = listCreate();
...@@ -1978,7 +2016,6 @@ void initServer(void) { ...@@ -1978,7 +2016,6 @@ void initServer(void) {
server.aof_last_write_status = C_OK; server.aof_last_write_status = C_OK;
server.aof_last_write_errno = 0; server.aof_last_write_errno = 0;
server.repl_good_slaves_count = 0; server.repl_good_slaves_count = 0;
updateCachedTime();
/* Create the timer callback, this is our way to process many background /* Create the timer callback, this is our way to process many background
* operations incrementally, like clients timeout, eviction of unaccessed * operations incrementally, like clients timeout, eviction of unaccessed
...@@ -2342,7 +2379,7 @@ void call(client *c, int flags) { ...@@ -2342,7 +2379,7 @@ void call(client *c, int flags) {
if (c->flags & CLIENT_FORCE_AOF) propagate_flags |= PROPAGATE_AOF; if (c->flags & CLIENT_FORCE_AOF) propagate_flags |= PROPAGATE_AOF;
/* However prevent AOF / replication propagation if the command /* However prevent AOF / replication propagation if the command
* implementatino called preventCommandPropagation() or similar, * implementations called preventCommandPropagation() or similar,
* or if we don't have the call() flags to do so. */ * or if we don't have the call() flags to do so. */
if (c->flags & CLIENT_PREVENT_REPL_PROP || if (c->flags & CLIENT_PREVENT_REPL_PROP ||
!(flags & CMD_CALL_PROPAGATE_REPL)) !(flags & CMD_CALL_PROPAGATE_REPL))
...@@ -2412,8 +2449,13 @@ int processCommand(client *c) { ...@@ -2412,8 +2449,13 @@ int processCommand(client *c) {
c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr); c->cmd = c->lastcmd = lookupCommand(c->argv[0]->ptr);
if (!c->cmd) { if (!c->cmd) {
flagTransaction(c); flagTransaction(c);
addReplyErrorFormat(c,"unknown command '%s'", sds args = sdsempty();
(char*)c->argv[0]->ptr); int i;
for (i=1; i < c->argc && sdslen(args) < 128; i++)
args = sdscatprintf(args, "`%.*s`, ", 128-(int)sdslen(args), (char*)c->argv[i]->ptr);
addReplyErrorFormat(c,"unknown command `%s`, with args beginning with: %s",
(char*)c->argv[0]->ptr, args);
sdsfree(args);
return C_OK; return C_OK;
} else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) || } else if ((c->cmd->arity > 0 && c->cmd->arity != c->argc) ||
(c->argc < -c->cmd->arity)) { (c->argc < -c->cmd->arity)) {
...@@ -2482,7 +2524,8 @@ int processCommand(client *c) { ...@@ -2482,7 +2524,8 @@ int processCommand(client *c) {
if (((server.stop_writes_on_bgsave_err && if (((server.stop_writes_on_bgsave_err &&
server.saveparamslen > 0 && server.saveparamslen > 0 &&
server.lastbgsave_status == C_ERR) || server.lastbgsave_status == C_ERR) ||
server.aof_last_write_status == C_ERR) && (server.aof_state != AOF_OFF &&
server.aof_last_write_status == C_ERR)) &&
server.masterhost == NULL && server.masterhost == NULL &&
(c->cmd->flags & CMD_WRITE || (c->cmd->flags & CMD_WRITE ||
c->cmd->proc == pingCommand)) c->cmd->proc == pingCommand))
...@@ -2635,7 +2678,7 @@ int prepareForShutdown(int flags) { ...@@ -2635,7 +2678,7 @@ int prepareForShutdown(int flags) {
/* Append only file: flush buffers and fsync() the AOF at exit */ /* Append only file: flush buffers and fsync() the AOF at exit */
serverLog(LL_NOTICE,"Calling fsync() on the AOF file."); serverLog(LL_NOTICE,"Calling fsync() on the AOF file.");
flushAppendOnlyFile(1); flushAppendOnlyFile(1);
aof_fsync(server.aof_fd); redis_fsync(server.aof_fd);
} }
/* Create a new RDB file before exiting. */ /* Create a new RDB file before exiting. */
...@@ -2824,9 +2867,9 @@ void commandCommand(client *c) { ...@@ -2824,9 +2867,9 @@ void commandCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"(no subcommand) -- Return details about all Redis commands.", "(no subcommand) -- Return details about all Redis commands.",
"count -- Return the total number of commands in this Redis server.", "COUNT -- Return the total number of commands in this Redis server.",
"getkeys <full-command> -- Return the keys from a full Redis command.", "GETKEYS <full-command> -- Return the keys from a full Redis command.",
"info [command-name ...] -- Return details about multiple Redis commands.", "INFO [command-name ...] -- Return details about multiple Redis commands.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -2850,7 +2893,10 @@ NULL ...@@ -2850,7 +2893,10 @@ NULL
int *keys, numkeys, j; int *keys, numkeys, j;
if (!cmd) { if (!cmd) {
addReplyErrorFormat(c,"Invalid command specified"); addReplyError(c,"Invalid command specified");
return;
} else if (cmd->getkeys_proc == NULL && cmd->firstkey == 0) {
addReplyError(c,"The command has no key arguments");
return; return;
} else if ((cmd->arity > 0 && cmd->arity != c->argc-2) || } else if ((cmd->arity > 0 && cmd->arity != c->argc-2) ||
((c->argc-2) < -cmd->arity)) ((c->argc-2) < -cmd->arity))
...@@ -2860,11 +2906,15 @@ NULL ...@@ -2860,11 +2906,15 @@ NULL
} }
keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys); keys = getKeysFromCommand(cmd,c->argv+2,c->argc-2,&numkeys);
if (!keys) {
addReplyError(c,"Invalid arguments specified for command");
} else {
addReplyMultiBulkLen(c,numkeys); addReplyMultiBulkLen(c,numkeys);
for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]); for (j = 0; j < numkeys; j++) addReplyBulk(c,c->argv[keys[j]+2]);
getKeysFreeResult(keys); getKeysFreeResult(keys);
}
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try COMMAND HELP", (char*)c->argv[1]->ptr); addReplySubcommandSyntaxError(c);
} }
} }
...@@ -2876,7 +2926,6 @@ void bytesToHuman(char *s, unsigned long long n) { ...@@ -2876,7 +2926,6 @@ void bytesToHuman(char *s, unsigned long long n) {
if (n < 1024) { if (n < 1024) {
/* Bytes */ /* Bytes */
sprintf(s,"%lluB",n); sprintf(s,"%lluB",n);
return;
} else if (n < (1024*1024)) { } else if (n < (1024*1024)) {
d = (double)n/(1024); d = (double)n/(1024);
sprintf(s,"%.2fK",d); sprintf(s,"%.2fK",d);
...@@ -2906,7 +2955,6 @@ sds genRedisInfoString(char *section) { ...@@ -2906,7 +2955,6 @@ sds genRedisInfoString(char *section) {
time_t uptime = server.unixtime-server.stat_starttime; time_t uptime = server.unixtime-server.stat_starttime;
int j; int j;
struct rusage self_ru, c_ru; struct rusage self_ru, c_ru;
unsigned long lol, bib;
int allsections = 0, defsections = 0; int allsections = 0, defsections = 0;
int sections = 0; int sections = 0;
...@@ -2916,7 +2964,6 @@ sds genRedisInfoString(char *section) { ...@@ -2916,7 +2964,6 @@ sds genRedisInfoString(char *section) {
getrusage(RUSAGE_SELF, &self_ru); getrusage(RUSAGE_SELF, &self_ru);
getrusage(RUSAGE_CHILDREN, &c_ru); getrusage(RUSAGE_CHILDREN, &c_ru);
getClientsMaxBuffers(&lol,&bib);
/* Server */ /* Server */
if (allsections || defsections || !strcasecmp(section,"server")) { if (allsections || defsections || !strcasecmp(section,"server")) {
...@@ -2986,6 +3033,8 @@ sds genRedisInfoString(char *section) { ...@@ -2986,6 +3033,8 @@ sds genRedisInfoString(char *section) {
/* Clients */ /* Clients */
if (allsections || defsections || !strcasecmp(section,"clients")) { if (allsections || defsections || !strcasecmp(section,"clients")) {
unsigned long lol, bib;
getClientsMaxBuffers(&lol,&bib);
if (sections++) info = sdscat(info,"\r\n"); if (sections++) info = sdscat(info,"\r\n");
info = sdscatprintf(info, info = sdscatprintf(info,
"# Clients\r\n" "# Clients\r\n"
...@@ -3058,6 +3107,11 @@ sds genRedisInfoString(char *section) { ...@@ -3058,6 +3107,11 @@ sds genRedisInfoString(char *section) {
"rss_overhead_bytes:%zu\r\n" "rss_overhead_bytes:%zu\r\n"
"mem_fragmentation_ratio:%.2f\r\n" "mem_fragmentation_ratio:%.2f\r\n"
"mem_fragmentation_bytes:%zu\r\n" "mem_fragmentation_bytes:%zu\r\n"
"mem_not_counted_for_evict:%zu\r\n"
"mem_replication_backlog:%zu\r\n"
"mem_clients_slaves:%zu\r\n"
"mem_clients_normal:%zu\r\n"
"mem_aof_buffer:%zu\r\n"
"mem_allocator:%s\r\n" "mem_allocator:%s\r\n"
"active_defrag_running:%d\r\n" "active_defrag_running:%d\r\n"
"lazyfree_pending_objects:%zu\r\n", "lazyfree_pending_objects:%zu\r\n",
...@@ -3090,6 +3144,11 @@ sds genRedisInfoString(char *section) { ...@@ -3090,6 +3144,11 @@ sds genRedisInfoString(char *section) {
mh->rss_extra_bytes, mh->rss_extra_bytes,
mh->total_frag, /* this is the total RSS overhead, including fragmentation, */ mh->total_frag, /* this is the total RSS overhead, including fragmentation, */
mh->total_frag_bytes, /* named so for backwards compatibility */ mh->total_frag_bytes, /* named so for backwards compatibility */
freeMemoryGetNotCountedMemory(),
mh->repl_backlog,
mh->clients_slaves,
mh->clients_normal,
mh->aof_buffer,
ZMALLOC_LIB, ZMALLOC_LIB,
server.active_defrag_running, server.active_defrag_running,
lazyfreeGetPendingObjectsCount() lazyfreeGetPendingObjectsCount()
...@@ -3832,9 +3891,11 @@ int main(int argc, char **argv) { ...@@ -3832,9 +3891,11 @@ int main(int argc, char **argv) {
spt_init(argc, argv); spt_init(argc, argv);
#endif #endif
setlocale(LC_COLLATE,""); setlocale(LC_COLLATE,"");
tzset(); /* Populates 'timezone' global. */
zmalloc_set_oom_handler(redisOutOfMemoryHandler); zmalloc_set_oom_handler(redisOutOfMemoryHandler);
srand(time(NULL)^getpid()); srand(time(NULL)^getpid());
gettimeofday(&tv,NULL); gettimeofday(&tv,NULL);
char hashseed[16]; char hashseed[16];
getRandomHexChars(hashseed,sizeof(hashseed)); getRandomHexChars(hashseed,sizeof(hashseed));
dictSetHashFunctionSeed((uint8_t*)hashseed); dictSetHashFunctionSeed((uint8_t*)hashseed);
...@@ -3891,7 +3952,7 @@ int main(int argc, char **argv) { ...@@ -3891,7 +3952,7 @@ int main(int argc, char **argv) {
configfile = argv[j]; configfile = argv[j];
server.configfile = getAbsolutePath(configfile); server.configfile = getAbsolutePath(configfile);
/* Replace the config file in server.exec_argv with /* Replace the config file in server.exec_argv with
* its absoulte path. */ * its absolute path. */
zfree(server.exec_argv[j]); zfree(server.exec_argv[j]);
server.exec_argv[j] = zstrdup(server.configfile); server.exec_argv[j] = zstrdup(server.configfile);
j++; j++;
......
...@@ -142,6 +142,7 @@ typedef long long mstime_t; /* millisecond time type. */ ...@@ -142,6 +142,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE 1 #define CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE 1
#define CONFIG_DEFAULT_ACTIVE_REHASHING 1 #define CONFIG_DEFAULT_ACTIVE_REHASHING 1
#define CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1 #define CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC 1
#define CONFIG_DEFAULT_RDB_SAVE_INCREMENTAL_FSYNC 1
#define CONFIG_DEFAULT_MIN_SLAVES_TO_WRITE 0 #define CONFIG_DEFAULT_MIN_SLAVES_TO_WRITE 0
#define CONFIG_DEFAULT_MIN_SLAVES_MAX_LAG 10 #define CONFIG_DEFAULT_MIN_SLAVES_MAX_LAG 10
#define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */ #define NET_IP_STR_LEN 46 /* INET6_ADDRSTRLEN is 46, but we need to be sure */
...@@ -183,7 +184,9 @@ typedef long long mstime_t; /* millisecond time type. */ ...@@ -183,7 +184,9 @@ typedef long long mstime_t; /* millisecond time type. */
#define PROTO_INLINE_MAX_SIZE (1024*64) /* Max size of inline reads */ #define PROTO_INLINE_MAX_SIZE (1024*64) /* Max size of inline reads */
#define PROTO_MBULK_BIG_ARG (1024*32) #define PROTO_MBULK_BIG_ARG (1024*32)
#define LONG_STR_SIZE 21 /* Bytes needed for long -> str + '\0' */ #define LONG_STR_SIZE 21 /* Bytes needed for long -> str + '\0' */
#define AOF_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */ #define REDIS_AUTOSYNC_BYTES (1024*1024*32) /* fdatasync every 32MB */
#define LIMIT_PENDING_QUERYBUF (4*1024*1024) /* 4mb */
/* When configuring the server eventloop, we setup it so that the total number /* When configuring the server eventloop, we setup it so that the total number
* of file descriptors we can handle are server.maxclients + RESERVED_FDS + * of file descriptors we can handle are server.maxclients + RESERVED_FDS +
...@@ -339,7 +342,7 @@ typedef long long mstime_t; /* millisecond time type. */ ...@@ -339,7 +342,7 @@ typedef long long mstime_t; /* millisecond time type. */
/* Anti-warning macro... */ /* Anti-warning macro... */
#define UNUSED(V) ((void) V) #define UNUSED(V) ((void) V)
#define ZSKIPLIST_MAXLEVEL 32 /* Should be enough for 2^32 elements */ #define ZSKIPLIST_MAXLEVEL 64 /* Should be enough for 2^64 elements */
#define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */ #define ZSKIPLIST_P 0.25 /* Skiplist P = 1/4 */
/* Append only defines */ /* Append only defines */
...@@ -348,12 +351,14 @@ typedef long long mstime_t; /* millisecond time type. */ ...@@ -348,12 +351,14 @@ typedef long long mstime_t; /* millisecond time type. */
#define AOF_FSYNC_EVERYSEC 2 #define AOF_FSYNC_EVERYSEC 2
#define CONFIG_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC #define CONFIG_DEFAULT_AOF_FSYNC AOF_FSYNC_EVERYSEC
/* Zip structure related defaults */ /* Zipped structures related defaults */
#define OBJ_HASH_MAX_ZIPLIST_ENTRIES 512 #define OBJ_HASH_MAX_ZIPLIST_ENTRIES 512
#define OBJ_HASH_MAX_ZIPLIST_VALUE 64 #define OBJ_HASH_MAX_ZIPLIST_VALUE 64
#define OBJ_SET_MAX_INTSET_ENTRIES 512 #define OBJ_SET_MAX_INTSET_ENTRIES 512
#define OBJ_ZSET_MAX_ZIPLIST_ENTRIES 128 #define OBJ_ZSET_MAX_ZIPLIST_ENTRIES 128
#define OBJ_ZSET_MAX_ZIPLIST_VALUE 64 #define OBJ_ZSET_MAX_ZIPLIST_VALUE 64
#define OBJ_STREAM_NODE_MAX_BYTES 4096
#define OBJ_STREAM_NODE_MAX_ENTRIES 100
/* List defaults */ /* List defaults */
#define OBJ_LIST_MAX_ZIPLIST_SIZE -2 #define OBJ_LIST_MAX_ZIPLIST_SIZE -2
...@@ -614,6 +619,13 @@ typedef struct redisObject { ...@@ -614,6 +619,13 @@ typedef struct redisObject {
struct evictionPoolEntry; /* Defined in evict.c */ struct evictionPoolEntry; /* Defined in evict.c */
/* This structure is used in order to represent the output buffer of a client,
* which is actually a linked list of blocks like that, that is: client->reply. */
typedef struct clientReplyBlock {
size_t size, used;
char buf[];
} clientReplyBlock;
/* Redis database representation. There are multiple databases identified /* Redis database representation. There are multiple databases identified
* by integers from 0 (the default database) up to the max configured * by integers from 0 (the default database) up to the max configured
* database. The database number is the 'id' field in the structure. */ * database. The database number is the 'id' field in the structure. */
...@@ -660,6 +672,7 @@ typedef struct blockingState { ...@@ -660,6 +672,7 @@ typedef struct blockingState {
robj *xread_group; /* XREADGROUP group name. */ robj *xread_group; /* XREADGROUP group name. */
robj *xread_consumer; /* XREADGROUP consumer name. */ robj *xread_consumer; /* XREADGROUP consumer name. */
mstime_t xread_retry_time, xread_retry_ttl; mstime_t xread_retry_time, xread_retry_ttl;
int xread_group_noack;
/* BLOCKED_WAIT */ /* BLOCKED_WAIT */
int numreplicas; /* Number of replicas we are waiting for ACK. */ int numreplicas; /* Number of replicas we are waiting for ACK. */
...@@ -695,9 +708,10 @@ typedef struct client { ...@@ -695,9 +708,10 @@ typedef struct client {
redisDb *db; /* Pointer to currently SELECTed DB. */ redisDb *db; /* Pointer to currently SELECTed DB. */
robj *name; /* As set by CLIENT SETNAME. */ robj *name; /* As set by CLIENT SETNAME. */
sds querybuf; /* Buffer we use to accumulate client queries. */ sds querybuf; /* Buffer we use to accumulate client queries. */
sds pending_querybuf; /* If this is a master, this buffer represents the sds pending_querybuf; /* If this client is flagged as master, this buffer
yet not applied replication stream that we represents the yet not applied portion of the
are receiving from the master. */ replication stream that we are receiving from
the master. */
size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */ size_t querybuf_peak; /* Recent (100ms or more) peak of querybuf size. */
int argc; /* Num of arguments of current command. */ int argc; /* Num of arguments of current command. */
robj **argv; /* Arguments of current command. */ robj **argv; /* Arguments of current command. */
...@@ -780,7 +794,7 @@ typedef struct zskiplistNode { ...@@ -780,7 +794,7 @@ typedef struct zskiplistNode {
struct zskiplistNode *backward; struct zskiplistNode *backward;
struct zskiplistLevel { struct zskiplistLevel {
struct zskiplistNode *forward; struct zskiplistNode *forward;
unsigned int span; unsigned long span;
} level[]; } level[];
} zskiplistNode; } zskiplistNode;
...@@ -879,13 +893,13 @@ typedef struct rdbSaveInfo { ...@@ -879,13 +893,13 @@ typedef struct rdbSaveInfo {
#define RDB_SAVE_INFO_INIT {-1,0,"000000000000000000000000000000",-1} #define RDB_SAVE_INFO_INIT {-1,0,"000000000000000000000000000000",-1}
typedef struct malloc_stats { struct malloc_stats {
size_t zmalloc_used; size_t zmalloc_used;
size_t process_rss; size_t process_rss;
size_t allocator_allocated; size_t allocator_allocated;
size_t allocator_active; size_t allocator_active;
size_t allocator_resident; size_t allocator_resident;
} malloc_stats; };
/*----------------------------------------------------------------------------- /*-----------------------------------------------------------------------------
* Global server state * Global server state
...@@ -949,6 +963,7 @@ struct redisServer { ...@@ -949,6 +963,7 @@ struct redisServer {
list *clients_pending_write; /* There is to write or install handler. */ list *clients_pending_write; /* There is to write or install handler. */
list *slaves, *monitors; /* List of slaves and MONITORs */ list *slaves, *monitors; /* List of slaves and MONITORs */
client *current_client; /* Current client, only used on crash report */ client *current_client; /* Current client, only used on crash report */
rax *clients_index; /* Active clients dictionary by client ID. */
int clients_paused; /* True if clients are currently paused */ int clients_paused; /* True if clients are currently paused */
mstime_t clients_pause_end_time; /* Time when we undo clients_paused */ mstime_t clients_pause_end_time; /* Time when we undo clients_paused */
char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */ char neterr[ANET_ERR_LEN]; /* Error buffer for anet.c */
...@@ -992,7 +1007,7 @@ struct redisServer { ...@@ -992,7 +1007,7 @@ struct redisServer {
long long slowlog_entry_id; /* SLOWLOG current entry ID */ long long slowlog_entry_id; /* SLOWLOG current entry ID */
long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */ long long slowlog_log_slower_than; /* SLOWLOG time limit (to get logged) */
unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */ unsigned long slowlog_max_len; /* SLOWLOG max number of items logged */
malloc_stats cron_malloc_stats; /* sampled in serverCron(). */ struct malloc_stats cron_malloc_stats; /* sampled in serverCron(). */
long long stat_net_input_bytes; /* Bytes read from network. */ long long stat_net_input_bytes; /* Bytes read from network. */
long long stat_net_output_bytes; /* Bytes written to network. */ long long stat_net_output_bytes; /* Bytes written to network. */
size_t stat_rdb_cow_bytes; /* Copy on write bytes during RDB saving. */ size_t stat_rdb_cow_bytes; /* Copy on write bytes during RDB saving. */
...@@ -1044,7 +1059,8 @@ struct redisServer { ...@@ -1044,7 +1059,8 @@ struct redisServer {
time_t aof_rewrite_time_start; /* Current AOF rewrite start time. */ time_t aof_rewrite_time_start; /* Current AOF rewrite start time. */
int aof_lastbgrewrite_status; /* C_OK or C_ERR */ int aof_lastbgrewrite_status; /* C_OK or C_ERR */
unsigned long aof_delayed_fsync; /* delayed AOF fsync() counter */ unsigned long aof_delayed_fsync; /* delayed AOF fsync() counter */
int aof_rewrite_incremental_fsync;/* fsync incrementally while rewriting? */ int aof_rewrite_incremental_fsync;/* fsync incrementally while aof rewriting? */
int rdb_save_incremental_fsync; /* fsync incrementally while rdb saving? */
int aof_last_write_status; /* C_OK or C_ERR */ int aof_last_write_status; /* C_OK or C_ERR */
int aof_last_write_errno; /* Valid if aof_last_write_status is ERR */ int aof_last_write_errno; /* Valid if aof_last_write_status is ERR */
int aof_load_truncated; /* Don't stop on unexpected AOF EOF. */ int aof_load_truncated; /* Don't stop on unexpected AOF EOF. */
...@@ -1177,11 +1193,15 @@ struct redisServer { ...@@ -1177,11 +1193,15 @@ struct redisServer {
size_t zset_max_ziplist_entries; size_t zset_max_ziplist_entries;
size_t zset_max_ziplist_value; size_t zset_max_ziplist_value;
size_t hll_sparse_max_bytes; size_t hll_sparse_max_bytes;
size_t stream_node_max_bytes;
int64_t stream_node_max_entries;
/* List parameters */ /* List parameters */
int list_max_ziplist_size; int list_max_ziplist_size;
int list_compress_depth; int list_compress_depth;
/* time cache */ /* time cache */
time_t unixtime; /* Unix time sampled every cron cycle. */ time_t unixtime; /* Unix time sampled every cron cycle. */
time_t timezone; /* Cached timezone. As set by tzset(). */
int daylight_active; /* Currently in daylight saving time. */
long long mstime; /* Like 'unixtime' but with milliseconds resolution. */ long long mstime; /* Like 'unixtime' but with milliseconds resolution. */
/* Pubsub */ /* Pubsub */
dict *pubsub_channels; /* Map channels to list of subscribed clients */ dict *pubsub_channels; /* Map channels to list of subscribed clients */
...@@ -1406,15 +1426,17 @@ void addReplyHumanLongDouble(client *c, long double d); ...@@ -1406,15 +1426,17 @@ void addReplyHumanLongDouble(client *c, long double d);
void addReplyLongLong(client *c, long long ll); void addReplyLongLong(client *c, long long ll);
void addReplyMultiBulkLen(client *c, long length); void addReplyMultiBulkLen(client *c, long length);
void addReplyHelp(client *c, const char **help); void addReplyHelp(client *c, const char **help);
void addReplySubcommandSyntaxError(client *c);
void copyClientOutputBuffer(client *dst, client *src); void copyClientOutputBuffer(client *dst, client *src);
size_t sdsZmallocSize(sds s); size_t sdsZmallocSize(sds s);
size_t getStringObjectSdsUsedMemory(robj *o); size_t getStringObjectSdsUsedMemory(robj *o);
void freeClientReplyValue(void *o);
void *dupClientReplyValue(void *o); void *dupClientReplyValue(void *o);
void getClientsMaxBuffers(unsigned long *longest_output_list, void getClientsMaxBuffers(unsigned long *longest_output_list,
unsigned long *biggest_input_buffer); unsigned long *biggest_input_buffer);
char *getClientPeerId(client *client); char *getClientPeerId(client *client);
sds catClientInfoString(sds s, client *client); sds catClientInfoString(sds s, client *client);
sds getAllClientsInfoString(void); sds getAllClientsInfoString(int type);
void rewriteClientCommandVector(client *c, int argc, ...); void rewriteClientCommandVector(client *c, int argc, ...);
void rewriteClientCommandArgument(client *c, int i, robj *newval); void rewriteClientCommandArgument(client *c, int i, robj *newval);
void replaceClientCommandVector(client *c, int argc, robj **argv); void replaceClientCommandVector(client *c, int argc, robj **argv);
...@@ -1495,6 +1517,7 @@ robj *tryObjectEncoding(robj *o); ...@@ -1495,6 +1517,7 @@ robj *tryObjectEncoding(robj *o);
robj *getDecodedObject(robj *o); robj *getDecodedObject(robj *o);
size_t stringObjectLen(robj *o); size_t stringObjectLen(robj *o);
robj *createStringObjectFromLongLong(long long value); robj *createStringObjectFromLongLong(long long value);
robj *createStringObjectFromLongLongForValue(long long value);
robj *createStringObjectFromLongDouble(long double value, int humanfriendly); robj *createStringObjectFromLongDouble(long double value, int humanfriendly);
robj *createQuicklistObject(void); robj *createQuicklistObject(void);
robj *createZiplistObject(void); robj *createZiplistObject(void);
...@@ -1589,11 +1612,11 @@ void receiveChildInfo(void); ...@@ -1589,11 +1612,11 @@ void receiveChildInfo(void);
#define ZADD_NONE 0 #define ZADD_NONE 0
#define ZADD_INCR (1<<0) /* Increment the score instead of setting it. */ #define ZADD_INCR (1<<0) /* Increment the score instead of setting it. */
#define ZADD_NX (1<<1) /* Don't touch elements not already existing. */ #define ZADD_NX (1<<1) /* Don't touch elements not already existing. */
#define ZADD_XX (1<<2) /* Only touch elements already exisitng. */ #define ZADD_XX (1<<2) /* Only touch elements already existing. */
/* Output flags. */ /* Output flags. */
#define ZADD_NOP (1<<3) /* Operation not performed because of conditionals.*/ #define ZADD_NOP (1<<3) /* Operation not performed because of conditionals.*/
#define ZADD_NAN (1<<4) /* Only touch elements already exisitng. */ #define ZADD_NAN (1<<4) /* Only touch elements already existing. */
#define ZADD_ADDED (1<<5) /* The element was new and was added. */ #define ZADD_ADDED (1<<5) /* The element was new and was added. */
#define ZADD_UPDATED (1<<6) /* The element already existed, score updated. */ #define ZADD_UPDATED (1<<6) /* The element already existed, score updated. */
...@@ -1624,7 +1647,7 @@ void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr); ...@@ -1624,7 +1647,7 @@ void zzlNext(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr); void zzlPrev(unsigned char *zl, unsigned char **eptr, unsigned char **sptr);
unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range); unsigned char *zzlFirstInRange(unsigned char *zl, zrangespec *range);
unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range); unsigned char *zzlLastInRange(unsigned char *zl, zrangespec *range);
unsigned int zsetLength(const robj *zobj); unsigned long zsetLength(const robj *zobj);
void zsetConvert(robj *zobj, int encoding); void zsetConvert(robj *zobj, int encoding);
void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen); void zsetConvertToZiplistIfNeeded(robj *zobj, size_t maxelelen);
int zsetScore(robj *zobj, sds member, double *score); int zsetScore(robj *zobj, sds member, double *score);
...@@ -1649,6 +1672,7 @@ int zslLexValueLteMax(sds value, zlexrangespec *spec); ...@@ -1649,6 +1672,7 @@ int zslLexValueLteMax(sds value, zlexrangespec *spec);
/* Core functions */ /* Core functions */
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level); int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);
size_t freeMemoryGetNotCountedMemory();
int freeMemoryIfNeeded(void); int freeMemoryIfNeeded(void);
int processCommand(client *c); int processCommand(client *c);
void setupSignalHandlers(void); void setupSignalHandlers(void);
...@@ -1765,6 +1789,8 @@ robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply); ...@@ -1765,6 +1789,8 @@ robj *lookupKeyWriteOrReply(client *c, robj *key, robj *reply);
robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags); robj *lookupKeyReadWithFlags(redisDb *db, robj *key, int flags);
robj *objectCommandLookup(client *c, robj *key); robj *objectCommandLookup(client *c, robj *key);
robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply); robj *objectCommandLookupOrReply(client *c, robj *key, robj *reply);
void objectSetLRUOrLFU(robj *val, long long lfu_freq, long long lru_idle,
long long lru_clock);
#define LOOKUP_NONE 0 #define LOOKUP_NONE 0
#define LOOKUP_NOTOUCH (1<<0) #define LOOKUP_NOTOUCH (1<<0)
void dbAdd(redisDb *db, robj *key, robj *val); void dbAdd(redisDb *db, robj *key, robj *val);
......
...@@ -142,12 +142,12 @@ uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) { ...@@ -142,12 +142,12 @@ uint64_t siphash(const uint8_t *in, const size_t inlen, const uint8_t *k) {
} }
switch (left) { switch (left) {
case 7: b |= ((uint64_t)in[6]) << 48; case 7: b |= ((uint64_t)in[6]) << 48; /* fall-thru */
case 6: b |= ((uint64_t)in[5]) << 40; case 6: b |= ((uint64_t)in[5]) << 40; /* fall-thru */
case 5: b |= ((uint64_t)in[4]) << 32; case 5: b |= ((uint64_t)in[4]) << 32; /* fall-thru */
case 4: b |= ((uint64_t)in[3]) << 24; case 4: b |= ((uint64_t)in[3]) << 24; /* fall-thru */
case 3: b |= ((uint64_t)in[2]) << 16; case 3: b |= ((uint64_t)in[2]) << 16; /* fall-thru */
case 2: b |= ((uint64_t)in[1]) << 8; case 2: b |= ((uint64_t)in[1]) << 8; /* fall-thru */
case 1: b |= ((uint64_t)in[0]); break; case 1: b |= ((uint64_t)in[0]); break;
case 0: break; case 0: break;
} }
...@@ -202,12 +202,12 @@ uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k) ...@@ -202,12 +202,12 @@ uint64_t siphash_nocase(const uint8_t *in, const size_t inlen, const uint8_t *k)
} }
switch (left) { switch (left) {
case 7: b |= ((uint64_t)siptlw(in[6])) << 48; case 7: b |= ((uint64_t)siptlw(in[6])) << 48; /* fall-thru */
case 6: b |= ((uint64_t)siptlw(in[5])) << 40; case 6: b |= ((uint64_t)siptlw(in[5])) << 40; /* fall-thru */
case 5: b |= ((uint64_t)siptlw(in[4])) << 32; case 5: b |= ((uint64_t)siptlw(in[4])) << 32; /* fall-thru */
case 4: b |= ((uint64_t)siptlw(in[3])) << 24; case 4: b |= ((uint64_t)siptlw(in[3])) << 24; /* fall-thru */
case 3: b |= ((uint64_t)siptlw(in[2])) << 16; case 3: b |= ((uint64_t)siptlw(in[2])) << 16; /* fall-thru */
case 2: b |= ((uint64_t)siptlw(in[1])) << 8; case 2: b |= ((uint64_t)siptlw(in[1])) << 8; /* fall-thru */
case 1: b |= ((uint64_t)siptlw(in[0])); break; case 1: b |= ((uint64_t)siptlw(in[0])); break;
case 0: break; case 0: break;
} }
......
...@@ -142,11 +142,11 @@ void slowlogReset(void) { ...@@ -142,11 +142,11 @@ void slowlogReset(void) {
void slowlogCommand(client *c) { void slowlogCommand(client *c) {
if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) { if (c->argc == 2 && !strcasecmp(c->argv[1]->ptr,"help")) {
const char *help[] = { const char *help[] = {
"get [count] -- Return top entries from the slowlog (default: 10)." "GET [count] -- Return top entries from the slowlog (default: 10)."
" Entries are made of:", " Entries are made of:",
" id, timestamp, time in microseconds, arguments array, client IP and port, client name", " id, timestamp, time in microseconds, arguments array, client IP and port, client name",
"len -- Return the length of the slowlog.", "LEN -- Return the length of the slowlog.",
"reset -- Reset the slowlog.", "RESET -- Reset the slowlog.",
NULL NULL
}; };
addReplyHelp(c, help); addReplyHelp(c, help);
...@@ -187,6 +187,6 @@ NULL ...@@ -187,6 +187,6 @@ NULL
} }
setDeferredMultiBulkLength(c,totentries,sent); setDeferredMultiBulkLength(c,totentries,sent);
} else { } else {
addReplyErrorFormat(c, "Unknown subcommand or wrong number of arguments for '%s'. Try SLOWLOG HELP", (char*)c->argv[1]->ptr); addReplySubcommandSyntaxError(c);
} }
} }
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment